From 7d15b919681bb9ec0088b4b27c6abf62d6dfb9b1 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Sat, 25 Jul 2009 01:56:15 -0600 Subject: [PATCH] big batch of progress more complete build system xml scene file converted to json started man page extracted png icon from the icns new ebuild file in the extra stuff a desktop file included many new classes, notably mippleton, interpolator many code changes to existing classes --- COPYING | 682 ++++++++++ Makefile.am | 14 +- configure.ac | 195 ++- data/Makefile.am | 9 + .../animations}/AlienWarrior.json | 0 .../animations}/BigExplosion.json | 0 .../animations}/Bonuses.json | 0 .../animations}/Bullets.json | 0 .../animations}/Effects.json | 0 .../animations}/Heroine.json | 0 .../animation => data/animations}/Jetbot.json | 0 .../animations}/RobotTrooper.json | 0 data/scenes/Test.json | 1118 +++++++++++++++++ {share/scene => data/scenes}/Test.xml | 0 .../textures}/AlienWarrior.png | Bin .../textures}/BackgroundFar.png | Bin .../textures}/BackgroundNear.png | Bin .../textures}/BigExplosion.png | Bin {share/texture => data/textures}/Bonuses.png | Bin {share/texture => data/textures}/Building.png | Bin {share/texture => data/textures}/Font.png | Bin {share/texture => data/textures}/Heroine.png | Bin {share/texture => data/textures}/Jetbot.png | Bin .../texture => data/textures}/Particles.png | Bin .../textures}/RobotTrooper.png | Bin {share/texture => data/textures}/Scenery.png | Bin .../texture => data/textures}/StatusBars.png | Bin .../texture => data/textures}/TowerBlock1.png | Bin {share/texture => data/textures}/Trees.png | Bin .../tilemaps/AlienWarrior.json | 1 - .../tilemaps/BackgroundFar.json | 5 +- .../tilemaps/BackgroundNear.json | 5 +- .../tilemaps/BigExplosion.json | 5 +- .../tilemaps/Bonuses.json | 5 +- .../tilemaps/Building.json | 5 +- .../Font.data => data/tilemaps/Font.json | 1 - .../tilemaps/Heroine.json | 1 - .../Jetbot.data => data/tilemaps/Jetbot.json | 1 - .../tilemaps/Particles.json | 5 +- .../tilemaps/RobotTrooper.json | 1 - .../tilemaps/Scenery.json | 5 +- .../tilemaps/StatusBars.json | 1 - .../tilemaps/TowerBlock1.json | 5 +- .../Trees.data => data/tilemaps/Trees.json | 5 +- data/yoink.desktop | 10 + data/yoink.png | Bin 0 -> 8864 bytes {src => data}/yoinkrc | 8 +- doc/Makefile.am | 3 + doc/screenshot.jpg | Bin 0 -> 51189 bytes doc/yoink.6.in | 236 ++++ extra/yoink.ebuild | 53 + m4/boost.m4 | 920 ++++++++++++++ m4/sdl.m4 | 175 +++ share/character/Heroine.json | 4 - share/texture/AlienWarrior.plist | 16 - share/texture/BackgroundFar.plist | 21 - share/texture/BackgroundNear.plist | 21 - share/texture/BigExplosion.plist | 25 - share/texture/Bonuses.plist | 25 - share/texture/Building.plist | 21 - share/texture/Default.plist | 16 - share/texture/Font.plist | 16 - share/texture/Heroine.plist | 16 - share/texture/Jetbot.plist | 16 - share/texture/Particles.plist | 25 - share/texture/RobotTrooper.plist | 16 - share/texture/Scenery.plist | 25 - share/texture/StatusBars.plist | 16 - share/texture/TowerBlock1.plist | 25 - share/texture/Trees.plist | 25 - src/Character.cc | 57 + src/Character.hh | 21 +- src/Makefile.am | 40 +- src/TilemapFont.cc | 61 + src/TilemapFont.hh | 59 + src/Typesetter.cc | 51 + src/Typesetter.hh | 56 + src/YoinkApp.cc | 290 +++-- src/YoinkApp.hh | 43 +- src/aabb.hh | 65 + src/animation.cc | 312 ++++- src/animation.hh | 25 +- src/camera.hh | 48 + src/{rectangle.cc => cullable.hh} | 56 +- src/deserializer.cc | 17 +- src/deserializer.hh | 58 +- src/dispatcher.cc | 26 +- src/dispatcher.hh | 17 +- src/drawable.hh | 11 +- src/engine.cc | 68 +- src/engine.hh | 8 +- src/event.hh | 120 +- src/interpolator.hh | 267 ++++ src/math.hh | 26 +- src/mippleton.hh | 109 ++ src/opengl.hh | 2 + src/profiler.hh | 2 + src/random.cc | 2 + src/random.hh | 2 + src/rectangle.hh | 124 -- src/resource.cc | 28 +- src/resource.hh | 11 +- src/scene.cc | 138 ++ src/scene.hh | 62 + src/serializable.cc | 40 + src/serializable.hh | 14 +- src/serializer.cc | 39 +- src/serializer.hh | 37 +- src/settings.cc | 67 +- src/settings.hh | 7 +- src/singleton.hh | 17 +- src/stringtools.cc | 2 + src/stringtools.hh | 2 + src/texture.cc | 269 ++-- src/texture.hh | 18 +- src/thread.hh | 2 + src/tilemap.cc | 204 +++ src/tilemap.hh | 92 +- src/timer.cc | 51 +- src/timer.hh | 2 + src/video.cc | 4 + src/video.hh | 2 + yajl/Makefile.am | 38 +- 123 files changed, 5889 insertions(+), 1103 deletions(-) create mode 100644 data/Makefile.am rename {share/animation => data/animations}/AlienWarrior.json (100%) rename {share/animation => data/animations}/BigExplosion.json (100%) rename {share/animation => data/animations}/Bonuses.json (100%) rename {share/animation => data/animations}/Bullets.json (100%) rename {share/animation => data/animations}/Effects.json (100%) rename {share/animation => data/animations}/Heroine.json (100%) rename {share/animation => data/animations}/Jetbot.json (100%) rename {share/animation => data/animations}/RobotTrooper.json (100%) create mode 100644 data/scenes/Test.json rename {share/scene => data/scenes}/Test.xml (100%) rename {share/texture => data/textures}/AlienWarrior.png (100%) rename {share/texture => data/textures}/BackgroundFar.png (100%) rename {share/texture => data/textures}/BackgroundNear.png (100%) rename {share/texture => data/textures}/BigExplosion.png (100%) rename {share/texture => data/textures}/Bonuses.png (100%) rename {share/texture => data/textures}/Building.png (100%) rename {share/texture => data/textures}/Font.png (100%) rename {share/texture => data/textures}/Heroine.png (100%) rename {share/texture => data/textures}/Jetbot.png (100%) rename {share/texture => data/textures}/Particles.png (100%) rename {share/texture => data/textures}/RobotTrooper.png (100%) rename {share/texture => data/textures}/Scenery.png (100%) rename {share/texture => data/textures}/StatusBars.png (100%) rename {share/texture => data/textures}/TowerBlock1.png (100%) rename {share/texture => data/textures}/Trees.png (100%) rename share/texture/AlienWarrior.data => data/tilemaps/AlienWarrior.json (80%) rename share/texture/BackgroundNear.data => data/tilemaps/BackgroundFar.json (59%) rename share/texture/BackgroundFar.data => data/tilemaps/BackgroundNear.json (54%) rename share/texture/BigExplosion.data => data/tilemaps/BigExplosion.json (54%) rename share/texture/Bonuses.data => data/tilemaps/Bonuses.json (54%) rename share/texture/Building.data => data/tilemaps/Building.json (54%) rename share/texture/Font.data => data/tilemaps/Font.json (81%) rename share/texture/RobotTrooper.data => data/tilemaps/Heroine.json (81%) rename share/texture/Jetbot.data => data/tilemaps/Jetbot.json (81%) rename share/texture/Particles.data => data/tilemaps/Particles.json (54%) rename share/texture/Heroine.data => data/tilemaps/RobotTrooper.json (81%) rename share/texture/Scenery.data => data/tilemaps/Scenery.json (59%) rename share/texture/StatusBars.data => data/tilemaps/StatusBars.json (81%) rename share/texture/TowerBlock1.data => data/tilemaps/TowerBlock1.json (54%) rename share/texture/Trees.data => data/tilemaps/Trees.json (54%) create mode 100644 data/yoink.desktop create mode 100644 data/yoink.png rename {src => data}/yoinkrc (65%) create mode 100644 doc/Makefile.am create mode 100644 doc/screenshot.jpg create mode 100644 doc/yoink.6.in create mode 100644 extra/yoink.ebuild create mode 100644 m4/boost.m4 create mode 100644 m4/sdl.m4 delete mode 100644 share/character/Heroine.json delete mode 100644 share/texture/AlienWarrior.plist delete mode 100644 share/texture/BackgroundFar.plist delete mode 100644 share/texture/BackgroundNear.plist delete mode 100644 share/texture/BigExplosion.plist delete mode 100644 share/texture/Bonuses.plist delete mode 100644 share/texture/Building.plist delete mode 100644 share/texture/Default.plist delete mode 100644 share/texture/Font.plist delete mode 100644 share/texture/Heroine.plist delete mode 100644 share/texture/Jetbot.plist delete mode 100644 share/texture/Particles.plist delete mode 100644 share/texture/RobotTrooper.plist delete mode 100644 share/texture/Scenery.plist delete mode 100644 share/texture/StatusBars.plist delete mode 100644 share/texture/TowerBlock1.plist delete mode 100644 share/texture/Trees.plist create mode 100644 src/Character.cc create mode 100644 src/TilemapFont.cc create mode 100644 src/TilemapFont.hh create mode 100644 src/Typesetter.cc create mode 100644 src/Typesetter.hh create mode 100644 src/aabb.hh create mode 100644 src/camera.hh rename src/{rectangle.cc => cullable.hh} (55%) create mode 100644 src/interpolator.hh create mode 100644 src/mippleton.hh delete mode 100644 src/rectangle.hh create mode 100644 src/scene.cc create mode 100644 src/scene.hh create mode 100644 src/tilemap.cc diff --git a/COPYING b/COPYING index 4394081..2d0a39c 100644 --- a/COPYING +++ b/COPYING @@ -570,3 +570,685 @@ necessary. Here is a sample; alter the names: That's all there is to it! +------------------------------------------------------------------------------ + +The file m4/boost.m4 is licensed according to the following terms and +conditions: + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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 Lesser General +Public License instead of this License. But first, please read +. + + + diff --git a/Makefile.am b/Makefile.am index d5f6037..ad16dbe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1,13 @@ -SUBDIRS = yajl src + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = yajl src data doc + +EXTRA_DIST = yajl + +run: all + $(top_srcdir)/src/yoink + +debug: all + gdb $(top_srcdir)/src/yoink + diff --git a/configure.ac b/configure.ac index 15cba2c..2d839c9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,43 +1,192 @@ -AC_PREREQ(2.60) -AC_INIT([yoink],[0.1],[chaz@dogcows.com]) -AM_INIT_AUTOMAKE + +# +# Yoink +# Process this file with autoconf to produce a configure script. +# + +AC_PREREQ([2.60]) + +AC_INIT([Yoink], [0.1], [chaz@dogcows.com], [yoink]) + +AC_CANONICAL_TARGET + AC_CONFIG_SRCDIR([src/YoinkApp.cc]) +AC_CONFIG_MACRO_DIR([m4]) + +AM_INIT_AUTOMAKE + + +# +# Checks for programs. +# -AC_PROG_CC AC_PROG_CXX -AC_PROG_RANLIB +#AC_PROG_AWK +AC_PROG_CC +AC_PROG_CPP AC_PROG_INSTALL -AM_PROG_CC_C_O +#AC_PROG_LN_S +#AC_PROG_MAKE_SET +AC_PROG_LIBTOOL -AC_HEADER_STDC +# +# Checks for configuration arguments. +# -AC_SEARCH_LIBS([glClear], [GL], [have_opengl=yes]) -if test ! "x${have_opengl}" = xyes +AC_ARG_ENABLE([debug], + [ --enable-debug include debugging symbols and features], + [debug=$enableval + if test x$debug = xyes + then + CFLAGS="-Wall -Werror -g -O0 -DDEBUG" + CXXFLAGS="-Wall -Werror -g -O0 -DDEBUG" + else + CFLAGS="-O2 -DNDEBUG" + CXXFLAGS="-O2 -DNDEBUG" + fi], + [CFLAGS="-O2 -DNDEBUG" + CXXFLAGS="-O2 -DNDEBUG"]) + +AC_ARG_ENABLE([profile], + [ --enable-profile make a binary for use with gprof], + [profile=$enableval + if test x$profile = xyes + then + CFLAGS="$CFLAGS -pg" + CXXFLAGS="$CXXFLAGS -pg" + fi]) + + +if test x$prefix = xNONE then - AC_MSG_ERROR([libGL is required]) + prefix="$ac_default_prefix" fi -AC_SEARCH_LIBS([SDL_Init], [SDL], [have_sdl=yes]) -if test ! "x${have_sdl}" = xyes +if test x$datadir = x'${datarootdir}' then - AC_MSG_ERROR([libSDL is required]) + eval datarootdir="$datarootdir" + eval datadir="$datadir/yoink" fi -AC_SEARCH_LIBS([IMG_Load], [SDL_image], [have_sdlimage=yes]) -if test ! "x${have_sdlimage}" = xyes +AC_DEFINE_UNQUOTED([YOINK_DATADIR], ["$datadir"], + [Define to path of game asset directory.]) + +AC_DEFINE_UNQUOTED([YOINK_CONFIGFILES], + ["\$HOME/.yoinkrc:/etc/yoinkrc:$datadir/yoinkrc"], + [Define to colon-delimited config file paths.]) + + +# +# Checks for libraries. +# + +AM_PATH_SDL([1.2.14], + [CFLAGS="$CFLAGS $SDL_CFLAGS" + CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS"]) + +#BOOST_REQUIRE([1.35]) +#CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" +BOOST_SMART_PTR +BOOST_STRING_ALGO +BOOST_BIND +BOOST_FUNCTION + +AC_SEARCH_LIBS([IMG_Load], [SDL_image],, + [AC_MSG_ERROR([libSDL_image is required])]) + +AC_SEARCH_LIBS([glBegin], [GL],, + [AC_MSG_ERROR([libGL is required])]) + +AC_SEARCH_LIBS([clock_gettime], [rt], + [AC_DEFINE([HAVE_LIBRT], 1, + [Define to 1 if you have the 'rt' library.])]) + + +# +# Checks for header files. +# + +AC_HEADER_STDBOOL +AC_HEADER_STDC +AC_CHECK_HEADERS([stddef.h stdint.h stdlib.h string.h unistd.h]) + + +# +# Checks for typedefs, structures, and compiler characteristics. +# + +AC_C_STRINGIZE +AC_C_INLINE + +AC_TYPE_UINT8_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T + + +# +# Checks for library functions. +# + +AC_FUNC_ERROR_AT_LINE +AC_FUNC_STRTOD +AC_CHECK_FUNCS([strchr strcspn strrchr strstr]) + + +# +# Find the data files to install. +# + +DATA_FILES=$(echo $(cd data; \ + find . -name "*.png" -o -name "*.json" -o -name yoinkrc)) +AC_SUBST([DATA_FILES]) + + +# +# Create the build files. +# + +AC_CONFIG_FILES([Makefile + data/Makefile + src/Makefile + doc/Makefile + doc/yoink.6 + yajl/Makefile]) + +AC_CONFIG_HEADERS([src/config.h]) + +AC_OUTPUT + + +# +# Print a friendly little message. +# + +echo "=====================================" +echo " Configuration complete!" +echo "" + +echo " Prefix: $prefix" +echo " Data: $datadir" +echo "" + +if test x$debug = xyes then - AC_MSG_ERROR([libSDL_image is required]) + echo " * Debugging enabled." + echo "" fi -AC_SEARCH_LIBS([clock_gettime], [rt], [have_librt=yes]) -if test "x${have_librt}" = xyes +if test x$profile = xyes then - AC_DEFINE([HAVE_LIBRT], 1, [high-resolution timer enabled]) + echo " * Profiling enabled." + echo "" fi -AC_CONFIG_FILES([Makefile - yajl/Makefile - src/Makefile]) -AC_OUTPUT +echo " To finish the installation, execute:" +echo " make" +echo " make install" +echo "=====================================" diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 0000000..8387830 --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,9 @@ + +nobase_dist_data_DATA = @DATA_FILES@ + +appsdir=$(prefix)/share/applications +dist_apps_DATA = yoink.desktop + +pixmapdir=$(prefix)/share/pixmaps +dist_pixmap_DATA = yoink.png + diff --git a/share/animation/AlienWarrior.json b/data/animations/AlienWarrior.json similarity index 100% rename from share/animation/AlienWarrior.json rename to data/animations/AlienWarrior.json diff --git a/share/animation/BigExplosion.json b/data/animations/BigExplosion.json similarity index 100% rename from share/animation/BigExplosion.json rename to data/animations/BigExplosion.json diff --git a/share/animation/Bonuses.json b/data/animations/Bonuses.json similarity index 100% rename from share/animation/Bonuses.json rename to data/animations/Bonuses.json diff --git a/share/animation/Bullets.json b/data/animations/Bullets.json similarity index 100% rename from share/animation/Bullets.json rename to data/animations/Bullets.json diff --git a/share/animation/Effects.json b/data/animations/Effects.json similarity index 100% rename from share/animation/Effects.json rename to data/animations/Effects.json diff --git a/share/animation/Heroine.json b/data/animations/Heroine.json similarity index 100% rename from share/animation/Heroine.json rename to data/animations/Heroine.json diff --git a/share/animation/Jetbot.json b/data/animations/Jetbot.json similarity index 100% rename from share/animation/Jetbot.json rename to data/animations/Jetbot.json diff --git a/share/animation/RobotTrooper.json b/data/animations/RobotTrooper.json similarity index 100% rename from share/animation/RobotTrooper.json rename to data/animations/RobotTrooper.json diff --git a/data/scenes/Test.json b/data/scenes/Test.json new file mode 100644 index 0000000..7b0fd63 --- /dev/null +++ b/data/scenes/Test.json @@ -0,0 +1,1118 @@ +{ + "playfield_bounds": [0, 0, -100, 1280, 500, 100], + "maximum_bounds": [-800, 0, -300, 2400, 1000, 600], + "instructions": + [ + + /* Left end tower block */ + + /* Front */ + + "reset_transform", + "translate", [-5, 0, 5], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 5, + "tiles": + [ + 2, 2, 2, 2, 2, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 4, 4, 4, 4, 4 + ] + }, + + /* Right side */ + + "reset_transform", + "rotate", ["y", 90], + "translate", [0, 0, 5], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 5, + "surface_type": "right", + "tiles": + [ + 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, + 4, 5, 5, 5, 4 + ] + }, + + /* Top */ + + "reset_transform", + "rotate", ["x", 90], + "translate", [-5, 15, 0], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 5, + "surface_type": "top", + "tiles": + [ + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3 + ] + }, + + /* Leftmost background tower block */ + + /* Front */ + + "reset_transform", + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 7, + "detail": 1, + "tiles": + [ + 2, 2, 2, 2, 2, 2, 2, + 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 6, 1, 0, + 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0, 1, 0, + 4, 4, 5, 5, 5, 4, 4 + ] + }, + + /* Right side */ + + "reset_transform", + "rotate", ["y", 90], + "translate", [7, 0, 0], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 6, + "detail": 1, + "tiles": + [ + 2, 2, 2, 2, 2, 2, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 4, 4, 4, 4, 4, 4 + ] + }, + + /* Top */ + + "reset_transform", + "rotate", ["x", 90], + "translate", [-2, 8, -6], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 9, + "detail": 1, + "tiles": + [ + 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3 + ] + }, + + /* Foreground building with pitched roof */ + + /* Left wall */ + + "reset_transform", + "rotate", ["y", -90], + "translate", [10, 0, 1], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 4, + "surface_type": "left", + "tiles": + [ + -1, 9, 11, -1, + 9, 10, 12, 11, + 15, 7, 7, 16, + 3, 5, 6, 4, + 3, 6, 5, 4 + ] + }, + + /* Right wall */ + + "reset_transform", + "rotate", ["y", -90], + "translate", [13, 0, 1], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 4, + "surface_type": "right", + "tiles": + [ + -1, 9, 11, -1, + 9, 10, 12, 11, + 15, 7, 7, 16, + 3, 5, 6, 4, + 3, 8, 5, 4 + ] + }, + + /* Front wall */ + + "reset_transform", + "translate", [10, 0, 5], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 3, + "tiles": + [ + 15, 7, 16, + 3, 5, 4, + 3, 6, 4 + ] + }, + + /* Pitched roof */ + + "reset_transform", + "rotate", ["x", 135], + "scale", [1, 1.5, 1.5], + "translate", [10, 5, 3], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 3, + "tiles": + [ + 13, 13, 13, + 13, 13, 13 + ] + }, + + /* Finial */ + + "reset_transform", + "translate", [10, 5, 3], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 3, + "tiles": + [ + 18, 18, 18 + ] + }, + + /* Cheaty invisible platform */ + + "reset_transform", + "translate", [10, 4, 3], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 3, + "surface_type": "top", + "tiles": + [ + -1, -1, -1 + ] + }, + + /* The ground */ + + /* Courtyard */ + + "reset_transform", + "rotate", ["x", 90], + "translate", [-3, 0, 0], + "scale", [32], + "texture", "Scenery", + "tilemap", + { + "width": 13, + "surface_type": "top", + "tiles": + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + -1, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, + -1, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, + -1, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, + -1, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, + -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + ] + }, + + /* Front grass */ + + "reset_transform", + "scale", [8, 1, 1], + "translate", [1, -0.5, 5], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 8, + "detail": 2 + }, + + /* Back grass */ + + "reset_transform", + "scale", [8, 1, 1], + "translate", [1, -0.5, 1], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 8, + "detail": 2 + }, + + /* Left grass */ + + "reset_transform", + "scale", [4, 1, 1], + "rotate", ["y", -90], + "translate", [1, -0.5, 1], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 4, + "detail": 2 + }, + + /* Right grass */ + + "reset_transform", + "scale", [4, 1, 1], + "rotate", ["y", -90], + "translate", [9, -0.5, 1], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 4, + "detail": 2 + }, + + /* Fence behind house */ + + "reset_transform", + "scale", [11, 1, 1], + "translate", [7, 0, 0], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 4, + "u_scale": 11, + "detail": 2 + }, + + /* Background building with pitched roof */ + + /* Front wall */ + + "reset_transform", + "translate", [19, 0, 0], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 4, + "detail": 1, + "tiles": + [ + -1, 9, 11, -1, + 9, 10, 12, 11, + 15, 7, 7, 16, + 3, 6, 5, 4, + 3, 5, 6, 4, + 3, 8, 5, 4 + ] + }, + + /* Left wall */ + + "reset_transform", + "rotate", ["y", -90], + "translate", [19, 0, -3], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 3, + "surface_type": "left", + "detail": 1, + "tiles": + [ + 15, 1, 16, + 3, 7, 4, + 3, 5, 4, + 3, 0, 4 + ] + }, + + /* Right wall */ + + "reset_transform", + "rotate", ["y", -90], + "translate", [23, 0, -3], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 3, + "surface_type": "right", + "detail": 1, + "tiles": + [ + 15, 0, 16, + 3, 7, 4, + 3, 6, 4, + 3, 2, 4 + ] + }, + + /* Left pitched roof */ + + "reset_transform", + "rotate", ["x", 135], + "scale", [1, 1.5, 1.5], + "rotate", ["y", -90], + "translate", [21, 6, -3], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 3, + "detail": 1, + "tiles": + [ + 13, 13, 13, + 13, 13, 13 + ] + }, + + /* Right pitched roof */ + + "reset_transform", + "rotate", ["x", -135], + "scale", [1, 1.5, 1.5], + "rotate", ["y", -90], + "translate", [21, 6, -3], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 3, + "detail": 1, + "tiles": + [ + 13, 13, 13, + 13, 13, 13 + ] + }, + + /* Finial */ + + "reset_transform", + "rotate", ["y", -90], + "translate", [21, 6, -3], + "scale", [32], + "texture", "Building", + "tilemap", + { + "width": 3, + "detail": 1, + "tiles": + [ + 18, 18, 18 + ] + }, + + /* More ground to the right */ + + /* Ground under house */ + + "reset_transform", + "rotate", ["x", 90], + "translate", [10, 0, 0], + "scale", [32], + "texture", "Scenery", + "tilemap", + { + "width": 3, + "surface_type": "top", + "tiles": + [ + 1, 1, 1, + 1, 1, 1, + -1, -1, -1, + -1, -1, -1, + -1, -1, -1, + -1, -1, -1, + 1, 1, 1 + ] + }, + + /* Left part of center courtyard */ + + "reset_transform", + "rotate", ["x", 90], + "translate", [13, 0, 0], + "scale", [32], + "texture", "Scenery", + "tilemap", + { + "width": 8, + "surface_type": "top", + "tiles": + [ + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 0 + ] + }, + + /* Front grass */ + + "reset_transform", + "scale", [12, 1, 1], + "translate", [14, -0.5, 5], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 12, + "detail": 2 + }, + + /* Back grass */ + + "reset_transform", + "scale", [4, 1, 1], + "translate", [14, -0.5, 1], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 4, + "detail": 2 + }, + + /* Front grass next to door */ + + "reset_transform", + "scale", [1, 1, 1], + "translate", [13, -0.5, 3], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 1, + "detail": 2 + }, + + /* Back grass next to door */ + + "reset_transform", + "scale", [1, 1, 1], + "translate", [13, -0.5, 2], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 1, + "detail": 2 + }, + + /* Left grass */ + + "reset_transform", + "rotate", ["y", -90], + "translate", [14, -0.5, 1], + "scale", [32], + "texture", "Scenery", + "tilemap", + { + "width": 4, + "detail": 2, + "tiles": + [ + 2, -1, 2, 2 + ] + }, + + /* Grass left of house */ + + "reset_transform", + "rotate", ["y", -90], + "scale", [1, 1, 1], + "translate", [18, -0.5, 0], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 1, + "detail": 2 + }, + + /* Grass right of house */ + + "reset_transform", + "rotate", ["y", -90], + "scale", [1, 1, 1], + "translate", [24, -0.5, 0], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 1, + "detail": 2 + }, + + /* Front grass in center */ + + "reset_transform", + "scale", [4, 1, 1], + "translate", [19, -0.5, 4], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 4, + "detail": 2 + }, + + /* Back grass in center */ + + "reset_transform", + "scale", [4, 1, 1], + "translate", [19, -0.5, 2], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 4, + "detail": 2 + }, + + /* Left grass in center */ + + "reset_transform", + "scale", [2, 1, 1], + "rotate", ["y", -90], + "translate", [19, -0.5, 2], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 2, + "detail": 2 + }, + + /* Right grass in center */ + + "reset_transform", + "scale", [2, 1, 1], + "rotate", ["y", -90], + "translate", [23, -0.5, 2], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 2, + "detail": 2 + }, + + /* Still more ground */ + + /* Right part of center courtyard */ + + "reset_transform", + "rotate", ["x", 90], + "translate", [21, 0, 0], + "scale", [32], + "texture", "Scenery", + "tilemap", + { + "width": 7, + "surface_type": "top", + "tiles": + [ + 1, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1 + ] + }, + + /* Fence to right of back house */ + + "reset_transform", + "scale", [4, 1, 1], + "translate", [24, 0, 0], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 4, + "u_scale": 4, + "detail": 2 + }, + + /* Grass in front of fence */ + + "reset_transform", + "scale", [4, 1, 1], + "translate", [24, -0.5, 1], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 4, + "detail": 2 + }, + + /* Grass to left of tower block */ + + "reset_transform", + "scale", [2, 1, 1], + "rotate", ["y", -90], + "translate", [26, -0.5, 5], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 2, + "detail": 2 + }, + + /* Grass to right of tower block */ + + "reset_transform", + "scale", [2, 1, 1], + "rotate", ["y", -90], + "translate", [35, -0.5, 5], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 2, + "detail": 2 + }, + + /* Next bit of grass */ + + "reset_transform", + "scale", [5, 1, 1], + "translate", [35, -0.5, 5], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 5, + "detail": 2 + }, + + /* Back grass */ + + "reset_transform", + "scale", [6, 1, 1], + "translate", [34, -0.5, 1], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 6, + "detail": 2 + }, + + /* Extra bit of back grass */ + + "reset_transform", + "scale", [1, 1, 1], + "rotate", ["y", -90], + "translate", [34, -0.5, 0], + "scale", [32], + "texture", "Scenery", + "billboard", + { + "tile": 2, + "u_scale": 1, + "detail": 2 + }, + + /* Ground around tower block */ + + "reset_transform", + "rotate", ["x", 90], + "translate", [28, 0, 4], + "scale", [32], + "texture", "Scenery", + "tilemap", + { + "width": 5, + "surface_type": "top", + "tiles": + [ + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + ] + }, + + /* Rightmost ground */ + + "reset_transform", + "rotate", ["x", 90], + "translate", [33, 0, 0], + "scale", [32], + "texture", "Scenery", + "tilemap", + { + "width": 10, + "surface_type": "top", + "tiles": + [ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, + 0, 1, 1, 1, 1, 1, 1, -1, -1, -1 + ] + }, + + /* Right foreground tower block */ + + /* Front */ + + "reset_transform", + "translate", [28, 0, 4], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 5, + "tiles": + [ + 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 6, 0, + 4, 4, 4, 4, 4 + ] + }, + + /* Right side */ + + "reset_transform", + "rotate", ["y", 90], + "translate", [33, 0, 4], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 6, + "surface_type": "right", + "tiles": + [ + 2, 2, 2, 2, 2, 2, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 5, 4, 5, 5, 4, 5 + ] + }, + + /* Left side */ + + "reset_transform", + "rotate", ["y", 90], + "translate", [28, 0, 4], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 6, + "surface_type": "left", + "tiles": + [ + 2, 2, 2, 2, 2, 2, + 0, 1, 6, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, + 5, 4, 5, 5, 4, 5 + ] + }, + + /* Top */ + + "reset_transform", + "rotate", ["x", 90], + "translate", [28, 7, -2], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 5, + "surface_type": "top", + "tiles": + [ + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3 + ] + }, + + /* Right end tower block */ + + /* Front */ + + "reset_transform", + "translate", [40, 0, 5], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 5, + "tiles": + [ + 2, 2, 2, 2, 2, + 0, 1, 0, 0, 1, + 0, 1, 0, 0, 1, + 0, 1, 0, 0, 1, + 0, 1, 0, 0, 1, + 6, 1, 0, 0, 1, + 0, 1, 0, 0, 1, + 0, 1, 0, 0, 1, + 0, 1, 0, 0, 1, + 0, 1, 0, 0, 1, + 0, 1, 0, 0, 1, + 6, 1, 0, 0, 1, + 0, 1, 0, 0, 1, + 0, 1, 0, 0, 1, + 4, 4, 4, 4, 4 + ] + }, + + /* Left side */ + + "reset_transform", + "rotate", ["y", 90], + "translate", [40, 0, 5], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 5, + "surface_type": "left", + "tiles": + [ + 2, 2, 2, 2, 2, + 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 6, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 6, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 4, 5, 5, 5, 4 + ] + }, + + /* Top */ + + "reset_transform", + "rotate", ["x", 90], + "translate", [40, 15, 0], + "scale", [32], + "texture", "TowerBlock1", + "tilemap", + { + "width": 5, + "surface_type": "top", + "tiles": + [ + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3 + ] + }, + + /* Background */ + + "reset_transform", + "translate", [-0.3, -0.17, -900], + "scale", [3200, 1600, 1], + "texture", "BackgroundFar", + "billboard", + { + "fog": false + }, + "translate", [0, 0, 300], + "texture", "BackgroundNear", + "billboard", + { + "blend": true, + "fog": false + }, + + /* Trees */ + + "texture", "Trees", + + /* Left courtyard */ + + "reset_transform", + "scale", [96], + "translate", [250, -2.5, 16], + "billboard", + { + "tile": 1, + "detail": 1 + }, + + /* Center courtyard */ + + "reset_transform", + "scale", [96], + "translate", [610, -2.5, 85], + "billboard", + { + "tile": 0 + }, + "reset_transform", + "scale", [96], + "translate", [650, -2.5, 115], + "billboard", + { + "tile": 1 + }, + + /* Right courtyard */ + + "reset_transform", + "scale", [96], + "translate", [1080, -2.5, 10], + "billboard", + { + "tile": 1, + "detail": 1 + }, + "reset_transform", + "scale", [96], + "translate", [1120, -2.5, -15], + "billboard", + { + "tile": 0, + "detail": 1 + }, + "reset_transform", + "scale", [96], + "translate", [1220, -2.5, -30], + "billboard", + { + "tile": 1, + "detail": 1 + } + + ] +} diff --git a/share/scene/Test.xml b/data/scenes/Test.xml similarity index 100% rename from share/scene/Test.xml rename to data/scenes/Test.xml diff --git a/share/texture/AlienWarrior.png b/data/textures/AlienWarrior.png similarity index 100% rename from share/texture/AlienWarrior.png rename to data/textures/AlienWarrior.png diff --git a/share/texture/BackgroundFar.png b/data/textures/BackgroundFar.png similarity index 100% rename from share/texture/BackgroundFar.png rename to data/textures/BackgroundFar.png diff --git a/share/texture/BackgroundNear.png b/data/textures/BackgroundNear.png similarity index 100% rename from share/texture/BackgroundNear.png rename to data/textures/BackgroundNear.png diff --git a/share/texture/BigExplosion.png b/data/textures/BigExplosion.png similarity index 100% rename from share/texture/BigExplosion.png rename to data/textures/BigExplosion.png diff --git a/share/texture/Bonuses.png b/data/textures/Bonuses.png similarity index 100% rename from share/texture/Bonuses.png rename to data/textures/Bonuses.png diff --git a/share/texture/Building.png b/data/textures/Building.png similarity index 100% rename from share/texture/Building.png rename to data/textures/Building.png diff --git a/share/texture/Font.png b/data/textures/Font.png similarity index 100% rename from share/texture/Font.png rename to data/textures/Font.png diff --git a/share/texture/Heroine.png b/data/textures/Heroine.png similarity index 100% rename from share/texture/Heroine.png rename to data/textures/Heroine.png diff --git a/share/texture/Jetbot.png b/data/textures/Jetbot.png similarity index 100% rename from share/texture/Jetbot.png rename to data/textures/Jetbot.png diff --git a/share/texture/Particles.png b/data/textures/Particles.png similarity index 100% rename from share/texture/Particles.png rename to data/textures/Particles.png diff --git a/share/texture/RobotTrooper.png b/data/textures/RobotTrooper.png similarity index 100% rename from share/texture/RobotTrooper.png rename to data/textures/RobotTrooper.png diff --git a/share/texture/Scenery.png b/data/textures/Scenery.png similarity index 100% rename from share/texture/Scenery.png rename to data/textures/Scenery.png diff --git a/share/texture/StatusBars.png b/data/textures/StatusBars.png similarity index 100% rename from share/texture/StatusBars.png rename to data/textures/StatusBars.png diff --git a/share/texture/TowerBlock1.png b/data/textures/TowerBlock1.png similarity index 100% rename from share/texture/TowerBlock1.png rename to data/textures/TowerBlock1.png diff --git a/share/texture/Trees.png b/data/textures/Trees.png similarity index 100% rename from share/texture/Trees.png rename to data/textures/Trees.png diff --git a/share/texture/AlienWarrior.data b/data/tilemaps/AlienWarrior.json similarity index 80% rename from share/texture/AlienWarrior.data rename to data/tilemaps/AlienWarrior.json index 564934b..471fed4 100644 --- a/share/texture/AlienWarrior.data +++ b/data/tilemaps/AlienWarrior.json @@ -1,7 +1,6 @@ { "TilesU": 8, "TilesV": 4, - "InvertAlpha": 0, "MinFilter": Nearest, "MagFilter": Nearest } diff --git a/share/texture/BackgroundNear.data b/data/tilemaps/BackgroundFar.json similarity index 59% rename from share/texture/BackgroundNear.data rename to data/tilemaps/BackgroundFar.json index 3272855..8780bea 100644 --- a/share/texture/BackgroundNear.data +++ b/data/tilemaps/BackgroundFar.json @@ -1,9 +1,8 @@ { "TilesU": 1, "TilesV": 1, - "InvertAlpha": 0, "MinFilter": "Linear", "MagFilter": "Linear", - "WrapU": "Wrap", - "WrapV": "Wrap" + "WrapU": "Clamp", + "WrapV": "Clamp" } diff --git a/share/texture/BackgroundFar.data b/data/tilemaps/BackgroundNear.json similarity index 54% rename from share/texture/BackgroundFar.data rename to data/tilemaps/BackgroundNear.json index 11ab592..2ae0637 100644 --- a/share/texture/BackgroundFar.data +++ b/data/tilemaps/BackgroundNear.json @@ -1,9 +1,8 @@ { "TilesU": 1, "TilesV": 1, - "InvertAlpha": 0, "MinFilter": "Linear", "MagFilter": "Linear", - "WrapU": "ClampToEdge", - "WrapV": "ClampToEdge" + "WrapU": "Repeat", + "WrapV": "Repeat" } diff --git a/share/texture/BigExplosion.data b/data/tilemaps/BigExplosion.json similarity index 54% rename from share/texture/BigExplosion.data rename to data/tilemaps/BigExplosion.json index 104ebf0..b57a190 100644 --- a/share/texture/BigExplosion.data +++ b/data/tilemaps/BigExplosion.json @@ -1,9 +1,8 @@ { "TilesU": 8, "TilesV": 1, - "InvertAlpha": 0, "MinFilter": "Linear", "MagFilter": "Linear", - "WrapU": "ClampToEdge", - "WrapV": "ClampToEdge" + "WrapU": "Clamp", + "WrapV": "Clamp" } diff --git a/share/texture/Bonuses.data b/data/tilemaps/Bonuses.json similarity index 54% rename from share/texture/Bonuses.data rename to data/tilemaps/Bonuses.json index a8e3d9d..5c024d5 100644 --- a/share/texture/Bonuses.data +++ b/data/tilemaps/Bonuses.json @@ -1,9 +1,8 @@ { "TilesU": 8, "TilesV": 4, - "InvertAlpha": 0, "MinFilter": "Linear", "MagFilter": "Linear", - "WrapU": "ClampToEdge", - "WrapV": "ClampToEdge" + "WrapU": "Clamp", + "WrapV": "Clamp" } diff --git a/share/texture/Building.data b/data/tilemaps/Building.json similarity index 54% rename from share/texture/Building.data rename to data/tilemaps/Building.json index f041b09..ebc6842 100644 --- a/share/texture/Building.data +++ b/data/tilemaps/Building.json @@ -1,9 +1,8 @@ { "TilesU": 8, "TilesV": 4, - "InvertAlpha": 0, "MinFilter": "Nearest", "MagFilter": "Nearest", - "WrapU": "ClampToEdge", - "WrapV": "ClampToEdge" + "WrapU": "Clamp", + "WrapV": "Clamp" } diff --git a/share/texture/Font.data b/data/tilemaps/Font.json similarity index 81% rename from share/texture/Font.data rename to data/tilemaps/Font.json index 2056f7d..8b1214b 100644 --- a/share/texture/Font.data +++ b/data/tilemaps/Font.json @@ -1,7 +1,6 @@ { "TilesU": 8, "TilesV": 8, - "InvertAlpha": 0, "MinFilter": "Nearest", "MagFilter": "Nearest" } diff --git a/share/texture/RobotTrooper.data b/data/tilemaps/Heroine.json similarity index 81% rename from share/texture/RobotTrooper.data rename to data/tilemaps/Heroine.json index fba6165..3420a10 100644 --- a/share/texture/RobotTrooper.data +++ b/data/tilemaps/Heroine.json @@ -1,7 +1,6 @@ { "TilesU": 8, "TilesV": 4, - "InvertAlpha": 0, "MinFilter": "Nearest", "MagFilter": "Nearest" } diff --git a/share/texture/Jetbot.data b/data/tilemaps/Jetbot.json similarity index 81% rename from share/texture/Jetbot.data rename to data/tilemaps/Jetbot.json index f6d762a..52af3b0 100644 --- a/share/texture/Jetbot.data +++ b/data/tilemaps/Jetbot.json @@ -1,7 +1,6 @@ { "TilesU": 4, "TilesV": 2, - "InvertAlpha": 0, "MinFilter": "Nearest", "MagFilter": "Nearest" } diff --git a/share/texture/Particles.data b/data/tilemaps/Particles.json similarity index 54% rename from share/texture/Particles.data rename to data/tilemaps/Particles.json index a8e3d9d..5c024d5 100644 --- a/share/texture/Particles.data +++ b/data/tilemaps/Particles.json @@ -1,9 +1,8 @@ { "TilesU": 8, "TilesV": 4, - "InvertAlpha": 0, "MinFilter": "Linear", "MagFilter": "Linear", - "WrapU": "ClampToEdge", - "WrapV": "ClampToEdge" + "WrapU": "Clamp", + "WrapV": "Clamp" } diff --git a/share/texture/Heroine.data b/data/tilemaps/RobotTrooper.json similarity index 81% rename from share/texture/Heroine.data rename to data/tilemaps/RobotTrooper.json index fba6165..3420a10 100644 --- a/share/texture/Heroine.data +++ b/data/tilemaps/RobotTrooper.json @@ -1,7 +1,6 @@ { "TilesU": 8, "TilesV": 4, - "InvertAlpha": 0, "MinFilter": "Nearest", "MagFilter": "Nearest" } diff --git a/share/texture/Scenery.data b/data/tilemaps/Scenery.json similarity index 59% rename from share/texture/Scenery.data rename to data/tilemaps/Scenery.json index aee8ed3..83339cf 100644 --- a/share/texture/Scenery.data +++ b/data/tilemaps/Scenery.json @@ -1,9 +1,8 @@ { "TilesU": 4, "TilesV": 4, - "InvertAlpha": 0, "MinFilter": "Linear", "MagFilter": "Linear", - "WrapU": "Wrap", - "WrapV": "Wrap" + "WrapU": "Repeat", + "WrapV": "Repeat" } diff --git a/share/texture/StatusBars.data b/data/tilemaps/StatusBars.json similarity index 81% rename from share/texture/StatusBars.data rename to data/tilemaps/StatusBars.json index 9b94ec7..01b5ed0 100644 --- a/share/texture/StatusBars.data +++ b/data/tilemaps/StatusBars.json @@ -1,7 +1,6 @@ { "TilesU": 4, "TilesV": 1, - "InvertAlpha": 0, "MinFilter": "Nearest", "MagFilter": "Nearest" } diff --git a/share/texture/TowerBlock1.data b/data/tilemaps/TowerBlock1.json similarity index 54% rename from share/texture/TowerBlock1.data rename to data/tilemaps/TowerBlock1.json index 1d5cade..98e7587 100644 --- a/share/texture/TowerBlock1.data +++ b/data/tilemaps/TowerBlock1.json @@ -1,9 +1,8 @@ { "TilesU": 4, "TilesV": 4, - "InvertAlpha": 0, "MinFilter": "Linear", "MagFilter": "Linear", - "WrapU": "ClampToEdge", - "WrapV": "ClampToEdge" + "WrapU": "Clamp", + "WrapV": "Clamp" } diff --git a/share/texture/Trees.data b/data/tilemaps/Trees.json similarity index 54% rename from share/texture/Trees.data rename to data/tilemaps/Trees.json index 665f66a..247ec97 100644 --- a/share/texture/Trees.data +++ b/data/tilemaps/Trees.json @@ -1,9 +1,8 @@ { "TilesU": 2, "TilesV": 1, - "InvertAlpha": 0, "MinFilter": "Linear", "MagFilter": "Linear", - "WrapU": "ClampToEdge", - "WrapV": "ClampToEdge" + "WrapU": "Clamp", + "WrapV": "Clamp" } diff --git a/data/yoink.desktop b/data/yoink.desktop new file mode 100644 index 0000000..2f301e2 --- /dev/null +++ b/data/yoink.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=1.0 +Name=Yoink +Type=Application +Comment=Alien-stomping Entertainment +Exec=yoink +TryExec=yoink +Icon=yoink +Categories=Game;ActionGame; diff --git a/data/yoink.png b/data/yoink.png new file mode 100644 index 0000000000000000000000000000000000000000..d40be6bff7c7a7e4e7a418d063d9be4b45952b98 GIT binary patch literal 8864 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 literal 0 HcmV?d00001 diff --git a/src/yoinkrc b/data/yoinkrc similarity index 65% rename from src/yoinkrc rename to data/yoinkrc index f58c13d..67be5c1 100644 --- a/src/yoinkrc +++ b/data/yoinkrc @@ -4,11 +4,13 @@ "video.resizable": true, "video.mode": [800, 600], "video.colorbuffers": [8, 8, 8, 8], - //"video.multisamplebuffers": 6, - //"video.multisamplesamples": 6, + "video.multisamplebuffers": 6, + "video.multisamplesamples": 6, "video.cursor": true, "video.grab": false, "video.doublebuffer": true, "video.swapcontrol": true, - "engine.maxfps": 40 + "engine.maxfps": 45, + "engine.printfps": true, + "engine.timestep": 0.01 } diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..c74ebdb --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,3 @@ + +man6_MANS = yoink.6 + diff --git a/doc/screenshot.jpg b/doc/screenshot.jpg new file mode 100644 index 0000000000000000000000000000000000000000..66e8e268b1d682b47a78480de7f9e98456d56392 GIT binary patch literal 51189 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 literal 0 HcmV?d00001 diff --git a/doc/yoink.6.in b/doc/yoink.6.in new file mode 100644 index 0000000..a7ad776 --- /dev/null +++ b/doc/yoink.6.in @@ -0,0 +1,236 @@ +.\" +.\" Copyright (c) 2009, Charles McGarvey +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions are met: +.\" +.\" * Redistributions of source code must retain the above copyright notice, +.\" this list of conditions and the following disclaimer. +.\" * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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. +.\" +.hy +.TH YOINK 6 "July 24, 2009" +.SH NAME +Yoink \- An alien-smashing action game. +.SH SYNOPSIS +.B yoink [-h|--help] [-v|--version] [OPTION=VALUE]... +.br +.SH DESCRIPTION +.PP +Leap tall buildings! Crush stupid robots beneath your feet! Wield your +extra-terrestrial powers in the defence of humanity, and send those alien +invaders back from whence they came! Do all these things (apart from the last +one at the moment) in Yoink! You play the part of a flying alien heroine who +must defend her home on Earth from other airborne alien invaders. The game +draws inspiration from classic arcade games like Joust, Bombjack, Rampage, and +Defender--simple, fast-moving action. +.PP +.TP +.B -h, --help +display this help and exit +.TP +.B -v, --version +output version information and exit +.PP +To attack, you must dive on the enemy at high speed. If you're going too +slowly, you'll just drift harmlessly by. Diving from above gives different +results from swooping in and hitting them from the side. If you're too close to +attack, try to build up speed by running away and bouncing off a nearby +building! +.PP +By charging your special alien powers, you can throw fireballs. The orange bar +at the top of the screen represents your power level--at maximum, you can +destroy almost anything. Aiming can be tricky, but with a little practice it's +quite easy to launch them in the right direction. Try doing a little swoop or +circle in the air to line yourself up before releasing your fireball. +.PP +The heroine has limited energy, measured by the blue bar at the top of the +screen. When it runs out, it's game over! She can regain lost energy by +picking up bonuses dropped by enemies. +.PP +To complete the current attack wave, you must destroy all the enemies. Hunt +around, especially in the sky, if you can't find the last few. +.br +.SH OPTIONS +.PP +There are a plethora of options available for tweaking various aspects of the +game. All options can be set either from a configuration file or by passing +them as arguments. Some of the more common options can be set from within the +game. +.PP +A +.B yoink +configuration file ("yoinkrc") consists of key-value pairs organized in a +logical hierarchy. The format of the file is human-readable, so you can get in +there with your favorite text editor if you like to work under the hood. +.B yoink +looks for configuration files and loads them in this order, with the options in +prior configuration files taking precedence over the same options if they exist +in multiple configuration files: +.TP +1. $YOINK_CONFIGFILE +This is an optional environment variable. +.TP +2. $HOME/.yoinkrc +This is a specific user's configuration file. +.TP +3. /etc/yoinkrc +This is a system-wide configuration file. +.TP +4. @datadir@/yoinkrc +This is the base configuration file which should be considered read-only. Look +to this file as an example of the format used for configuration files. +.PP +Options that are passed as arguments take precedence over options loaded from +the configuration file(s). This mechanism is good for running the game with a +temporary setting which you do not intend to retain. Keep in mind that if you +edit and save options in-game, any options you have passed as arguments during +the invocation of the game will be saved to the $HOME/.yoinkrc configuration +file. You may have to go into that file and remove any options you didn't +intend to set. When passing options as arguments, you must use the fully +qualified name of the option if it exists in a subgroup. For example: +.PP +.TP +yoink video.fullscreen=true +Set the option video.fullscreen to true. This will run the game in full-screen +mode. +.TP +yoink video.maxfps=60 +Set the option video.maxfps to 60. This will cap the display rate at 60Hz. +.PP +You can also set options with array values. Arrays can be passed on the +command line by surrounding all the parts with square brackets and separating +each part by a comma. For example: +.TP +yoink video.mode=[1024,768] +Set the option video.mode to an array with numbers 1024 and 768. The video size +will be 1024x768. +.PP +Here is a list of some of the options available: +.TP +.B engine.timestep +The amount of time in seconds between each update of the physics state. A value +of 0.01 or lower is ideal for accurate physics approximations. Values that are +much lower may introduce errors in the game. +.TP +.B input.grab +Takes a boolean (true or false). If true, the cursor pointer will be "stuck" +within the area of the window, and many key combinations which would otherwise +be handled by the window manager will instead be dropped. This is a low-level +option of limited usefulness. +.TP +.B video.colorbuffers +This takes an array of four number values which represent the number of bits to +use for red, green, blue, and the alpha channel. This is a low-level option of +limited usefulness. The default value is almost always preferable. +.TP +.B video.cursor +This option effects the visibility of the cursor while it is "hovering" over the +window. If the value is true, the cursor will be visible. Otherwise, the +cursor will be hidden. +.TP +.B video.doublebuffer +If true, double-buffering will be used to render animations with minimal +distortions. Otherwise, a single buffer will be used. The recommended value is +true. +.TP +.B video.fullscreen +If true, the window will capture the display and render the game in full screen +splendor. A value of false means the game will run in a window. +.TP +.B video.maxfps +The maximum number of frames to be drawn per second. A value of 50 is pretty +good. If your computer is pretty old, can get away with decreasing this value +and still have reasonably smooth animation. You can set this to a very high +number to effectively render as many frames as is possible, but the actual rate +could be limited by vertical display synchronization, depending on the X11 +driver and settings used. You should not set this option higher than the point +where the vertical synchronization effectively limits the draw rate or else the +game may not be able to update the physics on schedule which could actually +significantly lower the quality of the animation. +.TP +.B video.mode +The resolution or size of the window. The value is an array with three number +elements representing the width, height, and bits per pixel that make up the +video mode.. A typical value is [800,600,32] for a size of 800x600 pixels with +millions of colors. +.TP +.B video.multisamplebuffers +The number of multisample buffers used. +.TP +.B video.multisamplesamples +The number of multisample samples used. +.TP +.B video.printfps +If true, the current number of frames being draw per second will be printed to +the console. This is usually off by default, but you can set this to true if +you're interested in the draw rate you're actually getting. +.TP +.B video.resizable +If true, the window will be resizable by the window manager. This option is +meaningless if the game is drawing to the full screen. +.TP +.B video.swapcontrol +If true, drawing will take place at a time which will minimize distortion caused +by the vertical refreshing of displays. The recommended value is true. +.br +.SH ENVIRONMENT +.PP +.B yoink +responds to some variables in the environment: +.TP +HOME +If set to a path of a valid directory (presumably a user's home directory), +.B yoink +will load options from the configuration file at $HOME/.yoinkrc, if it exists. +Saving options within the game will cause this file to be over-written with the +new options. +.TP +USER +.B yoink +uses this variable to guess the user's nickname, for a high score entry or +whatever. +.TP +YOINK_CONFIGFILE +If set to a path of a valid configuration file, +.B yoink +will load the options from that file, and those options will take precedence +over options loaded from other configuration files. Any in-game saving will +cause this file to be over-written by the new options rather than the file at +$HOME/.yoinkrc. +.TP +YOINK_DATADIR +If set to a path of a valid directory, +.B yoink +will look in this directory first when it is loading game assets. Set this +variable if you move the game's assets to another directory or want to load your +own assets. +.br +.SH BUGS +.PP +The pixelated graphics are actually intentional. It adds to the charm of the +game, don't you think? +.PP +Send bug reports and patches to: +.br +Charles McGarvey +.SH AUTHOR +.PP +Neil Carter was the original creator of Yoink, his winning entry in the +uDevGames 2003 Mac game development contest. Charles McGarvey rewrote the game +with SDL and is the current maintainer. diff --git a/extra/yoink.ebuild b/extra/yoink.ebuild new file mode 100644 index 0000000..69fa2f0 --- /dev/null +++ b/extra/yoink.ebuild @@ -0,0 +1,53 @@ +# Copyright 1999-2009 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +EAPI=2 +inherit autotools eutils games + +DESCRIPTION="Alien-smashing action game" +HOMEPAGE="http://www.dogcows.com/" +SRC_URI="http://www.dogcows.com/yoink/${P}.tar.bz2" + +LICENSE="BSD-2" +SLOT="0" +KEYWORDS="~amd64 ~ppc ~x86" +IUSE="debug" + +RDEPEND="media-libs/libsdl[joystick,video] + media-libs/sdl-image[png]" +DEPEND="${RDEPEND} + dev-util/pkgconfig" + +src_prepare() { + sed -i \ + -e "s/-Werror//g" \ + configure.ac \ + || die "sed failed" + sed -i \ + -e "/apps/d" \ + -e "/pixmap/d" \ + data/Makefile.am \ + || die "sed failed" + sed -i \ + -e "/man/d" \ + doc/Makefile.am \ + || die "sed failed" + eautoreconf +} + +src_configure() { + egamesconf \ + --disable-dependency-tracking \ + --datadir="${GAMES_DATADIR}/${PN}" \ + $(use_enable debug) +} + +src_install() { + emake DESTDIR="${D}" install || die "emake install failed" + dodoc AUTHORS ChangeLog README TODO + doman doc/yoink.6 + doicon data/yoink.png + make_desktop_entry ${PN} Yoink + prepgamesdirs +} diff --git a/m4/boost.m4 b/m4/boost.m4 new file mode 100644 index 0000000..c4ba5ec --- /dev/null +++ b/m4/boost.m4 @@ -0,0 +1,920 @@ +# boost.m4: Locate Boost headers and libraries for autoconf-based projects. +# Copyright (C) 2007, 2008, 2009 Benoit Sigoure +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Additional permission under section 7 of the GNU General Public +# License, version 3 ("GPLv3"): +# +# If you convey this file as part of a work that contains a +# configuration script generated by Autoconf, you may do so under +# terms of your choice. +# +# 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, see . + +# serial 10 +# Original sources can be found at http://repo.or.cz/w/boost.m4.git +# You can fetch the latest version of the script by doing: +# wget 'http://repo.or.cz/w/boost.m4.git?a=blob_plain;f=build-aux/boost.m4;hb=HEAD' -O boost.m4 + +# ------ # +# README # +# ------ # + +# This file provides several macros to use the various Boost libraries. +# The first macro is BOOST_REQUIRE. It will simply check if it's possible to +# find the Boost headers of a given (optional) minimum version and it will +# define BOOST_CPPFLAGS accordingly. It will add an option --with-boost to +# your configure so that users can specify non standard locations. +# If the user's environment contains BOOST_ROOT and --with-boost was not +# specified, --with-boost=$BOOST_ROOT is implicitly used. +# For more README and documentation, go to http://repo.or.cz/w/boost.m4.git +# Note: THESE MACROS ASSUME THAT YOU USE LIBTOOL. If you don't, don't worry, +# simply read the README, it will show you what to do step by step. + +m4_pattern_forbid([^_?BOOST_]) + + +# _BOOST_SED_CPP(SED-PROGRAM, PROGRAM, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# -------------------------------------------------------- +# Same as AC_EGREP_CPP, but leave the result in conftest.i. +# PATTERN is *not* overquoted, as in AC_EGREP_CPP. It could be useful +# to turn this into a macro which extracts the value of any macro. +m4_define([_BOOST_SED_CPP], +[AC_LANG_PREPROC_REQUIRE()dnl +AC_REQUIRE([AC_PROG_SED])dnl +AC_LANG_CONFTEST([AC_LANG_SOURCE([[$2]])]) +AS_IF([dnl eval is necessary to expand ac_cpp. +dnl Ultrix and Pyramid sh refuse to redirect output of eval, so use subshell. +(eval "$ac_cpp conftest.$ac_ext") 2>&AS_MESSAGE_LOG_FD | + $SED -n -e "$1" >conftest.i 2>&1], + [$3], + [$4])dnl +rm -f conftest* +])# AC_EGREP_CPP + + + +# BOOST_REQUIRE([VERSION]) +# ------------------------ +# Look for Boost. If version is given, it must either be a literal of the form +# "X.Y.Z" where X, Y and Z are integers (the ".Z" part being optional) or a +# variable "$var". +# Defines the value BOOST_CPPFLAGS. This macro only checks for headers with +# the required version, it does not check for any of the Boost libraries. +# FIXME: Add a 2nd optional argument so that it's not fatal if Boost isn't found +# and add an AC_DEFINE to tell whether HAVE_BOOST. +AC_DEFUN([BOOST_REQUIRE], +[boost_save_IFS=$IFS +boost_version_req="$1" +IFS=. +set x $boost_version_req 0 0 0 +IFS=$boost_save_IFS +shift +boost_version_req=`expr "$[1]" '*' 100000 + "$[2]" '*' 100 + "$[3]"` +AC_ARG_WITH([boost], + [AS_HELP_STRING([--with-boost=DIR], + [prefix of Boost $1 @<:@guess@:>@])])dnl +AC_ARG_VAR([BOOST_ROOT],[Location of Boost installation])dnl +# If BOOST_ROOT is set and the user has not provided a value to +# --with-boost, then treat BOOST_ROOT as if it the user supplied it. +if test x"$BOOST_ROOT" != x; then + if test x"$with_boost" = x; then + AC_MSG_NOTICE([Detected BOOST_ROOT; continuing with --with-boost=$BOOST_ROOT]) + with_boost=$BOOST_ROOT + else + AC_MSG_NOTICE([Detected BOOST_ROOT=$BOOST_ROOT, but overridden by --with-boost=$with_boost]) + fi +fi +AC_SUBST([DISTCHECK_CONFIGURE_FLAGS], + ["$DISTCHECK_CONFIGURE_FLAGS '--with-boost=$with_boost'"]) +boost_save_CPPFLAGS=$CPPFLAGS + AC_CACHE_CHECK([for Boost headers version >= $boost_version_req], + [boost_cv_inc_path], + [boost_cv_inc_path=no +AC_LANG_PUSH([C++])dnl +m4_pattern_allow([^BOOST_VERSION$])dnl + AC_LANG_CONFTEST([AC_LANG_PROGRAM([[#include +#if !defined BOOST_VERSION +# error BOOST_VERSION is not defined +#elif BOOST_VERSION < $boost_version_req +# error Boost headers version < $boost_version_req +#endif +]])]) + # If the user provided a value to --with-boost, use it and only it. + case $with_boost in #( + ''|yes) set x '' /opt/local/include /usr/local/include /opt/include \ + /usr/include C:/Boost/include;; #( + *) set x "$with_boost/include" "$with_boost";; + esac + shift + for boost_dir + do + # Without --layout=system, Boost (or at least some versions) installs + # itself in /include/boost-. This inner loop helps to + # find headers in such directories. + # I didn't indent this loop on purpose (to avoid over-indented code) + for boost_inc in "$boost_dir" "$boost_dir"/boost-* + do + test x"$boost_inc" != x && CPPFLAGS="$CPPFLAGS -I$boost_inc" + AC_COMPILE_IFELSE([], [boost_cv_inc_path=yes], [boost_cv_version=no]) + if test x"$boost_cv_inc_path" = xyes; then + if test x"$boost_inc" != x; then + boost_cv_inc_path=$boost_inc + fi + break 2 + fi + done + done +AC_LANG_POP([C++])dnl + ]) + case $boost_cv_inc_path in #( + no) AC_MSG_ERROR([cannot find Boost headers version >= $boost_version_req]);;#( + yes) BOOST_CPPFLAGS=;;#( + *) AC_SUBST([BOOST_CPPFLAGS], ["-I$boost_cv_inc_path"]);; + esac + AC_CACHE_CHECK([for Boost's header version], + [boost_cv_lib_version], + [m4_pattern_allow([^BOOST_LIB_VERSION$])dnl + _BOOST_SED_CPP([/^boost-lib-version = /{s///;s/\"//g;p;g;}], + [#include +boost-lib-version = BOOST_LIB_VERSION], + [boost_cv_lib_version=`cat conftest.i`])]) + # e.g. "134" for 1_34_1 or "135" for 1_35 + boost_major_version=`echo "$boost_cv_lib_version" | sed 's/_//;s/_.*//'` + case $boost_major_version in #( + '' | *[[!0-9]]*) + AC_MSG_ERROR([Invalid value: boost_major_version=$boost_major_version]) + ;; + esac +CPPFLAGS=$boost_save_CPPFLAGS +])# BOOST_REQUIRE + +# BOOST_STATIC() +# -------------- +# Add the "--enable-static-boost" configure argument. If this argument is given +# on the command line, static versions of the libraries will be looked up. +AC_DEFUN([BOOST_STATIC], + [AC_ARG_ENABLE([static-boost], + [AC_HELP_STRING([--enable-static-boost], + [Prefer the static boost libraries over the shared ones [no]])], + [enable_static_boost=yes], + [enable_static_boost=no])])# BOOST_STATIC + +# BOOST_FIND_HEADER([HEADER-NAME], [ACTION-IF-NOT-FOUND], [ACTION-IF-FOUND]) +# -------------------------------------------------------------------------- +# Wrapper around AC_CHECK_HEADER for Boost headers. Useful to check for +# some parts of the Boost library which are only made of headers and don't +# require linking (such as Boost.Foreach). +# +# Default ACTION-IF-NOT-FOUND: Fail with a fatal error. +# +# Default ACTION-IF-FOUND: define the preprocessor symbol HAVE_ in +# case of success # (where HEADER-NAME is written LIKE_THIS, e.g., +# HAVE_BOOST_FOREACH_HPP). +AC_DEFUN([BOOST_FIND_HEADER], +[AC_REQUIRE([BOOST_REQUIRE])dnl +AC_LANG_PUSH([C++])dnl +boost_save_CPPFLAGS=$CPPFLAGS +CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" +AC_CHECK_HEADER([$1], + [m4_default([$3], [AC_DEFINE(AS_TR_CPP([HAVE_$1]), [1], + [Define to 1 if you have <$1>])])], + [m4_default([$2], [AC_MSG_ERROR([cannot find $1])])]) +CPPFLAGS=$boost_save_CPPFLAGS +AC_LANG_POP([C++])dnl +])# BOOST_FIND_HEADER + + +# BOOST_FIND_LIB([LIB-NAME], [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], +# [CXX-PROLOGUE]) +# ------------------------------------------------------------------------- +# Look for the Boost library LIB-NAME (e.g., LIB-NAME = `thread', for +# libboost_thread). Check that HEADER-NAME works and check that +# libboost_LIB-NAME can link with the code CXX-TEST. The optional argument +# CXX-PROLOGUE can be used to include some C++ code before the `main' +# function. +# +# Invokes BOOST_FIND_HEADER([HEADER-NAME]) (see above). +# +# Boost libraries typically come compiled with several flavors (with different +# runtime options) so PREFERRED-RT-OPT is the preferred suffix. A suffix is one +# or more of the following letters: sgdpn (in that order). s = static +# runtime, d = debug build, g = debug/diagnostic runtime, p = STLPort build, +# n = (unsure) STLPort build without iostreams from STLPort (it looks like `n' +# must always be used along with `p'). Additionally, PREFERRED-RT-OPT can +# start with `mt-' to indicate that there is a preference for multi-thread +# builds. Some sample values for PREFERRED-RT-OPT: (nothing), mt, d, mt-d, gdp +# ... If you want to make sure you have a specific version of Boost +# (eg, >= 1.33) you *must* invoke BOOST_REQUIRE before this macro. +AC_DEFUN([BOOST_FIND_LIB], +[AC_REQUIRE([_BOOST_FIND_COMPILER_TAG])dnl +AC_REQUIRE([BOOST_REQUIRE])dnl +AC_REQUIRE([BOOST_STATIC])dnl +AC_REQUIRE([_BOOST_GUESS_WHETHER_TO_USE_MT])dnl +AC_LANG_PUSH([C++])dnl +AS_VAR_PUSHDEF([Boost_lib], [boost_cv_lib_$1])dnl +AS_VAR_PUSHDEF([Boost_lib_LDFLAGS], [boost_cv_lib_$1_LDFLAGS])dnl +AS_VAR_PUSHDEF([Boost_lib_LIBS], [boost_cv_lib_$1_LIBS])dnl +BOOST_FIND_HEADER([$3]) +boost_save_CPPFLAGS=$CPPFLAGS +CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" +# Now let's try to find the library. The algorithm is as follows: first look +# for a given library name according to the user's PREFERRED-RT-OPT. For each +# library name, we prefer to use the ones that carry the tag (toolset name). +# Each library is searched through the various standard paths were Boost is +# usually installed. If we can't find the standard variants, we try to +# enforce -mt (for instance on MacOSX, libboost_threads.dylib doesn't exist +# but there's -obviously- libboost_threads-mt.dylib). +AC_CACHE_CHECK([for the Boost $1 library], [Boost_lib], + [Boost_lib=no + case "$2" in #( + mt | mt-) boost_mt=-mt; boost_rtopt=;; #( + mt* | mt-*) boost_mt=-mt; boost_rtopt=`expr "X$2" : 'Xmt-*\(.*\)'`;; #( + *) boost_mt=; boost_rtopt=$2;; + esac + if test $enable_static_boost = yes; then + boost_rtopt="s$boost_rtopt" + fi + # Find the proper debug variant depending on what we've been asked to find. + case $boost_rtopt in #( + *d*) boost_rt_d=$boost_rtopt;; #( + *[[sgpn]]*) # Insert the `d' at the right place (in between `sg' and `pn') + boost_rt_d=`echo "$boost_rtopt" | sed 's/\(s*g*\)\(p*n*\)/\1\2/'`;; #( + *) boost_rt_d='-d';; + esac + # If the PREFERRED-RT-OPT are not empty, prepend a `-'. + test -n "$boost_rtopt" && boost_rtopt="-$boost_rtopt" + $boost_guess_use_mt && boost_mt=-mt + # Look for the abs path the static archive. + # $libext is computed by Libtool but let's make sure it's non empty. + test -z "$libext" && + AC_MSG_ERROR([the libext variable is empty, did you invoke Libtool?]) + boost_save_ac_objext=$ac_objext + # Generate the test file. + AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <$3> +$5], [$4])]) +dnl Optimization hacks: compiling C++ is slow, especially with Boost. What +dnl we're trying to do here is guess the right combination of link flags +dnl (LIBS / LDFLAGS) to use a given library. This can take several +dnl iterations before it succeeds and is thus *very* slow. So what we do +dnl instead is that we compile the code first (and thus get an object file, +dnl typically conftest.o). Then we try various combinations of link flags +dnl until we succeed to link conftest.o in an executable. The problem is +dnl that the various TRY_LINK / COMPILE_IFELSE macros of Autoconf always +dnl remove all the temporary files including conftest.o. So the trick here +dnl is to temporarily change the value of ac_objext so that conftest.o is +dnl preserved accross tests. This is obviously fragile and I will burn in +dnl hell for not respecting Autoconf's documented interfaces, but in the +dnl mean time, it optimizes the macro by a factor of 5 to 30. +dnl Another small optimization: the first argument of AC_COMPILE_IFELSE left +dnl empty because the test file is generated only once above (before we +dnl start the for loops). + AC_COMPILE_IFELSE([], + [ac_objext=do_not_rm_me_plz], + [AC_MSG_ERROR([Cannot compile a test that uses Boost $1])]) + ac_objext=$boost_save_ac_objext + boost_failed_libs= +# Don't bother to ident the 6 nested for loops, only the 2 innermost ones +# matter. +for boost_tag_ in -$boost_cv_lib_tag ''; do +for boost_ver_ in -$boost_cv_lib_version ''; do +for boost_mt_ in $boost_mt -mt ''; do +for boost_rtopt_ in $boost_rtopt '' -d; do + for boost_lib in \ + boost_$1$boost_tag_$boost_mt_$boost_rtopt_$boost_ver_ \ + boost_$1$boost_tag_$boost_rtopt_$boost_ver_ \ + boost_$1$boost_tag_$boost_mt_$boost_ver_ \ + boost_$1$boost_tag_$boost_ver_ + do + # Avoid testing twice the same lib + case $boost_failed_libs in #( + *@$boost_lib@*) continue;; + esac + # If with_boost is empty, we'll search in /lib first, which is not quite + # right so instead we'll try to a location based on where the headers are. + boost_tmp_lib=$with_boost + test x"$with_boost" = x && boost_tmp_lib=${boost_cv_inc_path%/include} + for boost_ldpath in "$boost_tmp_lib/lib" '' \ + /opt/local/lib /usr/local/lib /opt/lib /usr/lib \ + "$with_boost" C:/Boost/lib /lib /usr/lib64 /lib64 + do + test -e "$boost_ldpath" || continue + boost_save_LDFLAGS=$LDFLAGS + # Are we looking for a static library? + case $boost_ldpath:$boost_rtopt_ in #( + *?*:*s*) # Yes (Non empty boost_ldpath + s in rt opt) + Boost_lib_LIBS="$boost_ldpath/lib$boost_lib.$libext" + test -e "$Boost_lib_LIBS" || continue;; #( + *) # No: use -lboost_foo to find the shared library. + Boost_lib_LIBS="-l$boost_lib";; + esac + boost_save_LIBS=$LIBS + LIBS="$Boost_lib_LIBS $LIBS" + test x"$boost_ldpath" != x && LDFLAGS="$LDFLAGS -L$boost_ldpath" +dnl First argument of AC_LINK_IFELSE left empty because the test file is +dnl generated only once above (before we start the for loops). + _BOOST_AC_LINK_IFELSE([], + [Boost_lib=yes], [Boost_lib=no]) + ac_objext=$boost_save_ac_objext + LDFLAGS=$boost_save_LDFLAGS + LIBS=$boost_save_LIBS + if test x"$Boost_lib" = xyes; then + Boost_lib_LDFLAGS="-L$boost_ldpath -R$boost_ldpath" + break 6 + else + boost_failed_libs="$boost_failed_libs@$boost_lib@" + fi + done + done +done +done +done +done +rm -f conftest.$ac_objext +]) +case $Boost_lib in #( + no) AC_MSG_ERROR([Could not find the flags to link with Boost $1]) + ;; +esac +AC_SUBST(AS_TR_CPP([BOOST_$1_LDFLAGS]), [$Boost_lib_LDFLAGS]) +AC_SUBST(AS_TR_CPP([BOOST_$1_LIBS]), [$Boost_lib_LIBS]) +CPPFLAGS=$boost_save_CPPFLAGS +AS_VAR_POPDEF([Boost_lib])dnl +AS_VAR_POPDEF([Boost_lib_LDFLAGS])dnl +AS_VAR_POPDEF([Boost_lib_LIBS])dnl +AC_LANG_POP([C++])dnl +])# BOOST_FIND_LIB + + +# --------------------------------------- # +# Checks for the various Boost libraries. # +# --------------------------------------- # + +# List of boost libraries: http://www.boost.org/libs/libraries.htm +# The page http://beta.boost.org/doc/libs is useful: it gives the first release +# version of each library (among other things). + + +# BOOST_ASIO() +# ------------ +# Look for Boost.Asio (new in Boost 1.35). +AC_DEFUN([BOOST_ASIO], +[AC_REQUIRE([BOOST_SYSTEM])dnl +BOOST_FIND_HEADER([boost/asio.hpp])]) + + +# BOOST_BIND() +# ------------ +# Look for Boost.Bind +AC_DEFUN([BOOST_BIND], +[BOOST_FIND_HEADER([boost/bind.hpp])]) + + +# BOOST_CONVERSION() +# ------------------ +# Look for Boost.Conversion (cast / lexical_cast) +AC_DEFUN([BOOST_CONVERSION], +[BOOST_FIND_HEADER([boost/cast.hpp]) +BOOST_FIND_HEADER([boost/lexical_cast.hpp]) +])# BOOST_CONVERSION + + +# BOOST_DATE_TIME([PREFERRED-RT-OPT]) +# ----------------------------------- +# Look for Boost.Date_Time. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +AC_DEFUN([BOOST_DATE_TIME], +[BOOST_FIND_LIB([date_time], [$1], + [boost/date_time/posix_time/posix_time.hpp], + [boost::posix_time::ptime t;]) +])# BOOST_DATE_TIME + + +# BOOST_FILESYSTEM([PREFERRED-RT-OPT]) +# ------------------------------------ +# Look for Boost.Filesystem. For the documentation of PREFERRED-RT-OPT, see +# the documentation of BOOST_FIND_LIB above. +# Do not check for boost/filesystem.hpp because this file was introduced in +# 1.34. +AC_DEFUN([BOOST_FILESYSTEM], +[# Do we have to check for Boost.System? This link-time dependency was +# added as of 1.35.0. If we have a version <1.35, we must not attempt to +# find Boost.System as it didn't exist by then. +if test $boost_major_version -ge 135; then +BOOST_SYSTEM([$1]) +fi # end of the Boost.System check. +boost_filesystem_save_LIBS=$LIBS +boost_filesystem_save_LDFLAGS=$LDFLAGS +m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl +LIBS="$LIBS $BOOST_SYSTEM_LIBS" +LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" +BOOST_FIND_LIB([filesystem], [$1], + [boost/filesystem/path.hpp], [boost::filesystem::path p;]) +LIBS=$boost_filesystem_save_LIBS +LDFLAGS=$boost_filesystem_save_LDFLAGS +])# BOOST_FILESYSTEM + + +# BOOST_FOREACH() +# --------------- +# Look for Boost.Foreach +AC_DEFUN([BOOST_FOREACH], +[BOOST_FIND_HEADER([boost/foreach.hpp])]) + + +# BOOST_FORMAT() +# -------------- +# Look for Boost.Format +# Note: we can't check for boost/format/format_fwd.hpp because the header isn't +# standalone. It can't be compiled because it triggers the following error: +# boost/format/detail/config_macros.hpp:88: error: 'locale' in namespace 'std' +# does not name a type +AC_DEFUN([BOOST_FORMAT], +[BOOST_FIND_HEADER([boost/format.hpp])]) + + +# BOOST_FUNCTION() +# ---------------- +# Look for Boost.Function +AC_DEFUN([BOOST_FUNCTION], +[BOOST_FIND_HEADER([boost/function.hpp])]) + + +# BOOST_GRAPH([PREFERRED-RT-OPT]) +# ------------------------------- +# Look for Boost.Graphs. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +AC_DEFUN([BOOST_GRAPH], +[BOOST_FIND_LIB([graph], [$1], + [boost/graph/adjacency_list.hpp], [boost::adjacency_list<> g;]) +])# BOOST_GRAPH + + +# BOOST_IOSTREAMS([PREFERRED-RT-OPT]) +# ------------------------------- +# Look for Boost.IOStreams. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +AC_DEFUN([BOOST_IOSTREAMS], +[BOOST_FIND_LIB([iostreams], [$1], + [boost/iostreams/device/file_descriptor.hpp], + [boost::iostreams::file_descriptor fd(0); fd.close();]) +])# BOOST_IOSTREAMS + + +# BOOST_HASH() +# ------------ +# Look for Boost.Functional/Hash +AC_DEFUN([BOOST_HASH], +[BOOST_FIND_HEADER([boost/functional/hash.hpp])]) + + +# BOOST_LAMBDA() +# -------------- +# Look for Boost.Lambda +AC_DEFUN([BOOST_LAMBDA], +[BOOST_FIND_HEADER([boost/lambda/lambda.hpp])]) + + +# BOOST_OPTIONAL() +# ---------------- +# Look for Boost.Optional +AC_DEFUN([BOOST_OPTIONAL], +[BOOST_FIND_HEADER([boost/optional.hpp])]) + + +# BOOST_PREPROCESSOR() +# -------------------- +# Look for Boost.Preprocessor +AC_DEFUN([BOOST_PREPROCESSOR], +[BOOST_FIND_HEADER([boost/preprocessor/repeat.hpp])]) + + +# BOOST_PROGRAM_OPTIONS([PREFERRED-RT-OPT]) +# ----------------------------------------- +# Look for Boost.Program_options. For the documentation of PREFERRED-RT-OPT, see +# the documentation of BOOST_FIND_LIB above. +AC_DEFUN([BOOST_PROGRAM_OPTIONS], +[BOOST_FIND_LIB([program_options], [$1], + [boost/program_options.hpp], + [boost::program_options::options_description d("test");]) +])# BOOST_PROGRAM_OPTIONS + + +# BOOST_REF() +# ----------- +# Look for Boost.Ref +AC_DEFUN([BOOST_REF], +[BOOST_FIND_HEADER([boost/ref.hpp])]) + + +# BOOST_REGEX([PREFERRED-RT-OPT]) +# ------------------------------- +# Look for Boost.Regex. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +AC_DEFUN([BOOST_REGEX], +[BOOST_FIND_LIB([regex], [$1], + [boost/regex.hpp], + [boost::regex exp("*"); boost::regex_match("foo", exp);]) +])# BOOST_REGEX + + +# BOOST_SERIALIZATION([PREFERRED-RT-OPT]) +# --------------------------------------- +# Look for Boost.Serialization. For the documentation of PREFERRED-RT-OPT, see +# the documentation of BOOST_FIND_LIB above. +AC_DEFUN([BOOST_SERIALIZATION], +[BOOST_FIND_LIB([serialization], [$1], + [boost/archive/text_oarchive.hpp], + [std::ostream* o = 0; // Cheap way to get an ostream... + boost::archive::text_oarchive t(*o);]) +])# BOOST_SIGNALS + + +# BOOST_SIGNALS([PREFERRED-RT-OPT]) +# --------------------------------- +# Look for Boost.Signals. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +AC_DEFUN([BOOST_SIGNALS], +[BOOST_FIND_LIB([signals], [$1], + [boost/signal.hpp], + [boost::signal s;]) +])# BOOST_SIGNALS + + +# BOOST_SMART_PTR() +# ----------------- +# Look for Boost.SmartPtr +AC_DEFUN([BOOST_SMART_PTR], +[BOOST_FIND_HEADER([boost/scoped_ptr.hpp]) +BOOST_FIND_HEADER([boost/shared_ptr.hpp]) +]) + + +# BOOST_STATICASSERT() +# -------------------- +# Look for Boost.StaticAssert +AC_DEFUN([BOOST_STATICASSERT], +[BOOST_FIND_HEADER([boost/static_assert.hpp])]) + + +# BOOST_STRING_ALGO() +# ------------------- +# Look for Boost.StringAlgo +AC_DEFUN([BOOST_STRING_ALGO], +[BOOST_FIND_HEADER([boost/algorithm/string.hpp]) +]) + + +# BOOST_SYSTEM([PREFERRED-RT-OPT]) +# -------------------------------- +# Look for Boost.System. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. This library was introduced in Boost +# 1.35.0. +AC_DEFUN([BOOST_SYSTEM], +[BOOST_FIND_LIB([system], [$1], + [boost/system/error_code.hpp], + [boost::system::error_code e; e.clear();]) +])# BOOST_SYSTEM + + +# BOOST_TEST([PREFERRED-RT-OPT]) +# ------------------------------ +# Look for Boost.Test. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +AC_DEFUN([BOOST_TEST], +[m4_pattern_allow([^BOOST_CHECK$])dnl +BOOST_FIND_LIB([unit_test_framework], [$1], + [boost/test/unit_test.hpp], [BOOST_CHECK(2 == 2);], + [using boost::unit_test::test_suite; + test_suite* init_unit_test_suite(int argc, char ** argv) + { return NULL; }]) +])# BOOST_TEST + + +# BOOST_THREADS([PREFERRED-RT-OPT]) +# --------------------------------- +# Look for Boost.Thread. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +# FIXME: Provide an alias "BOOST_THREAD". +AC_DEFUN([BOOST_THREADS], +[dnl Having the pthread flag is required at least on GCC3 where +dnl boost/thread.hpp would complain if we try to compile without +dnl -pthread on GNU/Linux. +AC_REQUIRE([_BOOST_PTHREAD_FLAG])dnl +boost_threads_save_LIBS=$LIBS +boost_threads_save_CPPFLAGS=$CPPFLAGS +LIBS="$LIBS $boost_cv_pthread_flag" +# Yes, we *need* to put the -pthread thing in CPPFLAGS because with GCC3, +# boost/thread.hpp will trigger a #error if -pthread isn't used: +# boost/config/requires_threads.hpp:47:5: #error "Compiler threading support +# is not turned on. Please set the correct command line options for +# threading: -pthread (Linux), -pthreads (Solaris) or -mthreads (Mingw32)" +CPPFLAGS="$CPPFLAGS $boost_cv_pthread_flag" +BOOST_FIND_LIB([thread], [$1], + [boost/thread.hpp], [boost::thread t; boost::mutex m;]) +BOOST_THREAD_LIBS="$BOOST_THREAD_LIBS $boost_cv_pthread_flag" +BOOST_CPPFLAGS="$BOOST_CPPFLAGS $boost_cv_pthread_flag" +LIBS=$boost_threads_save_LIBS +CPPFLAGS=$boost_threads_save_CPPFLAGS +])# BOOST_THREADS + + +# BOOST_TOKENIZER() +# ----------------- +# Look for Boost.Tokenizer +AC_DEFUN([BOOST_TOKENIZER], +[BOOST_FIND_HEADER([boost/tokenizer.hpp])]) + + +# BOOST_TRIBOOL() +# --------------- +# Look for Boost.Tribool +AC_DEFUN([BOOST_TRIBOOL], +[BOOST_FIND_HEADER([boost/logic/tribool_fwd.hpp]) +BOOST_FIND_HEADER([boost/logic/tribool.hpp]) +]) + + +# BOOST_TUPLE() +# ------------- +# Look for Boost.Tuple +AC_DEFUN([BOOST_TUPLE], +[BOOST_FIND_HEADER([boost/tuple/tuple.hpp])]) + + +# BOOST_TYPETRAITS() +# -------------------- +# Look for Boost.TypeTraits +AC_DEFUN([BOOST_TYPETRAITS], +[BOOST_FIND_HEADER([boost/type_traits.hpp])]) + + +# BOOST_UTILITY() +# --------------- +# Look for Boost.Utility (noncopyable, result_of, base-from-member idiom, +# etc.) +AC_DEFUN([BOOST_UTILITY], +[BOOST_FIND_HEADER([boost/utility.hpp])]) + + +# BOOST_VARIANT() +# --------------- +# Look for Boost.Variant. +AC_DEFUN([BOOST_VARIANT], +[BOOST_FIND_HEADER([boost/variant/variant_fwd.hpp]) +BOOST_FIND_HEADER([boost/variant.hpp])]) + + +# BOOST_WAVE([PREFERRED-RT-OPT]) +# ------------------------------ +# NOTE: If you intend to use Wave/Spirit with thread support, make sure you +# call BOOST_THREADS first. +# Look for Boost.Wave. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +AC_DEFUN([BOOST_WAVE], +[AC_REQUIRE([BOOST_FILESYSTEM])dnl +AC_REQUIRE([BOOST_DATE_TIME])dnl +boost_wave_save_LIBS=$LIBS +boost_wave_save_LDFLAGS=$LDFLAGS +m4_pattern_allow([^BOOST_((FILE)?SYSTEM|DATE_TIME|THREAD)_(LIBS|LDFLAGS)$])dnl +LIBS="$LIBS $BOOST_SYSTEM_LIBS $BOOST_FILESYSTEM_LIBS $BOOST_DATE_TIME_LIBS\ +$BOOST_THREAD_LIBS" +LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS\ +$BOOST_DATE_TIME_LDFLAGS $BOOST_THREAD_LDFLAGS" +BOOST_FIND_LIB([wave], [$1], + [boost/wave.hpp], + [boost::wave::token_id id; get_token_name(id);]) +LIBS=$boost_wave_save_LIBS +LDFLAGS=$boost_wave_save_LDFLAGS +])# BOOST_WAVE + + +# BOOST_XPRESSIVE() +# ----------------- +# Look for Boost.Xpressive (new since 1.36.0). +AC_DEFUN([BOOST_XPRESSIVE], +[BOOST_FIND_HEADER([boost/xpressive/xpressive.hpp])]) + + +# ----------------- # +# Internal helpers. # +# ----------------- # + + +# _BOOST_PTHREAD_FLAG() +# --------------------- +# Internal helper for BOOST_THREADS. Based on ACX_PTHREAD: +# http://autoconf-archive.cryp.to/acx_pthread.html +AC_DEFUN([_BOOST_PTHREAD_FLAG], +[AC_REQUIRE([AC_PROG_CXX])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_LANG_PUSH([C++])dnl +AC_CACHE_CHECK([for the flags needed to use pthreads], [boost_cv_pthread_flag], +[ boost_cv_pthread_flag= + # The ordering *is* (sometimes) important. Some notes on the + # individual items follow: + # (none): in case threads are in libc; should be tried before -Kthread and + # other compiler flags to prevent continual compiler warnings + # -lpthreads: AIX (must check this before -lpthread) + # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # -llthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + # -pthread: GNU Linux/GCC (kernel threads), BSD/GCC (userland threads) + # -pthreads: Solaris/GCC + # -mthreads: MinGW32/GCC, Lynx/GCC + # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it + # doesn't hurt to check since this sometimes defines pthreads too; + # also defines -D_REENTRANT) + # ... -mt is also the pthreads flag for HP/aCC + # -lpthread: GNU Linux, etc. + # --thread-safe: KAI C++ + case $host_os in #( + *solaris*) + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + boost_pthread_flags="-pthreads -lpthread -mt -pthread";; #( + *) + boost_pthread_flags="-lpthreads -Kthread -kthread -llthread -pthread \ + -pthreads -mthreads -lpthread --thread-safe -mt";; + esac + # Generate the test file. + AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0);])]) + for boost_pthread_flag in '' $boost_pthread_flags; do + boost_pthread_ok=false +dnl Re-use the test file already generated. + boost_pthreads__save_LIBS=$LIBS + LIBS="$LIBS $boost_pthread_flag" + AC_LINK_IFELSE([], + [if grep ".*$boost_pthread_flag" conftest.err; then + echo "This flag seems to have triggered warnings" >&AS_MESSAGE_LOG_FD + else + boost_pthread_ok=:; boost_cv_pthread_flag=$boost_pthread_flag + fi]) + LIBS=$boost_pthreads__save_LIBS + $boost_pthread_ok && break + done +]) +AC_LANG_POP([C++])dnl +])# _BOOST_PTHREAD_FLAG + + +# _BOOST_gcc_test(MAJOR, MINOR) +# ----------------------------- +# Internal helper for _BOOST_FIND_COMPILER_TAG. +m4_define([_BOOST_gcc_test], +["defined __GNUC__ && __GNUC__ == $1 && __GNUC_MINOR__ == $2 && !defined __ICC @ gcc$1$2"])dnl + + +# _BOOST_FIND_COMPILER_TAG() +# -------------------------- +# Internal. When Boost is installed without --layout=system, each library +# filename will hold a suffix that encodes the compiler used during the +# build. The Boost build system seems to call this a `tag'. +AC_DEFUN([_BOOST_FIND_COMPILER_TAG], +[AC_REQUIRE([AC_PROG_CXX])dnl +AC_CACHE_CHECK([for the toolset name used by Boost for $CXX], [boost_cv_lib_tag], +[AC_LANG_PUSH([C++])dnl + boost_cv_lib_tag=unknown + # The following tests are mostly inspired by boost/config/auto_link.hpp + # The list is sorted to most recent/common to oldest compiler (in order + # to increase the likelihood of finding the right compiler with the + # least number of compilation attempt). + # Beware that some tests are sensible to the order (for instance, we must + # look for MinGW before looking for GCC3). + # I used one compilation test per compiler with a #error to recognize + # each compiler so that it works even when cross-compiling (let me know + # if you know a better approach). + # Known missing tags (known from Boost's tools/build/v2/tools/common.jam): + # como, edg, kcc, bck, mp, sw, tru, xlc + # I'm not sure about my test for `il' (be careful: Intel's ICC pre-defines + # the same defines as GCC's). + # TODO: Move the test on GCC 4.4 up once it's released. + for i in \ + _BOOST_gcc_test(4, 3) \ + _BOOST_gcc_test(4, 2) \ + _BOOST_gcc_test(4, 1) \ + _BOOST_gcc_test(4, 0) \ + "defined __GNUC__ && __GNUC__ == 3 && !defined __ICC \ + && (defined WIN32 || defined WINNT || defined _WIN32 || defined __WIN32 \ + || defined __WIN32__ || defined __WINNT || defined __WINNT__) @ mgw" \ + _BOOST_gcc_test(3, 4) \ + _BOOST_gcc_test(3, 3) \ + "defined _MSC_VER && _MSC_VER >= 1400 @ vc80" \ + _BOOST_gcc_test(3, 2) \ + "defined _MSC_VER && _MSC_VER == 1310 @ vc71" \ + _BOOST_gcc_test(3, 1) \ + _BOOST_gcc_test(3, 0) \ + "defined __BORLANDC__ @ bcb" \ + "defined __ICC && (defined __unix || defined __unix__) @ il" \ + "defined __ICL @ iw" \ + "defined _MSC_VER && _MSC_VER == 1300 @ vc7" \ + _BOOST_gcc_test(4, 4) \ + _BOOST_gcc_test(2, 95) \ + "defined __MWERKS__ && __MWERKS__ <= 0x32FF @ cw9" \ + "defined _MSC_VER && _MSC_VER < 1300 && !defined UNDER_CE @ vc6" \ + "defined _MSC_VER && _MSC_VER < 1300 && defined UNDER_CE @ evc4" \ + "defined __MWERKS__ && __MWERKS__ <= 0x31FF @ cw8" + do + boost_tag_test=`expr "X$i" : 'X\([[^@]]*\) @ '` + boost_tag=`expr "X$i" : 'X[[^@]]* @ \(.*\)'` + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#if $boost_tag_test +/* OK */ +#else +# error $boost_tag_test +#endif +]])], [boost_cv_lib_tag=$boost_tag; break], []) + done +AC_LANG_POP([C++])dnl + case $boost_cv_lib_tag in #( + # Some newer (>= 1.35?) versions of Boost seem to only use "gcc" as opposed + # to "gcc41" for instance. + *-gcc | *'-gcc ') :;; #( Don't re-add -gcc: it's already in there. + gcc*) + # We can specify multiple tags in this variable because it's used by + # BOOST_FIND_LIB that does a `for tag in -$boost_cv_lib_tag' ... + boost_cv_lib_tag="$boost_cv_lib_tag -gcc" + ;; #( + unknown) + AC_MSG_WARN([[could not figure out which toolset name to use for $CXX]]) + boost_cv_lib_tag= + ;; + esac +])dnl end of AC_CACHE_CHECK +])# _BOOST_FIND_COMPILER_TAG + + +# _BOOST_GUESS_WHETHER_TO_USE_MT() +# -------------------------------- +# Compile a small test to try to guess whether we should favor MT (Multi +# Thread) flavors of Boost. Sets boost_guess_use_mt accordingly. +AC_DEFUN([_BOOST_GUESS_WHETHER_TO_USE_MT], +[# Check whether we do better use `mt' even though we weren't ask to. +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#if defined _REENTRANT || defined _MT || defined __MT__ +/* use -mt */ +#else +# error MT not needed +#endif +]])], [boost_guess_use_mt=:], [boost_guess_use_mt=false]) +]) + +# _BOOST_AC_LINK_IFELSE(PROGRAM, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# ------------------------------------------------------------------- +# Fork of _AC_LINK_IFELSE that preserves conftest.o across calls. Fragile, +# will break when Autoconf changes its internals. Requires that you manually +# rm -f conftest.$ac_objext in between to really different tests, otherwise +# you will try to link a conftest.o left behind by a previous test. +# Used to aggressively optimize BOOST_FIND_LIB (see the big comment in this +# macro) +m4_define([_BOOST_AC_LINK_IFELSE], +[m4_ifvaln([$1], [AC_LANG_CONFTEST([$1])])dnl +rm -f conftest$ac_exeext +boost_ac_ext_save=$ac_ext +boost_use_source=: +# If we already have a .o, re-use it. We change $ac_ext so that $ac_link +# tries to link the existing object file instead of compiling from source. +test -f conftest.$ac_objext && ac_ext=$ac_objext && boost_use_source=false && + _AS_ECHO_LOG([re-using the existing conftest.$ac_objext]) +AS_IF([_AC_DO_STDERR($ac_link) && { + test -z "$ac_[]_AC_LANG_ABBREV[]_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_executable_p conftest$ac_exeext +dnl FIXME: use AS_TEST_X instead when 2.61 is widespread enough. + }], + [$2], + [if $boost_use_source; then + _AC_MSG_LOG_CONFTEST + fi + $3]) +dnl Delete also the IPA/IPO (Inter Procedural Analysis/Optimization) +dnl information created by the PGI compiler (conftest_ipa8_conftest.oo), +dnl as it would interfere with the next link command. +rm -f core conftest.err conftest_ipa8_conftest.oo \ + conftest$ac_exeext m4_ifval([$1], [conftest.$ac_ext])[]dnl +])# _BOOST_AC_LINK_IFELSE + +# Local Variables: +# mode: autoconf +# End: diff --git a/m4/sdl.m4 b/m4/sdl.m4 new file mode 100644 index 0000000..ada3a41 --- /dev/null +++ b/m4/sdl.m4 @@ -0,0 +1,175 @@ +# Configure paths for SDL +# Sam Lantinga 9/21/99 +# stolen from Manish Singh +# stolen back from Frank Belew +# stolen from Manish Singh +# Shamelessly stolen from Owen Taylor + +dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS +dnl +AC_DEFUN([AM_PATH_SDL], +[dnl +dnl Get the cflags and libraries from the sdl-config script +dnl +AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], + sdl_prefix="$withval", sdl_prefix="") +AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], + sdl_exec_prefix="$withval", sdl_exec_prefix="") +AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], + , enable_sdltest=yes) + + if test x$sdl_exec_prefix != x ; then + sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config + fi + fi + if test x$sdl_prefix != x ; then + sdl_args="$sdl_args --prefix=$sdl_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_prefix/bin/sdl-config + fi + fi + + AC_REQUIRE([AC_CANONICAL_TARGET]) + PATH="$prefix/bin:$prefix/usr/bin:$PATH" + AC_PATH_PROG(SDL_CONFIG, sdl-config, no, [$PATH]) + min_sdl_version=ifelse([$1], ,0.11.0,$1) + AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) + no_sdl="" + if test "$SDL_CONFIG" = "no" ; then + no_sdl=yes + else + SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags` + SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs` + + sdl_major_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_sdltest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" +dnl +dnl Now check if the installed SDL is sufficiently new. (Also sanity +dnl checks the results of sdl-config to some extent +dnl + rm -f conf.sdltest + AC_TRY_RUN([ +#include +#include +#include +#include "SDL.h" + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) + { + new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } + else + new_str = NULL; + + return new_str; +} + +int main (int argc, char *argv[]) +{ + int major, minor, micro; + char *tmp_version; + + /* This hangs on some systems (?) + system ("touch conf.sdltest"); + */ + { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_sdl_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_sdl_version"); + exit(1); + } + + if (($sdl_major_version > major) || + (($sdl_major_version == major) && ($sdl_minor_version > minor)) || + (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); + printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro); + printf("*** best to upgrade to the required version.\n"); + printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n"); + printf("*** to point to the correct copy of sdl-config, and remove the file\n"); + printf("*** config.cache before re-running configure\n"); + return 1; + } +} + +],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_sdl" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$SDL_CONFIG" = "no" ; then + echo "*** The sdl-config script installed by SDL could not be found" + echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the SDL_CONFIG environment variable to the" + echo "*** full path to sdl-config." + else + if test -f conf.sdltest ; then + : + else + echo "*** Could not run SDL test program, checking why..." + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" + AC_TRY_LINK([ +#include +#include "SDL.h" + +int main(int argc, char *argv[]) +{ return 0; } +#undef main +#define main K_and_R_C_main +], [ return 0; ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding SDL or finding the wrong" + echo "*** version of SDL. If it is not finding SDL, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means SDL was incorrectly installed" + echo "*** or that you have moved SDL since it was installed. In the latter case, you" + echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + SDL_CFLAGS="" + SDL_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(SDL_CFLAGS) + AC_SUBST(SDL_LIBS) + rm -f conf.sdltest +]) diff --git a/share/character/Heroine.json b/share/character/Heroine.json deleted file mode 100644 index 0ea723b..0000000 --- a/share/character/Heroine.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "tilemap": "Heroine.png", - "animation": "Heroine.json" -} diff --git a/share/texture/AlienWarrior.plist b/share/texture/AlienWarrior.plist deleted file mode 100644 index d3adf1b..0000000 --- a/share/texture/AlienWarrior.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - TilesU - 8 - TilesV - 4 - InvertAlpha - 0 - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/BackgroundFar.plist b/share/texture/BackgroundFar.plist deleted file mode 100644 index a02e454..0000000 --- a/share/texture/BackgroundFar.plist +++ /dev/null @@ -1,21 +0,0 @@ - - - - - TilesU - 1 - TilesV - 1 - InvertAlpha - 0 - - WrapU - ClampToEdge - WrapV - ClampToEdge - MinFilter - Linear - MagFilter - Linear - - diff --git a/share/texture/BackgroundNear.plist b/share/texture/BackgroundNear.plist deleted file mode 100644 index 46fe92a..0000000 --- a/share/texture/BackgroundNear.plist +++ /dev/null @@ -1,21 +0,0 @@ - - - - - TilesU - 1 - TilesV - 1 - InvertAlpha - 0 - - WrapU - Wrap - WrapV - Wrap - MinFilter - Linear - MagFilter - Linear - - diff --git a/share/texture/BigExplosion.plist b/share/texture/BigExplosion.plist deleted file mode 100644 index f9c42cb..0000000 --- a/share/texture/BigExplosion.plist +++ /dev/null @@ -1,25 +0,0 @@ - - - - - TilesU - 8 - TilesV - 1 - InvertAlpha - 0 - - MinFilter - Linear - MagFilter - Linear - WrapU - ClampToEdge - WrapV - ClampToEdge - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/Bonuses.plist b/share/texture/Bonuses.plist deleted file mode 100644 index 486e2f8..0000000 --- a/share/texture/Bonuses.plist +++ /dev/null @@ -1,25 +0,0 @@ - - - - - TilesU - 8 - TilesV - 4 - InvertAlpha - 0 - - MinFilter - Linear - MagFilter - Linear - WrapU - ClampToEdge - WrapV - ClampToEdge - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/Building.plist b/share/texture/Building.plist deleted file mode 100644 index 79084ea..0000000 --- a/share/texture/Building.plist +++ /dev/null @@ -1,21 +0,0 @@ - - - - - TilesU - 8 - TilesV - 4 - InvertAlpha - 0 - - WrapU - ClampToEdge - WrapV - ClampToEdge - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/Default.plist b/share/texture/Default.plist deleted file mode 100644 index 18a47ff..0000000 --- a/share/texture/Default.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - MinFilter - Linear - MagFilter - Linear - WrapU - Repeat - WrapV - Repeat - InvertAlpha - 0 - - diff --git a/share/texture/Font.plist b/share/texture/Font.plist deleted file mode 100644 index 7688344..0000000 --- a/share/texture/Font.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - TilesU - 8 - TilesV - 8 - InvertAlpha - 0 - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/Heroine.plist b/share/texture/Heroine.plist deleted file mode 100644 index d3adf1b..0000000 --- a/share/texture/Heroine.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - TilesU - 8 - TilesV - 4 - InvertAlpha - 0 - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/Jetbot.plist b/share/texture/Jetbot.plist deleted file mode 100644 index 6f7f246..0000000 --- a/share/texture/Jetbot.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - TilesU - 4 - TilesV - 2 - InvertAlpha - 0 - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/Particles.plist b/share/texture/Particles.plist deleted file mode 100644 index 486e2f8..0000000 --- a/share/texture/Particles.plist +++ /dev/null @@ -1,25 +0,0 @@ - - - - - TilesU - 8 - TilesV - 4 - InvertAlpha - 0 - - MinFilter - Linear - MagFilter - Linear - WrapU - ClampToEdge - WrapV - ClampToEdge - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/RobotTrooper.plist b/share/texture/RobotTrooper.plist deleted file mode 100644 index d3adf1b..0000000 --- a/share/texture/RobotTrooper.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - TilesU - 8 - TilesV - 4 - InvertAlpha - 0 - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/Scenery.plist b/share/texture/Scenery.plist deleted file mode 100644 index 95aedca..0000000 --- a/share/texture/Scenery.plist +++ /dev/null @@ -1,25 +0,0 @@ - - - - - TilesU - 4 - TilesV - 4 - InvertAlpha - 0 - - MinFilter - Linear - MagFilter - Linear - WrapU - Wrap - WrapV - Wrap - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/StatusBars.plist b/share/texture/StatusBars.plist deleted file mode 100644 index 148307a..0000000 --- a/share/texture/StatusBars.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - TilesU - 4 - TilesV - 1 - InvertAlpha - 0 - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/TowerBlock1.plist b/share/texture/TowerBlock1.plist deleted file mode 100644 index f969901..0000000 --- a/share/texture/TowerBlock1.plist +++ /dev/null @@ -1,25 +0,0 @@ - - - - - TilesU - 4 - TilesV - 4 - InvertAlpha - 0 - - MinFilter - Linear - MagFilter - Linear - WrapU - ClampToEdge - WrapV - ClampToEdge - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/share/texture/Trees.plist b/share/texture/Trees.plist deleted file mode 100644 index 3c9b875..0000000 --- a/share/texture/Trees.plist +++ /dev/null @@ -1,25 +0,0 @@ - - - - - TilesU - 2 - TilesV - 1 - InvertAlpha - 0 - - MinFilter - Linear - MagFilter - Linear - WrapU - ClampToEdge - WrapV - ClampToEdge - MinFilter - Nearest - MagFilter - Nearest - - diff --git a/src/Character.cc b/src/Character.cc new file mode 100644 index 0000000..64866c5 --- /dev/null +++ b/src/Character.cc @@ -0,0 +1,57 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 "Character.hh" + + +Character::Character(const std::string& name) : + texture(name), + anim(name) {} + +Character::~Character() +{ + //delete texture; + //delete anim; +} + + +void Character::draw(dc::scalar alpha) {} + +dc::tilemap& Character::getTilemap() +{ + return texture; +} + +dc::animation& Character::getAnimation() +{ + return anim; +} + + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/Character.hh b/src/Character.hh index 2f175ad..c9cf11a 100644 --- a/src/Character.hh +++ b/src/Character.hh @@ -29,13 +29,17 @@ #ifndef _CHARACTER_HH_ #define _CHARACTER_HH_ +#include "resource.hh" +#include "drawable.hh" +#include "animation.hh" +#include "tilemap.hh" + + /** - * @file Character.hh * Parent class of animate objects with "personalities." */ - -class Character : public dc::resource, public dc::drawable +class Character : public dc::drawable { public: struct exception : public std::runtime_error @@ -45,13 +49,20 @@ public: }; Character(const std::string& name); + ~Character(); void draw(dc::scalar alpha); - dc::tilemap* texture; - dc::animation* anim; + dc::tilemap& getTilemap(); + dc::animation& getAnimation(); + +private: + dc::tilemap texture; + dc::animation anim; }; #endif // _CHARACTER_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/Makefile.am b/src/Makefile.am index 40dd173..510dc9e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,28 +1,33 @@ -noinst_LIBRARIES = libdc.a +noinst_LTLIBRARIES = libdc.la -libdc_a_SOURCES = \ +libdc_la_SOURCES = \ ConvertUTF.c \ ConvertUTF.h \ + aabb.hh \ + animation.cc \ + animation.hh \ deserializer.cc \ deserializer.hh \ dispatcher.cc \ dispatcher.hh \ + drawable.hh \ engine.cc \ engine.hh \ + event.hh \ fastevents.c \ fastevents.h \ - math.cc \ + interpolator.hh \ math.hh \ - matrix.hh \ + mippleton.hh \ opengl.hh \ - quaternion.hh \ + profiler.hh \ random.cc \ random.hh \ - rectangle.cc \ - rectangle.hh \ resource.cc \ resource.hh \ + scene.cc \ + scene.hh \ serializable.cc \ serializable.hh \ serializer.cc \ @@ -35,27 +40,34 @@ libdc_a_SOURCES = \ texture.cc \ texture.hh \ thread.hh \ + tilemap.cc \ tilemap.hh \ timer.cc \ timer.hh \ - vector.hh \ video.cc \ video.hh \ $(ENDLIST) -libdc_a_CPPFLAGS = -I/usr/include/SDL -I$(top_srcdir)/yajl/src -Wall -#libdc_a_LDFLAGS = -lstdc++ -lSDL_image -lSDL_sound -libdc_a_LIBADD = $(top_srcdir)/yajl/libyajl.a +libdc_la_CPPFLAGS = -I/usr/include/SDL -I$(top_srcdir)/yajl/src +libdc_la_LIBADD = $(top_srcdir)/yajl/libyajl.la bin_PROGRAMS = yoink yoink_SOURCES = \ + Character.cc \ + Character.hh \ + TilemapFont.cc \ + TilemapFont.hh \ + Typesetter.cc \ + Typesetter.hh \ YoinkApp.cc \ YoinkApp.hh \ $(ENDLIST) -yoink_CPPFLAGS = -I/usr/include/SDL -Wall -#yoink_LDFLAGS = -lstdc++ -lSDL_image -lSDL_sound -yoink_LDADD = libdc.a libtinyxml.a ../yajl/libyajl.a +yoink_CPPFLAGS = -I/usr/include/SDL +yoink_LDADD = libdc.la + + +EXTRA_DIST = cml diff --git a/src/TilemapFont.cc b/src/TilemapFont.cc new file mode 100644 index 0000000..60f070c --- /dev/null +++ b/src/TilemapFont.cc @@ -0,0 +1,61 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 "TilemapFont.hh" +#include + + +TilemapFont::TilemapFont() : + dc::tilemap("Font") +{} + + +void TilemapFont::getTileCoords(char symbol, dc::scalar coords[8], + dc::tilemap::orientation what) +{ + unsigned index = 0; + + if (symbol >= ' ' && symbol <= '_') + { + index = symbol - 32; + } + else if (symbol >= 'a' && symbol <= '~') + { + index = symbol - 64; + } + else + { + index = 0; + } + + dc::tilemap::getTileCoords(index, coords, what); +} + + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/TilemapFont.hh b/src/TilemapFont.hh new file mode 100644 index 0000000..5ec9064 --- /dev/null +++ b/src/TilemapFont.hh @@ -0,0 +1,59 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 _TILEMAPFONT_HH_ +#define _TILEMAPFONT_HH_ + +/** + * @file TilemapFont.hh + * Text on the screen. + */ + +#include "tilemap.hh" + + +class TilemapFont : public dc::tilemap +{ +public: + struct exception : public std::runtime_error + { + explicit exception(const std::string& what_arg) : + std::runtime_error(what_arg) {} + }; + + TilemapFont(); + + void getTileCoords(char symbol, dc::scalar coords[8], + dc::tilemap::orientation what = dc::tilemap::normal); +}; + + +#endif // _TILEMAPFONT_HH_ + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/Typesetter.cc b/src/Typesetter.cc new file mode 100644 index 0000000..f658c26 --- /dev/null +++ b/src/Typesetter.cc @@ -0,0 +1,51 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 "Typesetter.hh" + + +void Typesetter::print(const std::string& format, ...) +{ + va_list args; + char buffer[4096]; + int nPrinted; + + va_start(args, format); + buffer[0] = '\0'; + nPrinted = vsnprintf(buffer, sizeof(buffer), format.c_str(), args); + va_end(args); + + nPrinted = std::min(nPrinted, (int)sizeof(buffer) - 1); +} + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/Typesetter.hh b/src/Typesetter.hh new file mode 100644 index 0000000..d9ccf42 --- /dev/null +++ b/src/Typesetter.hh @@ -0,0 +1,56 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 _TYPESETTER_HH_ +#define _TYPESETTER_HH_ + +#include + +#include "math.hh" + + +class Typesetter +{ +public: + Typesetter(); + + void setLineSpacing(dc::scalar spacing); + + void print(const std::string& format, ...); + +private: + dc::scalar leftBound; + dc::scalar topBound; + dc::scalar lineSpacing; +}; + + +#endif // _TYPESETTER_HH_ + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/YoinkApp.cc b/src/YoinkApp.cc index 64decfb..4b024b4 100644 --- a/src/YoinkApp.cc +++ b/src/YoinkApp.cc @@ -29,154 +29,298 @@ #include #include +#include // getenv + #include #include "opengl.hh" #include "video.hh" +#include "settings.hh" -#include "vector.hh" +#include "math.hh" #include "YoinkApp.hh" +#include "timer.hh" + +#if HAVE_CONFIG_H +#include "config.h" +#endif + + +static std::string configFiles() +{ + std::string files; + + char* configFile = getenv("YOINK_CONFIGFILE"); + + if (configFile) + { + // if a config file from the environment variable is specified, we want + // to load it first + files += configFile; + files += ":"; + } + + files += YOINK_CONFIGFILES; + + return files; +} + -YoinkApp::YoinkApp(int argc, char* argv[]) - : dc::engine("Yoink "VERSION, argc, argv, "yoinkrc") +YoinkApp::YoinkApp(int argc, char* argv[]) : + dc::engine(PACKAGE_STRING, argc, argv, configFiles()) { - std::cout << "Yoink "VERSION << std::endl + std::cout << PACKAGE_STRING << std::endl << "Compiled " << __TIME__ " " __DATE__ << std::endl - << "Send requests, patches, and bug reports to <" PACKAGE_BUGREPORT - << ">." << std::endl << std::endl; + << "Send requests, patches, and bug reports to <" + PACKAGE_BUGREPORT << ">." << std::endl << std::endl; + + dc::resource::addSearchPath(YOINK_DATADIR); + + dc::dispatcher::instance().addHandler("video.context_recreated", + boost::bind(&YoinkApp::contextRecreated, this, _1), this); + setupGL(); state = 0.0; - glEnable(GL_TEXTURE_2D); + someChar = new Character("RobotTrooper"); + someChar->getAnimation().startSequence("Run"); - heroineTexture = new dc::texture("Heroine.png"); + font = new TilemapFont; - glShadeModel(GL_SMOOTH); - //glEnable(GL_DEPTH_TEST); - - // Enable transparency: - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + dc::vector2 coeffs[4]; + coeffs[0] = dc::vector2(0.0, 0.0); + coeffs[1] = dc::vector2(0.5, 0.0); + coeffs[2] = dc::vector2(0.5, 0.0); + coeffs[3] = dc::vector2(1.0, 0.0); + interp.init(coeffs, 1.0, dc::interpolator::oscillate); - glLoadIdentity(); + dc::scalar coeff[2] = {1.0, 0.0}; + fadeIn.init(coeff, 0.5f); + testScene = new dc::scene("Test"); } YoinkApp::~YoinkApp() { - delete heroineTexture; + delete someChar; + delete font; + + dc::dispatcher::instance().removeHandler(this); + std::cout << "Goodbye..." << std::endl; } -void YoinkApp::update(double t, double dt) +void YoinkApp::setupGL() +{ + glEnable(GL_TEXTURE_2D); + //glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + glShadeModel(GL_SMOOTH); + + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.0); + + glClearColor(0.0, 0.0, 1.0, 1.0); + + glLineWidth(10.0f); +} + +void YoinkApp::contextRecreated(const dc::notification& note) +{ + // Whenever the context and a new one created, it probably won't contain our + // state so we need to set that up again. + setupGL(); +} + + +void YoinkApp::update(dc::scalar t, dc::scalar dt) { - //dt *= 0.1; + //dt *= 0.2; + + fadeIn.update(dt); + + someChar->getAnimation().update(t, dt); + interp.update(dt); prevstate = state; state += dt; } -void drawrect(dc::scalar a, dc::scalar b, dc::scalar c, dc::scalar d) + +void YoinkApp::draw(dc::scalar alpha) { + dc::vector4 meh; + meh.random(0.0, 1.0); + static dc::vector4 c1(meh); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + dc::scalar drawstate = cml::lerp(prevstate, state, alpha); + dc::scalar sinstate = std::sin(drawstate); + dc::scalar cosstate = std::cos(drawstate); + + + // DRAW THE SCENE + testScene->draw(alpha); + + + someChar->getTilemap().bind(); + glColor3f(1.0, 1.0, 1.0); + + unsigned heroFrame = someChar->getAnimation().getFrame(); + + float coords[8]; + someChar->getTilemap().getTileCoords(heroFrame, coords); + glBegin(GL_QUADS); - glTexCoord2f(1.0, 1.0); - glVertex3d(a, d, 0.0); - glTexCoord2f(1.0, 0.0); - glVertex3d(a, b, 0.0); - glTexCoord2f(0.0, 0.0); - glVertex3d(c, b, 0.0); - glTexCoord2f(0.0, 1.0); - glVertex3d(c, d, 0.0); + glTexCoord2f(coords[0], coords[1]); + glVertex3f(-1.0, 0.0, 0.0); + glTexCoord2f(coords[2], coords[3]); + glVertex3f(0.0, 0.0, 0.0); + glTexCoord2f(coords[4], coords[5]); + glVertex3f(0.0, 1.0, 0.0); + glTexCoord2f(coords[6], coords[7]); + glVertex3f(-1.0, 1.0, 0.0); glEnd(); -} -void YoinkApp::draw(double alpha) -{ - static dc::vector3 c1 = dc::vector3::random(0.0, 1.0); - static dc::vector3 c2 = dc::vector3::random(0.0, 1.0); - glClearColor(1.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + someChar->getTilemap().getTileCoords(heroFrame, coords, + dc::tilemap::reverse); - double drawstate = state * alpha + prevstate * (1.0 - alpha); - double sinstate = std::sin(drawstate); - double cosstate = std::cos(drawstate); + glBegin(GL_QUADS); + glTexCoord2f(coords[0], coords[1]); + glVertex3f(0.0, 0.0, 0.0); + glTexCoord2f(coords[2], coords[3]); + glVertex3f(1.0, 0.0, 0.0); + glTexCoord2f(coords[4], coords[5]); + glVertex3f(1.0, 1.0, 0.0); + glTexCoord2f(coords[6], coords[7]); + glVertex3f(0.0, 1.0, 0.0); + glEnd(); + + glColor4f(1.0,0.0,0.0,0.5); glBindTexture(GL_TEXTURE_2D, 0); + glColor4fv(c1.data()); - glColor3dv(c1.array); - drawrect(-cosstate, -sinstate, sinstate, cosstate); - glColor3dv(c2.array); - drawrect(0.0, 0.0, sinstate, cosstate); + glRectd(-cosstate, -sinstate, sinstate, cosstate); + glRectf(0.0f, 0.0f, sinstate, cosstate); - glColor4f(1.0, 1.0, 1.0, 1.0); + font->bind(); + + font->getTileCoords('c', coords); - heroineTexture->bind(); + glBegin(GL_QUADS); + glTexCoord2f(coords[0], coords[1]); + glVertex3f(-1.0, 0.0, 0.0); + glTexCoord2f(coords[2], coords[3]); + glVertex3f(0.0, 0.0, 0.0); + glTexCoord2f(coords[4], coords[5]); + glVertex3f(0.0, 1.0, 0.0); + glTexCoord2f(coords[6], coords[7]); + glVertex3f(-1.0, 1.0, 0.0); + glEnd(); + + font->getTileCoords('h', coords); glBegin(GL_QUADS); - glTexCoord2f(0.0, 0.0); + glTexCoord2f(coords[0], coords[1]); glVertex3f(0.0, 0.0, 0.0); - glTexCoord2f(1.0/8.0, 0.0); + glTexCoord2f(coords[2], coords[3]); glVertex3f(1.0, 0.0, 0.0); - glTexCoord2f(1.0/8.0, 1.0/4.0); + glTexCoord2f(coords[4], coords[5]); glVertex3f(1.0, 1.0, 0.0); - glTexCoord2f(0.0, 1.0/4.0); + glTexCoord2f(coords[6], coords[7]); glVertex3f(0.0, 1.0, 0.0); glEnd(); + + font->getTileCoords('a', coords); + + glBegin(GL_QUADS); + glTexCoord2f(coords[0], coords[1]); + glVertex3f(-1.0, -1.0, 0.0); + glTexCoord2f(coords[2], coords[3]); + glVertex3f(0.0, -1.0, 0.0); + glTexCoord2f(coords[4], coords[5]); + glVertex3f(0.0, 0.0, 0.0); + glTexCoord2f(coords[6], coords[7]); + glVertex3f(-1.0, 0.0, 0.0); + glEnd(); + + font->getTileCoords('z', coords); + + glBegin(GL_QUADS); + glTexCoord2f(coords[0], coords[1]); + glVertex3f(0.0, -1.0, 0.0); + glTexCoord2f(coords[2], coords[3]); + glVertex3f(1.0, -1.0, 0.0); + glTexCoord2f(coords[4], coords[5]); + glVertex3f(1.0, 0.0, 0.0); + glTexCoord2f(coords[6], coords[7]); + glVertex3f(0.0, 0.0, 0.0); + glEnd(); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); - glFlush(); + glBindTexture(GL_TEXTURE_2D, 0); + glColor4f(0.0f, 0.0f, 0.0f, 1.0f); + + glBegin(GL_LINES); + glVertex2f(0.0f, 0.0f); + glVertex2fv(interp.getState(alpha).data()); + glEnd(); + + glColor4f(0.0f, 0.0f, 0.0f, fadeIn.getState(alpha)); + glRectf(-1.0f, -1.0f, 1.0f, 1.0f); + + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); } -void YoinkApp::dispatchEvent(const SDL_Event& event) +void YoinkApp::handleEvent(const dc::event& e) { - switch (event.type) + switch (e.type) { case SDL_KEYDOWN: - if (event.key.keysym.sym == SDLK_ESCAPE) + if (e.key.keysym.sym == SDLK_ESCAPE) { stop(); } - else if (event.key.keysym.sym == SDLK_f) + else if (e.key.keysym.sym == SDLK_f) { getVideo().toggleFull(); } + else if (e.key.keysym.sym == SDLK_a) + { + someChar->getAnimation().startSequence("Punch"); + } break; case SDL_QUIT: stop(); break; - } -} - -#include "dispatcher.hh" - -class Foo : public dc::notification -{ -public: - static void func(const dc::notification& meh) - { - std::cout << "func: " << std::endl; - } - - void snap(int zzz, const dc::notification& nice, float lean) - { - std::cout << "snap: " << zzz << "," << lean << std::endl; + case SDL_VIDEORESIZE: + glViewport(0, 0, e.resize.w, e.resize.h); + break; } -}; - -void MyHandler(const dc::notification& cool) -{ - std::cout << "MyHandler with a notification" << std::endl; } + int main(int argc, char* argv[]) { YoinkApp app(argc, argv); return app.run(); } +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/YoinkApp.hh b/src/YoinkApp.hh index 69b53d9..055a64c 100644 --- a/src/YoinkApp.hh +++ b/src/YoinkApp.hh @@ -29,12 +29,24 @@ #ifndef _YOINKAPP_HH_ #define _YOINKAPP_HH_ +/** + * @file YoinkApp.hh + * This is the big enchilada. + */ + #include #include +#include "dispatcher.hh" +#include "math.hh" +#include "interpolator.hh" #include "engine.hh" -#include "texture.hh" +#include "Character.hh" + +#include "TilemapFont.hh" + +#include "scene.hh" class YoinkApp : public dc::engine @@ -44,16 +56,33 @@ public: ~YoinkApp(); private: - void update(double t, double dt); - void draw(double alpha); - void dispatchEvent(const SDL_Event& event); + void update(dc::scalar t, dc::scalar dt); + void draw(dc::scalar alpha); + void handleEvent(const dc::event& e); + + /** + * Set OpenGL to a state we can know and depend on. + */ + void setupGL(); + void contextRecreated(const dc::notification& note); - dc::texture* heroineTexture; + //dc::animation* heroAnimation; + //dc::tilemap* heroineTexture; + Character* someChar; + //dc::binomial_interpolator interp; + dc::cerpv2 interp; + dc::lerps fadeIn; - double state; - double prevstate; + dc::scene* testScene; + + TilemapFont *font; + + dc::scalar state; + dc::scalar prevstate; }; #endif // _YOINKAPP_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/aabb.hh b/src/aabb.hh new file mode 100644 index 0000000..e608b21 --- /dev/null +++ b/src/aabb.hh @@ -0,0 +1,65 @@ + +/****************************************************************************** + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 _AABB_HH_ +#define _AABB_HH_ + +#include "math.hh" + + +namespace dc { + + +/** + * Axis-aligned Bounding Box + */ + +struct aabb +{ + aabb() {} + + aabb(const vector3& minPoint, const vector3& maxPoint) : + min(minPoint), + max(maxPoint) {} + + aabb (scalar minX, scalar minY, scalar minZ, + scalar maxX, scalar maxY, scalar maxZ) : + min(minX, minY, minZ), + max(maxX, maxY, maxZ) {} + + vector3 min; + vector3 max; +}; + + +} // namespace dc + +#endif // _AABB_HH_ + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/animation.cc b/src/animation.cc index 745f0e4..c03a49d 100644 --- a/src/animation.cc +++ b/src/animation.cc @@ -26,25 +26,329 @@ *******************************************************************************/ +#include +#include + +#include "serializable.hh" +#include "mippleton.hh" #include "animation.hh" namespace dc { -class animation_impl +/** + * The collection of nested animation classes. The animation implementation + * consists of an animation_impl classes which is allocated and initialized with + * the interface object. This class contains the specific fields which are + * required to run a single instance of an animation. The sequence data is + * loaded in a difference class which can be shared amongst multiple animation + * implementation instances. + */ + +struct animation::animation_impl { -public: - class sequence + /** + * Contains "global" animation data for the various animations which get + * loaded. This is a mippleton, so it will be shared amongst any animation + * which wants to use these loaded sequences. + */ + + struct animation_data : public mippleton { + /** + * A frame of an animation sequence. A frame is merely an index which + * presumably represents a "slide" or tile which should be displayed, + * and the duration that is how long the slide will be shown. + */ + + struct frame + { + unsigned index; ///< Frame index. + scalar duration; ///< Frame duration. + + /** + * Construction is initialization. The frame data is loaded from a + * frame map which is probably loaded within an animation file. + */ + + frame(serializable_ptr root) : + index(0), + duration(1.0) + { + std::map rootObj; + + if (root->get(rootObj)) + { + std::map::iterator i; + for (i = rootObj.begin(); i != rootObj.end(); i++) + { + std::string key = (*i).first; + if (key == "index") + { + long value = 0; + (*i).second->get(value); + index = unsigned(value); + } + else if (key == "duration") + { + double value = 0.0; + (*i).second->getNumber(value); + duration = scalar(value); + } + } + } + } + }; + + + /** + * A sequence is just a few attributes and a list of frames in the order + * that they should be played. + */ + + struct sequence + { + std::vector frames; ///< List of frames. + scalar delay; ///< Scale frame durations. + bool loop; ///< Does the sequence repeat? + std::string next; ///< Next sequence name. + + /** + * Construction is initialization. The constructor loads sequence + * data from the sequence map, presumably loaded from an animation + * file. The rest of the loading takes place in the frame's + * constructor which loads each individual frame. + */ + + sequence(serializable_ptr root) : + delay(0.0), + loop(true) + { + std::map rootObj; + + if (root->get(rootObj)) + { + std::map::iterator i; + for (i = rootObj.begin(); i != rootObj.end(); i++) + { + std::string key = (*i).first; + + if (key == "frames") + { + std::vector framesObj; + + if ((*i).second->get(framesObj)) + { + std::vector::iterator j; + + for (j = framesObj.begin(); + j != framesObj.end(); j++) + { + if (*j) + { + frames.push_back(frame(*j)); + } + } + } + } + else if (key == "delay") + { + double value; + (*i).second->getNumber(value); + delay = scalar(value); + } + else if (key == "loop") + { + (*i).second->get(loop); + } + else if (key == "next") + { + (*i).second->get(next); + } + } + } + } + }; + + + /** + * Starts loading a file with animation data. Such a file is formatted + * as a map of named sequences. The sequence constructor loads each + * individual sequence. + */ + + void loadFromFile() + { + std::string filePath = animation::getPathToResource(getName()); + + deserializer in(filePath); + + serializable_ptr root = in.deserialize(); + + if (root) + { + std::map rootObj; + + if (root->get(rootObj)) + { + std::map::iterator i; + + for (i = rootObj.begin(); i != rootObj.end(); i++) + { + sequences.insert(std::pair((*i).first, + sequence((*i).second))); + } + } + } + } + + /** + * Construction is initialization. The animation class data container + * registers itself as a mippleton and then loads the animation data. + */ + + explicit animation_data(const std::string& name) : + mippleton(name) + { + loadFromFile(); + } + + std::map sequences; ///< List of sequences. }; - std::map sequences; + + /** + * Construction is intialization. + */ + + animation_impl(const std::string& name) : + data(animation_data::retain(name), &animation_data::release), + currentSequence(0), + frameCounter(0), + frameIndex(0), + timeAccum(0), + frameDuration(0) {} + + + /** + * Sets up the animation classes to "play" a named sequence. If another + * sequence was active, it will be replaced as the current sequence. Future + * updates will progress the new sequence. + */ + + void startSequence(const std::string& sequenceName) + { + std::map::iterator i; + + i = data->sequences.find(sequenceName); + + if (i != data->sequences.end()) + { + currentSequence = &(*i).second; + frameCounter = 0; + frameIndex = currentSequence->frames[0].index; + timeAccum = 0.0; + frameDuration = currentSequence->delay * + currentSequence->frames[0].duration; + } + } + + /** + * Updates or progresses the animation sequence. If the time interval + * surpasses the duration of the current frame, a new frame becomes the + * current frame. If the last frame of a sequence expires, the active + * sequence will switch automatically to the designated "next" sequence, or + * if none is specified but the sequence is set to loop, the first frame of + * the sequence will become the current frame, and the animation essentially + * starts over again. + */ + + void update(scalar t, scalar dt) + { + if (currentSequence) + { + timeAccum += dt; + + if (timeAccum >= frameDuration) + { + if (++frameCounter >= currentSequence->frames.size()) + { + if (!currentSequence->next.empty()) + { + startSequence(currentSequence->next); + } + else if (currentSequence->loop) + { + frameCounter = 0; + } + else + { + frameCounter--; + currentSequence = 0; + } + } + + frameIndex = currentSequence->frames[frameCounter].index; + timeAccum = frameDuration - timeAccum; + frameDuration = currentSequence->delay * + currentSequence->frames[frameCounter].duration; + } + } + } + + boost::shared_ptr data; ///< Internal data. + + animation_data::sequence* currentSequence; ///< Active sequence. + unsigned frameCounter; ///< Current frame. + unsigned frameIndex; ///< Index of current frame. + scalar timeAccum; ///< Time accumulation. + scalar frameDuration; ///< Scaled frame duration. }; +animation::animation(const std::string& name) : + // pass through + impl(new animation::animation_impl(name)) {} + + +void animation::startSequence(const std::string& sequenceName) +{ + // pass through + impl->startSequence(sequenceName); +} + +void animation::update(scalar t, scalar dt) +{ + // pass through + impl->update(t, dt); +} + + +/** + * Gets the index for the current frame. This is presumably called by some + * drawing code which will draw the correct current frame. + */ + +unsigned animation::getFrame() const +{ + return impl->frameIndex; +} + + +/** + * Specialized search location for animation files. They can be found in the + * "animations" subdirectory of any of the searched directories. + */ + +std::string animation::getPathToResource(const std::string& name) +{ + return resource::getPathToResource("animations/" + name + ".json"); +} } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/animation.hh b/src/animation.hh index 85307ac..ca56581 100644 --- a/src/animation.hh +++ b/src/animation.hh @@ -34,26 +34,39 @@ * Motion picture!! */ +#include + #include +#include "resource.hh" +#include "math.hh" + namespace dc { -class animation_impl; +/** + * A class to manage frame-based animation. Animation sequences can be loaded + * from file, then named sequences are started. The animation is updated + * periodically (each update cycle), and the correct current frame is + * determined. This class is generic enough that a frame can mean just about + * anything to whatever drawing context is used to render the frame. + */ class animation : public resource { public: animation(const std::string& name); - void setSequence(const std::string& sequence); - bool isSequenceDone(); + void startSequence(const std::string& sequenceName); + + void update(scalar t, scalar dt); + unsigned getFrame() const; - void update(scalar dt); - unsigned getFrame(); + static std::string getPathToResource(const std::string& name); private: + class animation_impl; boost::shared_ptr impl; }; @@ -62,3 +75,5 @@ private: #endif // _ANIMATION_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/camera.hh b/src/camera.hh new file mode 100644 index 0000000..dcdc55d --- /dev/null +++ b/src/camera.hh @@ -0,0 +1,48 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 _CAMERA_HH_ +#define _CAMERA_HH_ + + +namespace dc { + + +class camera +{ +public: +private: +}; + + +} // namespace dc + +#endif // _CAMERA_HH_ + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/rectangle.cc b/src/cullable.hh similarity index 55% rename from src/rectangle.cc rename to src/cullable.hh index 0f9bb9f..e4fe4ca 100644 --- a/src/rectangle.cc +++ b/src/cullable.hh @@ -26,59 +26,29 @@ *******************************************************************************/ -#include +#ifndef _CULLABLE_HH_ +#define _CULLABLE_HH_ -#include "rectangle.hh" +#include "camera.hh" namespace dc { -bool rectangle::containsRect(const rectangle& r) const -{ - point o = r.origin; - point s = r.size; - if (origin.x <= o.x && origin.y <= o.y) - { - scalar a = origin.x + size.x; - scalar b = origin.y + size.y; - scalar c = o.x + s.x; - scalar d = o.y + s.y; - return o.x <= a && o.y <= b && origin.x <= c && - origin.y <= d && c <= a && d <= b; - } - return false; -} +/** + * Interface for anything that can be culled given a camera's frustrum. + */ -bool rectangle::intersectsRect(const rectangle& r) const +class cullable { - // TODO - Perhaps this could be optimized if it proves to be a bottleneck. - rectangle rect = intersectionWith(r); - return rect.isValid(); -} +public: + virtual bool isVisible(const camera& cam) = 0; +}; -rectangle rectangle::unionWith(const rectangle& r) const -{ - point o = r.origin; - point s = r.size; - scalar a = std::min(origin.x, o.x); - scalar b = std::min(origin.y, o.y); - return rectangle(point(a, b), - point(std::max(origin.x + size.x, o.x + s.x) - a, - std::max(origin.y + size.y, o.y + s.y) - b)); -} -rectangle rectangle::intersectionWith(const rectangle& r) const -{ - point o = r.origin; - point s = r.size; - scalar a = std::max(origin.x, o.x); - scalar b = std::max(origin.y, o.y); - return rectangle(point(a, b), - point(std::min(origin.x + size.x, o.x + s.x) - a, - std::min(origin.y + size.y, o.y + s.y) - b)); -} +} // namespace dc +#endif // _CULLABLE_HH_ -} // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ diff --git a/src/deserializer.cc b/src/deserializer.cc index 4c3f8f2..8ad3059 100644 --- a/src/deserializer.cc +++ b/src/deserializer.cc @@ -38,7 +38,7 @@ namespace dc { -class deserializer_impl +class deserializer::deserializer_impl { public: deserializer_impl(const std::string& filePath, bool comments = false, @@ -72,9 +72,9 @@ public: void throwError() { unsigned char* errorMsg = yajl_get_error(hand, 0, 0, 0); - deserializer::exception error((char*)errorMsg); + deserializer::exception problem((char*)errorMsg); yajl_free_error(hand, errorMsg); - throw error; + throw problem; } @@ -114,6 +114,7 @@ public: static int parsedBeginMap(void* ctx) { ((deserializer_impl*)ctx)->parsed.push(new wrapped_dictionary); + return 1; } static int parsedMapKey(void* ctx, const unsigned char* value, @@ -205,10 +206,13 @@ private: deserializer::deserializer(const std::string& filePath, bool comments, - bool check) : impl(new deserializer_impl(filePath, comments, check)) {} + bool check) : + // pass through + impl(new deserializer::deserializer_impl(filePath, comments, check)) {} deserializer::deserializer(std::istream& input, bool comments, bool check) : - impl(new deserializer_impl(input, comments, check)) {} + // pass through + impl(new deserializer::deserializer_impl(input, comments, check)) {} serializable_ptr deserializer::deserialize() @@ -235,9 +239,12 @@ serializable* deserializer::pullNext() void deserializer::pop() { + // pass through impl->parsed.pop(); } } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/deserializer.hh b/src/deserializer.hh index 733ce39..f2b0bcc 100644 --- a/src/deserializer.hh +++ b/src/deserializer.hh @@ -44,23 +44,72 @@ namespace dc { -class serializable; +class serializable; // forward declaration typedef boost::shared_ptr serializable_ptr; -class deserializer_impl; - class deserializer { public: + + /** + * Construction is initialization. A deserializer can be constructed with + * either an input stream or a string representing a filename that will be + * read from. If a stream is provided, it will not be closed after parsing, + * but the parser may read past the end of usable bytes, so you should not + * use a deserializer on a stream that you expect to continue to use after + * the deserialization. + * @param comments If true, C and C++-style comments will be allowed and + * ignored by the parser. + * @param check If true, UTF-8 strings will be checked for validity and an + * exception thrown if such a problem exists. Otherwise strings will not be + * checked. + */ + deserializer(const std::string& filePath, bool comments = false, bool check = false); deserializer(std::istream& input, bool comments = false, bool check = false); + + /** + * Parse the object from of the stream. The stream is considered to be + * dominated by the parser and may read and discard bytes past the end of + * the actual parsed object. Only one object can be deserialized by the + * deserializer. + */ + serializable_ptr deserialize(); + /** + * Used by serializable objects to parse themselves. These methods should + * generally not be used directory for deserialization. This one returns + * the next object in the queue which has been parsed. This method may + * block if more data is pending and an object has not bee constructed yet. + * The caller takes ownership of the object which has been allocated with + * the new operator and must therefore be sure to delete the object as + * appropriated. Null (0) will be returned by this method to signify one of + * three things: 1) the end of an array, 2) the end of a map/dictionary, or + * 3) there is nothing more to be obtained. Container objects will be empty + * and will have to be filled with their contained elements by repeatedly + * calling this method until a null is returned. This method will continue + * to return the same value until pop() is called which will cause this + * method to return the next object as expected. + */ + serializable* pullNext(); + + /** + * If the object returned by pullNext() has been received successfully and + * the caller is ready for the next object, this method should be called to + * take that object off of the queue. + */ + void pop(); + + /** + * This exception is thrown upon deserialization errors. + */ + struct exception : std::runtime_error { explicit exception(const std::string& what_arg) : @@ -68,6 +117,7 @@ public: }; private: + class deserializer_impl; boost::shared_ptr impl; }; @@ -77,3 +127,5 @@ private: #endif // _DESERIALIZER_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/dispatcher.cc b/src/dispatcher.cc index 509b69d..d42077a 100644 --- a/src/dispatcher.cc +++ b/src/dispatcher.cc @@ -37,7 +37,7 @@ namespace dc { notification::~notification() {} -class dispatcher_impl +class dispatcher::dispatcher_impl { public: dispatcher_impl() : id(1) {} @@ -62,9 +62,11 @@ public: }; -dispatcher::dispatcher() : impl(new dispatcher_impl) {} +dispatcher::dispatcher() : impl(new dispatcher::dispatcher_impl) {} +// TODO these methods are ugly + dispatcher::handler dispatcher::addHandler(const std::string& message, const function& callback) { @@ -74,8 +76,8 @@ dispatcher::handler dispatcher::addHandler(const std::string& message, dispatcher::handler dispatcher::addHandler(const std::string& message, const function& callback, handler id) { - std::pair - callbackPair(message, dispatcher_impl::callback_t(id, callback)); + std::pair + callbackPair(message, dispatcher::dispatcher_impl::callback_t(id, callback)); std::pair handlerPair(id, message); @@ -88,16 +90,16 @@ dispatcher::handler dispatcher::addHandler(const std::string& message, void dispatcher::removeHandler(handler id) { - std::pair + std::pair handlers(impl->handlers.equal_range(id)); - dispatcher_impl::handler_it_t i; + dispatcher::dispatcher_impl::handler_it_t i; for (i = handlers.first; i != handlers.second; i++) { - dispatcher_impl::callback_it_t it = impl->callbacks.find((*i).second); - dispatcher_impl::callback_it_t last = impl->callbacks.end(); + dispatcher::dispatcher_impl::callback_it_t it = impl->callbacks.find((*i).second); + dispatcher::dispatcher_impl::callback_it_t last = impl->callbacks.end(); - dispatcher_impl::callback_it_t j; + dispatcher::dispatcher_impl::callback_it_t j; for (j = it; j != last; j++) { if (((*j).second).first == id) @@ -119,10 +121,10 @@ void dispatcher::dispatch(const std::string& message) void dispatcher::dispatch(const std::string& message, const notification& param) { - std::pair + std::pair callbacks(impl->callbacks.equal_range(message)); - dispatcher_impl::callback_it_t i; + dispatcher::dispatcher_impl::callback_it_t i; for (i = callbacks.first; i != callbacks.second; i++) { function callback = ((*i).second).second; @@ -133,3 +135,5 @@ void dispatcher::dispatch(const std::string& message, const notification& param) } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/dispatcher.hh b/src/dispatcher.hh index e45e88e..1a7744a 100644 --- a/src/dispatcher.hh +++ b/src/dispatcher.hh @@ -29,11 +29,6 @@ #ifndef _DISPATCHER_HH_ #define _DISPATCHER_HH_ -/** - * @file dispatcher.hh - * Dispatching of information. - */ - #include #include @@ -45,7 +40,10 @@ namespace dc { -// Subclass this to create more specific notes to dispatch. +/** + * Interface for a notification class. + */ + class notification { public: @@ -53,7 +51,9 @@ public: }; -class dispatcher_impl; +/** + * Dispatcher of notifications to interested parties. + */ class dispatcher : public singleton { @@ -74,6 +74,7 @@ public: void dispatch(const std::string& message, const notification& param); private: + class dispatcher_impl; boost::shared_ptr impl; }; @@ -82,3 +83,5 @@ private: #endif // _DISPATCHER_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/drawable.hh b/src/drawable.hh index 835fb61..031cf79 100644 --- a/src/drawable.hh +++ b/src/drawable.hh @@ -29,15 +29,16 @@ #ifndef _DRAWABLE_HH_ #define _DRAWABLE_HH_ -/** - * @file drawable.hh - * Interface for a drawable object. - */ +#include "math.hh" namespace dc { +/** + * Interface for anything that can be drawn. + */ + class drawable { public: @@ -49,3 +50,5 @@ public: #endif // _DRAWABLE_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/engine.cc b/src/engine.cc index 3e40361..ec6faa1 100644 --- a/src/engine.cc +++ b/src/engine.cc @@ -46,12 +46,13 @@ namespace dc { -class engine_impl +class engine::engine_impl { public: engine_impl(const std::string& name, int argc, char* argv[], - const std::string& configFile, engine* outer) : config(argc, argv), - interface(outer) + const std::string& configFile, engine* outer) : + config(argc, argv), + interface(outer) { if (SDL_Init(SDL_INIT_EVERYTHING | SDL_INIT_EVENTTHREAD) != 0) { @@ -69,8 +70,9 @@ public: screen = video_ptr(new video(name)); screen->makeActive(); - timestep = 0.01; - config.get("engine.timestep", timestep); + double ts = 0.01; + config.get("engine.timestep", ts); + timestep = scalar(ts); long maxfps = 40; config.get("engine.maxfps", maxfps); @@ -99,10 +101,10 @@ public: scalar nextFPSUpdate = ticksNow + 1.0; scalar totalTime = 0.0; - scalar accumulator = 0.0; + scalar accumulator = timestep; - fps = 0.0; - int frameAccum = 0.0; + fps = 0; + int frameAccum = 0; running = true; do @@ -113,7 +115,7 @@ public: accumulator += newTicks - ticksNow; ticksNow = newTicks; - if (ticksNow >= nextStep) + if (accumulator >= timestep) { interface->update(totalTime, timestep); @@ -121,20 +123,28 @@ public: accumulator -= timestep; nextStep += timestep; - if (ticksNow >= nextStep) nextStep = ticksNow + timestep; + if (ticksNow >= nextStep) + { + // we missed some scheduled steps, so reset the schedule + nextStep = ticksNow + timestep; + accumulator = 0.0; + } } if (ticksNow >= nextDraw) { frameAccum++; - if (ticksNow >= nextFPSUpdate) + if (ticksNow >= nextFPSUpdate) // determine the actual fps { - fps = frameAccum;// + (ticksNow - nextFPSUpdate) / 1.0; + fps = frameAccum; frameAccum = 0; nextFPSUpdate += 1.0; - if (ticksNow >= nextFPSUpdate) nextFPSUpdate = ticksNow + 1.0; + if (ticksNow >= nextFPSUpdate) + { + nextFPSUpdate = ticksNow + 1.0; + } if (printfps) { @@ -146,25 +156,32 @@ public: screen->swap(); nextDraw += drawrate; - if (ticksNow >= nextDraw) nextDraw = ticksNow + drawrate; + if (ticksNow >= nextDraw) + { + // we missed some scheduled draws, so reset the schedule + nextDraw = ticksNow + drawrate; + } } + // be a good citizen and give back what you don't need sleep(std::min(nextStep, nextDraw), true); } while (running); + + return 0; } void dispatchEvents() { - SDL_Event event; + SDL_Event e; - while (FE_PollEvent(&event) == 1) + while (FE_PollEvent(&e) == 1) { - switch (event.type) + switch (e.type) { case SDL_KEYDOWN: - if (event.key.keysym.sym == SDLK_ESCAPE && + if (e.key.keysym.sym == SDLK_ESCAPE && (SDL_GetModState() & KMOD_CTRL) ) { exit(0); @@ -172,11 +189,11 @@ public: break; case SDL_VIDEORESIZE: - screen->resize(event.resize.w, event.resize.h); + screen->resize(e.resize.w, e.resize.h); break; } - interface->dispatchEvent(event); + interface->handleEvent(e); } } @@ -196,16 +213,17 @@ public: engine* interface; }; + engine::engine(const std::string& name, int argc, char* argv[], - const std::string& configFile) - : impl(new engine_impl(name, argc, argv, configFile, this)) {} + const std::string& configFile) : + impl(new engine::engine_impl(name, argc, argv, configFile, this)) {} engine::~engine() {} int engine::run() { - impl->run(); + return impl->run(); } void engine::stop() @@ -248,8 +266,10 @@ long engine::getFPS() void engine::update(scalar t, scalar dt) {} void engine::draw(scalar alpha) {} -void engine::dispatchEvent(const SDL_Event& event) {} +void engine::handleEvent(const SDL_Event& e) {} } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/engine.hh b/src/engine.hh index bc3bdaf..98141cb 100644 --- a/src/engine.hh +++ b/src/engine.hh @@ -30,16 +30,15 @@ #define _ENGINE_HH_ #include -#include #include "singleton.hh" +#include "event.hh" #include "dispatcher.hh" namespace dc { -class engine_impl; class video; class engine : public singleton @@ -63,9 +62,10 @@ public: // Override these if you want. virtual void update(scalar t, scalar dt); virtual void draw(scalar alpha); - virtual void dispatchEvent(const SDL_Event& event); + virtual void handleEvent(const event& e); private: + class engine_impl; boost::shared_ptr impl; }; @@ -74,3 +74,5 @@ private: #endif // _ENGINE_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/event.hh b/src/event.hh index 84a073e..8ca219a 100644 --- a/src/event.hh +++ b/src/event.hh @@ -28,125 +28,25 @@ #ifndef _EVENT_HH_ #define _EVENT_HH_ - -#include "bz/date.h" -#include "bz/object.h" - - -namespace bz { +#include -class event -{ -public: - typedef enum - { - left_click_down = 1<<1, - left_click_up = 1<<2, - right_click_down = 1<<3, - right_click_up = 1<<4, - other_click_down = 1<<5, - other_click_up = 1<<6, - mouse_moved = 1<<7, - mouse_entered = 1<<8, - mouse_exited = 1<<9, - key_down = 1<<10, - key_up = 1<<11, - activated = 1<<12, - iconified = 1<<13, - deactivated = 1<<14, - - file_new = 1<<20, - file_open = 1<<21, - file_revert = 1<<22, - file_save = 1<<23, - file_save_as = 1<<24, - edit_undo = 1<<25, - edit_redo = 1<<26, - edit_copy = 1<<27, - edit_cut = 1<<28, - edit_paste = 1<<29, - edit_select_all = 1<<30, - app_quit = 1<<31, - any = 0xffffffffU - } type; - - // This constructor is for keyboard events: - event( type theType, // What happened? - unsigned char keycode, // The ASCII value. - int modifiers, // Ctrl, Shift, Opt, etc... - bool isRepeat ) : // Is the key held down? - type_(theType), keycode_(keycode), modifiers_(modifiers), - isRepeat_(isRepeat) - { - timestamp_ = date(); - id_ = getIdentifier(); - } - - // This constructor is for mouse events: - event( type theType, // What happened? - vec2d location, // Where? (window coordinates) - vec2d delta, // How far has it moved? - int nClick, // How many consecutive clicks? - float pressure ) : // How hard was it pushed? - type_(theType), location_(location), delta_(delta), nClick_(nClick), - pressure_(pressure) - { - timestamp_ = date(); - id_ = getIdentifier(); - } - // This constructor is for other event types: - event( type theType ) : type_(theType) // What happened? - { - timestamp_ = date(); - id_ = getIdentifier(); - } +namespace dc { - // Accessors for all event types: - type kind() const { return type_; } - const date& timestamp() const { return timestamp_; } - unsigned int identifier() const { return id_; } - void *userInfo() const { return userInfo_; } - void setUserInfo( void* userInfo ) { userInfo_ = userInfo; } - unsigned int tag() const { return tag_; } - void setTag( unsigned int tag ) { tag_ = tag; } +// The event handling in SDL is so big that it would take more time than it's +// worth to add an object-oriented abstraction layer that would completely cover +// what SDL has already layed down. Fortunately, SDL event structures are easy +// to work with, and it is not the purpose of this library to completely hide +// its dependencies and provide full functionality. - // Accessors for keyboard events: - unsigned char keycode() const { return keycode_; } - int modifiers() const { return modifiers_; } - bool isRepeat() const { return isRepeat_; } +typedef SDL_Event event; - // Accessors for mouse events: - const vec2d& location() const { return location_; } - const vec2d& delta() const { return delta_; } - int clicks() const { return nClick_; } - float pressure() const { return pressure_; } -private: - unsigned int getIdentifier() { - static unsigned int identifier = 1; - return identifier++; - } - - type type_; - date timestamp_; - unsigned int id_; - void *userInfo_; - unsigned int tag_; - - unsigned char keycode_; - unsigned char modifiers_; - bool isRepeat_; - - vec2d location_; - vec2d delta_; - int nClick_; - float pressure_; -}; - } // namespace dc #endif // _EVENT_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/interpolator.hh b/src/interpolator.hh new file mode 100644 index 0000000..74708d6 --- /dev/null +++ b/src/interpolator.hh @@ -0,0 +1,267 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 _INTERPOLATOR_HH_ +#define _INTERPOLATOR_HH_ + + +namespace dc { + + +class interpolator +{ + void clamp(scalar& value) + { + if (value > 1.0) + { + switch (theMode) + { + case stop: + value = 1.0; + stopped = true; + break; + case repeat: + value -= 1.0; + break; + case oscillate: + value = 2.0 - value; + scale *= -1.0; + break; + } + } + else if (value < 0.0) + { + switch (theMode) + { + case stop: + value = 0.0; + stopped = true; + break; + case repeat: + value += 1.0; + break; + case oscillate: + value = -value; + scale *= -1.0; + break; + } + } + } + +public: + typedef enum + { + stop = 0, + repeat = 1, + oscillate = 2 + } mode; + + void init(scalar seconds = 1.0, mode onFinish = stop) + { + scale = 1.0 / seconds; + alpha = 0.0; + setMode(onFinish); + } + + + void setMode(mode onFinish) + { + theMode = onFinish; + stopped = false; + } + + + void update(scalar dt) + { + if (!stopped) + { + alpha += dt * scale; + clamp(alpha); + calculate(alpha); + } + } + + virtual void calculate(scalar alpha) = 0; + +private: + mode theMode; + scalar alpha; + scalar scale; + bool stopped; +}; + +template +class interpolator_base : public interpolator +{ +public: + void init(scalar seconds = 1.0, mode onFinish = stop) + { + interpolator::init(seconds, onFinish); + + calculate(0.0); // set value + calculate(0.0); // set previous + } + + void calculate(scalar alpha) + { + previous = value; + calculate(value, alpha); + } + + virtual void calculate(T& value, scalar alpha) = 0; + + const T& getValue() + { + return value; + } + + const T getState(scalar alpha) + { + return cml::lerp(previous, value, alpha); + } + +private: + T value; + T previous; +}; + + +template +class binomial_interpolator : public interpolator_base +{ +public: + binomial_interpolator() {} + + explicit binomial_interpolator(const T coeff[D+1], scalar seconds = 1.0, + interpolator::mode onFinish = interpolator::stop) + { + init(coeff, seconds, onFinish); + } + + void init(const T coeff[D+1], scalar seconds = 1.0, + interpolator::mode onFinish = interpolator::stop) + { + scalar fac[D+1]; + + fac[0] = 1.0; + fac[1] = 1.0; + + // build an array of the computed factorials we will need + for (int i = 2; i <= D; i++) + { + fac[i] = i * fac[i - 1]; + } + + // combine the coefficients for fast updating + for (int i = 0; i <= D; i++) + { + // n! / (k! * (n - k)!) + coefficient[i] = coeff[i] * fac[D] / (fac[i] * fac[D - i]); + } + + interpolator_base::init(seconds, onFinish); + } + + + void calculate(T& value, scalar alpha) + { + scalar beta = 1.0 - alpha; + + value = coefficient[0] * std::pow(beta, D); + + for (int i = 1; i <= D; i++) + { + value += coefficient[i] * std::pow(beta, D - i) * std::pow(alpha, i); + } + } + +private: + + T coefficient[D+1]; +}; + + +template +class binomial_interpolator : public interpolator_base +{ +public: + binomial_interpolator() {} + + explicit binomial_interpolator(const T coeff[2], scalar seconds = 1.0, + interpolator::mode onFinish = interpolator::stop) + //interpolator_base(seconds, onFinish) + { + init(coeff, seconds, onFinish); + } + + void init(const T coeff[2], scalar seconds = 1.0, + interpolator::mode onFinish = interpolator::stop) + { + coefficient[0] = coeff[0]; + coefficient[1] = coeff[1]; + + interpolator_base::init(seconds, onFinish); + } + + + void calculate(T& value, scalar alpha) + { + value = cml::lerp(coefficient[0], coefficient[1], alpha); + } + +private: + T coefficient[2]; +}; + + +// Here are some aliases for more common interpolators. Also see the +// interpolation functions in cml for other types of interpolation such as +// slerp and some multi-alpha interpolators. + +typedef binomial_interpolator lerps; // linear +typedef binomial_interpolator lerpv2; +typedef binomial_interpolator lerpv3; +typedef binomial_interpolator lerpv4; + +typedef binomial_interpolator qerps; // quadratic +typedef binomial_interpolator qerpv2; +typedef binomial_interpolator qerpv3; +typedef binomial_interpolator qerpv4; + +typedef binomial_interpolator cerps; // cubic +typedef binomial_interpolator cerpv2; +typedef binomial_interpolator cerpv3; +typedef binomial_interpolator cerpv4; + + +} // namespace dc + +#endif // _INTERPOLATOR_HH_ + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/math.hh b/src/math.hh index 313ecc1..ab80dcc 100644 --- a/src/math.hh +++ b/src/math.hh @@ -37,29 +37,29 @@ #include #include +#include -namespace dc { +namespace dc { -typedef double scalar; ///< Scalar variable. -typedef cml::vector2d vector2; -typedef cml::vector3d vector3; -typedef cml::vector4d vector4; +// Basic types. -typedef cml::matrix33d_c matrix3; -typedef cml::matrix44d_c matrix4; +typedef float scalar; ///< Scalar type. -typedef cml::quaterniond_p quaternion; +typedef cml::vector2f vector2; +typedef cml::vector3f vector3; +typedef cml::vector4f vector4; -typedef cml::vector4f color; +typedef cml::matrix33f_c matrix3; +typedef cml::matrix44f_c matrix4; +typedef cml::quaternionf_p quaternion; -// Here's a simple way to check the equality of floating-point variables more -// reliably using approximation. +typedef vector4 color; -const scalar default_epsilon = 0.00001; ///< @see equals() +const scalar default_epsilon = 0.00001; /** * Check the equality of scalars with a certain degree of error allowed. @@ -75,3 +75,5 @@ inline bool equals(scalar a, scalar b, scalar epsilon = default_epsilon) #endif // _MATH_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/mippleton.hh b/src/mippleton.hh new file mode 100644 index 0000000..16985a3 --- /dev/null +++ b/src/mippleton.hh @@ -0,0 +1,109 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 _MIPPLETON_HH_ +#define _MIPPLETON_HH_ + +/** + * @file mippleton.hh + * Related to singletons, a mippleton is an object which can be obtained + * globally using a unique name. Unlike singletons, there can be multiple + * mippletons per class, each with a different name or identifier. Mippletons + * are create automatically when they are first requested (retained) and deleted + * after the last interested code releases its hold on the object. + */ + +#include +#include + + +namespace dc { + + +template +class mippleton +{ + typedef std::pair ptr_value_t; + typedef std::pair ptr_map_pair_t; + typedef std::map ptr_map_t; + + static ptr_map_t ptrs_; + std::string name_; + +public: + explicit mippleton(const std::string& name) : name_(name) {} + + inline const std::string& getName() const + { + return name_; + } + + inline static T* retain(const std::string& name) + { + typename ptr_map_t::iterator it; + + if ((it = ptrs_.find(name)) != ptrs_.end()) + { + (*it).second.first++; + return (*it).second.second; + } + else + { + T* newObj = new T(name); + ptrs_.insert(ptr_map_pair_t(name, ptr_value_t(1, newObj))); + return newObj; + } + } + + inline static void releaseByName(const std::string& name) + { + typename ptr_map_t::iterator it; + + if ((it = ptrs_.find(name)) != ptrs_.end() && -(*it).second.first == 0) + { + delete (*it).second.second; + ptrs_.erase(it); + } + } + + inline static void release(T* obj) + { + releaseByName(obj->getName()); + } +}; + +template +std::map > mippleton::ptrs_; + + +} // namespace dc + +#endif // _MIPPLETON_HH_ + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/opengl.hh b/src/opengl.hh index e04fe5c..287918d 100644 --- a/src/opengl.hh +++ b/src/opengl.hh @@ -41,3 +41,5 @@ #endif // _GL_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/profiler.hh b/src/profiler.hh index d1a8bb0..7b38e52 100644 --- a/src/profiler.hh +++ b/src/profiler.hh @@ -79,3 +79,5 @@ private: #endif // _PROFILER_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/random.cc b/src/random.cc index 22bbd4e..6cd25cf 100644 --- a/src/random.cc +++ b/src/random.cc @@ -113,3 +113,5 @@ double get() }; // namespace rng +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/random.hh b/src/random.hh index 5a34229..ac308c8 100644 --- a/src/random.hh +++ b/src/random.hh @@ -124,3 +124,5 @@ double get(); #endif // _RNG_RANDOM_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/rectangle.hh b/src/rectangle.hh deleted file mode 100644 index 1594d62..0000000 --- a/src/rectangle.hh +++ /dev/null @@ -1,124 +0,0 @@ - -/******************************************************************************* - - Copyright (c) 2009, Charles McGarvey - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 _RECTANGLE_HH_ -#define _RECTANGLE_HH_ - -#include "math.hh" -#include "vector.hh" - - -namespace dc { - -// -// A rectangle is a 2d shape defined by point (origin) and size (width & height) -// vectors. The usual rectangle maths are available. Valid rectangles have -// positive width and height. Invalid rectangles will be returned for invalid -// operations such as trying to get the intersection of two rectangles which -// don't intersect. Using an invalid rectangle is undefined, but it may be used -// after "fixing" it using the fix method which will cause the size to be -// positive and adjust the origin to be the bottom-left point. Check the -// validity of a rectangle using the isValid method especially if the rectangle -// is a result of an operation which could fail. -// - -class rectangle -{ -public: - // Constructors. - rectangle() {} - rectangle(point o, point s) { - origin = o; - size = s; - } - - // Rectangle validity. - bool isValid() const { return size.x >= 0.0 && size.y >= 0.0; } - void fix() - { - if (size.x <= 0.0) - { - size.x = 0.0 - size.x; - origin.x -= size.x; - } - if (size.y <= 0.0) - { - size.y = 0.0 - size.y; - origin.y -= size.y; - } - } - - // Rectangle operations. - scalar area() const { return size.x * size.y; } - scalar perimeter() const { - return size.x + size.x + size.y + size.y; - } - - bool containsPoint(point pt) const - { - return origin.x <= pt.x && origin.y <= pt.y && - pt.x <= origin.x + size.x && pt.y <= origin.y + size.y; - } - - void inset(scalar h, scalar v) - { - origin.x += h; - origin.y += v; - size.x -= h + h; - size.y -= v + v; - } - void offset(scalar h, scalar v) - { - origin.x -= h; - origin.y -= v; - size.x += h + h; - size.y += v + v; - } - - bool containsRect(const rectangle& r) const; - bool intersectsRect(const rectangle& r) const; - rectangle unionWith(const rectangle& r) const; - rectangle intersectionWith(const rectangle& r) const; - - // Checking equality. - bool operator == (const rectangle& r) const { - return origin == r.origin && size == r.size; - } - bool operator != (const rectangle& r) const { - return !(*this == r); - } - - // Data. - point origin, size; -}; - -} // namespace dc - - -#endif // _RECTANGLE_HH_ - diff --git a/src/resource.cc b/src/resource.cc index 758827c..16b5f21 100644 --- a/src/resource.cc +++ b/src/resource.cc @@ -37,26 +37,12 @@ namespace dc { std::vector resource::searchPaths_; -resource::resource(const std::string& name) throw(exception) -{ - filePath_ = getPathToResource(name); - - if (!filePath_.empty()) - { - throw exception("cannot find resource file " + name); - } -} - resource::~resource() {} -const std::string& resource::getPathToFile() -{ - return filePath_; -} - void resource::addSearchPath(const std::string& directory) { + // add a slash if there isn't one already if (directory[directory.length() - 1] != '/') { searchPaths_.push_back(directory + '/'); @@ -73,16 +59,22 @@ std::string resource::getPathToResource(const std::string& name) for (i = searchPaths_.begin(); i != searchPaths_.end(); i++) { - const char* fullPath = ((*i) + name).c_str(); - if (access(fullPath, R_OK) == 0) + std::string fullPath(*i); + fullPath += name; + + // TODO access(2) is not all that portable + if (access(fullPath.c_str(), R_OK) == 0) { - return std::string(fullPath); + return fullPath; } } + // empty string return std::string(); } } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/resource.hh b/src/resource.hh index eff9e3e..c5020f6 100644 --- a/src/resource.hh +++ b/src/resource.hh @@ -55,16 +55,8 @@ public: std::runtime_error(what_arg) {} }; - resource(const std::string& name) throw(exception); virtual ~resource(); - /** - * Get the path of the file associated with this resource. - * @return Path. - */ - - const std::string& getPathToFile(); - /** * Add a directory to search when looking for resource files. @@ -83,7 +75,6 @@ public: static std::string getPathToResource(const std::string& name); private: - std::string filePath_; static std::vector searchPaths_; }; @@ -92,3 +83,5 @@ private: #endif // _RESOURCE_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/scene.cc b/src/scene.cc new file mode 100644 index 0000000..7dd102b --- /dev/null +++ b/src/scene.cc @@ -0,0 +1,138 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 "mippleton.hh" +#include "deserializer.hh" +#include "serializable.hh" +#include "aabb.hh" + +#include "scene.hh" + + +namespace dc { + + +class scene::scene_impl : public mippleton +{ + static bool loadBox(aabb& theBox, serializable_ptr obj) + { + std::vector numbers; + + if (obj->get(numbers)) + { + if (numbers.size() == 6) + { + double num; + + if (numbers[0]->getNumber(num)) + { + + } + } + } + + return false; + } + +public: + + scene_impl(const std::string& name) : + mippleton(name) + { + loadFromFile(); + } + + + void loadFromFile() + { + std::string filePath = scene::getPathToResource(getName()); + + deserializer in(filePath, true); + + serializable_ptr root = in.deserialize(); + + if (root) + { + std::map rootObj; + + if (root->get(rootObj)) + { + std::map::iterator it; + + if ((it = rootObj.find("playfield_bounds")) != rootObj.end()) + { + loadBox(playfieldBounds, (*it).second); + } + if ((it = rootObj.find("maximum_bounds")) != rootObj.end()) + { + loadBox(maximumBounds, (*it).second); + } + + //for (i = rootObj.begin(); i != rootObj.end(); i++) + //{ + //sequences.insert(std::pair((*i).first, + //sequence((*i).second))); + //} + } + } + } + + aabb playfieldBounds; + aabb maximumBounds; + +}; + + +scene::scene(const std::string& name) : + // pass through + impl(scene::scene_impl::retain(name), &scene::scene_impl::release) {} + + +void scene::draw(scalar alpha) +{ +} + + +/** + * Specialized search location for scene files. They can be found in the + * "scenes" subdirectory of any of the searched directories. + */ + +std::string scene::getPathToResource(const std::string& name) +{ + return resource::getPathToResource("scenes/" + name + ".json"); +} + + +} // namespace dc + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/scene.hh b/src/scene.hh new file mode 100644 index 0000000..2938db8 --- /dev/null +++ b/src/scene.hh @@ -0,0 +1,62 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 _SCENE_HH_ +#define _SCENE_HH_ + +#include +#include + +#include "resource.hh" +#include "drawable.hh" + + +namespace dc { + + +class scene : public resource, public drawable +{ +public: + scene(const std::string& name); + + void draw(scalar alpha); + + static std::string getPathToResource(const std::string& name); + +private: + class scene_impl; + boost::shared_ptr impl; +}; + + +} // namespace dc + +#endif // _SCENE_HH_ + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/serializable.cc b/src/serializable.cc index 11e8d26..1da0613 100644 --- a/src/serializable.cc +++ b/src/serializable.cc @@ -72,11 +72,51 @@ bool serializable::get(std::map& value) return false; } + bool serializable::isNull() { return false; } +bool serializable::getNumber(long& value) +{ + if (get(value)) + { + return true; + } + else + { + double dValue; + if (get(dValue)) + { + value = long(dValue); + return true; + } + } + return false; +} + +bool serializable::getNumber(double& value) +{ + if (get(value)) + { + return true; + } + else + { + long lValue; + if (get(lValue)) + { + value = double(lValue); + return true; + } + } + return false; +} + + } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/serializable.hh b/src/serializable.hh index 5b8b403..967b213 100644 --- a/src/serializable.hh +++ b/src/serializable.hh @@ -34,9 +34,9 @@ #include #include +#include "stringtools.hh" #include "serializer.hh" #include "deserializer.hh" -#include "stringtools.hh" namespace dc { @@ -59,6 +59,15 @@ public: virtual bool get(std::wstring& value); virtual bool get(std::vector& value); virtual bool get(std::map& value); + + /* + * To get a number value which may have been parsed as either an integer or + * double, use these instead. + */ + + bool getNumber(long&); + bool getNumber(double&); + virtual bool isNull(); }; @@ -75,7 +84,6 @@ public: void print() const; bool get(T& value); - public: T variable; }; @@ -240,3 +248,5 @@ inline bool null::isNull() #endif // _SERIALIZABLE_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/serializer.cc b/src/serializer.cc index 06563ef..7b7596d 100644 --- a/src/serializer.cc +++ b/src/serializer.cc @@ -37,7 +37,7 @@ namespace dc { -class serializer_impl +class serializer::serializer_impl { public: serializer_impl(const std::string& filePath, const std::string& indent = "") @@ -72,6 +72,8 @@ public: throw serializer::exception("maximum archive depth exceeded"); case yajl_gen_in_error_state: throw serializer::exception("serializer already in error state"); + case yajl_gen_status_ok: + ; // There is no error here. Move along... } } @@ -105,10 +107,12 @@ private: serializer::serializer(const std::string& filePath, const std::string& indent) : - impl(new serializer_impl(filePath, indent)) {} + // pass through + impl(new serializer::serializer_impl(filePath, indent)) {} serializer::serializer(std::ostream& output, const std::string& indent) : - impl(new serializer_impl(output, indent)) {} + // pass through + impl(new serializer::serializer_impl(output, indent)) {} serializer::~serializer() { @@ -119,26 +123,30 @@ serializer::~serializer() void serializer::push(long value) { yajl_gen_status stat = yajl_gen_integer(impl->gen, value); - if (stat != yajl_gen_status_ok) serializer_impl::throwError(stat); + if (stat != yajl_gen_status_ok) + serializer::serializer_impl::throwError(stat); } void serializer::push(double value) { yajl_gen_status stat = yajl_gen_double(impl->gen, value); - if (stat != yajl_gen_status_ok) serializer_impl::throwError(stat); + if (stat != yajl_gen_status_ok) + serializer::serializer_impl::throwError(stat); } void serializer::push(bool value) { yajl_gen_status stat = yajl_gen_bool(impl->gen, value); - if (stat != yajl_gen_status_ok) serializer_impl::throwError(stat); + if (stat != yajl_gen_status_ok) + serializer::serializer_impl::throwError(stat); } void serializer::push(const std::string& value) { yajl_gen_status stat = yajl_gen_string(impl->gen, (const unsigned char*)value.c_str(), value.length()); - if (stat != yajl_gen_status_ok) serializer_impl::throwError(stat); + if (stat != yajl_gen_status_ok) + serializer::serializer_impl::throwError(stat); } void serializer::push(const std::wstring& value) @@ -149,32 +157,37 @@ void serializer::push(const std::wstring& value) void serializer::pushNull() { yajl_gen_status stat = yajl_gen_null(impl->gen); - if (stat != yajl_gen_status_ok) serializer_impl::throwError(stat); + if (stat != yajl_gen_status_ok) + serializer::serializer_impl::throwError(stat); } void serializer::pushMapHead() { yajl_gen_status stat = yajl_gen_map_open(impl->gen); - if (stat != yajl_gen_status_ok) serializer_impl::throwError(stat); + if (stat != yajl_gen_status_ok) + serializer::serializer_impl::throwError(stat); } void serializer::pushMapTail() { yajl_gen_status stat = yajl_gen_map_close(impl->gen); - if (stat != yajl_gen_status_ok) serializer_impl::throwError(stat); + if (stat != yajl_gen_status_ok) + serializer::serializer_impl::throwError(stat); } void serializer::pushArrayHead() { yajl_gen_status stat = yajl_gen_array_open(impl->gen); - if (stat != yajl_gen_status_ok) serializer_impl::throwError(stat); + if (stat != yajl_gen_status_ok) + serializer::serializer_impl::throwError(stat); } void serializer::pushArrayTail() { yajl_gen_status stat = yajl_gen_array_close(impl->gen); - if (stat != yajl_gen_status_ok) serializer_impl::throwError(stat); + if (stat != yajl_gen_status_ok) + serializer::serializer_impl::throwError(stat); } @@ -191,3 +204,5 @@ void serializer::flush() } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/serializer.hh b/src/serializer.hh index 8e61a62..206a793 100644 --- a/src/serializer.hh +++ b/src/serializer.hh @@ -44,16 +44,28 @@ namespace dc { -class serializer_impl; - class serializer { public: + + /** + * Construction is initialization. Use either an output stream or a string + * representing a path to a fill to which the serialized data will be + * written (replacing any previous file). + * @param indent If non-empty, the string's characters will be used as + * indentation. Otherwise, the serialized data will not be formatted neatly + * but will be tightly packed. + */ + serializer(const std::string& filePath, const std::string& indent = ""); serializer(std::ostream& output, const std::string& indent = ""); ~serializer(); + /** + * Push various types of data onto the stream. + */ + void push(long value); void push(double value); void push(bool value); @@ -61,14 +73,32 @@ public: void push(const std::wstring& value); void pushNull(); + /** + * Push a map onto the stream. Each map head must be matched by a map tail. + */ + void pushMapHead(); void pushMapTail(); + /** + * Push an array onto the stream. Each array head must be matched by an + * array tail. + */ + void pushArrayHead(); void pushArrayTail(); + /** + * Write any pending bytes to the stream. This is called automatically by + * the destructor of this class. + */ + void flush(); + /** + * This exception is thrown for serializer-related exceptional errors. + */ + struct exception : std::runtime_error { explicit exception(const std::string& what_arg) : @@ -76,6 +106,7 @@ public: }; private: + class serializer_impl; boost::shared_ptr impl; }; @@ -84,3 +115,5 @@ private: #endif // _SERIALIZER_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/settings.cc b/src/settings.cc index 07ec0e5..74ab384 100644 --- a/src/settings.cc +++ b/src/settings.cc @@ -27,7 +27,10 @@ *******************************************************************************/ #include -#include +#include // getenv +#include // strchr + +#include #include "settings.hh" @@ -64,37 +67,69 @@ void settings::parseArgs(int argc, char* argv[]) } catch (std::exception e) { + // it doesn't deserialize to anything we know, so just store it + // as a string map[key] = serializable_ptr(new wrapped_string(stringValue)); } } } } -void settings::loadFromFile(std::string filePath) + +void settings::loadFromFile(const std::string& filePath, bool precedence) { - deserializer in(filePath, true); + std::vector paths; + boost::split(paths, filePath, boost::is_any_of(":")); - try + loadFromFiles(paths, precedence); +} + +void settings::loadFromFiles(const std::vector& filePaths, + bool precedence) +{ + std::vector::const_iterator it; + + char* home = getenv("HOME"); + + for (it = filePaths.begin(); it != filePaths.end(); it++) { - serializable_ptr obj = in.deserialize(); - std::map dict; - if (obj && obj->get(dict)) + std::string path = *it; + + if (home) { - map.insert(dict.begin(), dict.end()); + boost::replace_first(path, "$HOME", home); } - else + + deserializer in(*it, true); + + std::cout << "Looking for a config file at " << path << std::endl; + try { - std::cerr << "The settings file " << filePath << - " does not contain any valid settings." << std::endl; + serializable_ptr obj = in.deserialize(); + std::map dict; + if (obj && obj->get(dict)) + { + if (!precedence) + { + map.insert(dict.begin(), dict.end()); + } + else + { + dict.insert(map.begin(), map.end()); + map = dict; + } + } + } + catch (deserializer::exception e) + { + std::cerr << "Cannot load settings from " << *it << + " because an exception was thrown: " << e.what() << std::endl; } - } - catch (deserializer::exception e) - { - std::cerr << "Cannot load settings from " << filePath << - " because an exception was thrown: " << e.what() << std::endl; } } } // namepsace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/settings.hh b/src/settings.hh index 5cb0972..9c2f2d8 100644 --- a/src/settings.hh +++ b/src/settings.hh @@ -51,7 +51,10 @@ public: settings(int argc, char* argv[]); void parseArgs(int argc, char* argv[]); - void loadFromFile(std::string filePath); + + void loadFromFile(const std::string& filePath, bool precedence = false); + void loadFromFiles(const std::vector& filePaths, + bool precedence = false); template bool get(const std::string& key, T& value); @@ -82,3 +85,5 @@ bool settings::get(const std::string& key, T& value) #endif // _SETTINGS_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/singleton.hh b/src/singleton.hh index 3b2ebf7..ec0d01e 100644 --- a/src/singleton.hh +++ b/src/singleton.hh @@ -32,12 +32,21 @@ #include +namespace dc { + + template class singleton { static T* ptr_; public: + struct exception : public std::runtime_error + { + explicit exception(const std::string& what_arg) : + std::runtime_error(what_arg) {} + }; + singleton() { if (!ptr_) @@ -60,12 +69,12 @@ public: { if (!ptr_) { - throw std::runtime_error("accessing uninstantiated singleton"); + throw exception("accessing uninstantiated singleton"); } return *ptr_; } - static T* instance__ptr() + static T* instance_ptr() { return ptr_; } @@ -74,5 +83,9 @@ public: template T* singleton::ptr_ = 0; +} // namespace dc + #endif // _SINGLETON_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/stringtools.cc b/src/stringtools.cc index 8954f9b..91d2a6c 100644 --- a/src/stringtools.cc +++ b/src/stringtools.cc @@ -151,3 +151,5 @@ std::string wideToMulti(const std::wstring& wideStr) } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/stringtools.hh b/src/stringtools.hh index 9bf4577..bd79eab 100644 --- a/src/stringtools.hh +++ b/src/stringtools.hh @@ -42,3 +42,5 @@ std::string wideToMulti(const std::wstring& wideStr); #endif // _STRINGTOOLS_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/texture.cc b/src/texture.cc index 2f8f97a..6d3e912 100644 --- a/src/texture.cc +++ b/src/texture.cc @@ -33,6 +33,8 @@ #include #include +#include "mippleton.hh" + #include "dispatcher.hh" #include "opengl.hh" @@ -42,43 +44,47 @@ namespace dc { -class texture_impl +/** + * The texture implementation just contains all the information about the image + * which is worth having in memory. The image data itself is not worth keeping + * in memory if the texture has been loaded to GL, but the name of the resource + * is retained so that it can be reloaded if necessary. The implementation is a + * mippleton so that multiple texture objects can share the same internal + * objects and avoid having duplicate textures loaded to GL. + */ + +class texture::texture_impl : public mippleton { + + /** + * Delete the texture (if it is loaded) from GL. + */ + void unloadFromGL() { - if (object) + if (object_) { - glDeleteTextures(1, &object); - object = 0; + glDeleteTextures(1, &object_); + object_ = 0; } } + /** + * If the GL context was recreated, we probably need to reload the texture. + * This may involve reading it from disk again, but hopefully the OS was + * smart enough to cache it if the client has plenty of RAM. + */ + void contextRecreated(const notification& note) { unloadFromGL(); uploadToGL(); } -public: - texture_impl(texture* outside, bool keepInMemory) - : interface(outside), keepData(keepInMemory), object(0), imageData(0) - { - dispatcher::instance().addHandler("video.context_recreated", - boost::bind(&texture_impl::contextRecreated, this, _1), - this); - } - - ~texture_impl() - { - dispatcher::instance().removeHandler(this); - - if (imageData) - { - SDL_FreeSurface(imageData); - } - unloadFromGL(); - } - + /** + * This is a helper method used by some of the texture loading code. It + * returns the first power of two which is greater than the input value. + */ static int powerOfTwo(int input) { @@ -91,17 +97,55 @@ public: return value; } +public: + + /** + * Construction is initialization. + */ + + explicit texture_impl(const std::string& name) : + mippleton(name), + width_(0), + height_(0), + mode_(0), + minFilter_(GL_NEAREST), + maxFilter_(GL_NEAREST), + wrapU_(GL_CLAMP), + wrapV_(GL_CLAMP), + object_(0) + { + uploadToGL(); + + // we want to know when the GL context is recreated + dispatcher::instance().addHandler("video.context_recreated", + boost::bind(&texture_impl::contextRecreated, this, _1), this); + } + + ~texture_impl() + { + unloadFromGL(); + + dispatcher::instance().removeHandler(this); + } + + + /** + * Adapted from some public domain code. This stuff is common enough that + * it really should be included in SDL_image... We need this because images + * loaded with SDL_image aren't exactly GL-ready right out of the box. This + * method makes them ready. + */ + static SDL_Surface* prepareImageForGL(SDL_Surface* surface) { - // Adapted from some public domain code. This stuff is common enough - // that it really should be included in SDL_image... We need this - // because images loaded with SDL_image aren't exactly OpenGL-ready - // right out of the box. - int w = powerOfTwo(surface->w); int h = powerOfTwo(surface->h); // 1. OpenGL images must (generally) have dimensions of a power-of-two. + // If this one doesn't, we can at least be more friendly by expanding + // the dimensions so that they are, though there will be some empty + // space within the range of normal texture coordinates. It's better of + // textures are the right size to begin with. SDL_Surface* image = SDL_CreateRGBSurface ( @@ -145,7 +189,8 @@ public: SDL_SetAlpha(surface, savedFlags, savedAlpha); } - // 2. OpenGL textures make more sense when they are "upside down." + // 2. OpenGL textures make more sense within the coordinate system when + // they are "upside down," so let's flip it. Uint8 line[image->pitch]; @@ -168,138 +213,192 @@ public: return image; } + /** + * Use SDL_image to load images from file. A surface with the image data is + * returned. + * @return Image data. + */ - void loadImageData() + SDL_Surface* loadImageData() { SDL_Surface* surface; - surface = IMG_Load(interface->getPathToFile().c_str()); + surface = IMG_Load(texture::getPathToResource(getName()).c_str()); if (!surface) { throw texture::exception("loading failed"); } - imageData = prepareImageForGL(surface); + SDL_Surface* temp = prepareImageForGL(surface); SDL_FreeSurface(surface); - if (!imageData) + if (!temp) { - throw texture::exception(""); + throw texture::exception("image couldn't be prepared for GL"); } - if (imageData->format->BytesPerPixel == 3) + if (temp->format->BytesPerPixel == 3) { - mode = GL_RGB; + mode_ = GL_RGB; } - else if (imageData->format->BytesPerPixel == 4) + else if (temp->format->BytesPerPixel == 4) { - mode = GL_RGBA; + mode_ = GL_RGBA; } else { - SDL_FreeSurface(imageData); + SDL_FreeSurface(temp); throw texture::exception("image is not the required 24 or 32 bpp"); } - width = imageData->w; - height = imageData->h; + width_ = temp->w; + height_ = temp->h; + + return temp; } + /** + * Upload the image to GL so that it will be accessible by a much more + * manageable handle and hopefully reside in video memory. + */ + void uploadToGL() { - if (object) + if (object_) { // Already loaded. return; } - if (!imageData) - { - loadImageData(); - } + SDL_Surface* imageData = loadImageData(); - glGenTextures(1, &object); - - glBindTexture(GL_TEXTURE_2D, object); + glGenTextures(1, &object_); + glBindTexture(GL_TEXTURE_2D, object_); glTexImage2D ( GL_TEXTURE_2D, 0, - mode, + mode_, imageData->w, imageData->h, 0, - mode, + mode_, GL_UNSIGNED_BYTE, imageData->pixels ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + setProperties(); - if (!keepData) - { - SDL_FreeSurface(imageData); - imageData = 0; - } + SDL_FreeSurface(imageData); } - unsigned width; - unsigned height; - int mode; - GLuint object; + /** + * Sets some texture properties such as the filters and external coordinate + * behavior. + */ + + void setProperties() + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, maxFilter_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapU_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapV_); + } + - bool keepData; - SDL_Surface* imageData; + unsigned width_; ///< Horizontal dimension of the image. + unsigned height_; ///< Vertical dimension. - texture* interface; + GLuint mode_; ///< Depth of the image, GL_RGB or GL_RGBA. + GLuint minFilter_; ///< Filter. + GLuint maxFilter_; ///< Filter. + GLuint wrapU_; ///< Wrapping behavior horizontally. + GLuint wrapV_; ///< Wrapping behavior vertically. + GLuint object_; ///< GL texture handle. }; -texture::texture(const std::string& name, bool keepInMemory) - : resource(name), impl(new texture_impl(this, keepInMemory)) {} +texture::texture(const std::string& name) : + // pass through + impl(texture::texture_impl::retain(name), &texture::texture_impl::release) +{} + +/** + * Bind the GL texture for mapping, etc. + */ void texture::bind() { glBindTexture(GL_TEXTURE_2D, getObject()); } + +/** + * Get the texture object, for the curious. + */ + GLuint texture::getObject() { - if (!impl->object) - { - impl->uploadToGL(); - } - - return impl->object; + // pass through + return impl->object_; } unsigned texture::getWidth() { - if (!impl->object) - { - impl->uploadToGL(); - } - - return impl->width; + // pass through + return impl->width_; } unsigned texture::getHeight() { - if (!impl->object) - { - impl->uploadToGL(); - } + // pass through + return impl->height_; +} + - return impl->height; +void texture::setMinFilter(GLuint filter) +{ + impl->minFilter_ = filter; +} + +void texture::setMaxFilter(GLuint filter) +{ + impl->maxFilter_ = filter; +} + +void texture::setWrapU(GLuint wrap) +{ + impl->wrapU_ = wrap; +} + +void texture::setWrapV(GLuint wrap) +{ + impl->wrapV_ = wrap; +} + + +void texture::applyChanges() +{ + bind(); + impl->setProperties(); +} + + +std::string texture::getPathToResource(const std::string& name) +{ + // TODO since this is a generic library class, more than PNG should be + // supported + return resource::getPathToResource("textures/" + name + ".png"); } } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/texture.hh b/src/texture.hh index 1be4c5a..f92ad23 100644 --- a/src/texture.hh +++ b/src/texture.hh @@ -38,19 +38,17 @@ #include -#include "resource.hh" #include "opengl.hh" +#include "resource.hh" namespace dc { -class texture_impl; - class texture : public resource { public: - texture(const std::string& name, bool keepInMemory = false); + texture(const std::string& name); void bind(); GLuint getObject(); @@ -58,6 +56,15 @@ public: unsigned getWidth(); unsigned getHeight(); + void setMinFilter(GLuint filter); + void setMaxFilter(GLuint filter); + void setWrapU(GLuint wrap); + void setWrapV(GLuint wrap); + + void applyChanges(); + + static std::string getPathToResource(const std::string& name); + struct exception : std::runtime_error { explicit exception(const std::string& what_arg) : @@ -65,6 +72,7 @@ public: }; private: + class texture_impl; boost::shared_ptr impl; }; @@ -73,3 +81,5 @@ private: #endif // _TEXTURE_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/thread.hh b/src/thread.hh index 990f9b0..443decd 100644 --- a/src/thread.hh +++ b/src/thread.hh @@ -213,3 +213,5 @@ private: #endif // _THREAD_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/tilemap.cc b/src/tilemap.cc new file mode 100644 index 0000000..0852f98 --- /dev/null +++ b/src/tilemap.cc @@ -0,0 +1,204 @@ + +/******************************************************************************* + + Copyright (c) 2009, Charles McGarvey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER 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 "mippleton.hh" +#include "serializable.hh" +#include "deserializer.hh" + +#include "opengl.hh" + +#include "tilemap.hh" + + +namespace dc { + + +class tilemap::tilemap_impl : public mippleton +{ +public: + tilemap_impl(const std::string& name) : + mippleton(name), + tilesU_(1), + tilesV_(1), + minFilter_(GL_NEAREST), + maxFilter_(GL_NEAREST), + wrapU_(GL_CLAMP), + wrapV_(GL_CLAMP) + { + loadFromFile(); + } + + void loadFromFile() + { + deserializer in(tilemap::getPathToResource(getName())); + + serializable_ptr root = in.deserialize(); + + if (root) + { + std::map rootMap; + + if (root->get(rootMap)) + { + std::map::iterator it; + + if ((it = rootMap.find("TilesU")) != rootMap.end()) + { + long value; + if ((*it).second->get(value)) + { + tilesU_ = unsigned(value); + } + } + if ((it = rootMap.find("TilesV")) != rootMap.end()) + { + long value; + if ((*it).second->get(value)) + { + tilesV_ = unsigned(value); + } + } + if ((it = rootMap.find("MinFilter")) != rootMap.end()) + { + std::string value; + if ((*it).second->get(value)) + { + if (value == "Linear") + { + minFilter_ = GL_LINEAR; + } + } + } + if ((it = rootMap.find("MaxFilter")) != rootMap.end()) + { + std::string value; + if ((*it).second->get(value)) + { + if (value == "Linear") + { + maxFilter_ = GL_LINEAR; + } + } + } + if ((it = rootMap.find("WrapU")) != rootMap.end()) + { + std::string value; + if ((*it).second->get(value)) + { + if (value == "Repeat") + { + wrapU_ = GL_REPEAT; + } + } + } + if ((it = rootMap.find("WrapV")) != rootMap.end()) + { + std::string value; + if ((*it).second->get(value)) + { + if (value == "Repeat") + { + wrapV_ = GL_REPEAT; + } + } + } + } + } + } + + unsigned tilesU_; + unsigned tilesV_; + GLuint minFilter_; + GLuint maxFilter_; + GLuint wrapU_; + GLuint wrapV_; +}; + + +tilemap::tilemap(const std::string& name) : + texture(name), + impl(tilemap::tilemap_impl::retain(name), &tilemap::tilemap_impl::release) +{ + setMinFilter(impl->minFilter_); + setMaxFilter(impl->maxFilter_); + setWrapU(impl->wrapU_); + setWrapV(impl->wrapV_); + applyChanges(); +} + + +void tilemap::getTileCoords(unsigned index, scalar coords[8]) +{ + assert(index < impl->tilesU_ * impl->tilesV_); + + scalar w = 1.0 / scalar(impl->tilesU_); + scalar h = 1.0 / scalar(impl->tilesV_); + + coords[0] = scalar(index % impl->tilesU_) * w; + coords[1] = (scalar(impl->tilesV_ - 1) - scalar(index / impl->tilesU_)) * h; + coords[2] = coords[0] + w; + coords[3] = coords[1]; + coords[4] = coords[2]; + coords[5] = coords[1] + h; + coords[6] = coords[0]; + coords[7] = coords[5]; +} + +void tilemap::getTileCoords(unsigned index, scalar coords[8], orientation what) +{ + getTileCoords(index, coords); + + if (what & flip) + { + coords[1] = coords[5]; + coords[5] = coords[3]; + coords[3] = coords[7]; + coords[7] = coords[5]; + } + if (what & reverse) + { + coords[0] = coords[2]; + coords[2] = coords[6]; + coords[4] = coords[6]; + coords[6] = coords[0]; + } +} + + +std::string tilemap::getPathToResource(const std::string& name) +{ + return resource::getPathToResource("tilemaps/" + name + ".json"); +} + + +} // namespace dc + +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/tilemap.hh b/src/tilemap.hh index 50e5c3d..f2f76ab 100644 --- a/src/tilemap.hh +++ b/src/tilemap.hh @@ -34,34 +34,15 @@ * Small subclass to give some basic tile-mapping functionality to textures. */ -#include - #include -#include "opengl.hh" #include "texture.hh" +#include "math.hh" namespace dc { -namespace tile { - -/** - * Possible orientations for texture coordinates. - */ - -typedef enum -{ - normal = 0, ///< Normal orientation. - flip = 1, ///< Flip over a horizontal axis. - reverse = 2, ///< Flip over a vertical axis. - flip_and_reverse = 3 ///< Flip over both. -} orientation; - -} // namespace tile - - /** * A tilemap is a texture which is meant to be divided into smaller sprites. * This class provides all the functionality of a texture and adds some methods @@ -72,52 +53,34 @@ typedef enum class tilemap : public texture { public: - tilemap(const std::string& filePath, bool keepInMemory = false, - unsigned tilesU = 1, unsigned tilesV = 1) - : texture(filePath, keepInMemory), tilesU_(tilesU), tilesV_(tilesV) {} - - /** - * Set the number of rows and columns of square tiles. - * @param tilesU Columns of tiles. - * @param tilesV Rows of tiles. + * Possible orientations for texture coordinates. */ - void setTileDimensions(unsigned tilesU, unsigned tilesV) + typedef enum { - assert(tilesU != 0 && tilesV != 0); - tilesU_ = tilesU; - tilesV_ = tilesV; - } + normal = 0, ///< Normal orientation. + flip = 1, ///< Flip over a horizontal axis. + reverse = 2, ///< Flip over a vertical axis. + flip_and_reverse = 3 ///< Flip over both. + } orientation; + + tilemap(const std::string& name); /** * Calculate texture coordinates for a tile at a certain index. Tiles are * indexed start with zero as the to-left tile and moving across, then down. * @param index The tile index. - * @param coords An array of floats where the texture coordinates will be + * @param coords An array of scalars where the texture coordinates will be * stored after this call. The first coordinate (u,v) will be in the first * two places and so on until all four coordinates are stored, therefore - * requiring enough room for an array of eight floats. The winding of the + * requiring enough room for an array of eight scalars. The winding of the * coordinates is always counter-clockwise (the GL default). */ - void getTileCoords(unsigned index, float coords[8]) - { - assert(index < tilesU_ * tilesV_); - - float w = 1.0 / float(tilesU_); - float h = 1.0 / float(tilesV_); + void getTileCoords(unsigned index, scalar coords[8]); - coords[0] = float(index % tilesU_) * w; - coords[1] = (float(tilesV_ - 1) - float(index / tilesU_)) * h; - coords[2] = coords[0] + w; - coords[3] = coords[1]; - coords[4] = coords[2]; - coords[5] = coords[1] + h; - coords[6] = coords[0]; - coords[7] = coords[5]; - } /** * This version let's you specify an orientation that will be reflected in @@ -126,29 +89,14 @@ public: * @param what The orientation; can be flip, reverse, or flip_and_reverse. */ - void getTileCoords(unsigned index, float coords[8], tile::orientation what) - { - getTileCoords(index, coords); - - if (what & tile::flip) - { - coords[1] = coords[5]; - coords[5] = coords[3]; - coords[3] = coords[7]; - coords[7] = coords[5]; - } - if (what & tile::reverse) - { - coords[0] = coords[2]; - coords[2] = coords[6]; - coords[4] = coords[6]; - coords[6] = coords[0]; - } - } + void getTileCoords(unsigned index, scalar coords[8], orientation what); + + + static std::string getPathToResource(const std::string& name); private: - unsigned tilesU_; - unsigned tilesV_; + class tilemap_impl; + boost::shared_ptr impl; }; @@ -156,3 +104,5 @@ private: #endif // _TILEMAP_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/timer.cc b/src/timer.cc index 2c7a1cb..de601ee 100644 --- a/src/timer.cc +++ b/src/timer.cc @@ -30,8 +30,6 @@ #include #include -#include - #include "timer.hh" @@ -40,44 +38,59 @@ namespace dc { #if HAVE_LIBRT -scalar ticks() +// Since the monotonic clock will provide us with the timer since the computer +// started, the number of seconds since that time could easily become so large +// that it cannot be accurately stored in a float (even with as little two days +// update), therefore we need to start from a more recent reference (when the +// program starts). Of course this isn't much of an issue if scalar is a +// double-precious number. + +static time_t setReference() { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { - throw std::runtime_error("cannot access monotonic clock"); + return 0; } - return scalar(ts.tv_sec) + scalar(ts.tv_nsec) / 1000000000.0; + return ts.tv_sec; } -void sleep(scalar seconds, bool absolute) +static const time_t reference = setReference(); + + +scalar ticks() { struct timespec ts; - int ret; - if (!absolute) seconds += ticks(); - ts.tv_sec = time_t(seconds); - ts.tv_nsec = time_t((seconds - scalar(ts.tv_sec)) * 1000000000.0); - - do + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { - ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, 0); + throw std::runtime_error("cannot access monotonic clock"); } - while (ret == -1 && errno == EINTR); + + return scalar(ts.tv_sec - reference) + scalar(ts.tv_nsec) / 1000000000.0; } + #else // ! HAVE_LIBRT -// If we don't have librt, we'll have to use different timing methods. + +// If we don't have librt, we'll have to use a different timing method. SDL +// only promises centisecond accuracy, but it may be better than nothing. + +#include scalar ticks() { - unsigned ms = SDL_GetTicks(); + Uint32 ms = SDL_GetTicks(); return scalar(ms / 1000) + scalar(ms % 1000) / 1000.0; } + +#endif // HAVE_LIBRT + + void sleep(scalar seconds, bool absolute) { struct timespec ts; @@ -85,7 +98,7 @@ void sleep(scalar seconds, bool absolute) if (absolute) seconds -= ticks(); ts.tv_sec = time_t(seconds); - ts.tv_nsec = time_t((seconds - scalar(ts.tv_sec)) * 1000000000.0); + ts.tv_nsec = long((seconds - scalar(ts.tv_sec)) * 1000000000.0); do { @@ -94,8 +107,8 @@ void sleep(scalar seconds, bool absolute) while (ret == -1 && errno == EINTR); } -#endif // HAVE_LIBRT - } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/timer.hh b/src/timer.hh index 3de2a95..8506327 100644 --- a/src/timer.hh +++ b/src/timer.hh @@ -65,3 +65,5 @@ void sleep(scalar seconds, bool absolute = false); #endif // _TIMER_HH_ +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/video.cc b/src/video.cc index 715c407..6960a0b 100644 --- a/src/video.cc +++ b/src/video.cc @@ -30,6 +30,7 @@ #include "serializable.hh" #include "settings.hh" +#include "dispatcher.hh" #include "video.hh" @@ -93,6 +94,7 @@ void video::recreateContext() SDL_FreeSurface(context_); context_ = 0; setVideoMode(attribs_.mode); + dc::dispatcher::instance().dispatch("video.context_recreated"); } void video::setOpenGLAttributes() @@ -345,3 +347,5 @@ video::attributes::attributes() } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ + diff --git a/src/video.hh b/src/video.hh index a16181c..ad89650 100644 --- a/src/video.hh +++ b/src/video.hh @@ -118,3 +118,5 @@ typedef boost::shared_ptr