Allure-0.8.3.0/0000755000000000000000000000000013315545742011303 5ustar0000000000000000Allure-0.8.3.0/COPYLEFT0000644000000000000000000012016413315545742012457 0ustar0000000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Allure Upstream-Contact: Mikolaj Konarski Source: https://hackage.haskell.org/package/Allure Files: * Copyright: 2008-2011 Andres Loeh 2010-2018 Mikolaj Konarski and others (see git history) License: AGPL-3+ Files: GameDefinition/fonts/*.fon Copyright: 1997-2016 Leon Marrick 1997-2016 Sheldon Simms III 1997-2016 Nick McConnell 2016-2018 Mikolaj Konarski License: GPL-2 Files: GameDefinition/fonts/Fix15Mono-Bold.woff Copyright: 2012-2015 The Mozilla Foundation and Telefonica S.A 2016-2018 Mikolaj Konarski License: OFL-1.1 Files: debian/* Copyright: held by the contributors mentioned in debian/changelog License: AGPL-3+ License: AGPL-3+ GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 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 Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. . The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are 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. . 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. . Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. . A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. . The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. . An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. . 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. . Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. . 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 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 work with which it is combined will remain governed by version 3 of the GNU General Public License. . 14. Revised Versions of this License. . The Free Software Foundation may publish revised and/or new versions of the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. . You should have received a copy of the GNU Affero General Public License along with this program. If not, see . . Also add information on how to contact you by electronic and paper mail. . If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. . 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 AGPL, see . License: GPL-2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-2'. License: OFL-1.1 SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 . PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. . The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. . DEFINITIONS Font Software refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. . Reserved Font Name refers to any names specified as such after the copyright statement(s). . Original Version refers to the collection of Font Software components as distributed by the Copyright Holder(s). . Modified Version refers to any derivative made by adding to, deleting, or substituting in part or in whole any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. . Author refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. . PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: . 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. . 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. . 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. . 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. . 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. . TERMINATION This license becomes null and void if any of the above conditions are not met. . DISCLAIMER THE FONT SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Allure-0.8.3.0/CREDITS0000644000000000000000000000267613315545742012336 0ustar0000000000000000All kinds of contributions to Allure of the Stars are gratefully welcome! Some of the contributors are listed below, in chronological order. Andres Loeh Mikolaj Konarski Krzysztof Pławski @Peritract Pablo Reszczynski Fonts 16x16x.fon, 8x8x.fon and 8x8xb.fon are are taken from https://github.com/angband/angband, copyrighted by Leon Marrick, Sheldon Simms III and Nick McConnell and released by them under GNU GPL version 2. Any further modifications by authors of LambdaHack are also released under GNU GPL version 2. The licence file is at GameDefinition/fonts/LICENSE.16x16x in the source code tree. Font Fix15Mono-Bold.woff is a modified version of https://github.com/mozilla/Fira/blob/master/ttf/FiraMono-Bold.ttf that is copyright 2012-2015, The Mozilla Foundation and Telefonica S.A. The modified font is released under the SIL Open Font License, as seen in GameDefinition/fonts/LICENSE.Fix15Mono-Bold in the source code tree. Modifications were performed with font editor FontForge and are as follows: * straighten and enlarge #, enlarge %, &, ', +, \,, -, :, ;, O, _, ` * centre a few other glyphs * create a small 0x22c5 * shrink 0xb7 a bit * extend all fonts by 150% and 150% (the extension resulted in an artifact in letter 'n', which was gleefully kept, and many other artifacts and distortions that should be fixed at some point) * set width of space, nbsp and # glyphs to 1170 (this is a hack to make DOM create square table cells) Allure-0.8.3.0/Makefile0000644000000000000000000004437513315545742012760 0ustar0000000000000000# Copyright (c) 2008--2011 Andres Loeh # Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) # This file is a part of the computer game Allure of the Stars # and is released under the terms of the GNU Affero General Public License. # For license and copyright information, see the file LICENSE. # play: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix play --dumpInitRngs shot: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix play --dumpInitRngs --printEachScreen expose-lore: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --dumpInitRngs --savePrefix know --newGame 2 --gameMode crawl --knowItems --benchmark --noAnim --maxFps 1000 configure-debug: cabal configure --enable-profiling --profiling-detail=all-functions -fwith_expensive_assertions --disable-optimization configure-prof: cabal configure --enable-profiling --profiling-detail=exported-functions -frelease ghcjs-configure: cabal configure --disable-library-profiling --disable-profiling --ghcjs --ghcjs-option=-dedupe -f-release chrome-prof: google-chrome --no-sandbox --js-flags="--logfile=%t.log --prof" ../allureofthestars.github.io/play/index.html minific: ccjs dist/build/Allure/Allure.jsexe/all.js --compilation_level=ADVANCED_OPTIMIZATIONS --isolation_mode=IIFE --assume_function_wrapper --jscomp_off="*" --externs=node --externs=dist/build/Allure/Allure.jsexe/all.js.externs > ../allureofthestars.github.io/play/allure.all.js # Low delay to display animations swiftly and not bore the public too much. # Delay can't be lower than 2, because browsers sometimes treat delay 1 # specially and add their extra delay. create-gif : find ~/.Allure/screenshots/ -name 'prtscn*.bmp' -print0 | xargs -0 -r mogrify -format gif gifsicle -O3 --careful -d2 -l ~/.Allure/screenshots/prtscn*.gif -o ~/.Allure/screenshots/screenshot.gif frontendRaid: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode raid frontendBrawl: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode brawl frontendShootout: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode shootout frontendEscape: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 3 --dumpInitRngs --automateAll --gameMode escape frontendZoo: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode zoo frontendAmbush: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode ambush frontendCrawl: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl frontendCrawlEmpty: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode "crawl empty" frontendSafari: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode safari frontendSafariSurvival: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode "safari survival" frontendBattle: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode battle frontendBattleSurvival: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode "battle survival" frontendDefense: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode defense frontendDefenseEmpty: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode "defense empty" benchMemoryAnim: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --maxFps 100000 --benchmark --stopAfterFrames 33000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 120 --setMainRng 47 --frontendNull --noAnim +RTS -s -A1M -RTS benchBattle: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7 benchAnimBattle: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 3 --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7 benchFrontendBattle: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7 benchCrawl: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0 benchFrontendCrawl: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0 benchNull: benchBattle benchAnimBattle benchCrawl bench: benchBattle benchAnimBattle benchFrontendBattle benchCrawl benchFrontendCrawl nativeBenchCrawl: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0 nativeBenchBattle: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 0 --setMainRng 0 nativeBench: nativeBenchBattle nativeBenchCrawl nodeBenchCrawl: node dist/build/Allure/Allure.jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 2 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0 nodeBenchBattle: node dist/build/Allure/Allure.jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 0 --setMainRng 0 nodeBench: nodeBenchBattle nodeBenchCrawl test-travis: test-short test-medium benchNull test: test-short test-medium benchNull test-short: test-short-new test-short-load test-medium: testRaid-medium testBrawl-medium testShootout-medium testEscape-medium testZoo-medium testAmbush-medium testCrawl-medium testCrawlEmpty-medium testCrawl-medium-know testSafari-medium testSafariSurvival-medium testBattle-medium testBattleSurvival-medium testDefenseEmpty-medium testRaid-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode raid 2> /tmp/teletypetest.log testBrawl-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode brawl 2> /tmp/teletypetest.log testShootout-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode shootout 2> /tmp/teletypetest.log testEscape-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode escape 2> /tmp/teletypetest.log testZoo-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 2 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode zoo 2> /tmp/teletypetest.log testAmbush-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode ambush 2> /tmp/teletypetest.log testCrawl-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 200 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl 2> /tmp/teletypetest.log testCrawlEmpty-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode "crawl empty" 2> /tmp/teletypetest.log testCrawl-medium-know: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix know --newGame 3 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --knowItems 2> /tmp/teletypetest.log testSafari-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 2 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode safari 2> /tmp/teletypetest.log testSafariSurvival-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 8 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" 2> /tmp/teletypetest.log testBattle-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode battle 2> /tmp/teletypetest.log testBattleSurvival-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 7 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" 2> /tmp/teletypetest.log testDefense-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 9 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 200 --dumpInitRngs --automateAll --keepAutomated --gameMode defense 2> /tmp/teletypetest.log testDefenseEmpty-medium: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 9 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode "defense empty" 2> /tmp/teletypetest.log test-short-new: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootout --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log # "--setDungeonRng 0 --setMainRng 0" is needed for determinism relative to seed # generated before game save test-short-load: dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootouti --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log version: dist/build/Allure/Allure --version #in LambdaHack/ # cabal install --disable-library-profiling --disable-profiling --disable-documentation -f-release build-binary-common: cabal install --disable-library-profiling --disable-profiling --disable-documentation -f-release --only-dependencies cabal configure --disable-library-profiling --disable-profiling -f-release --prefix=/ --datadir=. --datasubdir=. cabal build exe:Allure mkdir -p AllureOfTheStars/GameDefinition/fonts cabal copy --destdir=AllureOfTheStarsInstall cp GameDefinition/config.ui.default AllureOfTheStars/GameDefinition cp GameDefinition/fonts/16x16x.fon AllureOfTheStars/GameDefinition/fonts cp GameDefinition/fonts/8x8xb.fon AllureOfTheStars/GameDefinition/fonts cp GameDefinition/fonts/8x8x.fon AllureOfTheStars/GameDefinition/fonts cp GameDefinition/fonts/LICENSE.16x16x AllureOfTheStars/GameDefinition/fonts cp GameDefinition/fonts/Fix15Mono-Bold.woff AllureOfTheStars/GameDefinition/fonts cp GameDefinition/fonts/LICENSE.Fix15Mono-Bold AllureOfTheStars/GameDefinition/fonts cp GameDefinition/PLAYING.md AllureOfTheStars/GameDefinition cp GameDefinition/InGameHelp.txt AllureOfTheStars/GameDefinition cp README.md AllureOfTheStars cp CHANGELOG.md AllureOfTheStars cp LICENSE AllureOfTheStars cp COPYLEFT AllureOfTheStars cp CREDITS AllureOfTheStars build-binary-ubuntu: build-binary-common cp AllureOfTheStarsInstall/bin/Allure AllureOfTheStars dist/build/Allure/Allure --version > /dev/null; \ LH_VERSION=$$(cat ~/.Allure/stdout.txt); \ tar -czf Allure_$${LH_VERSION}_ubuntu-16.04-amd64.tar.gz AllureOfTheStars build-binary-macosx: build-binary-common cp AllureOfTheStarsInstall/bin/Allure AllureOfTheStars dist/build/Allure/Allure --version > /dev/null; \ LH_VERSION=$$(cat ~/.Allure/stdout.txt); \ OS_VERSION=$$(sw_vers -productVersion); \ tar -czf Allure_$${LH_VERSION}_macosx-$${OS_VERSION}-amd64.tar.gz AllureOfTheStars new-build-dev: cabal new-build --datadir=. --disable-optimization -j1 all Allure-0.8.3.0/Allure.cabal0000644000000000000000000001723113315545742013517 0ustar0000000000000000name: Allure -- The package version. See the Haskell package versioning policy (PVP) -- for standards guiding when and how versions should be incremented. -- http://www.haskell.org/haskellwiki/Package_versioning_policy -- PVP summary:+-+------- breaking API changes -- | | +----- minor or non-breaking API additions -- | | | +--- code changes with no API change version: 0.8.3.0 synopsis: Near-future Sci-Fi roguelike and tactical squad game description: Allure of the Stars is a near-future Sci-Fi roguelike and tactical squad game. Binaries and the game manual are available at homepage, where you can also try the game out in the browser at . (It runs fastest on Chrome. Keyboard commands and savefiles are supported only on recent enough versions of browsers. Mouse should work everywhere.) . Not a single picture in this game. You have to imagine everything yourself, like with a book (a grown-up book, without pictures). Once you learn to imagine things, though, you can keep exploring and mastering the world and making up stories for a long time. . The game is written in Haskell using the LambdaHack roguelike game engine . Please see the changelog file for recent improvements and the issue tracker for short-term plans. Long term goals are high replayability and auto-balancing through procedural content generation and persistent content modification based on player behaviour. Contributions are welcome. homepage: http://allureofthestars.com bug-reports: http://github.com/AllureOfTheStars/Allure/issues license: OtherLicense license-file: COPYLEFT tested-with: GHC==8.0.2, GHC==8.2.2, GHC==8.4.3 data-files: GameDefinition/config.ui.default, GameDefinition/fonts/16x16x.fon, GameDefinition/fonts/8x8xb.fon, GameDefinition/fonts/8x8x.fon, GameDefinition/fonts/LICENSE.16x16x, GameDefinition/fonts/Fix15Mono-Bold.woff, GameDefinition/fonts/LICENSE.Fix15Mono-Bold, GameDefinition/InGameHelp.txt, README.md, CHANGELOG.md, LICENSE, COPYLEFT, CREDITS extra-source-files: GameDefinition/MainMenu.ascii, GameDefinition/PLAYING.md, Makefile author: Andres Loeh, Mikolaj Konarski and others maintainer: Mikolaj Konarski category: Game build-type: Simple cabal-version: >= 1.10 source-repository head type: git location: git://github.com/AllureOfTheStars/Allure.git executable Allure hs-source-dirs: GameDefinition main-is: Main.hs other-modules: Client.UI.Content.KeyKind, Content.CaveKind, Content.ItemKind, Content.ItemKindEmbed, Content.ItemKindActor, Content.ItemKindOrgan, Content.ItemKindBlast, Content.ItemKindTemporary, Content.ModeKind, Content.ModeKindPlayer, Content.PlaceKind, Content.RuleKind, Content.TileKind, Implementation.MonadClientImplementation, Implementation.MonadServerImplementation, Implementation.TieKnot, Paths_Allure build-depends: LambdaHack >= 0.8.3.0 && < 0.8.4.0, template-haskell >= 2.6, async >= 2, base >= 4.9 && < 99, containers >= 0.5.3.0, enummapset >= 0.5.2.2, filepath >= 1.2.0.1, optparse-applicative >= 0.13, random >= 1.1, text >= 0.11.2.3, transformers >= 0.4 default-language: Haskell2010 default-extensions: MonoLocalBinds, ScopedTypeVariables, OverloadedStrings BangPatterns, RecordWildCards, NamedFieldPuns, MultiWayIf, LambdaCase, StrictData, CPP other-extensions: TemplateHaskell ghc-options: -Wall -Wcompat -Worphans -Wincomplete-uni-patterns -Wincomplete-record-updates -Wimplicit-prelude -Wmissing-home-modules -Widentities -Wredundant-constraints ghc-options: -Wall-missed-specialisations ghc-options: -fno-ignore-asserts -fexpose-all-unfoldings -fspecialise-aggressively ghc-options: -threaded -rtsopts -- Minimize median lag at the cost of occasional bigger GC lag, -- which fortunately sometimes fits into idle time between turns): ghc-options: -with-rtsopts=-A99m if impl(ghcjs) { -- This is the largest GHCJS_BUSY_YIELD value that does not cause dropped frames -- on my machine with default --maxFps. cpp-options: -DGHCJS_BUSY_YIELD=50 cpp-options: -DUSE_JSFILE } else { build-depends: zlib >= 0.5.3.1 } test-suite test type: exitcode-stdio-1.0 hs-source-dirs: GameDefinition, test main-is: test.hs other-modules: Client.UI.Content.KeyKind, Content.CaveKind, Content.ItemKind, Content.ItemKindEmbed, Content.ItemKindActor, Content.ItemKindOrgan, Content.ItemKindBlast, Content.ItemKindTemporary, Content.ModeKind, Content.ModeKindPlayer, Content.PlaceKind, Content.RuleKind, Content.TileKind, Implementation.MonadClientImplementation, Implementation.MonadServerImplementation, Implementation.TieKnot, Paths_Allure build-depends: LambdaHack, template-haskell >= 2.6, base >= 4.9 && < 99, containers >= 0.5.3.0, enummapset >= 0.5.2.2, filepath >= 1.2.0.1, optparse-applicative >= 0.13, random >= 1.1, text >= 0.11.2.3, transformers >= 0.4 default-language: Haskell2010 default-extensions: MonoLocalBinds, ScopedTypeVariables, OverloadedStrings BangPatterns, RecordWildCards, NamedFieldPuns, MultiWayIf, LambdaCase, StrictData, CPP other-extensions: TemplateHaskell ghc-options: -Wall -Wcompat -Worphans -Wincomplete-uni-patterns -Wincomplete-record-updates -Wimplicit-prelude -Wmissing-home-modules -Widentities -Wredundant-constraints ghc-options: -fno-ignore-asserts -fexpose-all-unfoldings -fspecialise-aggressively ghc-options: -threaded -rtsopts -- Minimize median lag at the cost of occasional bigger GC lag, -- which fortunately sometimes fits into idle time between turns): ghc-options: -with-rtsopts=-A99m if impl(ghcjs) { -- This is the largest GHCJS_BUSY_YIELD value that does not cause dropped frames -- on my machine with default --maxFps. cpp-options: -DGHCJS_BUSY_YIELD=50 } else { build-depends: zlib >= 0.5.3.1 } Allure-0.8.3.0/CHANGELOG.md0000644000000000000000000003707513315545742013130 0ustar0000000000000000## [v0.8.3.0](https://github.com/AllureOfTheStars/Allure/compare/v0.8.1.2...v0.8.3.0) - Add a hack to run SDL2 on the main thread, fixing the OS X crash - Warn visually when impressed and Calm running low, risking domination - Display actor as red when low Calm and impressed or when low HP - Fix, complete and fine tune UI, AI and server skill and weapon checks - Fix a bug where item aspects look different to clients than to the server - Change the requirements for the main menu ASCII art - Tweak loot and terrain a tiny bit ## [v0.8.1.2](https://github.com/AllureOfTheStars/Allure/compare/v0.8.1.1...v0.8.1.2) - Fix typos detected by lintian - Fix the code that runs in case of old async (bug introduced in v0.8.1.1) ## [v0.8.1.1](https://github.com/AllureOfTheStars/Allure/v0.8.1.0...v0.8.1.1) - no player-visible changes - make it possible to compile with old async package - rewrite copyright information according to Debian format - make github display the correct main license ## [v0.8.1.0](https://github.com/AllureOfTheStars/Allure/v0.8.0.0...v0.8.1.0) - no player-visible changes - significantly reduce RAM usage when compiling library - update and extend CI ## [v0.8.0.0, aka 'Explosive dashboard'](https://github.com/AllureOfTheStars/Allure/compare/v0.7.1.0...v0.8.0.0) - display initial bits of backstory as a help screen and in-game - rework greying out menu items and permitting item application and projection - rework history collection; merge message repetitions more aggressively - display HP in red when below (configurable) warning threshold - tweak AI: actors remember they are fleeing; better leader choice, etc. - add to content specialized explosive projectiles; tune the effects - calculate loot score component based on fraction of dungeon loot collected - don't hardwire item price, but let it be specified in content - let all valuables glitter in the dark to avoid complete level exploration - teach AI to cure ailments and shake off impressions - rework detection effects; add detection of items embedded in tiles - automatically identify stolen items that only have minor effects - let projectiles hit each other if fragile and substantial enough - rework item kind identification code; change the way it's defined in content - make more item kinds (including some traps) secret - protect paralyzed actors with a stasis condition to avoid infinite paralysis - implement dumping screenshots in SDL2 and create animated GIFs in Makefile - generate most common consumables less often, but in depth-scaled bunches - make pushed actors alter tiles and trigger effects of embedded items - validate and cross-validate more content; reduce content creation boilerplate - make summoning more varied and prevent chain-summoning - add many ways to conditionally sequence effects - create large, merged rooms more often - generalize the terrain altering player command (C-c, mouse) - let RET, SPACE and ESC clear pending messages, if any - add dashboard with links to all menus and info screens - scale some organ and trap power with level depth - simplify level-scaled dice roll semantics - change scaled dice notation 'dl' to 'dL' for readability in-game - rebalance items and decrease dice variety to unclutter backpack - colour-code beneficial and harmful conditions in menu and in HUD - display item lore (also for organs, embedded items, explosions, etc.) - display embedded item descriptions as if they were tile descriptions - tweak blast visuals, lower particle counts, beautify their spread - tweak projectile visuals, e.g., display an extra frame when projectile dies - add intro screen and work on other ways to convey story - simplify a lot of code, including a bit of game rules - fix some bugs, tweak content, speed up some AI bottlenecks ## [v0.7.1.0, aka 'Ancient troubles'](https://github.com/AllureOfTheStars/Allure/compare/v0.7.0.0...v0.7.1.0) - add amazing cave and item (actor, blast, organ) descriptions - package for Windows as an installer and also as zip archives - fix a crash from SDL frontend under some OpenGL drivers (no thread-safety) - add WWW address to the Main Menu, for other sites that may run our JS blob ## [v0.7.0.0, aka 'The dice are cast'](https://github.com/AllureOfTheStars/Allure/compare/v0.6.2.0...v0.7.0.0) - decouple tile searching from tile alteration - refrain from identifying items that are not randomized - switch away from incapacitated leader to let others revive him - make rescue easier by not going into negative HP the first time - fix crowd of friends on another level slowing even actors that melee - fix missing report about items underneath an actor when changing levels - API breakage: change the syntax of dice in content - API addition: introduce cave descriptions - keep all client states in the server and optimize communication with clients - improve item choice for identification and item polymorphing - reset embedded items when altering tile - replace atomic command filtering with exception catching - reimplement dice as symbolic expressions inducing multiple RNG calls - switch to optparse-applicative and rewrite cli handling - add stack and cabal new-build project files - improve haddocks across the codebase ## [v0.6.2.0, aka 'Zoom out'](https://github.com/AllureOfTheStars/Allure/compare/v0.6.1.0...v0.6.2.0) - make fireworks slower and so easier to spot - make rattlesnake deeper but more common - announce no effect of activation - describe original and current faction of an actor - highlight dominated actors - mark organs with comma instead of percent and gems with dollar - make the healing cave dangerous to prevent camping - slightly balance various content - by default move item the same as last time - often spawn between heroes and stairs going deeper - fix totalUsefulness computation for negative effects - fix abandoning distant enemy target despite no alternatives - fix slow pushing of actors - fix a crash when many actors run towards stairs - hotfix: Pass zoom keys through to the browser - help players find the info about changing the font size - depend on GHC >= 8.0 and new vector ## [v0.6.1.0, aka 'Breaking one rule at a time'](https://github.com/AllureOfTheStars/Allure/compare/v0.6.0.0...v0.6.1.0) - major engine bugfix: fix redrawing after window minimized and restored - major engine bugfix: hack around vanishing texture on Windows - major engine bugfix: hack around SDL backends not thread-safe on Windows - update wrt the only breaking API change: specify font dir in game rules content - let the game use its own fonts, not fonts from the sample game in library - tweak some item creation to occur in character's pack, not on the ground - slightly balance various content - make sure the 'resolution' effect is not a drawback - make artifact weapon rarities more regular - avoid creating lit, open dungeon at the bottom, where foes have ranged weapons - number scenarios in user descriptions ## [v0.6.0.0, aka 'Too much to tell'](https://github.com/AllureOfTheStars/Allure/compare/v0.5.0.0...v0.6.0.0) - add and modify a lot of content: items, tiles, embedded items, scenarios - improve AI: targeting, stealth, moving in groups, item use, fleeing, etc. - make monsters more aggressive than animals - tie scenarios into a loose, optional storyline - add more level generators and more variety to room placement - make stairs not walkable and use them by bumping - align stair position on the levels they pass through - introduce noctovision - increase human vision to 12 so that normal speed missiles can be sidestepped - tweak and document weapon damage calculation - derive projectile damage mostly from their speed - make heavy projectiles better vs armor but easier to sidestep - improve hearing of unseen actions, actors and missiles impacts - let some missiles lit up on impact - make torches reusable flares and add blankets for dousing dynamic light - add detection effects and use them in items and tiles - make it possible to catch missiles, if not using weapons - make it possible to wait 0.1 of a turn, at the cost of no bracing - improve pathfinding, prefer less unknown, alterable and dark tiles on paths - slow down actors when acting at the same time, for speed with large factions - don't halve Calm at serious damage any more - eliminate alternative FOV modes, for speed - stop actors blocking FOV, for speed - let actor move diagonally to and from doors, for speed - improve blast (explosion) shapes visually and gameplay-wise - add SDL2 frontend and deprecate GTK frontend - add specialized square bitmap fonts and hack a scalable font - use middle dot instead of period on the map (except in teletype frontend) - add a browser frontend based on DOM, using ghcjs - improve targeting UI, e.g., cycle among items on the map - show an animation when actor teleports - add character stats menu and stat description texts - add item lore and organ lore menus - add a command to sort item slots and perform the sort at startup - add a single item manipulation menu and let it mark an item for later - make history display a menu and improve display of individual messages - display highscore dates according to the local timezone - make the help screen a menu, execute actions directly from it - rework the Main Menu - rework special positions highlight in all frontends - mark leader's target on the map (grey highlight) - visually mark currently chosen menu item and grey out impossible items - define mouse commands based on UI mode and screen area - let the game be fully playable only with mouse, use mouse wheel - pick menu items with mouse and with arrow keys - add more sanity checks for content - reorganize content in files to make rebasing on changed content easier - rework keybinding definition machinery - let clients, not the server, start frontends - version savefiles and move them aside if versions don't match - lots of bug fixes internal improvements and minor visual and text tweaks ## [v0.5.0.0, aka 'Halfway through space'](https://github.com/AllureOfTheStars/Allure/compare/v0.4.101.0...v0.5.0.0) - let AI put excess items in shared stash and use them out of shared stash - let UI multiple items pickup routine put items that don't fit into equipment into shared stash, if possible, not into inventory pack - re-enable the ability to hear close, invisible foes - add a few more AI and autonomous henchmen tactics (CTRL-T) - keep difficulty setting over session restart - change some game start keybindings - replace the Duel game mode with the Raid game mode - various bugfixes, minor improvements and balancing ## [v0.4.101.1, aka 'Officially fun'](https://github.com/AllureOfTheStars/Allure/compare/v0.4.100.0...v0.4.101.1) - the game is now officially fun to play, with a seal of the Galactic Council - introduce unique boss monsters and unique artifact items - add robots that heal the player, in particular as a mid-game reset for HP - move spaceship airlock to level 10 and beef up spaceship crew - let AI gang up, attempt stealth and react to player aggressiveness - spawn actors fast, close to the enemy and in large numbers - spawn actors less and less often on a given level, but with growing depth - prefer weapons with effects, if recharged - make the bracing melee bonus additive, not multiplicative - let explosions buffet actors around - make braced actors immune to translocation effects - make actor domination yet less common and deadly - use mouse for movement, actor selection, aiming - don't run straight with selected actors, but go-to cross-hair with them - speed up default frame rate, slow down projectiles visually - rework item manipulation UI - you can pick up many items at once and it costs only one turn - allow actors to apply and project from the shared stash - reverse messages shown in player diary - display actor organs and stats - split highscore tables wrt game modes - move score calculation formula to content - don't keep the default/example config file commented out; was misleading - update vs the naughtily changed v0.5.0.0 of LambdaHack content API ## [v0.4.100.0, aka 'The last interstellar thaw'](https://github.com/AllureOfTheStars/Allure/compare/v0.4.99.0...v0.4.100.0) - update vs the unexpectedly thawed v0.5.0.0 of LambdaHack content API - unexpectedly add items with timeouts and temporary effects - start campaign on level 3 and don't spawn aliens until level 4 - rebalance campaign (probably still too hard) - tweak skills of some factions and actors - rename tablets to chips to make their vanishing easier to understand - make colorful characters bold (if it resizes your fonts, turn off via colorIsBold = False in config file or --noColorIsBold on commandline) - start the game with a screensaver safari mode - improve keyboard handling on Windows - add i386 Linux and Windows compilation targets to Makefile ## [v0.4.99.0, aka 'Player escapes through airlock'](https://github.com/AllureOfTheStars/Allure/compare/v0.4.14...v0.4.99.0) - balance game content a bit (campaign still unbalanced) - fix a bug where doors can't be closed - assign AI tactics to players, in particular use follow-the-leader in safari - specify monster spawn rate per-cave - generally update content to the new v0.5.0.0 of LambdaHack content API ## [v0.4.14, aka 'Out of cosmic balance'](https://github.com/AllureOfTheStars/Allure/compare/v0.4.12...v0.4.14) - add tons of new (unbalanced) items, actors and descriptions - add a simple cabal test in addition to make-test and travis-test - add items of Wonder and of Marvel - add game mechanics, items and places to enable stealthy tactics - add lots of shrapnel (explosions) and organs (body parts) - expose a bit of the plot via new game modes and their order ## [v0.4.12](https://github.com/AllureOfTheStars/Allure/compare/v0.4.10...v0.4.12) - make walls lit by default to simplify exploration - improve and simplify dungeon generation - simplify running and permit multi-actor runs - let items explode and generate shrapnel projectiles - add game difficulty setting (initial HP scaling right now) - allow recording, playing back and looping commands - implement pathfinding via per-actor BFS over the whole level - extend setting targets for actors in UI tremendously - implement autoexplore, go-to-target, etc., as macros - let AI use pathfinding, switch leaders, pick levels to swarm to - force level/leader changes on spawners (even when played by humans) - extend and redesign UI bottom status lines ## [v0.4.10](https://github.com/AllureOfTheStars/Allure/compare/v0.4.8...v0.4.10) - screensaver game modes (AI vs AI) - improved AI (can now climbs stairs, etc.) - multiple, multi-floor staircases - multiple savefiles - configurable framerate and combat animations ## [v0.4.8](https://github.com/AllureOfTheStars/Allure/compare/v0.4.6.5...v0.4.8) - experimental multiplayer modes - a lot of gameplay changes induced by the engine overhaul and in particular the client-server rewrite ## [v0.4.6.5](https://github.com/AllureOfTheStars/Allure/compare/v0.4.6...v0.4.6.5) - this is a minor release, primarily intended to fix the broken compilation on Hackage - changes since 0.4.6 are mostly unrelated to gameplay: - strictly typed config files split into UI and rules - a switch from Text to String throughout the codebase - use of the external library miniutter for English sentence generation ## [v0.4.6](https://github.com/AllureOfTheStars/Allure/compare/v0.4.4...v0.4.6) - the Main Menu - improved and configurable mode of squad combat ## [v0.4.4](https://github.com/AllureOfTheStars/Allure/compare/v0.4.3...v0.4.4) - missiles flying for three turns (by an old kosmikus' idea) - visual feedback for targeting - animations of combat and individual monster moves ## [v0.4.3](https://github.com/AllureOfTheStars/Allure/compare/v0.4.2...v0.4.3) - the Allure of the Stars game depends on the LambdaHack engine library Allure-0.8.3.0/LICENSE0000644000000000000000000010333013315545742012310 0ustar0000000000000000 GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 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 Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are 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. 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. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. 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 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 work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. 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 AGPL, see . Allure-0.8.3.0/Setup.hs0000644000000000000000000000005713315545742012741 0ustar0000000000000000import Distribution.Simple main = defaultMain Allure-0.8.3.0/README.md0000644000000000000000000002155013315545742012565 0ustar0000000000000000Allure of the Stars =================== [![Build Status](https://travis-ci.org/AllureOfTheStars/Allure.svg?branch=master)](https://travis-ci.org/AllureOfTheStars/Allure) [![Hackage](https://img.shields.io/hackage/v/Allure.svg)](https://hackage.haskell.org/package/Allure) [![Join the chat at https://gitter.im/AllureOfTheStars/Allure](https://badges.gitter.im/AllureOfTheStars/Allure.svg)](https://gitter.im/AllureOfTheStars/Allure?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Allure of the Stars is a near-future Sci-Fi roguelike[2] and tactical squad game. Binaries and the game manual are available at homepage[6], where you can also try the game out in the browser at http://allureofthestars.com/play. (It runs fastest on Chrome. Keyboard commands and savefiles are supported only on recent enough versions of browsers. Mouse should work everywhere.) ![gameplay screenshot](https://raw.githubusercontent.com/AllureOfTheStars/media/master/screenshot/allureofthestars.com.ambush.gif) Not a single picture in this game. You have to imagine everything yourself, like with a book (a grown-up book, without pictures). Once you learn to imagine things, though, you can keep exploring and mastering the world and making up stories for a long time. The game is written in Haskell[1] using the LambdaHack[10] roguelike game engine. Please see the changelog file for recent improvements and the issue tracker for short-term plans. Long term goals are high replayability and auto-balancing through procedural content generation and persistent content modification based on player behaviour. Contributions are welcome. Game installation from binary archives -------------------------------------- The game runs rather slowly in the browser (fastest on Chrome) and you are limited to only one font, though it's scalable. Also, savefiles are prone to corruption on the browser, e.g., when it's closed while the game is still saving progress (which takes a long time). Hence, after trying out the game, you may prefer to use a native binary for your architecture, if it exists. Pre-compiled game binaries are available through the release page[11] (and, for Windows, continuously from AppVeyor[18]). To use a pre-compiled binary archive, unpack it and run the executable in the unpacked directory or use program shortcuts from the installer, if available. On Linux, make sure you have the SDL2 libraries installed on your system (e.g., libsdl2-2.0-0, libsdl2-ttf-2.0-0 on Ubuntu). On Mac OS X, you need SDL2 installed, e.g., from [libsdlorg](https://www.libsdl.org/download-2.0.php). For Windows, the SDL2 and all other needed libraries are already contained in the game's binary archive. Note that Windows binaries no longer work on Windows XP, since Cygwin and MSYS2 dropped support for XP. Screen and keyboard configuration --------------------------------- The game UI can be configured via a config file. The default settings, the same that are built into the binary, are in [GameDefinition/config.ui.default](https://github.com/AllureOfTheStars/Allure/blob/master/GameDefinition/config.ui.default). When the game is run for the first time, the file is copied to the default user data folder, which is `~/.Allure/` on Linux, `C:\Users\\AppData\Roaming\Allure\` (or `C:\Documents And Settings\user\Application Data\Allure\` or something else altogether) on Windows, and in RMB menu, under `Inspect/Application/Local Storage` when run inside the Chrome browser. Screen font can be changed by editing the config file in the user data folder. For a small game window, the highly optimized 16x16x.fon and 8x8x.fon bitmap fonts are the best, but for larger window sizes or if you require international characters (e.g. to give custom names to player characters), a modern scalable font supplied with the game is the only option. The game window automatically scales according to the specified font size. If you don't have a numeric keypad, you can use mouse or laptop keys (uk8o79jl) for movement or you can enable the Vi keys (aka roguelike keys) in the config file. If numeric keypad doesn't work, toggling the Num Lock key sometimes helps. If running with the Shift key and keypad keys doesn't work, try Control key instead. The game is fully playable with mouse only, as well as with keyboard only, but the most efficient combination for some players is mouse for go-to, inspecting, and aiming at distant positions and keyboard for everything else. Compilation from source ----------------------- If you want to compile native binaries from the source code, use Cabal (already a part of your OS distribution, or available within The Haskell Platform[7]), which also takes care of all the dependencies. The recommended frontend is based on SDL2, so you need the SDL2 libraries for your OS. On Linux, remember to install the -dev versions as well, e.g., libsdl2-dev and libsdl2-ttf-dev on Ubuntu Linux 16.04. (Compilation to JavaScript for the browser is more complicated and requires the ghcjs[15] compiler and optionally the Google Closure Compiler[16] as well.) The latest official version of the game can be downloaded, compiled for SDL2 and installed automatically by Cabal from Hackage[3] as follows cabal update cabal install Allure For a newer version, install a matching LambdaHack library snapshot, clone the game source from github[5] and run `cabal install` from the main directory. Testing and debugging --------------------- The [Makefile](https://github.com/AllureOfTheStars/Allure/blob/master/Makefile) contains many sample test commands. Numerous tests that use the screensaver game modes (AI vs. AI) and the teletype frontend are gathered in `make test`. Of these, travis runs `test-travis` on each push to github. Test commands with prefix `frontend` start AI vs. AI games with the standard, user-friendly frontend. Run `Allure --help` to see a brief description of all debug options. Of these, the `--sniff` option is very useful (though verbose and initially cryptic), for displaying the traffic between clients and the server. Some options in the config file may prove useful too, though they mostly overlap with commandline options (and will be totally merged at some point). Coding style ------------ Stylish Haskell is used for slight auto-formatting at buffer save; see [.stylish-haskell.yaml](https://github.com/AllureOfTheStars/Allure/blob/master/.stylish-haskell.yaml). As defined in the file, indentation is 2 spaces wide and screen is 80-columns wide. Spaces are used, not tabs. Spurious whitespace avoided. Spaces around arithmetic operators encouraged. Generally, relax and try to stick to the style apparent in a file you are editing. Put big formatting changes in separate commits. Haddocks are provided for all module headers and for all functions and types from major modules, in particular the modules that are interfaces for a whole directory of modules. Apart of that, only very important functions and types are distinguished by having a haddock. If minor ones have comments, they should not be haddocks and they are permitted to describe implementation details and be out of date. Prefer assertions to comments, unless too verbose. Further information ------------------- For more information, visit the wiki[4] and see [PLAYING.md](https://github.com/AllureOfTheStars/Allure/blob/master/GameDefinition/PLAYING.md), [CREDITS](https://github.com/AllureOfTheStars/Allure/blob/master/CREDITS) and [COPYLEFT](https://github.com/AllureOfTheStars/Allure/blob/master/COPYLEFT). Have fun! Copyright --------- Copyright (c) 2008--2011 Andres Loeh Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) Allure of the Stars is free software: you can redistribute it and/or modify it under the terms of the GNU Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program in file LICENSE. If not, see . Exceptions and detailed copyright information is contained in file COPYLEFT. [1]: http://www.haskell.org/ [2]: http://roguebasin.roguelikedevelopment.org/index.php?title=Berlin_Interpretation [3]: http://hackage.haskell.org/package/Allure [4]: https://github.com/AllureOfTheStars/Allure/wiki [5]: http://github.com/AllureOfTheStars/Allure [6]: http://allureofthestars.com [7]: http://www.haskell.org/platform [10]: http://github.com/LambdaHack/LambdaHack [11]: https://github.com/AllureOfTheStars/Allure/releases [15]: https://github.com/ghcjs/ghcjs [16]: https://www.npmjs.com/package/google-closure-compiler [18]: https://ci.appveyor.com/project/Mikolaj/allure/build/artifacts Allure-0.8.3.0/GameDefinition/0000755000000000000000000000000013315545742014165 5ustar0000000000000000Allure-0.8.3.0/GameDefinition/PLAYING.md0000644000000000000000000003613213315545742015617 0ustar0000000000000000Playing Allure of the Stars =========================== The following blurb is a copy of the game intro screen. Your party of trusted adventurers is about to test its fortune by plundering a vast derelict passenger cruiser. The spaceship was abandoned and lost after an accident at the Solar System's outer frontier, but recently unexpectedly reappeared. Reportedly, deposit safes and jewelry inventories were never salvaged. Neither were emptied the robot holds, the arboretum nor the extravagant whole-deck habitat of natural and enhanced wild animals. Up to this day, countless autonomous life support and damage mitigation subsystems with redundant power sources busy themselves throughout dozens of decks. They seem successful, because there are abundant traces of ongoing metabolism all over the ship. That should make exploration so much easier. Yours can't be the only shady Neptune Area crew interested in the giant wreck, so you shouldn't count on reaching it unopposed. The feral outer frontier denizens are not famed for scruples nor for restraint when using hazardous nano, cyber and bio technologies, so never assume you are safe. Be ready to hide in shadows, create distractions, set up ambushes, bump into unspeakable horrors, puzzling machinery and astounding treasures and make creative use of all you find, because you are on your own. If ever you turn your back in fear, expect to be chased tirelessly by sight, sound and smell. Once the few basic command keys and on-screen symbols are learned, mastery and enjoyment of the game is the matter of tactical skill and literary imagination. To be honest, a lot of imagination is required at this stage of game development, but the game is already playable and winnable. Contributions are welcome. If the game window is too large for your screen or you experience other technical issues, please consult [README.md](https://github.com/AllureOfTheStars/Allure/blob/master/README.md) or describe your problem on gitter or the issue tracker. Heroes ------ The heroes are marked on the map with symbols `@` and `1` through `9`. The currently chosen party leader is highlighted on the map and his attributes are displayed at the bottommost status line, which in its most complex form looks as follows. *@12 4d1+5% Calm: 20/60 HP: 33/50 Target: basilisk [**__] The line starts with the list of party members, with the current leader highlighted. Most commands involve only the leader, including movement with keyboard's keypad or `LMB` (left mouse button). If more heroes are selected, e.g., by clicking on the list with `RMB` (right mouse button), they run together whenever `:` or `RMB` over map area is pressed. Next on the status line is the damage of the currently best melee weapon the leader can use, then his current and maximum Calm (morale, composure, focus, attentiveness), then his current and maximum HP (hit points, health). At the end, the personal target of the leader is described, in this case a basilisk monster, with hit points drawn as a bar. Additionally, the colon after "Calm" turning into a dot signifies that the leader is in a position without ambient illumination and a brace sign instead of colon after "HP" means the leader is braced for combat (see section [Basic Commands](#basic-commands)). Instead of a monster, the target area may describe a position on the map, a recently spotted item on the floor or an item in inventory selected for further action or, if none are available, just display the current leader name. Weapon damage and other item stats are displayed using the dice notation `xdy`, which means `x` rolls of `y`-sided dice. A variant denoted `xdLy` is additionally scaled by the level depth in proportion to the maximal level depth (at the first level it's always one, then it grows up to full rolled value at the last level). Section [Monsters](#monsters) below describes combat resolution in detail, including the percentage damage bonus seen in the example. The second, upper status line describes the current level in relation to the party. 5 Lofty hall [33% seen] X-hair: exact spot (71,12) p15 l10 First comes the depth of the current level and its name. Then the percentage of its explorable tiles already seen by the heroes. The `X-hair` (aiming crosshair) is the common focus of the whole party, marked on the map and manipulated with mouse or movement keys in aiming mode. In this example, the corsshair points at an exact position on the map and at the end of the status line comes the length of the shortest path from the leader position to the spot and the straight-line distance between the two points. Game map -------- The map of any particular scenario may consist of one or many levels and each level consists of a large number of tiles. The game world is persistent, i.e., every time the player visits a level during a single game, its layout is the same. The basic tile kinds are as follows. game map terrain type on-screen symbol wall # tree or rock or man-made column O rubble & bush, transparent obstacle % trap, ice ^ closed door + open door ' smoke or fog ; ground, corridor . stairs or exit up < stairs or exit down > So, for example, the following map shows a room with a closed door connected by a corridor with a room with an open door, a pillar, staircase down and rubble that obscures one of the corners. #### #### #..#########..&& #..+.......'.O.>&# #..#########.....# #### ####### Basic Commands -------------- This section is a copy of the first two screens of in-game help and a screen introducing mouse commands. The help pages are automatically generated based on a game's keybinding content and on overrides in the player's config file. The remaining in-game help screens, not shown here, list all game commands grouped by categories, in detail. A text snapshot of the complete in-game help is in [InGameHelp.txt](InGameHelp.txt). Walk throughout a level with mouse or numeric keypad (left diagram below) or its compact laptop replacement (middle) or the Vi text editor keys (right, enabled in config.ui.ini). Run, until disturbed, by adding Shift or Control. Go-to with LMB (left mouse button). Run collectively with RMB. 7 8 9 7 8 9 y k u \|/ \|/ \|/ 4-5-6 u-i-o h-.-l /|\ /|\ /|\ 1 2 3 j k l b j n In aiming mode, the same keys (and mouse) move the x-hair (aiming crosshair). Press 'KP_5' ('5' on keypad, or 'i' or '.') to wait, bracing for impact, which reduces any damage taken and prevents displacement by foes. Press 'C-KP_5' (the same key with Control) to wait 0.1 of a turn, without bracing. You displace enemies by running into them with Shift/Control or RMB. Search, open, descend and attack by bumping into walls, doors, stairs and enemies. The best item to attack with is automatically chosen from among weapons in your personal equipment and your body parts. The following commands, joined with the basic set above, let you accomplish anything in the game, though not necessarily with the fewest keystrokes. You can also play the game exclusively with a mouse, or both mouse and keyboard. See the ending help screens for mouse commands. Lastly, you can select a command with arrows or mouse directly from the help screen or the dashboard and execute it on the spot. keys command g or , grab item(s) ESC cancel aiming/open main menu RET or INS accept target/open dashboard SPACE clear messages/display history S-TAB cycle among all party members KP_* or ! cycle x-hair among enemies C-c open or close or alter + swerve the aiming line Screen area and UI mode (aiming/exploration) determine mouse click effects. Here is an overview of effects of each button over most of the game map area. The list includes not only left and right buttons, but also the optional middle mouse button (MMB) and even the mouse wheel, which is normally used over menus, to page-scroll them. For mice without RMB, one can use C-LMB (Control key and left mouse button). keys command LMB set x-hair to enemy/go to pointer for 25 steps RMB or C-LMB fling at enemy/run to pointer collectively for 25 steps C-RMB open or close or alter at pointer MMB snap x-hair to floor under pointer WHEEL-UP swerve the aiming line WHEEL-DN unswerve the aiming line Advanced Commands ----------------- For ranged attacks, setting the aiming crosshair beforehand is not mandatory, because x-hair is set automatically as soon as a monster comes into view and can still be adjusted for as long as the missile to fling is not chosen. However, sometimes you want to examine the level map tile by tile or assign persistent personal targets to party members. The latter is essential in the rare cases when your henchmen (non-leader characters) can move autonomously or fire opportunistically (via innate skills or rare equipment). Also, if your henchman is adjacent to more than one enemy, setting his target makes him melee a particular foe. You can enter the detailed aiming mode with the `*` keypad key that selects enemies or the `/` keypad key that cycles among items on the floor and marks a tile underneath an item. You can move x-hair with direction keys and assign a personal target to the leader with `RET` key (Return, Enter). The details of the shared x-hair position and of the personal target are described in the status lines at the bottom of the screen, as explained in section [Heroes](#heroes) above. Commands for saving and exiting the current game, starting a new game, configuring convenience settings for the current game and challenges for the next game are listed in the main menu, brought up by the `ESC` key. Game difficulty, from the challenges menu, determines hitpoints at birth for any actor of any UI-using faction. The "lone wolf" challenge mode reduces player's starting actors to exactly one (consequently, this does not affect the initial 'raid' scenario). The "cold fish" challenge mode makes it impossible for player characters to be healed by actors from other factions (this is a significant restriction in the final 'crawl' scenario). For a person new to roguelikes, the 'raid' scenario offers a gentle introduction. The subsequent game scenarios gradually introduce squad combat, stealth, opportunity fire, asymmetric battles and more. Starting from the second scenario, the player controls a whole team of characters and will develop his repertoire of squad formations, preferred rendezvous locations and the use of light sources. The last scenario takes place in a multi-floor setting, giving player the choice of exploration of a single level at a time or portions of many levels along a single staircase and also of guarding staircases against enemies from other levels or, inversely, avoiding the staircases. Monsters -------- The life of the heroes is full of dangers. Monstrosities, natural and out of this world, roam the dark corridors and crawl from damp holes day and night. While heroes pay attention to all other party members and take care to move one at a time, monsters don't care about each other and all move at once, sometimes brutally colliding by accident. When a hero bumps into a monster or a monster attacks the hero, melee combat occurs. Heroes and monsters running into one another (with the `Shift` or `Control` key) do not inflict damage, but change places. This gives the opponent a free blow, but can improve the tactical situation or aid escape. In some circumstances actors are immune to the displacing, e.g., when both parties form a continuous front-line. In melee combat, the best equipped weapon (or the best fighting organ) of each opponent is taken into account for determining the damage and any extra effects of the blow. Since an item needs to be recharged in order to have its full effect, weapons on timeout are only considered according to their raw damage dice (the same as displayed at bottommost status line). To determine the damage dealt, the outcome of the weapon's damage dice roll is multiplied by a percentage bonus. The bonus is calculated by taking the damage bonus (summed from the equipped items of the attacker, capped at 200%) minus the melee armor modifier of the defender (capped at 200%, as well), with the outcome bounded between -99% and 99%, which means that at least 1% of damage always gets through and the damage is always lower than twice the dice roll. The current leader's melee bonus, armor modifier and other detailed stats can be viewed via the `#` command. In ranged combat, the missile is assumed to be attacking the defender in melee, using itself as the weapon, with the usual dice and damage bonus. This time, the ranged armor stat of the defender is taken into account and, additionally, the speed of the missile (based on shape and weight) figures in the calculation. You may propel any item from your inventory (by default you are offered only the appropriate items; press `?`to cycle item menu modes). Only items of a few kinds inflict any damage, but some have other effects, beneficial, detrimental or mixed. In-game detailed item descriptions contain melee and ranged damage estimates. They do not take into account damage from effects and, if bonuses are not known, guesses are based on averages for the item kind in question. The displayed figures are rounded, but the game internally keeps track of minute fractions of HP. The stress of combat drains Calm, gradually limiting the use of items and, if Calm reaches zero and the actor is impressed by his foes, making him defect and surrender to their domination. Whenever the monster's or hero's hit points reach zero, the combatant is incapacitated and promptly dies. When the last hero dies or is dominated, the scenario ends in defeat. On Winning and Dying -------------------- You win a scenario if you escape the location alive (which may prove difficult, because your foes gradually build up the ambush squads blocking your escape routes) or, in scenarios with no exit locations, if you eliminate all opposition. In the former case, your score is based predominantly on the gold and precious gems you've plundered. In the latter case, your score is most influenced by the number of turns you spent overcoming your foes (the quicker the victory, the better; the slower the demise, the better). Bonus points, affected by the number of heroes lost, are awarded only if you win. The score is heavily modified by the chosen game difficulty, but not by any other challenges. When all your heroes fall, you are going to invariably see a new foolhardy party of adventurers clamoring to be led into the unknown. They start their conquest from a new entrance, with no experience and no equipment, and new, undaunted enemies bar their way. Lead the new hopeful explorers to fame, wealth and glory! Allure-0.8.3.0/GameDefinition/config.ui.default0000644000000000000000000000357513315545742017426 0ustar0000000000000000; Copyright (c) 2008--2011 Andres Loeh ; Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) ; This file is a part of the computer game Allure of the Stars ; and is released under the terms of the GNU Affero General Public License. ; For license and copyright information, see the file LICENSE. ; ; This is a copy of the default UI settings config file ; that is embedded in the game binary. A user config file can override ; these options. Option names are case-sensitive and only ';' for comments ; is permitted. ; ; The game looks for the config file at the same path where saved games ; directory is located. E.g. on Linux the file is at ; ~/.Allure/config.ui.ini ; and on Windows it can be at ; C:\Documents And Settings\user\Application Data\Allure\config.ui.ini ; or at ; C:\Users\\AppData\Roaming\Allure\config.ui.ini ; or elsewhere. [extra_commands] ; Angband compatibility (accept target) Cmd_2 = ("KP_Insert", ([CmdAim], "", ByAimMode {exploration = ExecuteIfClear Dashboard, aiming = Accept})) [hero_names] HeroName_0 = ("Haskell Alvin", "he") HeroName_1 = ("Alonzo Barkley", "he") HeroName_2 = ("Ines Galenti", "she") HeroName_3 = ("Ernst Abraham", "he") HeroName_4 = ("Samuel Saunders", "he") HeroName_5 = ("Roger Robin", "he") HeroName_6 = ("Christopher Flatt", "he") [ui] movementViKeys_hjklyubn = False movementLaptopKeys_uk8o79jl = True ; Monospace fonts that have fixed size regardless of boldness (on some OSes at least) gtkFontFamily = "DejaVu Sans Mono,Consolas,Courier New,Liberation Mono,Courier,FreeMono,Monospace" ;sdlFontFile = "Fix15Mono-Bold.woff" fontSize = 16 sdlTtfSizeAdd = -2 sdlFontFile = "16x16x.fon" sdlFonSizeAdd = 1 ;sdlFontFile = "8x8x.fon" ;sdlFonSizeAdd = 2 colorIsBold = True ; New historyMax takes effect after removal of savefiles. historyMax = 5000 maxFps = 30 noAnim = False runStopMsgs = False hpWarningPercent = 20 overrideCmdline = "" Allure-0.8.3.0/GameDefinition/InGameHelp.txt0000644000000000000000000001641513315545742016706 0ustar0000000000000000This is a snapshot of in-game help, rendered with default config file. For more general gameplay information see https://github.com/AllureOfTheStars/Allure/blob/master/GameDefinition/PLAYING.md Minimal cheat sheet for casual play (1/2). Walk throughout a level with mouse or numeric keypad (left diagram below) or its compact laptop replacement (middle) or the Vi text editor keys (right, enabled in config.ui.ini). Run, until disturbed, by adding Shift or Control. Go-to with LMB (left mouse button). Run collectively with RMB. 7 8 9 7 8 9 y k u \|/ \|/ \|/ 4-5-6 u-i-o h-.-l /|\ /|\ /|\ 1 2 3 j k l b j n In aiming mode, the same keys (and mouse) move the x-hair (aiming crosshair). Press 'KP_5' ('5' on keypad, or 'i' or '.') to wait, bracing for impact, which reduces any damage taken and prevents displacement by foes. Press 'C-KP_5' (the same key with Control) to wait 0.1 of a turn, without bracing. You displace enemies by running into them with Shift/Control or RMB. Search, open, descend and attack by bumping into walls, doors, stairs and enemies. The best item to attack with is automatically chosen from among weapons in your personal equipment and your body parts. Minimal cheat sheet for casual play (2/2). The following commands, joined with the basic set above, let you accomplish anything in the game, though not necessarily with the fewest keystrokes. You can also play the game exclusively with a mouse, or both mouse and keyboard. See the ending help screens for mouse commands. Lastly, you can select a command with arrows or mouse directly from the help screen or the dashboard and execute it on the spot. keys command g or , grab item(s) ESC cancel aiming/open main menu RET or INS accept target/open dashboard SPACE clear messages/display history S-TAB cycle among all party members KP_* or ! cycle x-hair among enemies C-c open or close or alter + swerve the aiming line Here is the complete list of commands from the snapshot of in-game help. For more general gameplay information see https://github.com/AllureOfTheStars/Allure/blob/master/GameDefinition/PLAYING.md All terrain exploration and alteration commands. keys command S-TAB cycle among all party members C-c open or close or alter TAB cycle among party members on the level c close door = select (or deselect) party member _ deselect (or select) all on the level ; go to x-hair for 25 steps : run to x-hair collectively for 25 steps x explore nearest unknown spot X autoexplore 25 times R rest (wait 25 times) C-R lurk (wait 0.1 turns 100 times) 0, 1 ... 6 pick a particular actor as the new leader Item menu commands. keys command g or , grab item(s) d or . drop item(s) f fling projectile C-f fling without aiming a apply consumable C-a apply and keep choice p pack item e equip item s stash and share item Remaining item-related commands. keys command ^ sort items by ownership, kind and stats P manage item pack of the leader G manage items on the ground E manage equipment of the leader S manage the shared party stash A manage all owned items @ describe organs of the leader # show stat summary of the leader ~ display known lore q quaff liquid r read chip t throw missile Aiming. keys command ESC cancel aiming/open main menu RET or INS accept target/open dashboard KP_* or ! cycle x-hair among enemies + swerve the aiming line KP_/ or / cycle x-hair among items - unswerve the aiming line \ cycle aiming modes C-? set x-hair to nearest unknown spot C-I set x-hair to nearest item C-{ set x-hair to nearest upstairs C-} set x-hair to nearest dnstairs < move aiming one level up > move aiming one level down BACKSPACE clear chosen item and target Assorted. keys command SPACE clear messages/display history F12 open dashboard ? or F1 display help v voice again the recorded commands V voice recorded commands 100 times C-v voice recorded commands 1000 times C-V voice recorded commands 25 times ' start recording commands C-P print screen Mouse overview. Screen area and UI mode (aiming/exploration) determine mouse click effects. Here is an overview of effects of each button over most of the game map area. The list includes not only left and right buttons, but also the optional middle mouse button (MMB) and even the mouse wheel, which is normally used over menus, to page-scroll them. For mice without RMB, one can use C-LMB (Control key and left mouse button). keys command LMB set x-hair to enemy/go to pointer for 25 steps RMB or C-LMB fling at enemy/run to pointer collectively for 25 steps C-RMB open or close or alter at pointer MMB snap x-hair to floor under pointer WHEEL-UP swerve the aiming line WHEEL-DN unswerve the aiming line Mouse in aiming mode. area LMB (left mouse button) RMB (right mouse button) message line clear messages/display history display help the map area set x-hair to enemy fling at enemy under pointer level number move aiming one level up move aiming one level down level caption accept target cancel aiming percent seen set x-hair to nearest upstairs set x-hair to nearest dnstairs x-hair info cycle x-hair among enemies cycle x-hair among items party roster pick new leader on screen select party member on screen Calm gauge rest (wait 25 times) lurk (wait 0.1 turns 100 times) HP gauge wait a turn, bracing for impact wait 0.1 of a turn target info fling without aiming clear chosen item and target Mouse in exploration mode. area LMB (left mouse button) RMB (right mouse button) message line clear messages/display history display help leader on map grab item(s) drop item(s) party on map pick new leader on screen select party member on screen the map area go to pointer for 25 steps run to pointer collectively level number move aiming one level up move aiming one level down level caption open dashboard open main menu percent seen explore nearest unknown spot autoexplore 25 times x-hair info cycle x-hair among enemies cycle x-hair among items party roster pick new leader on screen select party member on screen Calm gauge rest (wait 25 times) lurk (wait 0.1 turns 100 times) HP gauge wait a turn, bracing for impact wait 0.1 of a turn target info fling without aiming clear chosen item and target Allure-0.8.3.0/GameDefinition/MainMenu.ascii0000644000000000000000000000707513315545742016721 0ustar0000000000000000 ... jiii;.LGEDEi. . . . . ;KEKEGLLGEDt :tfL:,ittjjjjfffff .. tii;,iGEWDL; . . . . fEDEDGfLDDG . Lf.;itjjjfffLLLLf .tti;, GDKLt, . . . . . ,itLDEGLLGED. :.fi.;ttjjfffLEWW#DL .tti;::GELti Allure of the Stars .KtGDEDLfGEDt ...j;:;ttjjffLD#WWW#Kf .tti;.jGDt . . .. ,LDEDLfGEDt ...t.:itjjfffL WWWWW#K; jtti,iLDLt . ;jDEEGLGEDt ,t :itjjffLG WKWWWWE jtii: GEf; ,iGDEDGGEDt. tt :itjjffLG GWEWWWKE jtii: GGj; {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{.fGDEDGLDDt tt :itjfffLG KKKWWWEL jtti. GDj, {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ DLDEDGLDD.tj :itjjffLD LWDKWWWD jtti GDj, {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ ELDEDGL.jj :itjjffLD jKDEWWWEL jtti GGf, {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ ELDEE.ff::itjjffLG fKEDKWKKD ftti LGt: {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ ELE.Lf: ijjjffLG tEEDEWKWG fjjt.,jGf, {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ ,K:Gf,.ijjtffLD..tjEDGDKKWEE tti;,i;GL. {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ .:ftt itiiffLGDGDDDGLDKGKfE Ltjt;, jLj {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ :Lf;iitjifLLGfDDLijGEKEKDE.. Lffjif.Lft {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ .,:LL;,ijjfffLGLGLLLGDKKEELK . . ffjt iiGfj; {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{...:LL;;ijffffLGDEKKKKKEEKDj....... .Lffj:j fDi {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{:::fL;,itjfffLGLGDEEDEKDGtK.......... . Lfffifiif {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{,.Lf;.ijjfffLL,,Lfijjt,E,::.......... . fffj:i;t; .tfj,.tjjfffLL;ijfLGGGGGLjt;,::...... .. Lfffjjiii, ... :LL;,,tjjfffLL;;;;iiiiiiii,;;;,:::::.. .. LfffijijjLfi; ,:::,ffj,j;tjffffLG;;;;;;;;;;;;;;;;;,,:::::.. ... LLLff,i;jGLLLL LiLt:::tjjffffLG:,,,,,,,,,;;;;,,,,::....... .Version X.X.X (frontend: xxx, engine: LambdaHack X.X.X) ,,,,,,,,,,,:::::.. . Allure-0.8.3.0/GameDefinition/Main.hs0000644000000000000000000000626513315545742015416 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | The main source code file of Allure of the Stars. -- Module "TieKnot" is separated to make it usable in tests. module Main ( main ) where import Prelude () import Game.LambdaHack.Common.Prelude import Control.Concurrent import Control.Concurrent.Async import qualified Control.Exception as Ex import qualified Options.Applicative as OA import System.Exit #ifndef USE_JSFILE import qualified GHC.IO.Handle import System.FilePath import qualified System.IO as SIO import Game.LambdaHack.Common.File (tryCreateDir) #endif import Game.LambdaHack.Common.Misc import Game.LambdaHack.Server (serverOptionsPI) import Implementation.TieKnot -- | Parse commandline options, tie the engine, content and clients knot, -- run the game and handle exit. main :: IO () main = do -- This may be broken with JS and also bloats the outcome file, so disabled. #ifndef USE_JSFILE -- Special case hack, when the game is started not on a console. -- Without this, any attempt to output on stdout crashes a Windows exe -- (at least on Windows Vista) launched from the desktop or start menu. -- This is very crude and results in the inability to, e.g., process -- the output of @--help@ through a unix pipe. However, this should be -- effective on all Windows version, without the need to test all. isTerminal <- SIO.hIsTerminalDevice SIO.stdout unless isTerminal $ do dataDir <- appDataDir tryCreateDir dataDir fstdout <- SIO.openFile (dataDir "stdout.txt") SIO.WriteMode fstderr <- SIO.openFile (dataDir "stderr.txt") SIO.WriteMode GHC.IO.Handle.hDuplicateTo fstdout SIO.stdout GHC.IO.Handle.hDuplicateTo fstderr SIO.stderr let fillWorkaround = -- Set up void workaround if nothing specific required. void $ tryPutMVar workaroundOnMainThreadMVar $ return () #endif -- Fail here, not inside server code, so that savefiles are not removed, -- because they are not the source of the failure. !serverOptions <- OA.execParser serverOptionsPI -- Avoid the bound thread that would slow down the communication. a <- async $ tieKnot serverOptions #ifndef USE_JSFILE `Ex.finally` fillWorkaround -- Run a (possibly void) workaround. It's needed for architectures/frontends -- that need to perform some actions on the main thread -- (not just any bound thread), e.g., newer OS X drawing with SDL2. workaround <- takeMVar workaroundOnMainThreadMVar workaround #endif resOrEx <- waitCatch a let unwrapEx e = #if MIN_VERSION_async(2,2,1) case Ex.fromException e of Just (ExceptionInLinkedThread _ ex) -> unwrapEx ex _ -> #endif e case resOrEx of Right () -> return () Left e -> case Ex.fromException $ unwrapEx e of Just ExitSuccess -> exitSuccess -- we are in the main thread, so here it really exits _ -> Ex.throwIO $ unwrapEx e Allure-0.8.3.0/GameDefinition/Content/0000755000000000000000000000000013315545742015577 5ustar0000000000000000Allure-0.8.3.0/GameDefinition/Content/ItemKindActor.hs0000644000000000000000000007336513315545742020646 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Actor (or rather actor body trunk) definitions. module Content.ItemKindActor ( actors ) where import Prelude () import Game.LambdaHack.Common.Prelude import Game.LambdaHack.Common.Ability import Game.LambdaHack.Common.Color import Game.LambdaHack.Common.Flavour import Game.LambdaHack.Common.ItemAspect (Aspect (..)) import Game.LambdaHack.Common.Misc import Game.LambdaHack.Content.ItemKind actors :: [ItemKind] actors = [warrior, warrior2, warrior3, warrior4, warrior5, scout, ranger, escapist, ambusher, soldier, civilian, civilian2, civilian3, civilian4, civilian5, eye, fastEye, nose, elbow, torsor, goldenJackal, griffonVulture, skunk, armadillo, gilaMonster, rattlesnake, komodoDragon, hyena, alligator, rhinoceros, beeSwarm, hornetSwarm, thornbush] -- Allure-specific ++ [razorwireFence, electricFence, activeFence, steamFaucet, biogasFaucet, medbotFaucet, surveillanceDrone, shepherdDrone, huntingDrone, homeRobot, wasteRobot, lightRobot, heavyRobot, cleanerRobot] warrior, warrior2, warrior3, warrior4, warrior5, scout, ranger, escapist, ambusher, soldier, civilian, civilian2, civilian3, civilian4, civilian5, eye, fastEye, nose, elbow, torsor, goldenJackal, griffonVulture, skunk, armadillo, gilaMonster, rattlesnake, komodoDragon, hyena, alligator, rhinoceros, beeSwarm, hornetSwarm, thornbush :: ItemKind -- Allure-specific razorwireFence, electricFence, activeFence, steamFaucet, biogasFaucet, medbotFaucet, surveillanceDrone, shepherdDrone, huntingDrone, homeRobot, wasteRobot, lightRobot, heavyRobot, cleanerRobot :: ItemKind -- * Hunams warrior = ItemKind { isymbol = '@' , iname = "mercenary" -- modified if initial actors in hero faction , ifreq = [("hero", 100), ("mobile", 1)] , iflavour = zipPlain [BrWhite] , icount = 1 , irarity = [(1, 5)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 80 -- partially from clothes and assumed first aid , AddMaxCalm 70, AddSpeed 20, AddNocto 2 , AddAbility AbProject 2, AddAbility AbApply 1 , AddAbility AbAlter 2 ] , ieffects = [] , ifeature = [Durable] , idesc = "" -- , idesc = "A hardened veteran of combat." , ikit = [ ("fist", COrgan), ("foot", COrgan), ("eye 6", COrgan) , ("sapient brain", COrgan) ] } warrior2 = warrior { iname = "pilot" -- , idesc = "" } warrior3 = warrior { iname = "engineer" -- , idesc = "" } warrior4 = warrior { iname = "doctor" -- , idesc = "" } warrior5 = warrior { iname = "hacker" -- , idesc = "" } scout = warrior { iname = "scout" , ifreq = [("scout hero", 100), ("mobile", 1)] , ikit = ikit warrior ++ [ ("add sight", CEqp) , ("armor ranged", CEqp) , ("add nocto 1", CInv) ] -- , idesc = "" } ranger = warrior { iname = "ranger" , ifreq = [("ranger hero", 100), ("mobile", 1)] , ikit = ikit warrior ++ [ ("armor ranged", CEqp) , ("weak arrow", CInv) ] -- , idesc = "" } escapist = warrior { iname = "escapist" , ifreq = [("escapist hero", 100), ("mobile", 1)] , ikit = ikit warrior ++ [ ("add sight", CEqp) , ("armor ranged", CEqp) , ("weak arrow", CInv) -- mostly for probing , ("light source", CInv) , ("wooden torch", CInv) , ("blanket", CInv) ] -- , idesc = "" } ambusher = warrior { iname = "ambusher" , ifreq = [("ambusher hero", 100), ("mobile", 1)] , ikit = ikit warrior -- dark and numerous, so more kit without exploring ++ [ ("ring of opportunity sniper", CEqp) , ("any arrow", CSha) , ("weak arrow", CInv) , ("explosive", CSha) , ("light source", CEqp) , ("wooden torch", CInv) ] -- , idesc = "" } soldier = warrior { iname = "soldier" , ifreq = [("soldier hero", 100), ("mobile", 1)] , ikit = ikit warrior ++ [ ("starting weapon", CEqp) , ("explosive", CSha) ] -- , idesc = "" } civilian = warrior { iname = "clerk" , ifreq = [("civilian", 100), ("mobile", 1)] , iflavour = zipPlain [BrBlack] -- , idesc = "" } civilian2 = civilian { iname = "hairdresser" -- , idesc = "" } civilian3 = civilian { iname = "lawyer" -- , idesc = "" } civilian4 = civilian { iname = "peddler" -- , idesc = "" } civilian5 = civilian { iname = "tax collector" -- , idesc = "" } -- * Aliens -- They have bright colours, because they are not natural. eye = ItemKind { isymbol = 'w' , iname = "beckoning walker" , ifreq = [ ("monster", 100), ("mobile", 1) , ("mobile monster", 100), ("scout monster", 10) ] , iflavour = zipFancy [BrRed] , icount = 1 , irarity = [(4, 6), (10, 10)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 20, AddMaxCalm 70, AddSpeed 20, AddNocto 2 , AddAggression 1 , AddAbility AbProject 2, AddAbility AbApply 1 , AddAbility AbAlter 2 ] , ieffects = [] , ifeature = [Durable] , idesc = "Walks with a stately dignity. You read death in the slow beckoning gestures of its revolting upper appendages." , ikit = [ ("foot", COrgan), ("tentacle", COrgan) , ("eye 6", COrgan) , ("sapient brain", COrgan) ] } fastEye = ItemKind { isymbol = 'b' , iname = "rolling biter" , ifreq = [ ("monster", 100), ("mobile", 1) , ("mobile monster", 100), ("scout monster", 60) ] , iflavour = zipFancy [BrBlue] , icount = 1 , irarity = [(4, 3), (10, 10)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 5, AddMaxCalm 70, AddSpeed 30, AddNocto 2 , AddAggression 1 , AddAbility AbAlter 2 ] , ieffects = [] , ifeature = [Durable] , idesc = "It bites as blindingly fast as it runs. Or rolls? Or crawls? Also, cuts and pierces." , ikit = [ ("tentacle", COrgan), ("jaw", COrgan) , ("eye 3", COrgan), ("speed gland 10", COrgan) , ("sapient brain", COrgan) ] } nose = ItemKind -- depends solely on smell { isymbol = 'h' , iname = "tentacled horror" , ifreq = [("monster", 100), ("mobile", 1), ("mobile monster", 100)] , iflavour = zipFancy [BrGreen] , icount = 1 , irarity = [(4, 5), (10, 9)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 30, AddMaxCalm 30, AddSpeed 16, AddNocto 2 , AddAggression 1 , AddAbility AbProject (-1), AddAbility AbAlter 2 ] , ieffects = [] , ifeature = [Durable] , idesc = "A blind, slimy mass of clawing, stinging and burning. You'd think it's powerless, but as soon as it touches your trembling body, it's always one step ahead." , ikit = [ ("nostril", COrgan), ("small claw", COrgan) , ("tentacle", COrgan), ("tentacle", COrgan) , ("thorn", COrgan), ("venom tooth", COrgan) , ("sapient brain", COrgan) ] } elbow = ItemKind { isymbol = 's' , iname = "creepy shooter" , ifreq = [ ("monster", 100), ("mobile", 1) , ("mobile monster", 100), ("scout monster", 30) ] , iflavour = zipFancy [BrMagenta] , icount = 1 , irarity = [(6, 1), (10, 9)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 12, AddMaxCalm 80, AddSpeed 21, AddNocto 2 , AddAbility AbProject 2, AddAbility AbApply 1 , AddAbility AbAlter 2, AddAbility AbMelee (-1) ] , ieffects = [] , ifeature = [Durable] , idesc = "It moves in sudden jerks and never makes a noise. Speaks in hard objects hurled at deadly speeds." , ikit = [ ("speed gland 4", COrgan) , ("eye 8", COrgan) , ("any arrow", CSha), ("any arrow", CInv) , ("weak arrow", CInv), ("weak arrow", CInv) , ("sapient brain", COrgan) ] } torsor = ItemKind { isymbol = 'M' , iname = "The Maker of Contact" , ifreq = [("monster", 100), ("mobile", 1)] , iflavour = zipFancy [BrCyan] , icount = 1 , irarity = [(11 * 10/12, 0), (12 * 10/12, 1000)] -- unique , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 300, AddMaxCalm 100, AddSpeed 6, AddNocto 2 , AddAggression 3 , AddAbility AbProject 2, AddAbility AbApply 1 , AddAbility AbAlter 1] -- can't exit the gated level, the boss , ieffects = [] , ifeature = [Unique, Durable] , idesc = "The mind, the heart behind it all. Warmth and sympathy pour out through the graceful undulation of tentacles, sharp claws, snapping jaw, grinding teeth and tensing fangs." , ikit = [ ("tentacle", COrgan), ("hooked claw", COrgan) , ("large jaw", COrgan) , ("sting", COrgan), ("venom fang", COrgan) , ("eye 6", COrgan), ("speed gland 4", COrgan) , ("gem", CInv), ("gem", CInv), ("gem", CInv), ("gem", CInv) , ("sapient brain", COrgan) ] } -- * Animals -- They need rather strong melee, because they don't use items. -- Unless/until they level up. -- They have dull colors, except for yellow, because there is no dull variant. goldenJackal = ItemKind -- basically a much smaller and slower hyena { isymbol = 'j' , iname = "golden jackal" , ifreq = [ ("animal", 100), ("mobile", 1), ("mobile animal", 100) , ("scavenger", 50) ] , iflavour = zipPlain [BrYellow] , icount = 1 , irarity = [(1, 3)] , iverbHit = "thud" , iweight = 13000 , idamage = 0 , iaspects = [ AddMaxHP 12, AddMaxCalm 70, AddSpeed 24, AddNocto 2 ] , ieffects = [] , ifeature = [Durable] , idesc = "An opportunistic predator, feeding on carrion and the weak." , ikit = [ ("small jaw", COrgan), ("eye 6", COrgan), ("nostril", COrgan) , ("animal brain", COrgan) ] } griffonVulture = ItemKind { isymbol = 'v' , iname = "griffon vulture" , ifreq = [ ("animal", 100), ("mobile", 1), ("mobile animal", 100) , ("scavenger", 30) ] , iflavour = zipPlain [BrYellow] , icount = 1 , irarity = [(1, 5)] , iverbHit = "thud" , iweight = 13000 , idamage = 0 , iaspects = [ AddMaxHP 12, AddMaxCalm 80, AddSpeed 22, AddNocto 2 , AddAbility AbAlter (-2) ] -- can't use stairs nor doors -- Animals don't have leader, usually, so even if only one of level, -- it pays the communication overhead, so the speed is higher to get -- them on par with human leaders moving solo. Random double moves, -- on either side, are just too frustrating. , ieffects = [] , ifeature = [Durable] , idesc = "It soars high above, searching for vulnerable prey." , ikit = [ ("screeching beak", COrgan) -- in reality it grunts and hisses , ("small claw", COrgan), ("eye 7", COrgan) , ("animal brain", COrgan) ] } skunk = ItemKind { isymbol = 's' , iname = "hog-nosed skunk" , ifreq = [("animal", 100), ("mobile", 1), ("mobile animal", 100)] , iflavour = zipPlain [White] , icount = 1 , irarity = [(1, 5), (10, 3)] , iverbHit = "thud" , iweight = 4000 , idamage = 0 , iaspects = [ AddMaxHP 10, AddMaxCalm 30, AddSpeed 22, AddNocto 2 , AddAbility AbAlter (-2) ] -- can't use stairs nor doors , ieffects = [] , ifeature = [Durable] , idesc = "Its only defence is the terrible stench." , ikit = [ ("scent gland", COrgan) , ("small claw", COrgan), ("snout", COrgan) , ("nostril", COrgan), ("eye 3", COrgan) , ("animal brain", COrgan) ] } armadillo = ItemKind { isymbol = 'a' , iname = "giant armadillo" , ifreq = [("animal", 100), ("mobile", 1), ("mobile animal", 100)] , iflavour = zipPlain [Brown] , icount = 1 , irarity = [(1, 5)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 10, AddMaxCalm 30, AddSpeed 20, AddNocto 2 , AddAbility AbAlter (-2) ] -- can't use stairs nor doors , ieffects = [] , ifeature = [Durable] , idesc = "When threatened, it rolls into a ball." , ikit = [ ("hooked claw", COrgan), ("snout", COrgan) , ("armored skin", COrgan), ("armored skin", COrgan) , ("nostril", COrgan), ("eye 3", COrgan) , ("animal brain", COrgan) ] } gilaMonster = ItemKind { isymbol = 'g' , iname = "Gila monster" , ifreq = [("animal", 100), ("mobile", 1), ("mobile animal", 100)] , iflavour = zipPlain [Magenta] , icount = 1 , irarity = [(2, 5), (10, 3)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 12, AddMaxCalm 50, AddSpeed 18, AddNocto 2 , AddAbility AbAlter (-2) ] -- can't use stairs nor doors , ieffects = [] , ifeature = [Durable] , idesc = "Numbing venom ensures that even the fastest prey has no escape." , ikit = [ ("venom tooth", COrgan), ("small claw", COrgan) , ("eye 3", COrgan), ("nostril", COrgan) , ("animal brain", COrgan) ] } rattlesnake = ItemKind { isymbol = 's' , iname = "rattlesnake" , ifreq = [("animal", 100), ("mobile", 1), ("mobile animal", 100)] , iflavour = zipPlain [Brown] , icount = 1 , irarity = [(5, 1), (10, 12)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 25, AddMaxCalm 60, AddSpeed 16, AddNocto 2 , AddAbility AbAlter (-2) ] -- can't use stairs nor doors , ieffects = [] , ifeature = [Durable] , idesc = "Beware its rattle - it serves as a warning of an agonising death." , ikit = [ ("venom fang", COrgan) , ("eye 4", COrgan), ("nostril", COrgan) , ("animal brain", COrgan) ] } komodoDragon = ItemKind -- bad hearing; regeneration makes it very powerful { isymbol = 'k' , iname = "Komodo dragon" , ifreq = [("animal", 100), ("mobile", 1), ("mobile animal", 100)] , iflavour = zipPlain [Blue] , icount = 1 , irarity = [(9, 0), (10, 10)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 41, AddMaxCalm 60, AddSpeed 18, AddNocto 2 ] , ieffects = [] , ifeature = [Durable] , idesc = "Larger and more aggressive than any other lizard." , ikit = [ ("large tail", COrgan), ("jaw", COrgan) , ("hooked claw", COrgan), ("speed gland 4", COrgan) , ("armored skin", COrgan), ("eye 3", COrgan) , ("nostril", COrgan), ("animal brain", COrgan) ] } hyena = ItemKind { isymbol = 'h' , iname = "spotted hyena" , ifreq = [ ("animal", 100), ("mobile", 1), ("mobile animal", 100) , ("scavenger", 20) ] , iflavour = zipPlain [BrYellow] , icount = 1 , irarity = [(4, 1), (10, 8)] , iverbHit = "thud" , iweight = 60000 , idamage = 0 , iaspects = [ AddMaxHP 20, AddMaxCalm 70, AddSpeed 32, AddNocto 2 ] , ieffects = [] , ifeature = [Durable] , idesc = "Skulking in the shadows, waiting for easy prey." , ikit = [ ("jaw", COrgan), ("eye 6", COrgan), ("nostril", COrgan) , ("animal brain", COrgan) ] } alligator = ItemKind { isymbol = 'a' , iname = "alligator" , ifreq = [("animal", 100), ("mobile", 1), ("mobile animal", 100)] , iflavour = zipPlain [Blue] , icount = 1 , irarity = [(8, 1), (10, 9)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 41, AddMaxCalm 70, AddSpeed 18, AddNocto 2 ] , ieffects = [] , ifeature = [Durable] , idesc = "An armored predator from the dawn of time." , ikit = [ ("large jaw", COrgan), ("large tail", COrgan) , ("small claw", COrgan) , ("armored skin", COrgan), ("eye 6", COrgan) , ("animal brain", COrgan) ] } rhinoceros = ItemKind { isymbol = 'R' , iname = "The Maddened Rhinoceros" , ifreq = [("animal", 100), ("mobile", 1)] , iflavour = zipPlain [Brown] , icount = 1 , irarity = [(1 * 10/12, 1000000), (2 * 10/12, 0)] -- unique , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 90, AddMaxCalm 60, AddSpeed 27, AddNocto 2 , AddAggression 2 , AddAbility AbAlter (-1) ] -- can't switch levels, a miniboss , ieffects = [] , ifeature = [Unique, Durable] , idesc = "The last of its kind. Blind with rage. Charges at deadly speed." , ikit = [ ("armored skin", COrgan), ("eye 2", COrgan) , ("rhino horn", COrgan), ("snout", COrgan) , ("animal brain", COrgan) ] } -- * Non-animal animals beeSwarm = ItemKind { isymbol = 'b' , iname = "bee swarm" , ifreq = [("animal", 100), ("mobile", 1)] , iflavour = zipPlain [Brown] , icount = 1 , irarity = [(1, 2), (10, 4)] , iverbHit = "thud" , iweight = 1000 , idamage = 0 , iaspects = [ AddMaxHP 8, AddMaxCalm 60 , AddSpeed 30, AddNocto 2 -- armor in sting , AddAbility AbAlter (-2) ] -- can't use stairs nor doors , ieffects = [] , ifeature = [Durable] , idesc = "Every bee would die for the queen." , ikit = [ ("bee sting", COrgan), ("vision 6", COrgan) , ("insect mortality", COrgan), ("animal brain", COrgan) ] } hornetSwarm = ItemKind { isymbol = 'h' , iname = "hornet swarm" , ifreq = [("animal", 100), ("mobile", 1), ("mobile animal", 100)] , iflavour = zipPlain [Magenta] , icount = 1 , irarity = [(5, 1), (10, 8)] , iverbHit = "thud" , iweight = 1000 , idamage = 0 , iaspects = [ AddMaxHP 8, AddMaxCalm 70, AddSpeed 30, AddNocto 2 , AddAbility AbAlter (-2) -- can't use stairs nor doors , AddArmorMelee 80, AddArmorRanged 40 ] , ieffects = [] , ifeature = [Durable] , idesc = "A vicious cloud of stings and hate." , ikit = [ ("sting", COrgan), ("vision 8", COrgan) , ("insect mortality", COrgan), ("animal brain", COrgan) ] } thornbush = ItemKind { isymbol = 't' , iname = "thornbush" , ifreq = [("animal", 20), ("immobile animal", 30)] , iflavour = zipPlain [Brown] , icount = 1 , irarity = [(1, 10)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 20, AddMaxCalm 999, AddSpeed 22, AddNocto 2 , AddAbility AbWait 1, AddAbility AbMelee 1 ] , ieffects = [] , ifeature = [Durable] , idesc = "Each branch bears long, curved thorns." , ikit = [("thorn", COrgan), ("armored skin", COrgan)] } -- * Robots, Allure-specific -- Robots have any colors but only f, d and r letters. Avoid these letters -- for other factions. razorwireFence = ItemKind { isymbol = 'f' , iname = "razorwire fence" , ifreq = [("robot", 10), ("immobile robot", 10)] , iflavour = zipPlain [Cyan] , icount = 1 , irarity = [(3 * 10/12, 0), (4 * 10/12, 10)] -- quickly vanishes at depth , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 30, AddMaxCalm 999, AddSpeed 20, AddNocto 2 , AddAbility AbWait 1, AddAbility AbMelee 1 , AddArmorMelee 30, AddArmorRanged 15 ] , ieffects = [] , ifeature = [Durable] , idesc = "Must have been bought by previous ship owners to contain the wild animal infestation." , ikit = [("razor", COrgan)] } electricFence = ItemKind { isymbol = 'f' , iname = "electric fence" , ifreq = [("robot", 10), ("immobile robot", 10)] , iflavour = zipPlain [Blue] , icount = 1 , irarity = [(3 * 10/12, 0), (4 * 10/12, 10)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 10, AddMaxCalm 999, AddSpeed 40, AddNocto 2 , AddAbility AbWait 1, AddAbility AbMelee 1 , AddArmorMelee 30, AddArmorRanged 15 ] , ieffects = [] , ifeature = [Durable] , idesc = "Marginally intelligent electric shepherd. Originally used in orbital dairy farms and planetary zoos." , ikit = [("live wire", COrgan)] } activeFence = ItemKind { isymbol = 'f' , iname = "active fence" , ifreq = [("robot", 30), ("immobile robot", 30)] , iflavour = zipPlain [Red] , icount = 1 , irarity = [(5 * 10/12, 0), (6 * 10/12, 3)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 20, AddMaxCalm 999, AddSpeed 20, AddNocto 2 , AddAbility AbWait 1, AddAbility AbProject 3 , AddArmorMelee 30, AddArmorRanged 15 ] , ieffects = [] , ifeature = [Durable] , idesc = "Makeshift, mostly non-lethal, autonomous perimeter defense outpost." , ikit = [ ("vision 6", COrgan) , ("needle", CInv), ("can of sticky foam", CInv) ] } steamFaucet = ItemKind { isymbol = 'f' , iname = "steam faucet" , ifreq = [("robot", 30), ("immobile robot", 50)] , iflavour = zipPlain [BrBlue] , icount = 1 , irarity = [(1, 3), (5 * 10/12, 3)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 10, AddMaxCalm 999, AddSpeed 11, AddNocto 2 , AddAbility AbWait 1, AddAbility AbMelee 1 ] , ieffects = [] , ifeature = [Durable] , idesc = "A cracked valve on one of the superheated water pipes spreading radially outward from the tokamak level." , ikit = [("boiling vent", COrgan), ("boiling fissure", COrgan)] } biogasFaucet = ItemKind { isymbol = 'f' , iname = "biogas faucet" , ifreq = [("robot", 10), ("immobile robot", 30)] , iflavour = zipPlain [BrGreen] , icount = 1 , irarity = [(1, 10), (5 * 10/12, 10)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 20, AddMaxCalm 999, AddSpeed 22 , AddNocto 2, AddShine 3 , AddAbility AbWait 1, AddAbility AbMelee 1 ] , ieffects = [] , ifeature = [Durable] , idesc = "An emergency pressure-release vent on a smelly biogas pipe." , ikit = [("biogas vent", COrgan), ("biogas fissure", COrgan)] } medbotFaucet = ItemKind { isymbol = 'f' , iname = "nano medbot faucet" , ifreq = [("robot", 10), ("immobile robot", 110)] , iflavour = zipPlain [BrYellow] , icount = 1 , irarity = [(1, 10), (5 * 10/12, 10)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 20, AddMaxCalm 999, AddSpeed 22 , AddNocto 2, AddShine 3 , AddAbility AbWait 1, AddAbility AbMelee 1 ] , ieffects = [] , ifeature = [Durable] -- TODO: only heal humans , idesc = "A faucet of a malfunctioning nano medical robot dispenser. Let's hope the medbots are still effective." , ikit = [("medbot vent", COrgan), ("medbot fissure", COrgan)] } surveillanceDrone = ItemKind { isymbol = 'd' , iname = "surveillance drone" , ifreq = [("robot", 100), ("mobile", 100), ("mobile robot", 100)] , iflavour = zipPlain [Blue] , icount = 1 , irarity = [] -- TODO: too boring , iverbHit = "thud" , iweight = 1000 , idamage = 0 , iaspects = [ AddMaxHP 6, AddMaxCalm 90, AddSpeed 30, AddNocto 2 , AddAbility AbDisplace (-1), AddAbility AbMoveItem (-1) , AddAbility AbProject (-1), AddAbility AbMelee (-1) , AddArmorMelee 30, AddArmorRanged 15 ] , ieffects = [] , ifeature = [Durable] , idesc = "A video camera in each room would violate privacy of passengers, hence surveillance drones. Programmed to be easy to fend off, they keep a respectful distance." , ikit = [ ("vision 14", COrgan), ("robot brain", COrgan) ] } shepherdDrone = ItemKind { isymbol = 'd' , iname = "shepherd drone" , ifreq = [("robot", 100), ("mobile", 100), ("mobile robot", 100)] , iflavour = zipPlain [BrRed] , icount = 1 , irarity = [(1, 7)] , iverbHit = "thud" , iweight = 1000 , idamage = 0 , iaspects = [ AddMaxHP 3, AddMaxCalm 60, AddSpeed 30, AddNocto 2 , AddAbility AbDisplace (-1), AddAbility AbMoveItem (-1) , AddAbility AbProject (-1) , AddArmorMelee 80, AddArmorRanged 40 ] , ieffects = [] , ifeature = [Durable] , idesc = "A shabby drone for bringing cows home." , ikit = [ ("eye 4", COrgan), ("live wire", COrgan) , ("robot brain", COrgan) ] } huntingDrone = ItemKind { isymbol = 'd' , iname = "hunting drone" , ifreq = [("robot", 100), ("mobile", 100), ("mobile robot", 100)] , iflavour = zipPlain [Green] , icount = 1 , irarity = [(3, 0), (5, 2), (10, 4)] , iverbHit = "thud" , iweight = 500 , idamage = 0 , iaspects = [ AddMaxHP 6, AddMaxCalm 60, AddSpeed 40, AddNocto 2 , AddAbility AbDisplace (-1), AddAbility AbMoveItem (-1) , AddAbility AbMelee (-1) , AddArmorMelee 30, AddArmorRanged 15 ] , ieffects = [] , ifeature = [Durable] , idesc = "Originally designed for hunting down and putting to sleep stray animals. The sleeping agent has long since dried up." , ikit = [ ("eye 5", COrgan), ("needle", CInv) , ("robot brain", COrgan) ] } homeRobot = ItemKind { isymbol = 'r' , iname = "feral home robot" -- TODO: name another 'deranged', tertiary imperative: survival , ifreq = [("robot", 100), ("mobile", 100), ("mobile robot", 100)] , iflavour = zipPlain [Magenta] , icount = 1 , irarity = [(1, 20), (10, 6)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 10, AddMaxCalm 30, AddSpeed 20, AddNocto 2 , AddAbility AbProject (-1), AddAbility AbAlter 1 ] -- doors , ieffects = [] , ifeature = [Durable] , idesc = "Once a timid household robot, now sufficiently adapted to survive in the deadly environment." , ikit = [ ("fist", COrgan), ("eye 2", COrgan), ("nostril", COrgan) , ("robot brain", COrgan) ] } wasteRobot = ItemKind { isymbol = 'r' , iname = "waste disposal robot" , ifreq = [ ("robot", 100), ("mobile", 100), ("mobile robot", 100) , ("construction robot", 50) ] , iflavour = zipPlain [Green] , icount = 1 , irarity = [(1, 10), (10, 6)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 15, AddMaxCalm 30, AddSpeed 15, AddNocto 2 ] , ieffects = [] , ifeature = [Durable] , idesc = "You are not in its database, hence you are waste." , ikit = [ ("jaw", COrgan), ("tentacle", COrgan) , ("waste container", COrgan), ("armored skin", COrgan) , ("eye 2", COrgan), ("nostril", COrgan) , ("robot brain", COrgan) ] } lightRobot = ItemKind { isymbol = 'r' , iname = "decoration robot" , ifreq = [ ("robot", 100), ("mobile", 100), ("mobile robot", 100) , ("construction robot", 100) ] , iflavour = zipPlain [BrYellow] , icount = 1 , irarity = [(3, 1), (10, 10)] , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 15, AddMaxCalm 60, AddSpeed 30, AddNocto 2 , AddAbility AbProject 2, AddAbility AbAlter 2 ] -- uses stairs , ieffects = [] , ifeature = [Durable] , idesc = "Interior and exterior decoration robot. Strongly fancies deep reds recently." , ikit = [ ("hooked claw", COrgan), ("tentacle", COrgan) , ("spotlight", COrgan), ("armored skin", COrgan) , ("eye 5", COrgan), ("robot brain", COrgan) ] } heavyRobot = ItemKind { isymbol = 'r' , iname = "construction robot" , ifreq = [ ("robot", 100), ("mobile", 100), ("mobile robot", 100) , ("construction robot", 100) ] , iflavour = zipPlain [BrRed] , icount = 1 , irarity = [(8, 0), (10, 10)] , iverbHit = "thud" , iweight = 800000 , idamage = 0 , iaspects = [ AddMaxHP 41, AddMaxCalm 60, AddSpeed 20, AddNocto 2 , AddAbility AbProject 2, AddAbility AbAlter 2 ] -- uses stairs , ieffects = [] , ifeature = [Durable] , idesc = "Heavy multi-purpose construction robot. Excels at discharging, dismantling and demolition." , ikit = [ ("large jaw", COrgan), ("small claw", COrgan) , ("spotlight", COrgan), ("armored skin", COrgan) , ("eye 4", COrgan), ("robot brain", COrgan) , ("construction hooter", CInv) ] } cleanerRobot = ItemKind { isymbol = 'C' , iname = "The Void Cleaner Robot" , ifreq = [("robot", 100), ("mobile", 1)] , iflavour = zipPlain [BrGreen] , icount = 1 , irarity = [(9 * 10/12, 0), (10 * 10/12, 1000), (11 * 10/12, 0)] -- unique, appears at 10 of 12 , iverbHit = "thud" , iweight = 80000 , idamage = 0 , iaspects = [ AddMaxHP 120, AddMaxCalm 60, AddSpeed 18, AddNocto 2 , AddAggression 1 , AddAbility AbAlter 3 ] -- a miniboss; can remove rubble and ice, -- but can't exit the gated level , ieffects = [] , ifeature = [Unique, Durable] , idesc = "A waste disposal robot repaired with parts from a heavy construction robot, including a scaled up goal matrix. The cosmic void is now the only acceptable model of cleanliness." , ikit = [ ("waste container", COrgan), ("boiling vent", COrgan) , ("armored skin", COrgan), ("live wire", COrgan) , ("jaw", COrgan), ("hooked claw", COrgan), ("nostril", COrgan) , ("spotlight", COrgan), ("eye 2", COrgan) , ("robot brain", COrgan) , ("currency", CInv), ("currency", CInv), ("currency", CInv) , ("construction hooter", CInv) ] } Allure-0.8.3.0/GameDefinition/Content/ItemKindTemporary.hs0000644000000000000000000000737213315545742021553 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Temporary aspect pseudo-item definitions. module Content.ItemKindTemporary ( temporaries ) where import Prelude () import Game.LambdaHack.Common.Prelude import Game.LambdaHack.Common.Color import Game.LambdaHack.Common.Dice import Game.LambdaHack.Common.Flavour import Game.LambdaHack.Common.ItemAspect (Aspect (..)) import Game.LambdaHack.Common.Misc import Game.LambdaHack.Content.ItemKind temporaries :: [ItemKind] temporaries = [tmpStrengthened, tmpWeakened, tmpProtectedMelee, tmpProtectedRanged, tmpVulnerable, tmpResolute, tmpFast20, tmpSlow10, tmpFarSighted, tmpBlind, tmpKeenSmelling, tmpNoctovision, tmpDrunk, tmpRegenerating, tmpPoisoned, tmpSlow10Resistant, tmpPoisonResistant] tmpStrengthened, tmpWeakened, tmpProtectedMelee, tmpProtectedRanged, tmpVulnerable, tmpResolute, tmpFast20, tmpSlow10, tmpFarSighted, tmpBlind, tmpKeenSmelling, tmpNoctovision, tmpDrunk, tmpRegenerating, tmpPoisoned, tmpSlow10Resistant, tmpPoisonResistant :: ItemKind -- The @name@ is be used in item description, so it should be an adjective -- describing the temporary set of aspects. tmpAspects :: Text -> [Aspect] -> ItemKind tmpAspects name aspects = ItemKind { isymbol = '+' , iname = name , ifreq = [(toGroupName name, 1), ("condition", 1)] , iflavour = zipPlain [BrWhite] , icount = 1 , irarity = [(1, 1)] , iverbHit = "affect" , iweight = 0 , idamage = 0 , iaspects = -- timeout is 0; activates and vanishes soon, -- depending on initial timer setting aspects , ieffects = [ Recharging $ tmpLess name , OnSmash $ tmpLess name ] , ifeature = [Periodic, Fragile, Durable] -- hack: destroy on drop , idesc = "" -- no description needed; stats are enough , ikit = [] } tmpEffects :: Text -> Dice -> [Effect] -> ItemKind tmpEffects name icount effects = let tmp = tmpAspects name [] in tmp { icount , ieffects = effects ++ [ Recharging $ tmpNoLonger name , OnSmash $ tmpNoLonger name ] } tmpStrengthened = tmpAspects "strengthened" [AddHurtMelee 20] tmpWeakened = tmpAspects "weakened" [AddHurtMelee (-30)] -- don't cancel out ^ tmpProtectedMelee = tmpAspects "protected from melee" [AddArmorMelee 50] tmpProtectedRanged = tmpAspects "protected from ranged" [AddArmorRanged 25] tmpVulnerable = tmpAspects "painted red" [ AddArmorMelee (-50) , AddArmorRanged (-25) ] tmpResolute = tmpAspects "resolute" [AddMaxCalm 60] tmpFast20 = tmpAspects "hasted" [AddSpeed 20] tmpSlow10 = tmpAspects "slowed" [AddSpeed (-10)] tmpFarSighted = tmpAspects "far-sighted" [AddSight 5] tmpBlind = tmpAspects "blind" [AddSight (-99)] tmpKeenSmelling = tmpAspects "keen-smelling" [AddSmell 2] tmpNoctovision = tmpAspects "shiny-eyed" [AddNocto 2] tmpDrunk = tmpAspects "drunk" [ AddHurtMelee 30 -- fury , AddArmorMelee (-20) , AddArmorRanged (-20) , AddSight (-8) ] tmpRegenerating = tmpEffects "regenerating" (4 + 1 `d` 2) [Recharging (RefillHP 1)] tmpPoisoned = tmpEffects "poisoned" (4 + 1 `d` 2) [Recharging (RefillHP (-1))] tmpSlow10Resistant = tmpEffects "slow resistant" (8 + 1 `d` 4) [Recharging (DropItem 1 1 COrgan "slowed")] tmpPoisonResistant = tmpEffects "poison resistant" (8 + 1 `d` 4) [Recharging (DropItem 1 maxBound COrgan "poisoned")] Allure-0.8.3.0/GameDefinition/Content/CaveKind.hs0000644000000000000000000004576713315545742017642 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Cave properties. module Content.CaveKind ( content ) where import Prelude () import Game.LambdaHack.Common.Prelude import Data.Ratio import Game.LambdaHack.Common.Dice import Game.LambdaHack.Common.Misc import Game.LambdaHack.Content.CaveKind content :: [CaveKind] content = [rogue, rogue2, arena, arena2, laboratory, empty, noise, noise2, bridge, shallow2rogue, shallow2arena, shallow2empty, shallow1empty, emptyExit, raid, brawl, shootout, escape, zoo, ambush, battle, safari1, safari2, safari3] rogue, rogue2, arena, arena2, laboratory, empty, noise, noise2, bridge, shallow2rogue, shallow2arena, shallow2empty, shallow1empty, emptyExit, raid, brawl, shootout, escape, zoo, ambush, battle, safari1, safari2, safari3 :: CaveKind rogue = CaveKind { csymbol = 'R' , cname = "Insulated storage area" , cfreq = [ ("default random", 100), ("deep random", 70) , ("caveRogue", 1) ] , cxsize = fst normalLevelBound + 1 , cysize = snd normalLevelBound + 1 , cgrid = DiceXY (3 `d` 2) 4 , cminPlaceSize = DiceXY (2 `d` 2 + 4) 5 , cmaxPlaceSize = DiceXY 16 20 , cdarkChance = 1 `d` 54 + 1 `dL` 20 , cnightChance = 51 -- always night , cauxConnects = 1%2 , cmaxVoid = 1%6 , cminStairDist = 15 , cextraStairs = 1 + 1 `d` 2 , cdoorChance = 3%4 , copenChance = 1%5 , chidden = 7 , cactorCoeff = 65 -- the maze requires time to explore , cactorFreq = [("monster", 50), ("animal", 25), ("robot", 25)] , citemNum = 6 `d` 5 - 4 `dL` 1 -- deeper down quality over quantity , citemFreq = [ ("common item", 40), ("treasure", 60) , ("curious item", 10) ] , cplaceFreq = [("rogue", 100)] , cpassable = False , cdefTile = "fillerWall" , cdarkCorTile = "floorCorridorDark" , clitCorTile = "floorCorridorLit" , cfillerTile = "fillerWall" , couterFenceTile = "basic outer fence" , clegendDarkTile = "legendDark" , clegendLitTile = "legendLit" , cescapeGroup = Nothing , cstairFreq = [("staircase lift", 100)] , cdesc = "Winding tunnels stretch into the dark." } rogue2 = rogue { cfreq = [("deep random", 30)] , cname = "Dark storage area" , cdarkChance = 51 -- all rooms dark , cnightChance = 0 -- always day , cdesc = "The lights had gone out." } arena = rogue { csymbol = 'A' , cname = "Recreational deck" , cfreq = [("default random", 40), ("caveArena", 1)] , cgrid = DiceXY (2 + 1 `d` 2) (1 + 1 `d` 2) , cminPlaceSize = DiceXY (2 `d` 2 + 4) 6 , cmaxPlaceSize = DiceXY 16 12 , cdarkChance = 49 + 1 `d` 10 -- almost all rooms dark (1 in 10 lit) -- Light is not too deadly, because not many obstructions and so -- foes visible from far away and few foes have ranged combat -- at shallow depth. , cnightChance = 0 -- always day , cauxConnects = 1 , cmaxVoid = 1%8 , cextraStairs = 1 , chidden = 0 , cactorCoeff = 50 , cactorFreq = [("monster", 25), ("animal", 70), ("robot", 5)] , citemNum = 4 `d` 5 -- few rooms , citemFreq = [ ("common item", 20), ("treasure", 40), ("any scroll", 40) , ("curious item", 10) ] , cplaceFreq = [("arena", 100)] , cpassable = True , cdefTile = "arenaSetLit" , cdarkCorTile = "trailLit" -- let trails give off light , clitCorTile = "trailLit" , cdesc = "" } arena2 = arena { cname = "Casino" , cfreq = [("deep random", 30)] , cdarkChance = 41 + 1 `d` 10 -- almost all rooms lit (1 in 10 dark) -- Trails provide enough light for fun stealth. , cnightChance = 51 -- always night , citemNum = 6 `d` 5 -- rare, so make it exciting , citemFreq = [ ("common item", 20) , ("treasure", 80) -- lives up to the name , ("curious item", 20) ] , cdefTile = "arenaSetDark" , cdesc = "" } laboratory = arena2 { csymbol = 'L' , cname = "Laboratory" , cfreq = [("deep random", 20), ("caveLaboratory", 1)] , cgrid = DiceXY (2 `d` 2 + 7) 3 , cminPlaceSize = DiceXY (3 `d` 2 + 4) 5 , cdarkChance = 1 `d` 54 + 1 `dL` 20 -- most rooms lit, to compensate for corridors , cnightChance = 0 -- always day , cauxConnects = 1%10 , cmaxVoid = 1%10 , cextraStairs = 1 , cdoorChance = 1 , copenChance = 1%2 , chidden = 7 , citemNum = 6 `d` 5 -- reward difficulty , citemFreq = [ ("common item", 20), ("treasure", 40), ("explosive", 40) , ("curious item", 20) ] , cplaceFreq = [("laboratory", 100)] , cpassable = False , cdefTile = "fillerWall" , cdarkCorTile = "labTrailLit" -- let lab smoke give off light always , clitCorTile = "labTrailLit" , cstairFreq = [("staircase lift", 100)] , cdesc = "Shattered glassware and the sharp scent of spilt chemicals show that something terrible happened here." } empty = rogue { csymbol = 'E' , cname = "Construction site" , cfreq = [("caveEmpty", 1)] , cgrid = DiceXY 2 2 , cminPlaceSize = DiceXY 12 9 , cmaxPlaceSize = DiceXY 48 32 -- favour large rooms , cdarkChance = 1 `d` 100 + 1 `dL` 100 , cnightChance = 0 -- always day , cauxConnects = 3%2 , cminStairDist = 30 , cmaxVoid = 0 -- too few rooms to have void and fog common anyway , cextraStairs = 1 , cdoorChance = 0 , copenChance = 0 , chidden = 0 , cactorCoeff = 40 , cactorFreq = [("monster", 25), ("animal", 5), ("robot", 70)] , citemNum = 5 `d` 5 -- few rooms , cplaceFreq = [("empty", 100)] , cpassable = True , cdefTile = "emptySet" , cdarkCorTile = "floorArenaDark" , clitCorTile = "floorArenaLit" , cstairFreq = [("staircase", 50), ("staircase lift", 50)] , cdesc = "Not much to see here yet." } noise = rogue { csymbol = 'N' , cname = "Flight hardware hub" , cfreq = [("caveNoise", 1)] , cgrid = DiceXY (2 + 1 `d` 3) 3 , cminPlaceSize = DiceXY 8 6 , cmaxPlaceSize = DiceXY 20 10 , cdarkChance = 51 -- Light is deadly, because nowhere to hide and pillars enable spawning -- very close to heroes. , cnightChance = 0 -- harder variant, but looks cheerful , cauxConnects = 1%10 , cmaxVoid = 1%100 , cextraStairs = 1 + 1 `d` 2 , cdoorChance = 1 -- to enable the doorlessWall hack and have no lit tiles , chidden = 0 , cactorCoeff = 80 -- the maze requires time to explore , cactorFreq = [("monster", 70), ("animal", 15), ("robot", 15)] , citemNum = 6 `d` 5 -- an incentive to explore the labyrinth , citemFreq = [ ("common item", 20), ("treasure", 60), ("explosive", 20) , ("curious item", 20) ] , cpassable = True , cplaceFreq = [("noise", 100)] , cdefTile = "noiseSet" , couterFenceTile = "noise fence" -- ensures no cut-off parts from collapsed , cdarkCorTile = "floorArenaDark" , clitCorTile = "floorArenaLit" , cdesc = "" } noise2 = noise { cname = "Power distribution hub" , cfreq = [("caveNoise2", 1)] , cnightChance = 51 -- easier variant, but looks sinister , citemNum = 11 `d` 5 -- an incentive to explore the final labyrinth , citemFreq = [ ("common item", 40), ("treasure", 60) , ("curious item", 20) ] , cplaceFreq = [("noise", 1), ("mine", 99)] , cstairFreq = [("gated staircase", 100)] , cdesc = "" } bridge = rogue { csymbol = 'B' , cname = "Captain's bridge" , cfreq = [("caveBridge", 1)] , cdarkChance = 0 -- all rooms lit, for a gentle start , cextraStairs = 1 , cactorCoeff = 200 -- it's quite deep already, so spawn slowly , cactorFreq = [("animal", 100)] , citemNum = 8 `d` 5 -- lure them in with loot , citemFreq = filter ((`notElem` ["treasure", "curious item"]) . fst) $ citemFreq rogue , cdesc = "The bridge is gutted out and deserted. There are animal cries down below and ominous silence up above." } shallow2rogue = rogue { cfreq = [("shallow random 2", 50)] , cactorCoeff = cactorCoeff rogue `div` 2 -- more difficult , cactorFreq = filter ((/= "monster") . fst) $ cactorFreq rogue , citemFreq = filter ((/= "treasure") . fst) $ citemFreq rogue , cdesc = "" } shallow2arena = arena { cfreq = [("shallow random 2", 100)] , cactorCoeff = cactorCoeff arena `div` 2 , cactorFreq = filter ((/= "monster") . fst) $ cactorFreq arena , citemFreq = filter ((/= "treasure") . fst) $ citemFreq arena , cdesc = "" } shallow2empty = empty { cfreq = [("shallow random 2", 10)] , cactorCoeff = cactorCoeff empty `div` 2 , cactorFreq = filter ((/= "monster") . fst) $ cactorFreq empty , citemFreq = filter ((/= "treasure") . fst) $ citemFreq empty , cdefTile = "emptyExitSet" -- avoid floor windows , cdesc = "" } shallow1empty = empty { cname = "Outermost deck" , cfreq = [("outermost", 100)] , cactorCoeff = 4 -- shallower than LH, so fewer immediate actors, so boost , cactorFreq = [("animal", 3), ("robot", 2), ("immobile robot", 95)] -- The medbot faucets on lvl 1 act like HP resets. Needed to avoid -- cascading failure, if the particular starting conditions were -- very hard. Items are not reset, even if they are bad, which provides -- enough of a continuity. The faucets on lvl 1 are not OP and can't be -- abused, because they spawn less and less often and also HP doesn't -- effectively accumulate over max. , citemFreq = filter ((/= "treasure") . fst) $ citemFreq empty , couterFenceTile = "oriels fence" , cdesc = "The black sky outside sucks light through the oriels." } emptyExit = empty { cname = "Shuttle servicing level" , cfreq = [("caveEmptyExit", 1)] , cdefTile = "emptyExitSet" , cdarkCorTile = "transport route" , clitCorTile = "transport route" , couterFenceTile = "noise fence" -- for flavour , cescapeGroup = Just "escape spaceship down" , cstairFreq = [("gated staircase", 100)] , cdesc = "Empty husks and strewn entrails of small craft litter the hangar among neglected cranes and airlocks welded shut." } raid = rogue { csymbol = 'S' , cname = "Triton City sewers" , cfreq = [("caveRaid", 1)] , cdarkChance = 0 -- all rooms lit, for a gentle start , cmaxVoid = 1%10 , cactorCoeff = 250 -- deep level with no kit, so slow spawning , cactorFreq = [("animal", 50), ("robot", 50)] , citemNum = 6 `d` 8 -- just one level, hard enemies, treasure , citemFreq = [("common item", 100), ("currency", 500)] , cescapeGroup = Just "escape up" , cdesc = "" } brawl = rogue -- many random solid tiles, to break LOS, since it's a day -- and this scenario is not focused on ranged combat; -- also, sanctuaries against missiles in shadow under trees { csymbol = 'S' , cname = "Woodland biosphere" , cfreq = [("caveBrawl", 1)] , cgrid = DiceXY (2 `d` 2 + 2) 3 , cminPlaceSize = DiceXY 3 3 , cmaxPlaceSize = DiceXY 7 5 , cdarkChance = 51 , cnightChance = 0 , cdoorChance = 1 , copenChance = 0 , cextraStairs = 1 , chidden = 0 , cactorFreq = [] , citemNum = 5 `d` 8 , citemFreq = [("common item", 100)] , cplaceFreq = [("brawl", 60), ("rogue", 40)] , cpassable = True , cdefTile = "brawlSetLit" , cdarkCorTile = "floorArenaLit" , clitCorTile = "floorArenaLit" , cdesc = "" } shootout = rogue -- a scenario with strong missiles; -- few solid tiles, but only translucent tiles or walkable -- opaque tiles, to make scouting and sniping more interesting -- and to avoid obstructing view too much, since this -- scenario is about ranged combat at long range { csymbol = 'S' , cname = "Hydroponic farm" -- still a neutral, offcial wording , cfreq = [("caveShootout", 1)] , cgrid = DiceXY (1 `d` 2 + 7) 3 , cminPlaceSize = DiceXY 3 3 , cmaxPlaceSize = DiceXY 3 4 , cdarkChance = 51 , cnightChance = 0 , cdoorChance = 1 , copenChance = 0 , cextraStairs = 1 , chidden = 0 , cactorFreq = [] , citemNum = 5 `d` 16 -- less items in inventory, more to be picked up, -- to reward explorer and aggressor and punish camper , citemFreq = [ ("common item", 30) , ("any arrow", 400), ("harpoon", 300), ("explosive", 50) ] -- Many consumable buffs are needed in symmetric maps -- so that aggressor prepares them in advance and camper -- needs to waste initial turns to buff for the defence. , cplaceFreq = [("shootout", 100)] , cpassable = True , cdefTile = "shootoutSetLit" , cdarkCorTile = "floorArenaLit" , clitCorTile = "floorArenaLit" } escape = rogue -- a scenario with weak missiles, because heroes don't depend -- on them; dark, so solid obstacles are to hide from missiles, -- not view; obstacles are not lit, to frustrate the AI; -- lots of small lights to cross, to have some risks { csymbol = 'E' , cname = "Red Collar Bros den" -- tension rises; non-official name , cfreq = [("caveEscape", 1)] , cgrid = DiceXY -- (2 `d` 2 + 3) 4 -- park, so lamps in lines (2 `d` 2 + 6) 3 -- for now, to fit larger places , cminPlaceSize = DiceXY 3 3 , cmaxPlaceSize = DiceXY 9 9 -- bias towards larger lamp areas , cdarkChance = 51 -- colonnade rooms should always be dark , cnightChance = 51 -- always night , cauxConnects = 3%2 , cmaxVoid = 1%10 , cextraStairs = 1 , chidden = 0 , cactorFreq = [] , citemNum = 6 `d` 8 , citemFreq = [ ("common item", 30), ("gem", 150) , ("weak arrow", 500), ("harpoon", 400) , ("explosive", 100) ] , cplaceFreq = [("park", 100)] , cpassable = True , cdefTile = "escapeSetDark" -- different tiles, not burning yet , cdarkCorTile = "alarmingTrailLit" -- let trails give off light , clitCorTile = "alarmingTrailLit" , cescapeGroup = Just "escape outdoor down" , cdesc = "" } zoo = rogue -- few lights and many solids, to help the less numerous heroes { csymbol = 'Z' , cname = "Municipal zoo in flames" -- non-official adjective , cfreq = [("caveZoo", 1)] , cgrid = DiceXY (2 `d` 2 + 6) 3 , cminPlaceSize = DiceXY 4 4 , cmaxPlaceSize = DiceXY 12 12 , cdarkChance = 51 -- always dark rooms , cnightChance = 51 -- always night , cauxConnects = 1%4 , cmaxVoid = 1%20 , cdoorChance = 7%10 , copenChance = 9%10 , cextraStairs = 1 , chidden = 0 , cactorFreq = [] , citemNum = 7 `d` 8 , citemFreq = [("common item", 100), ("light source", 1000)] , cplaceFreq = [("zoo", 50)] , cpassable = True , cdefTile = "zooSet" , cdarkCorTile = "trailLit" -- let trails give off light , clitCorTile = "trailLit" , cdesc = "" } ambush = rogue -- a scenario with strong missiles; -- dark, so solid obstacles are to hide from missiles, -- not view, and they are all lit, because stopped missiles -- are frustrating, while a few LOS-only obstacles are not lit; -- lots of small lights to cross, to give a chance to snipe; -- a crucial difference wrt shootout is that trajectories -- of missiles are usually not seen, so enemy can't be guessed; -- camping doesn't pay off, because enemies can sneak and only -- active scouting, throwing flares and shooting discovers them { csymbol = 'M' , cname = "Ravaged spaceport" -- non-official adjective , cfreq = [("caveAmbush", 1)] , cgrid = DiceXY -- (2 `d` 2 + 3) 4 -- spaceport, so lamps in lines (2 `d` 2 + 5) 3 -- for now, to fit larger places , cminPlaceSize = DiceXY 3 3 , cmaxPlaceSize = DiceXY 15 15 -- allow hangars , cdarkChance = 51 -- colonnade rooms should always be dark , cnightChance = 51 -- always night , cauxConnects = 3%2 , cmaxVoid = 1%20 , cextraStairs = 1 , chidden = 0 , cactorFreq = [] , citemNum = 5 `d` 8 , citemFreq = [ ("common item", 30) , ("any arrow", 400), ("harpoon", 300), ("explosive", 50) ] , cplaceFreq = [("ambush", 100)] , cpassable = True , cdefTile = "ambushSet" , cdarkCorTile = "trailLit" -- let trails give off light , clitCorTile = "trailLit" , cdesc = "" } battle = rogue -- few lights and many solids, to help the less numerous heroes { csymbol = 'B' , cname = "Burning public park" , cfreq = [("caveBattle", 1)] , cgrid = DiceXY (2 `d` 2 + 1) 3 , cminPlaceSize = DiceXY 4 4 , cmaxPlaceSize = DiceXY 9 7 , cdarkChance = 0 , cnightChance = 51 -- always night , cauxConnects = 1%4 , cmaxVoid = 1%20 , cdoorChance = 2%10 , copenChance = 9%10 , cextraStairs = 1 , chidden = 0 , cactorFreq = [] , citemNum = 5 `d` 8 , citemFreq = [("common item", 100), ("light source", 200)] , cplaceFreq = [("battle", 50), ("rogue", 50)] , cpassable = True , cdefTile = "battleSet" , cdarkCorTile = "trailLit" -- let trails give off light , clitCorTile = "trailLit" , couterFenceTile = "noise fence" -- ensures no cut-off parts from collapsed , cdesc = "" } safari1 = brawl { cname = "Hunam habitat" , cfreq = [("caveSafari1", 1)] , cstairFreq = [("staircase outdoor", 1)] , cdesc = "\"DLC 1. Hunams scavenge in a forest in their usual disgusting way.\"" } safari2 = ambush -- lamps instead of trees, but ok, it's only a simulation { cname = "Deep into the jungle" , cfreq = [("caveSafari2", 1)] , cstairFreq = [("staircase outdoor", 1)] , cdesc = "\"DLC 2. In the dark pure heart of the jungle noble animals roam freely.\"" } safari3 = zoo -- glass rooms, but ok, it's only a simulation { cname = "Jungle in flames" , cfreq = [("caveSafari3", 1)] , cescapeGroup = Just "escape outdoor down" , cstairFreq = [("staircase outdoor", 1)] , cdesc = "\"DLC 3. Jealous hunams set jungle on fire and flee.\"" } Allure-0.8.3.0/GameDefinition/Content/ModeKind.hs0000644000000000000000000005465113315545742017640 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Game mode definitions. module Content.ModeKind ( content ) where import Prelude () import Game.LambdaHack.Common.Prelude import qualified Data.IntMap.Strict as IM import Content.ModeKindPlayer import Game.LambdaHack.Common.Dice import Game.LambdaHack.Content.ModeKind content :: [ModeKind] content = [raid, brawl, shootout, escape, zoo, ambush, crawl, crawlEmpty, crawlSurvival, safari, safariSurvival, battle, battleSurvival, defense, defenseEmpty, screensaverRaid, screensaverBrawl, screensaverShootout, screensaverEscape, screensaverZoo, screensaverAmbush, screensaverCrawl, screensaverSafari] raid, brawl, shootout, escape, zoo, ambush, crawl, crawlEmpty, crawlSurvival, safari, safariSurvival, battle, battleSurvival, defense, defenseEmpty, screensaverRaid, screensaverBrawl, screensaverShootout, screensaverEscape, screensaverZoo, screensaverAmbush, screensaverCrawl, screensaverSafari :: ModeKind -- What other symmetric (two only-one-moves factions) and asymmetric vs crowd -- scenarios make sense (e.g., are good for a tutorial or for standalone -- extreme fun or are impossible as part of a crawl)? -- sparse melee at night: no, shade ambush in brawl is enough -- dense melee: no, keeping big party together is a chore and big enemy -- party is less fun than huge enemy party -- crowd melee in daylight: no, possible in crawl and at night is more fun -- sparse ranged at night: no, less fun than dense and if no reaction fire, -- just a camp fest or firing blindly -- dense ranged in daylight: no, less fun than at night with flares -- crowd ranged: no, fish in a barel, less predictable and more fun inside -- crawl, even without reaction fire raid = ModeKind -- mini-crawl { msymbol = 'r' , mname = "raid (1)" , mfreq = [("raid", 1), ("campaign scenario", 1)] , mroster = rosterRaid , mcaves = cavesRaid , mdesc = "Neptune Area Administration confirms isolated spottings of various oversize vermin in non-residential zones of the Triton moon's largest city. To put it plainly: Triton City sewers need purging. The first person to break through to the other exit will be paid 100 gold grains. The Administration strongly urges participants not to resort to violence against each other." } brawl = ModeKind -- sparse melee in daylight, with shade for melee ambush { msymbol = 'k' , mname = "brawl (2)" , mfreq = [("brawl", 1), ("campaign scenario", 1)] , mroster = rosterBrawl , mcaves = cavesBrawl , mdesc = "\"You scoundrel! You cheated in the sewers. Come alone to the woody biosphere behind the saloon at noon, if you dare. Given that I win, I take back all your gold. Otherwise, you get the scrapping rights for the giant spaceliner's hull in orbit. Yes, it's mine, you tramp; here's the docking transmitter and the paperwork.\"" } -- The trajectory tip is important because of tactics of scout looking from -- behind a bush and others hiding in mist. If no suitable bushes, -- fire once and flee into mist or behind cover. Then whomever is out of LOS -- range or inside mist can shoot at the last seen enemy locations, -- adjusting and according to ounds and incoming missile trajectories. -- If the scount can't find bushes or glass building to set a lookout, -- the other team member are more spotters and guardians than snipers -- and that's their only role, so a small party makes sense. shootout = ModeKind -- sparse ranged in daylight { msymbol = 's' , mname = "shootout (3)" , mfreq = [("shootout", 1), ("campaign scenario", 1)] , mroster = rosterShootout , mcaves = cavesShootout , mdesc = "The fight crashes over to a nearby mechanized farm. Law enforcement, crippled by the ban on firearms, won't show up until only wounded and dying remain to be revived and locked up. Farm supplies, scattered around, beg to be flung at foes as improvised missiles. Intense light makes it easy to aim and to discern trajectory of soaring items (point at projectiles with the crosshair in aiming mode)." } escape = ModeKind -- asymmetric ranged and stealth race at night { msymbol = 'e' , mname = "escape (4)" , mfreq = [("escape", 1), ("campaign scenario", 1)] , mroster = rosterEscape , mcaves = cavesEscape , mdesc = "Bloodied spaceship deed in hand notwithstanding, you can reach the derelict spaceliner only via a shuttle from the Triton Spaceport across the city. After hours of being chased in the opposite direction towards the border wall, you sneak back and make a desperate dash through the very territory of the pursuing gang. Any valuables you come upon in this public park turned miscreant lair will be fair compensation for your losses, but you need to find the exit before the foes find you." } zoo = ModeKind -- asymmetric crowd melee at night { msymbol = 'b' , mname = "zoo (5)" , mfreq = [("zoo", 1), ("campaign scenario", 1)] , mroster = rosterZoo , mcaves = cavesZoo , mdesc = "As justified and satisfying as setting the enemy headquarters on fire has been, it backfires when the blaze immediately spreads to the public zoo on the path to the spaceport. Crazed animals mill around while the flames ignite greenery and consume nets, cages and security equipment. Whether that's a good sign or bad, apparently nobody is willing to pursue you any more." } -- The tactic is to sneak in the dark, highlight enemy with thrown torches -- (and douse thrown enemy torches with blankets) and only if this fails, -- actually scout using extended noctovision. -- With reaction fire, larger team is more fun. -- -- For now, while we have no shooters with timeout, massive ranged battles -- without reaction fire don't make sense, because then usually only one hero -- shoots (and often also scouts) and others just gather ammo. ambush = ModeKind -- dense ranged with reaction fire at night { msymbol = 'm' , mname = "ambush (6)" , mfreq = [("ambush", 1), ("campaign scenario", 1)] , mroster = rosterAmbush , mcaves = cavesAmbush , mdesc = "Not even the unexplained ruin of the largest and tightest security of Neptune's spaceports will prevent you from claiming your prize. After all, you didn't take to the space to let others decide your fate. Onward!" } crawl = ModeKind { msymbol = 'c' , mname = "crawl (long)" , mfreq = [("crawl", 1), ("campaign scenario", 1)] , mroster = rosterCrawl , mcaves = cavesCrawl , mdesc = "You get stranded while looting, with utmost satisfaction, the blasted bridge of an old and extravagantly luxurious cruise liner. The inert spaceship, supposedly long deserted and barely able to sustain life support, suddenly releases the shuttle you came in, lights up ion engines, manoeuvres deftly off Triton orbit and heads purposefully away from Neptune. There is static on all communication channels. Your plan of battle is to scour the dilapidated decks, gather all missing squad members and break through to the auxiliary engineering and docking hub somewhere among the giant spaceship's uppermost levels. You are determined to fight for the lives and freedom of your crew and not to leave the ship without taking of its wealth what is rightfully yours." } safari = ModeKind -- easter egg available only via screensaver { msymbol = 'f' , mname = "safari" , mfreq = [("safari", 1)] , mroster = rosterSafari , mcaves = cavesSafari , mdesc = "\"In this simulation you'll discover the joys of hunting the most exquisite of Earth's flora and fauna, both animal and semi-intelligent. Exit at the topmost level.\" This is a VR recording recovered from an alien nest debris." } -- * Testing modes crawlEmpty = ModeKind { msymbol = 'c' , mname = "crawl empty" , mfreq = [("crawl empty", 1)] , mroster = rosterCrawlEmpty , mcaves = cavesCrawl , mdesc = "Enjoy the free space." } crawlSurvival = ModeKind { msymbol = 'd' , mname = "crawl survival" , mfreq = [("crawl survival", 1)] , mroster = rosterCrawlSurvival , mcaves = cavesCrawl , mdesc = "Lure the human intruders deeper and deeper." } safariSurvival = ModeKind { msymbol = 'u' , mname = "safari survival" , mfreq = [("safari survival", 1)] , mroster = rosterSafariSurvival , mcaves = cavesSafari , mdesc = "In this simulation you'll discover the joys of being hunted among the most exquisite of Earth's flora and fauna, both animal and semi-intelligent." } battle = ModeKind { msymbol = 'b' , mname = "battle" , mfreq = [("battle", 1)] , mroster = rosterBattle , mcaves = cavesBattle , mdesc = "Odds are stacked against those that unleash the horrors of abstraction." } battleSurvival = ModeKind { msymbol = 'i' , mname = "battle survival" , mfreq = [("battle survival", 1)] , mroster = rosterBattleSurvival , mcaves = cavesBattle , mdesc = "Odds are stacked for those that breathe mathematics." } defense = ModeKind -- perhaps a real scenario in the future { msymbol = 'e' , mname = "defense" , mfreq = [("defense", 1)] , mroster = rosterDefense , mcaves = cavesCrawl , mdesc = "Don't let the half-witted humans derail your operation and flee, like the puny, naked, tentacle-less beasts that they are!" } defenseEmpty = ModeKind { msymbol = 'e' , mname = "defense empty" , mfreq = [("defense empty", 1)] , mroster = rosterDefenseEmpty , mcaves = cavesCrawl , mdesc = "Lord over." } -- * Screensaver modes screensave :: AutoLeader -> Roster -> Roster screensave auto r = let f [] = [] f ((player, initial) : rest) = (player {fleaderMode = LeaderAI auto}, initial) : rest in r {rosterList = f $ rosterList r} screensaverRaid = raid { mname = "auto-raid (1)" , mfreq = [("starting", 1), ("starting JS", 1), ("no confirms", 1)] , mroster = screensave (AutoLeader False False) rosterRaid } screensaverBrawl = brawl { mname = "auto-brawl (2)" , mfreq = [("no confirms", 1)] , mroster = screensave (AutoLeader False False) rosterBrawl } screensaverShootout = shootout { mname = "auto-shootout (3)" , mfreq = [("starting", 1), ("starting JS", 1), ("no confirms", 1)] , mroster = screensave (AutoLeader False False) rosterShootout } screensaverEscape = escape { mname = "auto-escape (4)" , mfreq = [("starting", 1), ("starting JS", 1), ("no confirms", 1)] , mroster = screensave (AutoLeader False False) rosterEscape } screensaverZoo = zoo { mname = "auto-zoo (5)" , mfreq = [("no confirms", 1)] , mroster = screensave (AutoLeader False False) rosterZoo } screensaverAmbush = ambush { mname = "auto-ambush (6)" , mfreq = [("no confirms", 1)] , mroster = screensave (AutoLeader False False) rosterAmbush } screensaverCrawl = crawl { mname = "auto-crawl (long)" , mfreq = [("no confirms", 1)] , mroster = screensave (AutoLeader False False) rosterCrawl } screensaverSafari = safari { mname = "auto-safari" , mfreq = [("starting", 1), ("starting JS", 1), ("no confirms", 1)] , mroster = -- changing leader by client needed, because of TFollow screensave (AutoLeader False True) rosterSafari } rosterRaid, rosterBrawl, rosterShootout, rosterEscape, rosterZoo, rosterAmbush, rosterCrawl, rosterCrawlEmpty, rosterCrawlSurvival, rosterSafari, rosterSafariSurvival, rosterBattle, rosterBattleSurvival, rosterDefense, rosterDefenseEmpty :: Roster rosterRaid = Roster { rosterList = [ ( playerHero {fhiCondPoly = hiRaid} , [(2, 1, "hero")] ) , ( playerAntiHero { fname = "Red Collar Bro" , fhiCondPoly = hiRaid } , [(2, 1, "hero")] ) , ( playerAnimal -- starting over escape , [(2, 2, "animal")] ) , ( playerRobot , [(2, 1, "robot")] ) , (playerHorror, []) ] -- for summoned monsters , rosterEnemy = [ ("Spacefarer", "Animal Kingdom") , ("Spacefarer", "Robot Anarchy") , ("Spacefarer", "Horror Den") , ("Spacefarer", "Red Collar Bro") , ("Red Collar Bro", "Animal Kingdom") , ("Red Collar Bro", "Robot Anarchy") , ("Red Collar Bro", "Horror Den") ] , rosterAlly = [("Robot Anarchy", "Animal Kingdom")] } rosterBrawl = Roster { rosterList = [ ( playerHero { fcanEscape = False , fhiCondPoly = hiDweller } , [(3, 3, "hero")] ) , ( playerAntiHero { fname = "Red Collar Bro" , fcanEscape = False , fhiCondPoly = hiDweller } , [(3, 3, "hero")] ) , (playerHorror, []) ] , rosterEnemy = [ ("Spacefarer", "Red Collar Bro") , ("Spacefarer", "Horror Den") , ("Red Collar Bro", "Horror Den") ] , rosterAlly = [] } -- Exactly one scout gets a sight boost, to help the aggressor, because he uses -- the scout for initial attack, while camper (on big enough maps) -- can't guess where the attack would come and so can't position his single -- scout to counter the stealthy advance. rosterShootout = Roster { rosterList = [ ( playerHero { fcanEscape = False , fhiCondPoly = hiDweller } , [(5, 1, "scout hero"), (5, 2, "ranger hero")] ) , ( playerAntiHero { fname = "Red Collar Bro" , fcanEscape = False , fhiCondPoly = hiDweller } , [(5, 1, "scout hero"), (5, 2, "ranger hero")] ) , (playerHorror, []) ] , rosterEnemy = [ ("Spacefarer", "Red Collar Bro") , ("Spacefarer", "Horror Den") , ("Red Collar Bro", "Horror Den") ] , rosterAlly = [] } rosterEscape = Roster { rosterList = [ ( playerHero {fhiCondPoly = hiEscapist} , [(7, 1, "scout hero"), (7, 2, "escapist hero")] ) , ( playerAntiHero { fname = "Red Collar Bro" , fcanEscape = False -- start on escape , fneverEmpty = False -- loot after killing , fhiCondPoly = hiDweller } , [(7, 1, "scout hero"), (7, 7, "ambusher hero")] ) , (playerHorror, []) ] , rosterEnemy = [ ("Spacefarer", "Red Collar Bro") , ("Spacefarer", "Horror Den") , ("Red Collar Bro", "Horror Den") ] , rosterAlly = [] } rosterZoo = Roster { rosterList = [ ( playerHero { fcanEscape = False , fhiCondPoly = hiDweller } , [(8, 5, "soldier hero")] ) , ( playerAnimal {fneverEmpty = True} , [(8, 100, "mobile animal")] ) , (playerHorror, []) ] -- for summoned monsters , rosterEnemy = [ ("Spacefarer", "Animal Kingdom") , ("Spacefarer", "Horror Den") ] , rosterAlly = [] } rosterAmbush = Roster { rosterList = [ ( playerHero { fcanEscape = False , fhiCondPoly = hiDweller } , [(9, 1, "scout hero"), (9, 5, "ambusher hero")] ) , ( playerAntiHero { fname = "Gray Off-World Mercenary" , fcanEscape = False , fhiCondPoly = hiDweller } , [(9, 1, "scout hero"), (9, 5, "ambusher hero")] ) , (playerHorror, []) ] , rosterEnemy = [ ("Spacefarer", "Gray Off-World Mercenary") , ("Spacefarer", "Horror Den") , ("Gray Off-World Mercenary", "Horror Den") ] , rosterAlly = [] } rosterCrawl = Roster { rosterList = [ ( playerHero , [(3, 3, "hero")] ) , ( playerMonster , [] ) , ( playerAnimal , -- Optional huge battle at the end: [(12, 100, "mobile animal")] ) , ( playerRobot , [] ) ] -- gentle introduction , rosterEnemy = [ ("Spacefarer", "Alien Hierarchy") , ("Spacefarer", "Animal Kingdom") , ("Spacefarer", "Robot Anarchy") ] , rosterAlly = [ ("Alien Hierarchy", "Animal Kingdom") , ("Alien Hierarchy", "Robot Anarchy") , ("Robot Anarchy", "Animal Kingdom") ] } rosterCrawlEmpty = Roster { rosterList = [ ( playerHero , [(1, 1, "hero")] ) , (playerHorror, []) ] -- for summoned monsters , rosterEnemy = [] , rosterAlly = [] } rosterCrawlSurvival = rosterCrawl { rosterList = [ ( playerHero { fleaderMode = LeaderAI $ AutoLeader True False , fhasUI = False } , [(3, 3, "hero")] ) , ( playerMonster , [] ) , ( playerAnimal {fhasUI = True} , -- Fun from the start to avoid empty initial level: [ (3, 5 + 1 `d` 2, "animal") -- many, because no spawning -- Optional huge battle at the end: , (12, 100, "mobile animal") ] ) , ( playerRobot , [] ) ] } -- gentle introduction -- No horrors faction needed, because spawned heroes land in civilian faction. rosterSafari = Roster { rosterList = [ ( playerMonsterTourist , [(4, 15, "monster")] ) , ( playerHunamConvict , [(4, 3, "civilian")] ) , ( playerAnimalMagnificent , [(7, 20, "mobile animal")] ) , ( playerAnimalExquisite -- start on escape , [(10, 30, "mobile animal")] ) , (playerHorror, []) ] -- construction hooter; neutral , rosterEnemy = [ ("Alien Tourist Office", "Hunam Convict") , ( "Alien Tourist Office" , "Animal Magnificent Specimen Variety" ) , ( "Alien Tourist Office" , "Animal Exquisite Herds and Packs Galore" ) , ( "Animal Magnificent Specimen Variety" , "Hunam Convict" ) , ( "Hunam Convict" , "Animal Exquisite Herds and Packs Galore" ) ] , rosterAlly = [ ( "Animal Magnificent Specimen Variety" , "Animal Exquisite Herds and Packs Galore" ) ] } rosterSafariSurvival = rosterSafari { rosterList = [ ( playerMonsterTourist { fleaderMode = LeaderAI $ AutoLeader True True , fhasUI = False } , [(4, 15, "monster")] ) , ( playerHunamConvict , [(4, 3, "civilian")] ) , ( playerAnimalMagnificent { fleaderMode = LeaderUI $ AutoLeader True False , fhasUI = True } , [(7, 20, "mobile animal")] ) , ( playerAnimalExquisite , [(10, 30, "mobile animal")] ) , (playerHorror, []) ] } rosterBattle = Roster { rosterList = [ ( playerHero { fcanEscape = False , fhiCondPoly = hiDweller } , [(5, 5, "soldier hero")] ) , ( playerMonster {fneverEmpty = True} , [(5, 35, "mobile monster")] ) , ( playerAnimal {fneverEmpty = True} , [(5, 20, "mobile animal")] ) , ( playerRobot {fneverEmpty = True} , [(5, 15, "mobile robot")] ) ] , rosterEnemy = [ ("Spacefarer", "Alien Hierarchy") , ("Spacefarer", "Animal Kingdom") , ("Spacefarer", "Robot Anarchy") ] , rosterAlly = [ ("Alien Hierarchy", "Animal Kingdom") , ("Alien Hierarchy", "Robot Anarchy") , ("Robot Anarchy", "Animal Kingdom") ] } rosterBattleSurvival = rosterBattle { rosterList = [ ( playerHero { fcanEscape = False , fhiCondPoly = hiDweller , fleaderMode = LeaderAI $ AutoLeader False False , fhasUI = False } , [(5, 5, "soldier hero")] ) , ( playerMonster {fneverEmpty = True} , [(5, 35, "mobile monster")] ) , ( playerAnimal { fneverEmpty = True , fhasUI = True } , [(5, 20, "mobile animal")] ) , ( playerRobot {fneverEmpty = True} , [(5, 15, "mobile robot")] ) ] } rosterDefense = rosterCrawl { rosterList = [ ( playerAntiHero , [(3, 3, "hero")] ) , ( playerAntiMonster , [] ) , ( playerAnimal , -- Fun from the start to avoid empty initial level: [ (3, 5 + 1 `d` 2, "animal") -- many, because no spawning -- Optional huge battle at the end: , (12, 100, "mobile animal") ] ) , ( playerRobot , [] ) ] } -- gentle introduction rosterDefenseEmpty = rosterCrawl { rosterList = [ ( playerAntiMonster {fneverEmpty = True} , [(4, 1, "scout monster")] ) , (playerHorror, []) ] -- for summoned animals , rosterEnemy = [] , rosterAlly = [] } cavesRaid, cavesBrawl, cavesShootout, cavesEscape, cavesZoo, cavesAmbush, cavesCrawl, cavesSafari, cavesBattle :: Caves cavesRaid = IM.fromList [(2, "caveRaid")] cavesBrawl = IM.fromList [(3, "caveBrawl")] cavesShootout = IM.fromList [(5, "caveShootout")] cavesEscape = IM.fromList [(7, "caveEscape")] cavesZoo = IM.fromList [(8, "caveZoo")] cavesAmbush = IM.fromList [(9, "caveAmbush")] cavesCrawl = IM.fromList $ [(1, "outermost")] ++ [(2, "shallow random 2")] ++ [(3, "caveBridge")] ++ [(4, "caveNoise")] ++ [(5, "default random")] ++ [(6, "default random")] ++ [(7, "deep random")] ++ [(8, "deep random")] ++ [(9, "deep random")] ++ [(10, "caveEmptyExit")] ++ [(11, "deep random")] ++ [(12, "caveNoise2")] cavesSafari = IM.fromList [ (4, "caveSafari1") , (7, "caveSafari2") , (10, "caveSafari3") ] cavesBattle = IM.fromList [(5, "caveBattle")] Allure-0.8.3.0/GameDefinition/Content/ItemKindBlast.hs0000644000000000000000000006655413315545742020645 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Blast definitions. module Content.ItemKindBlast ( blasts ) where import Prelude () import Game.LambdaHack.Common.Prelude import Game.LambdaHack.Common.Color import Game.LambdaHack.Common.Dice import Game.LambdaHack.Common.Flavour import Game.LambdaHack.Common.ItemAspect (Aspect (..)) import Game.LambdaHack.Common.Misc import Game.LambdaHack.Content.ItemKind blasts :: [ItemKind] blasts = [burningOil2, burningOil3, burningOil4, firecracker1, firecracker2, firecracker3, firecracker4, firecracker5, spreadFragmentation, spreadFragmentation8, focusedFragmentation, spreadConcussion, spreadConcussion8, focusedConcussion, spreadFlash, spreadFlash8, focusedFlash, singleSpark, glassPiece, focusedGlass,fragrance, pheromone, mistCalming, odorDistressing, mistHealing, mistHealing2, mistWounding, distortion, smoke, boilingWater, glue, waste, denseShower, sparseShower, protectingBalmMelee, protectingBalmRanged, vulnerabilityBalm, resolutionDust, hasteSpray, slownessMist, eyeDrop, ironFiling, smellyDroplet, eyeShine, whiskeySpray, youthSprinkle, poisonCloud, mistAntiSlow, mistAntidote] -- Allure-specific ++ [cruiseAdHologram, outerAdHologram, victoriaClassHologram, allureIntroHologram] burningOil2, burningOil3, burningOil4, firecracker1, firecracker2, firecracker3, firecracker4, firecracker5, spreadFragmentation, spreadFragmentation8, focusedFragmentation, spreadConcussion, spreadConcussion8, focusedConcussion, spreadFlash, spreadFlash8, focusedFlash, singleSpark, glassPiece, focusedGlass,fragrance, pheromone, mistCalming, odorDistressing, mistHealing, mistHealing2, mistWounding, distortion, smoke, boilingWater, glue, waste, denseShower, sparseShower, protectingBalmMelee, protectingBalmRanged, vulnerabilityBalm, resolutionDust, hasteSpray, slownessMist, eyeDrop, ironFiling, smellyDroplet, eyeShine, whiskeySpray, youthSprinkle, poisonCloud, mistAntiSlow, mistAntidote :: ItemKind -- Allure-specific cruiseAdHologram, outerAdHologram, victoriaClassHologram, allureIntroHologram :: ItemKind -- We take care (e.g., in burningOil below) that blasts are not faster -- than 100% fastest natural speed, or some frames would be skipped, -- which is a waste of prefectly good frames. -- * Parameterized blasts burningOil :: Int -> ItemKind burningOil n = ItemKind { isymbol = '*' , iname = "burning oil" , ifreq = [(toGroupName $ "burning oil" <+> tshow n, 1)] , iflavour = zipPlain [BrYellow] , icount = intToDice (n * 8) , irarity = [(1, 1)] , iverbHit = "sear" , iweight = 1 , idamage = 0 , iaspects = [AddShine 2] , ieffects = [ Burn 1 , toOrganBad "slowed" (2 + 1 `d` 2) ] -- tripping on oil , ifeature = [ toVelocity (min 100 $ n `div` 2 * 10) , Fragile, Blast ] , idesc = "Sticky oil, burning brightly." , ikit = [] } burningOil2 = burningOil 2 -- 2 steps, 2 turns burningOil3 = burningOil 3 -- 3 steps, 2 turns burningOil4 = burningOil 4 -- 4 steps, 2 turns firecracker :: Int -> ItemKind firecracker n = ItemKind { isymbol = '*' , iname = "firecracker" , ifreq = [(toGroupName $ if n == 5 then "firecracker" else "firecracker" <+> tshow n, 1)] , iflavour = zipPlain [brightCol !! ((n + 2) `mod` length brightCol)] , icount = if n <= 3 then 1 `d` min 2 n else 2 + 1 `d` 2 , irarity = [(1, 1)] , iverbHit = if n >= 4 then "singe" else "crack" , iweight = 1 , idamage = 0 , iaspects = [AddShine $ intToDice $ 1 + n `div` 2] , ieffects = [if n >= 4 then Burn 1 else RefillCalm (-2)] ++ [DropBestWeapon | n >= 4] ++ [ OnSmash $ Explode $ toGroupName $ "firecracker" <+> tshow (n - 1) | n >= 2 ] , ifeature = [toVelocity 5, Fragile, Blast] , idesc = "Scraps of burnt paper, covering little pockets of black powder, buffeted by colorful explosions." , ikit = [] } firecracker5 = firecracker 5 firecracker4 = firecracker 4 firecracker3 = firecracker 3 firecracker2 = firecracker 2 firecracker1 = firecracker 1 -- * Focused blasts spreadFragmentation = ItemKind { isymbol = '*' , iname = "fragmentation burst" , ifreq = [("violent fragmentation", 1)] , iflavour = zipPlain [Red] -- flying shards; some fire and smoke , icount = 16 -- strong but few, so not always hits target , irarity = [(1, 1)] , iverbHit = "tear apart" , iweight = 1 , idamage = 3 `d` 1 -- deadly and adjacent actor hit by 2 on average; -- however, moderate armour blocks completely , iaspects = [AddShine 3, AddHurtMelee $ -12 * 5] , ieffects = [DropItem 1 maxBound COrgan "condition"] , ifeature = [toLinger 20, Lobable, Fragile, Blast] -- 4 steps, 1 turn , idesc = "" , ikit = [] } spreadFragmentation8 = spreadFragmentation { iname = "fragmentation burst" , ifreq = [("fragmentation", 1)] , icount = 8 , ifeature = [toLinger 10, Lobable, Fragile, Blast] -- 2 steps, 1 turn -- smaller radius, so worse for area effect, but twice the direct damage } focusedFragmentation = ItemKind { isymbol = '`' , iname = "deflagration ignition" -- improvised fertilizer, etc. , ifreq = [("focused fragmentation", 1)] , iflavour = zipPlain [BrYellow] , icount = 4 -- 32 in total vs 16; on average 4 hits , irarity = [(1, 1)] , iverbHit = "ignite" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [OnSmash $ Explode "fragmentation"] , ifeature = [toLinger 0, Fragile, Blast] -- 0 steps, 1 turn -- when the target position is occupied, the explosion starts one step -- away, hence we set range to 0 steps, to limit dispersal , idesc = "" , ikit = [] } spreadConcussion = ItemKind { isymbol = '*' , iname = "concussion blast" , ifreq = [("violent concussion", 1)] , iflavour = zipPlain [Magenta] -- mosty shock wave; some fire and smoke , icount = 16 , irarity = [(1, 1)] , iverbHit = "shock" , iweight = 1 , idamage = 1 `d` 1 -- only air pressure, so not as deadly as fragmentation, -- but armour can't block completely that easily , iaspects = [AddShine 3, AddHurtMelee $ -8 * 5] , ieffects = [ DropItem maxBound 1 CEqp "misc armor" , PushActor (ThrowMod 400 25) -- 1 step, fast; after DropItem -- this produces spam for braced actors; too bad , DropItem 1 maxBound COrgan "condition" ] , ifeature = [toLinger 20, Lobable, Fragile, Blast] -- 4 steps, 1 turn -- outdoors it has short range, but we only model indoors in the game; -- it's much faster than black powder shock wave, but we are beyond -- human-noticeable speed differences on short distances anyway , idesc = "" , ikit = [] } spreadConcussion8 = spreadConcussion { iname = "concussion blast" , ifreq = [("concussion", 1)] , icount = 8 , ifeature = [toLinger 10, Lobable, Fragile, Blast] -- 2 steps, 1 turn } focusedConcussion = ItemKind { isymbol = '`' , iname = "detonation ignition" -- stabilized high explosive liquid , ifreq = [("focused concussion", 1)] , iflavour = zipPlain [BrYellow] , icount = 4 , irarity = [(1, 1)] , iverbHit = "ignite" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [OnSmash $ Explode "concussion"] , ifeature = [toLinger 0, Fragile, Blast] -- 0 steps, 1 turn , idesc = "" , ikit = [] } spreadFlash = ItemKind { isymbol = '`' , iname = "magnesium flash" -- or aluminum, but let's stick to one , ifreq = [("violent flash", 1)] , iflavour = zipPlain [BrWhite] -- very brigh flash , icount = 16 , irarity = [(1, 1)] , iverbHit = "dazzle" , iweight = 1 , idamage = 0 , iaspects = [AddShine 5] , ieffects = [toOrganBad "blind" 10, toOrganBad "weakened" 30] -- Wikipedia says: blind for five seconds and afterimage -- for much longer, harming aim , ifeature = [toLinger 20, Fragile, Blast] -- 4 steps, 1 turn , idesc = "A flash of fire." , ikit = [] } spreadFlash8 = spreadFlash { iname = "spark" , ifreq = [("spark", 1)] , icount = 8 , iverbHit = "blind" , ifeature = [toLinger 10, Fragile, Blast] -- 2 steps, 1 turn } focusedFlash = ItemKind { isymbol = '`' , iname = "magnesium ignition" , ifreq = [("focused flash", 1)] , iflavour = zipPlain [BrYellow] , icount = 4 , irarity = [(1, 1)] , iverbHit = "ignite" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [OnSmash $ Explode "spark"] , ifeature = [toLinger 0, Fragile, Blast] -- 0 steps, 1 turn , idesc = "" , ikit = [] } singleSpark = spreadFlash { iname = "single spark" , ifreq = [("single spark", 1)] , icount = 1 , iverbHit = "spark" , iaspects = [AddShine 3] , ieffects = [] , ifeature = [toLinger 5, Fragile, Blast] -- 1 step, 1 turn , idesc = "A glowing ember." , ikit = [] } glassPiece = ItemKind { isymbol = '*' , iname = "glass piece" , ifreq = [("glass hail", 1)] , iflavour = zipPlain [Blue] , icount = 8 , irarity = [(1, 1)] , iverbHit = "cut" , iweight = 1 , idamage = 1 `d` 1 , iaspects = [AddHurtMelee $ -15 * 5] -- brittle, not too dense; armor blocks , ieffects = [RefillHP (-1)] , ifeature = [toLinger 20, Fragile, Blast] -- 4 steps, 1 turn , idesc = "Swift, sharp edges." , ikit = [] } focusedGlass = glassPiece -- when blowing up windows { ifreq = [("focused glass hail", 1)] , icount = 4 , ieffects = [RefillHP (-1), OnSmash $ Explode "glass hail"] , ifeature = [toLinger 0, Fragile, Blast] -- 0 steps, 1 turn } -- * Assorted immediate effect blasts fragrance = ItemKind { isymbol = '`' , iname = "fragrance" -- instant, fast fragrance , ifreq = [("fragrance", 1)] , iflavour = zipPlain [Magenta] , icount = 12 , irarity = [(1, 1)] , iverbHit = "engulf" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [Impress] -- Linger 10, because sometimes it takes 2 turns due to starting just -- before actor turn's end (e.g., via a necklace). , ifeature = [toLinger 10, Fragile, Blast] -- 2 steps, 1 turn , idesc = "A pleasant scent." , ikit = [] } pheromone = ItemKind { isymbol = '`' , iname = "musky whiff" -- a kind of mist rather than fragrance , ifreq = [("pheromone", 1)] , iflavour = zipPlain [BrMagenta] , icount = 16 , irarity = [(1, 1)] , iverbHit = "tempt" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [Impress, RefillCalm (-10)] , ifeature = [toVelocity 10, Fragile, Blast] -- 2 steps, 2 turns , idesc = "A sharp, strong scent." , ikit = [] } mistCalming = ItemKind -- unused { isymbol = '`' , iname = "mist" , ifreq = [("calming mist", 1)] , iflavour = zipPlain [BrGreen] , icount = 8 , irarity = [(1, 1)] , iverbHit = "sooth" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [RefillCalm 2] , ifeature = [toVelocity 5, Fragile, Blast] -- 1 step, 1 turn , idesc = "A soothing, gentle cloud." , ikit = [] } odorDistressing = ItemKind { isymbol = '`' , iname = "distressing whiff" , ifreq = [("distressing odor", 1)] , iflavour = zipFancy [BrRed] -- salmon , icount = 8 , irarity = [(1, 1)] , iverbHit = "distress" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [RefillCalm (-20)] , ifeature = [toLinger 10, Fragile, Blast] -- 2 steps, 1 turn , idesc = "It turns the stomach." , ikit = [] } mistHealing = ItemKind { isymbol = '`' , iname = "mist" -- powerful, so slow and narrow , ifreq = [("healing mist", 1)] , iflavour = zipFancy [BrGreen] , icount = 8 , irarity = [(1, 1)] , iverbHit = "revitalize" , iweight = 1 , idamage = 0 , iaspects = [AddShine 1] , ieffects = [RefillHP 2] , ifeature = [toVelocity 5, Fragile, Blast] -- 1 step, 1 turn , idesc = "It fills the air with light and life." , ikit = [] } mistHealing2 = ItemKind { isymbol = '`' , iname = "mist" , ifreq = [("healing mist 2", 1)] , iflavour = zipPlain [Green] , icount = 8 , irarity = [(1, 1)] , iverbHit = "revitalize" , iweight = 1 , idamage = 0 , iaspects = [AddShine 2] , ieffects = [RefillHP 4] , ifeature = [toVelocity 5, Fragile, Blast] -- 1 step, 1 turn , idesc = "At its touch, wounds close and bruises fade." , ikit = [] } mistWounding = ItemKind { isymbol = '`' , iname = "mist" , ifreq = [("wounding mist", 1)] , iflavour = zipPlain [BrRed] , icount = 8 , irarity = [(1, 1)] , iverbHit = "devitalize" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [RefillHP (-2)] , ifeature = [toVelocity 5, Fragile, Blast] -- 1 step, 1 turn , idesc = "The air itself stings and itches." , ikit = [] } distortion = ItemKind { isymbol = 'v' , iname = "vortex" , ifreq = [("distortion", 1)] , iflavour = zipPlain [White] , icount = 8 -- braced are immune to Teleport; avoid failure messages , irarity = [(1, 1)] , iverbHit = "engulf" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [Teleport $ 15 + 1 `d` 10] , ifeature = [toLinger 10, Lobable, Fragile, Blast] -- 2 steps, 1 turn , idesc = "The air shifts oddly, as though light is being warped." , ikit = [] } smoke = ItemKind -- when stuff burns out -- unused { isymbol = '`' , iname = "smoke" , ifreq = [("smoke", 1)] , iflavour = zipPlain [BrBlack] , icount = 16 , irarity = [(1, 1)] , iverbHit = "choke" -- or "obscure" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [] , ifeature = [toVelocity 20, Fragile, Blast] -- 4 steps, 2 turns , idesc = "Twirling clouds of grey smoke." , ikit = [] } boilingWater = ItemKind { isymbol = '*' , iname = "boiling water" , ifreq = [("boiling water", 1)] , iflavour = zipPlain [White] , icount = 18 , irarity = [(1, 1)] , iverbHit = "boil" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [Burn 1] , ifeature = [toVelocity 30, Fragile, Blast] -- 6 steps, 2 turns , idesc = "It bubbles and hisses." , ikit = [] } glue = ItemKind { isymbol = '*' , iname = "hoof glue" , ifreq = [("glue", 1)] , iflavour = zipPlain [Cyan] , icount = 8 -- Paralyze doesn't stack; avoid failure messages , irarity = [(1, 1)] , iverbHit = "glue" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [Paralyze 10] , ifeature = [toVelocity 20, Fragile, Blast] -- 4 steps, 2 turns , idesc = "Thick and clinging." , ikit = [] } waste = ItemKind { isymbol = '*' , iname = "waste" , ifreq = [("waste", 1)] , iflavour = zipPlain [Brown] , icount = 16 , irarity = [(1, 1)] , iverbHit = "splosh" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [Burn 1] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "Sodden and foul-smelling." , ikit = [] } -- * Temporary condition blasts -- Almost all have @toLinger 10@, that travels 2 steps in 1 turn. -- These are very fast projectiles, not getting into the way of big -- actors and not burdening the engine for long. -- A few are slower 'mists'. denseShower = ItemKind { isymbol = '`' , iname = "dense shower" , ifreq = [("dense shower", 1)] , iflavour = zipFancy [Green] , icount = 12 , irarity = [(1, 1)] , iverbHit = "strengthen" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganGood "strengthened" (3 + 1 `d` 3)] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "A thick rain of droplets." , ikit = [] } sparseShower = ItemKind { isymbol = '`' , iname = "sparse shower" , ifreq = [("sparse shower", 1)] , iflavour = zipFancy [Red] , icount = 8 , irarity = [(1, 1)] , iverbHit = "weaken" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganBad "weakened" (3 + 1 `d` 3)] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "Light droplets that cling to clothing." , ikit = [] } protectingBalmMelee = ItemKind { isymbol = '`' , iname = "balm droplet" , ifreq = [("melee protective balm", 1)] , iflavour = zipFancy [Brown] , icount = 16 , irarity = [(1, 1)] , iverbHit = "balm" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganGood "protected from melee" (3 + 1 `d` 3)] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "A thick ointment that hardens the skin." , ikit = [] } protectingBalmRanged = ItemKind { isymbol = '`' , iname = "balm droplet" , ifreq = [("ranged protective balm", 1)] , iflavour = zipPlain [BrYellow] , icount = 16 , irarity = [(1, 1)] , iverbHit = "balm" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganGood "protected from ranged" (3 + 1 `d` 3)] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "Grease that protects from flying death." , ikit = [] } vulnerabilityBalm = ItemKind { isymbol = '`' , iname = "red paint" , ifreq = [("red paint", 1)] , iflavour = zipPlain [BrRed] , icount = 16 , irarity = [(1, 1)] , iverbHit = "paint" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganBad "painted red" (3 + 1 `d` 3)] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "" , ikit = [] } resolutionDust = ItemKind { isymbol = '`' , iname = "resolution dust" , ifreq = [("resolution dust", 1)] , iflavour = zipPlain [Brown] , icount = 16 , irarity = [(1, 1)] , iverbHit = "calm" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganGood "resolute" (3 + 1 `d` 3)] -- short enough duration that @calmEnough@ not a big problem , ifeature = [toLinger 10, Fragile, Blast] , idesc = "A handful of honest earth, to strengthen the soul." , ikit = [] } hasteSpray = ItemKind { isymbol = '`' , iname = "haste spray" , ifreq = [("haste spray", 1)] , iflavour = zipFancy [BrYellow] , icount = 16 , irarity = [(1, 1)] , iverbHit = "haste" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganGood "hasted" (3 + 1 `d` 3)] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "A quick spurt." , ikit = [] } slownessMist = ItemKind { isymbol = '`' , iname = "slowness mist" , ifreq = [("slowness mist", 1)] , iflavour = zipPlain [BrBlue] , icount = 8 , irarity = [(1, 1)] , iverbHit = "slow" , iweight = 0 , idamage = 0 , iaspects = [] , ieffects = [toOrganBad "slowed" (3 + 1 `d` 3)] , ifeature = [toVelocity 5, Fragile, Blast] -- 1 step, 1 turn, mist, slow , idesc = "Clammy fog, making each movement an effort." , ikit = [] } eyeDrop = ItemKind { isymbol = '`' , iname = "eye drop" , ifreq = [("eye drop", 1)] , iflavour = zipFancy [BrCyan] , icount = 16 , irarity = [(1, 1)] , iverbHit = "cleanse" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganGood "far-sighted" (3 + 1 `d` 3)] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "Not to be taken orally." , ikit = [] } ironFiling = ItemKind { isymbol = '`' , iname = "iron filing" , ifreq = [("iron filing", 1)] , iflavour = zipPlain [Red] , icount = 16 , irarity = [(1, 1)] , iverbHit = "blind" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganBad "blind" (10 + 1 `d` 10)] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "A shaving of bright metal." , ikit = [] } smellyDroplet = ItemKind { isymbol = '`' , iname = "smelly droplet" , ifreq = [("smelly droplet", 1)] , iflavour = zipFancy [Blue] , icount = 16 , irarity = [(1, 1)] , iverbHit = "sensitize" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganGood "keen-smelling" (3 + 1 `d` 3)] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "A viscous lump that stains the skin." , ikit = [] } eyeShine = ItemKind { isymbol = '`' , iname = "eye shine" , ifreq = [("eye shine", 1)] , iflavour = zipFancy [Cyan] , icount = 16 , irarity = [(1, 1)] , iverbHit = "smear" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganGood "shiny-eyed" (3 + 1 `d` 3)] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "They almost glow in the dark." , ikit = [] } whiskeySpray = ItemKind { isymbol = '`' , iname = "whiskey spray" , ifreq = [("whiskey spray", 1)] , iflavour = zipFancy [Brown] , icount = 16 , irarity = [(1, 1)] , iverbHit = "inebriate" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganGood "drunk" (3 + 1 `d` 3)] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "It burns in the best way." , ikit = [] } youthSprinkle = ItemKind { isymbol = '`' , iname = "youth sprinkle" , ifreq = [("youth sprinkle", 1)] , iflavour = zipFancy [BrGreen] , icount = 16 , irarity = [(1, 1)] , iverbHit = "sprinkle" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [toOrganNoTimer "regenerating"] , ifeature = [toLinger 10, Fragile, Blast] , idesc = "Bright and smelling of the Spring." , ikit = [] } poisonCloud = ItemKind { isymbol = '`' , iname = "poison cloud" , ifreq = [("poison cloud", 1)] , iflavour = zipFancy [BrMagenta] , icount = 16 , irarity = [(1, 1)] , iverbHit = "poison" , iweight = 0 , idamage = 0 , iaspects = [] , ieffects = [toOrganNoTimer "poisoned"] , ifeature = [toVelocity 10, Fragile, Blast] -- 2 steps, 2 turns , idesc = "Choking gas that stings the eyes." , ikit = [] } mistAntiSlow = ItemKind { isymbol = '`' , iname = "mist" , ifreq = [("anti-slow mist", 1)] , iflavour = zipFancy [BrYellow] , icount = 8 , irarity = [(1, 1)] , iverbHit = "propel" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [DropItem 1 1 COrgan "slowed"] , ifeature = [toVelocity 5, Fragile, Blast] -- 1 step, 1 turn , idesc = "A cleansing rain." , ikit = [] } mistAntidote = ItemKind { isymbol = '`' , iname = "mist" , ifreq = [("antidote mist", 1)] , iflavour = zipFancy [BrBlue] , icount = 8 , irarity = [(1, 1)] , iverbHit = "cure" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [DropItem 1 maxBound COrgan "poisoned"] , ifeature = [toVelocity 5, Fragile, Blast] -- 1 step, 1 turn , idesc = "Washes away death's dew." , ikit = [] } -- * Allure-specific -- ** Lore blasts -- They exist for a short time only, but the lore can be read -- from the lore menu. Only optional story bits should go there, -- becuase some players may not even notice them (at first, at least). -- This is designed not to spam gameplay with story. Gameplay first. -- Generally, 3 to 5 blasts of each kind should suffice for variety. -- More would induce long repetition to see all (they are shown at random). -- With mild exceptions, they should have no effects. cruiseAdHologram = ItemKind { isymbol = '`' , iname = "cruise ad hologram" , ifreq = [("cruise ad hologram", 1), ("advertisement", 20)] , iflavour = zipFancy [BrMagenta] , icount = 8 , irarity = [(1, 1)] , iverbHit = "excite" , iweight = 0 -- delay of 1 turn at the start, to easily read the text , idamage = 0 , iaspects = [] , ieffects = [toOrganGood "resolute" (5 + 1 `d` 2), DropBestWeapon] , ifeature = [toVelocity 5, Fragile, Blast] -- 1 step, 1 turn , idesc = "The fitful holographic clip shows a couple that laughs, watches in silence Saturn's rings through a huge window, throws treats to a little rhino frolicking in reduced gravity, runs through corridors wearing alien masks in a mock chase. An exited female voice proclaims: \"...safety, security and comfort...for each of your senses...personalized life support zones...robot servants...guessing your every wish...\"" , ikit = [] } outerAdHologram = cruiseAdHologram { iname = "cruise ad hologram" , ifreq = [("advertisement", 10)] , icount = 4 , ieffects = [] -- weak, 4 particles, no effect , idesc = "A composed young man in a hat looks straight into your eyes with unwavering stare and extols the opportunities, freedom and excitement of the outer Solar System frontier life with unshakable conviction. Names of Neptune-area realtors scroll at the bottom in small font with oversize serifs." } victoriaClassHologram = outerAdHologram { iname = "space fleet hologram" , ifreq = [("story-telling", 20)] , iflavour = zipFancy [BrBlue] , icount = 1 , iverbHit = "bore" , idesc = "A series of huge spaceships zoom in and out of view in a solemn procession. Male voice drones over crackling static: Victoria-class cruise liners are the largest passenger ships ever serially manufactured and the third largest in general, including transport vessel series. Bigger ships are sometimes cobbled ad-hoc, by wiring together cheap modules and primitive cargo hulls welded in space, but they are rarely certified for public commercial operation. Victoria-class passenger cruisers are produced for over three decades now, in slowly evolving configurations, one per two years on average. The design is as conservative, as possible. A disc large enough for comfortable artificial gravity through constant spinning. Fusion reactor in the middle of the axle powering engines protruding far back from the rear plane. Meteor shield at the front. Numerous redundant rechargeable power sources and autonomous life support areas, eliminating the \"all locked in a single can, breathing the same air\" space travel grievance. Actually, everything is redundant twice over, due to strict regulations. To sum it up, these are the most boring spaceships in the galaxy." } allureIntroHologram = victoriaClassHologram { iname = "spaceship hologram" , ifreq = [("story-telling", 10)] , idesc = "A wavy 3D wireframe of a spaceship rotates ponderously. Male voice drones: Allure of the Stars belongs to a long line of luxurious orbit-to-orbit cruise liners, the Victoria-class. It was named after the largest passenger sea vessel of the early 21st century and it shares the grandeur and the extravagance. This particular Victoria-class specimen was designed for long cruises to gas giants, their moons and the moon cities (and their notorious saloons). It has a meteor shield in the form of a flat, multi-layer. unpressurized cargo bay covering the front plane. Such extra cargo capacity enables long space journeys with no limits on resource usage. On shorter legs of the journeys it also enables opportunistic mass cargo transport (in accordance to strictest regulations and completely isolated from the airflow on passenger decks), which is always in demand at the profusely productive, but scarcely populated Solar System frontier. It also makes the unit much thicker than usual: the length from the tip of the cargo bay to the end of the engines is almost two thirds of the diameter of the disk. All in all, it is a particularly sturdy and self-sufficient member of a class famed for exceptional resilience and safety." } Allure-0.8.3.0/GameDefinition/Content/PlaceKind.hs0000644000000000000000000005473013315545742017776 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Room, hall and passage definitions. module Content.PlaceKind ( content ) where import Prelude () import Game.LambdaHack.Common.Prelude import Game.LambdaHack.Common.Misc import Game.LambdaHack.Content.PlaceKind content :: [PlaceKind] content = [rect, rectWindows, glasshouse, pulpit, ruin, collapsed, collapsed2, collapsed3, collapsed4, collapsed5, collapsed6, collapsed7, pillar, pillar2, pillar3, pillar4, colonnade, colonnade2, colonnade3, colonnade4, colonnade5, colonnade6, lampPost, lampPost2, lampPost3, lampPost4, treeShade, fogClump, fogClump2, smokeClump, smokeClump2FGround, bushClump, staircase, staircase2, staircase3, staircase4, staircase5, staircase6, staircase7, staircase8, staircase9, staircase10, staircase11, staircase12, staircase13, staircase14, staircase15, staircase16, staircase17, staircaseOutdoor, staircaseGated, escapeUp, escapeUp2, escapeUp3, escapeUp4, escapeUp5, escapeDown, escapeDown2, escapeDown3, escapeDown4, escapeDown5, escapeOutdoorDown] ++ map makeStaircaseUp lstaircase ++ map makeStaircaseDown lstaircase -- Allure-specific ++ [staircaseLift, escapeSpaceshipDown, escapeSpaceshipDown2, escapeSpaceshipDown3, escapeSpaceshipDown4, escapeSpaceshipDown5, colonnadeWide, oval, ovalFloor, ovalSquare, maze, maze2, maze3, mazeBig, mazeBig2, mazeBig3, cells, cells2, cells3, cells4, cells5, cells6, cells7] rect, rectWindows, glasshouse, pulpit, ruin, collapsed, collapsed2, collapsed3, collapsed4, collapsed5, collapsed6, collapsed7, pillar, pillar2, pillar3, pillar4, colonnade, colonnade2, colonnade3, colonnade4, colonnade5, colonnade6, lampPost, lampPost2, lampPost3, lampPost4, treeShade, fogClump, fogClump2, smokeClump, smokeClump2FGround, bushClump, staircase, staircase2, staircase3, staircase4, staircase5, staircase6, staircase7, staircase8, staircase9, staircase10, staircase11, staircase12, staircase13, staircase14, staircase15, staircase16, staircase17, staircaseOutdoor, staircaseGated, escapeUp, escapeUp2, escapeUp3, escapeUp4, escapeUp5, escapeDown, escapeDown2, escapeDown3, escapeDown4, escapeDown5, escapeOutdoorDown :: PlaceKind -- Allure-specific staircaseLift, escapeSpaceshipDown, escapeSpaceshipDown2, escapeSpaceshipDown3, escapeSpaceshipDown4, escapeSpaceshipDown5, colonnadeWide, oval, ovalFloor, ovalSquare, maze, maze2, maze3, mazeBig, mazeBig2, mazeBig3, cells, cells2, cells3, cells4, cells5, cells6, cells7 :: PlaceKind lstaircase :: [PlaceKind] lstaircase = [staircase, staircase2, staircase3, staircase4, staircase5, staircase6, staircase7, staircase8, staircase9, staircase10, staircase11, staircase12, staircase13, staircase14, staircase15, staircase16, staircase17, staircaseOutdoor, staircaseGated] -- Allure-specific ++ [staircaseLift] -- The dots below are @Char.chr 183@, as defined in @TileKind.floorSymbol@. rect = PlaceKind -- Valid for any nonempty area, hence low frequency. { psymbol = 'r' , pname = "room" , pfreq = [("rogue", 100), ("arena", 40), ("laboratory", 40), ("zoo", 9)] , prarity = [(1, 10), (10, 8)] , pcover = CStretch , pfence = FWall , ptopLeft = ["·"] , poverride = [] } rectWindows = PlaceKind { psymbol = 'w' , pname = "room" , pfreq = [("empty", 10), ("park", 6)] , prarity = [(1, 10), (10, 8)] , pcover = CStretch , pfence = FNone , ptopLeft = [ "#%" , "%·" ] , poverride = [('%', "rectWindowsOver_%_Lit")] -- for now I need to specify 'Lit' or I'd be randomly getting lit and dark -- tiles, until ooverride is extended to take night/dark into account } glasshouse = PlaceKind { psymbol = 'g' , pname = "glasshouse" , pfreq = [("arena", 40), ("shootout", 8), ("zoo", 9), ("ambush", 7)] , prarity = [(1, 10), (10, 8)] , pcover = CStretch , pfence = FNone , ptopLeft = [ "%%" , "%·" ] , poverride = [('%', "glasshouseOver_%_Lit")] } pulpit = PlaceKind { psymbol = 'p' , pname = "pulpit" , pfreq = [("arena", 10), ("zoo", 30)] , prarity = [(1, 10), (10, 8)] , pcover = CMirror , pfence = FGround , ptopLeft = [ "%%·" , "%··" , "··O" ] , poverride = [('%', "glasshouseOver_%_Lit"), ('O', "pulpit")] -- except for floor, this will all be lit, regardless of night/dark; OK } ruin = PlaceKind { psymbol = 'R' , pname = "ruin" , pfreq = [("battle", 33), ("noise", 50)] , prarity = [(1, 10), (10, 20)] , pcover = CStretch , pfence = FWall , ptopLeft = ["X"] , poverride = [] } collapsed = PlaceKind -- in a dark cave, they have little lights --- that's OK { psymbol = 'c' , pname = "hardware stack" , pfreq = [("noise", 1)] , prarity = [(1, 10), (10, 10)] , pcover = CStretch , pfence = FNone , ptopLeft = [ "#" ] , poverride = [('#', "doorlessWallOver_#")] } collapsed2 = collapsed { pfreq = [("noise", 100), ("battle", 20)] , ptopLeft = [ "X#" , "##" ] } collapsed3 = collapsed { pfreq = [("noise", 200), ("battle", 20)] , ptopLeft = [ "XX#" , "###" ] } collapsed4 = collapsed { pfreq = [("noise", 200), ("battle", 20)] , ptopLeft = [ "XXX#" , "####" ] } collapsed5 = collapsed { pfreq = [("noise", 300), ("battle", 50)] , ptopLeft = [ "XX#" , "X##" , "###" ] } collapsed6 = collapsed { pfreq = [("noise", 400), ("battle", 100)] , ptopLeft = [ "XXX#" , "X###" , "####" ] } collapsed7 = collapsed { pfreq = [("noise", 400), ("battle", 100)] , ptopLeft = [ "XXX#" , "XX##" , "####" ] } pillar = PlaceKind { psymbol = 'p' , pname = "commercial space" , pfreq = [ ("rogue", 500), ("arena", 1000), ("laboratory", 1000) , ("empty", 300), ("noise", 1000) ] , prarity = [(1, 10), (10, 10)] , pcover = CStretch , pfence = FWall -- Larger rooms require support pillars. , ptopLeft = [ "····" , "·#··" , "····" , "····" ] , poverride = [('&', "cache deposit")] } pillar2 = pillar { ptopLeft = [ "··#·" , "··#·" , "##··" , "····" ] } pillar3 = pillar { prarity = [(10, 5)] , ptopLeft = [ "&·#·" , "··#·" , "##··" , "····" ] } pillar4 = pillar { prarity = [(10, 5)] , ptopLeft = [ "&#··" , "#·#·" , "·##·" , "····" ] } colonnade = PlaceKind { psymbol = 'c' , pname = "colonnade" , pfreq = [ ("rogue", 30), ("arena", 70), ("laboratory", 40) , ("empty", 100), ("mine", 10000), ("park", 4000) ] , prarity = [(1, 3), (10, 3)] , pcover = CAlternate , pfence = FFloor , ptopLeft = [ "#·" , "·#" ] , poverride = [] } colonnade2 = colonnade { prarity = [(1, 2), (10, 2)] , ptopLeft = [ "#·" , "··" ] } colonnade3 = colonnade { prarity = [(1, 12), (10, 12)] , ptopLeft = [ "··#" , "·#·" , "#··" ] } colonnade4 = colonnade { prarity = [(1, 12), (10, 12)] , ptopLeft = [ "#··" , "·#·" , "··#" ] } colonnade5 = colonnade { prarity = [(1, 7), (10, 7)] , ptopLeft = [ "#··" , "··#" ] } colonnade6 = colonnade { ptopLeft = [ "#·" , "··" , "·#" ] } lampPost = PlaceKind { psymbol = 'l' , pname = "lamp post" , pfreq = [("park", 20), ("ambush", 20), ("zoo", 10), ("battle", 10)] , prarity = [(1, 10), (10, 10)] , pcover = CVerbatim , pfence = FNone , ptopLeft = [ "X·X" , "·O·" , "X·X" ] , poverride = [('O', "lampPostOver_O"), ('·', "floorActorLit")] } lampPost2 = lampPost { ptopLeft = [ "···" , "·O·" , "···" ] } lampPost3 = lampPost { pfreq = [("park", 3000), ("zoo", 50), ("battle", 110)] , ptopLeft = [ "XX·XX" , "X···X" , "··O··" , "X···X" , "XX·XX" ] } lampPost4 = lampPost { pfreq = [("park", 3000), ("zoo", 50), ("battle", 60)] , ptopLeft = [ "X···X" , "·····" , "··O··" , "·····" , "X···X" ] } treeShade = PlaceKind { psymbol = 't' , pname = "tree shade" , pfreq = [("brawl", 300)] , prarity = [(1, 10), (10, 10)] , pcover = CMirror , pfence = FNone , ptopLeft = [ "··s" , "sO·" , "Xs·" ] , poverride = [ ('O', "treeShadeOver_O_Lit"), ('s', "treeShadeOver_s_Lit") , ('·', "shaded ground") ] } fogClump = PlaceKind { psymbol = 'f' , pname = "foggy patch" , pfreq = [("shootout", 170)] , prarity = [(1, 1)] , pcover = CMirror , pfence = FNone , ptopLeft = [ "f;" , ";f" , ";f" ] , poverride = [('f', "fogClumpOver_f_Lit"), (';', "lit fog")] } fogClump2 = fogClump { pfreq = [("shootout", 400), ("empty", 7000)] , prarity = [(1, 1)] , pcover = CMirror , pfence = FNone , ptopLeft = [ "Xff" , "f;f" , ";;f" , "XfX" ] } smokeClump = PlaceKind { psymbol = 's' , pname = "smoky patch" , pfreq = [("zoo", 50), ("ambush", 50)] , prarity = [(1, 1)] , pcover = CMirror , pfence = FNone , ptopLeft = [ "f;" , ";f" , ";f" ] , poverride = [ ('f', "smokeClumpOver_f_Lit"), (';', "lit smoke") , ('·', "floorActorLit") ] } smokeClump2FGround = smokeClump { pfreq = [("laboratory", 100), ("zoo", 500), ("ambush", 500)] , prarity = [(1, 1)] , pcover = CMirror , pfence = FGround , ptopLeft = [ ";f;" , "f·f" , ";·f" , ";f;" ] } bushClump = PlaceKind { psymbol = 'b' , pname = "bushy patch" , pfreq = [("shootout", 120)] , prarity = [(1, 1)] , pcover = CMirror , pfence = FNone , ptopLeft = [ "f;" , ";f" , ";f" ] , poverride = [('f', "bushClumpOver_f_Lit"), (';', "bush Lit")] } staircase = PlaceKind { psymbol = '|' , pname = "staircase" , pfreq = [("staircase", 1)] , prarity = [(1, 1)] , pcover = CVerbatim , pfence = FGround , ptopLeft = [ "<#>" ] , poverride = [ ('<', "staircase up"), ('>', "staircase down") , ('I', "signboard") ] } staircase2 = staircase { pfreq = [("staircase", 1000)] , pfence = FFloor , ptopLeft = [ "#·#" , "···" , "<#>" , "···" , "#·#" ] } staircase3 = staircaseLift { pfreq = [("staircase lift", 1000)] , pfence = FWall , ptopLeft = [ "#···#" , "·····" , "··" , "·····" , "#···#" ] } staircase4 = staircaseLift { pfreq = [("staircase lift", 1000)] , pfence = FWall , ptopLeft = [ "#·#·#·#" , "·······" , "#·<#>·#" , "·······" , "#·#·#·#" ] } staircase5 = staircase { pfreq = [("staircase", 100)] , pfence = FGround , ptopLeft = [ "#·<#>·#" ] } staircase6 = staircase { pfreq = [("staircase", 100)] , pfence = FGround , ptopLeft = [ "#··<#>··#" ] } staircase7 = staircase { pfreq = [("staircase", 100)] , pfence = FGround , ptopLeft = [ "I·#·<#>·#·I" ] } staircase8 = staircase { pfreq = [("staircase", 1000)] , pfence = FFloor , ptopLeft = [ "#·····#" , "··<#>··" , "#·····#" ] } staircase9 = staircaseLift { pfreq = [("staircase lift", 1000)] , pfence = FWall , ptopLeft = [ "#·······#" , "·#·<#>·#·" , "#·······#" ] } staircase10 = staircase { pfreq = [("staircase", 1000)] , pfence = FFloor , ptopLeft = [ "·#·····#·" , "#··<#>··#" , "·#·····#·" ] } staircase11 = staircase { pfreq = [("staircase", 10000)] , pfence = FGround , ptopLeft = [ "··#·#··" , "#·····#" , "··<#>··" , "#·····#" , "··#·#··" ] } staircase12 = staircaseLift { pfreq = [("staircase lift", 1000)] , pfence = FWall , ptopLeft = [ "·····" , "·<#>·" , "·····" ] } staircase13 = staircaseLift { pfreq = [("staircase lift", 1000)] , pfence = FWall , ptopLeft = [ "·······" , "#·<#>·#" , "·······" ] } staircase14 = staircaseLift { pfreq = [("staircase lift", 1000)] , pfence = FWall , ptopLeft = [ "·········" , "·#·<#>·#·" , "·········" ] } staircase15 = staircaseLift { pfreq = [("staircase lift", 1000)] , pfence = FWall , ptopLeft = [ "···········" , "#·I·<#>·I·#" , "···········" ] } staircase16 = staircaseLift { pfreq = [("staircase lift", 1000)] , pfence = FWall , ptopLeft = [ "#·····#" , "··<#>··" , "#·····#" ] } staircase17 = staircaseLift { pfreq = [("staircase lift", 1000)] , pfence = FWall , ptopLeft = [ "·#·····#·" , "#··<#>··#" , "·#·····#·" ] } staircaseOutdoor = staircase { pname = "staircase outdoor" , pfreq = [("staircase outdoor", 1)] , poverride = [('<', "staircase outdoor up"), ('>', "staircase outdoor down")] } staircaseGated = staircase { pname = "gated staircase" , pfreq = [("gated staircase", 1)] , poverride = [('<', "gated staircase up"), ('>', "gated staircase down")] } escapeUp = PlaceKind { psymbol = '<' , pname = "escape airlock up" , pfreq = [("escape up", 1)] , prarity = [(1, 1)] , pcover = CVerbatim , pfence = FGround , ptopLeft = [ "<" ] , poverride = [] } escapeUp2 = escapeUp { pfreq = [("escape up", 1000)] , pfence = FFloor , ptopLeft = [ "#·#" , "·<·" , "#·#" ] } escapeUp3 = escapeUp { pfreq = [("escape down", 2000)] , pcover = CMirror , pfence = FWall , ptopLeft = [ "#··" , "·<·" , "#·#" ] } escapeUp4 = escapeUp { pfreq = [("escape up", 1000)] , pfence = FFloor , ptopLeft = [ "·#·" , "#<#" , "·#·" ] } escapeUp5 = escapeUp { pfreq = [("escape up", 2000)] , pcover = CMirror , pfence = FWall , ptopLeft = [ "#··" , "·<·" , "··#" ] } escapeDown = PlaceKind { psymbol = '>' , pname = "escape airlock down" , pfreq = [("escape down", 1)] , prarity = [(1, 1)] , pcover = CVerbatim , pfence = FGround , ptopLeft = [ ">" ] , poverride = [] } escapeDown2 = escapeDown { pfreq = [("escape down", 1000)] , pfence = FFloor , ptopLeft = [ "#·#" , "·>·" , "#·#" ] } escapeDown3 = escapeDown { pfreq = [("escape down", 2000)] , pcover = CMirror , pfence = FWall , ptopLeft = [ "#··" , "·>·" , "#·#" ] } escapeDown4 = escapeDown { pfreq = [("escape down", 1000)] , pfence = FFloor , ptopLeft = [ "·#·" , "#>#" , "·#·" ] } escapeDown5 = escapeDown { pfreq = [("escape down", 2000)] , pcover = CMirror , pfence = FWall , ptopLeft = [ "#··" , "·>·" , "··#" ] } escapeOutdoorDown = escapeDown { pfreq = [("escape outdoor down", 1)] , poverride = [('>', "escape outdoor down")] } -- * Allure-specific staircaseLift = staircase { pname = "lift" , pfreq = [("staircase lift", 1)] , poverride = [ ('<', "staircase lift up"), ('>', "staircase lift down") , ('I', "signboard") ] } escapeSpaceshipDown = escapeDown { pfreq = [("escape spaceship down", 1)] , poverride = [('>', "escape spaceship down")] } escapeSpaceshipDown2 = escapeDown2 { pfreq = [("escape spaceship down", 1000)] , poverride = [('>', "escape spaceship down")] } escapeSpaceshipDown3 = escapeDown3 { pfreq = [("escape spaceship down", 2000)] , poverride = [('>', "escape spaceship down")] } escapeSpaceshipDown4 = escapeDown4 { pfreq = [("escape spaceship down", 1000)] , poverride = [('>', "escape spaceship down")] } escapeSpaceshipDown5 = escapeDown5 { pfreq = [("escape spaceship down", 1000)] , poverride = [('>', "escape spaceship down")] } colonnadeWide = colonnade { prarity = [(1, 2), (10, 2)] , pfence = FWall , ptopLeft = [ "··" , "#·" ] } oval = PlaceKind { psymbol = 'o' , pname = "oval room" , pfreq = [ ("rogue", 1000), ("arena", 1000), ("empty", 1000) , ("zoo", 1000), ("ambush", 1000) ] , prarity = [(1, 10), (10, 10)] , pcover = CStretch , pfence = FWall , ptopLeft = [ "####·" , "##···" , "#····" , "#····" , "·····" ] , poverride = [] } ovalFloor = oval -- Without outer solid fence, visible from outside· { pfreq = [ ("rogue", 10000), ("arena", 10000), ("empty", 10000) , ("zoo", 10000), ("ambush", 10000) ] , pfence = FGround , ptopLeft = [ "XXXX+#" , "XX###·" , "X##···" , "X#····" , "+#····" , "#·····" ] } ovalSquare = ovalFloor { pfreq = [ ("rogue", 3000), ("arena", 3000), ("empty", 3000) , ("zoo", 3000) , ("ambush", 3000) ] , ptopLeft = [ "X###+" , "##···" , "#····" , "#····" , "+····" ] } maze = PlaceKind { psymbol = 'm' , pname = "mysterious maze" , pfreq = [("rogue", 20), ("arena", 20), ("empty", 20)] , prarity = [(1, 10), (10, 10)] , pcover = CStretch , pfence = FNone , ptopLeft = [ "·####" , "#·#··" , "##··#" , "#··#·" ] , poverride = [('&', "cache jewelry")] } maze2 = maze { pfreq = [("rogue", 40), ("arena", 40), ("empty", 40)] , ptopLeft = [ "··####" , "##·##·" , "#·#··#" , "#···#·" ] } maze3 = maze { pfreq = [("rogue", 70), ("arena", 70), ("empty", 70)] , ptopLeft = [ "·######" , "·##·##·" , "#··#···" , "#··#··#" ] } mazeBig = maze { pfreq = [("rogue", 500), ("arena", 500), ("empty", 500)] , ptopLeft = [ "··####" , "·#·#··" , "##&·##" , "##·##·" , "#·#··#" ] } mazeBig2 = mazeBig { ptopLeft = [ "··####" , "##·##·" , "#·#&··" , "#·#··#" , "#···#·" ] } mazeBig3 = mazeBig { pfreq = [("rogue", 800), ("arena", 800), ("empty", 800)] , ptopLeft = [ "·######" , "#·##·#·" , "#·#·#··" , "##·##·#" , "#··#···" ] } cells = PlaceKind { psymbol = '#' , pname = "broken holding pens" , pfreq = [ ("rogue", 20), ("arena", 20), (" laboratory", 20) , ("empty", 20), ("noise", 20), ("zoo", 200) ] , prarity = [(1, 10), (10, 10)] , pcover = CReflect , pfence = FWall , ptopLeft = [ "··#" , "·#·" , "#··" ] , poverride = [] } cells2 = cells -- this one is distinct enough from others { pfreq = [ ("rogue", 50), ("arena", 50), (" laboratory", 50) , ("empty", 50), ("noise", 50), ("zoo", 500) ] , ptopLeft = [ "··#" , "··#" , "##·" ] } cells3 = cells { ptopLeft = [ "#··" , "·#·" , "··#" ] } cells4 = cells { ptopLeft = [ "··#" , "·##" , "#··" ] } cells5 = cells { ptopLeft = [ "··#" , "·#·" , "##·" ] } cells6 = cells { pfreq = [ ("rogue", 1), ("arena", 2), ("laboratory", 2) , ("empty", 2), ("noise", 1), ("zoo", 10) ] , ptopLeft = [ "··#" , "##·" ] } cells7 = cells { pfreq = [ ("rogue", 1), ("arena", 2), (" laboratory", 2) , ("empty", 2), ("noise", 1) ] , pfence = FFloor , ptopLeft = [ "#·#" , "·#·" ] } -- * Helper functions makeStaircaseUp :: PlaceKind -> PlaceKind makeStaircaseUp s = s { psymbol = '<' , pname = pname s <+> "up" , pfreq = map (\(t, k) -> (toGroupName $ tshow t <+> "up", k)) $ pfreq s , poverride = ('>', "stair terminal") : filter ((/= '>') . fst) (poverride s) } makeStaircaseDown :: PlaceKind -> PlaceKind makeStaircaseDown s = s { psymbol = '>' , pname = pname s <+> "down" , pfreq = map (\(t, k) -> (toGroupName $ tshow t <+> "down", k)) $ pfreq s , poverride = ('<', "stair terminal") : filter ((/= '<') . fst) (poverride s) } Allure-0.8.3.0/GameDefinition/Content/RuleKind.hs0000644000000000000000000000564513315545742017662 0ustar0000000000000000{-# LANGUAGE TemplateHaskell #-} -- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Game rules and assorted game setup data. module Content.RuleKind ( content ) where import Prelude () import Game.LambdaHack.Common.Prelude import Language.Haskell.TH.Syntax import System.FilePath import System.IO (readFile) -- Cabal import qualified Paths_Allure as Self (getDataFileName, version) import Game.LambdaHack.Content.RuleKind content :: [RuleKind] content = [standard] standard :: RuleKind standard = RuleKind { rsymbol = 's' , rname = "standard Allure of the Stars ruleset" , rfreq = [("standard", 100)] , rtitle = "Allure of the Stars" , rfontDir = $(do x <- qRunIO (Self.getDataFileName "GameDefinition/fonts") lift x) , rexeVersion = Self.version -- The strings containing the default configuration file -- included from config.ui.default. , rcfgUIName = "config.ui" <.> "ini" , rcfgUIDefault = $(do let path = "GameDefinition" "config.ui" <.> "default" qAddDependentFile path x <- qRunIO (readFile path) lift x) -- ASCII art for the main menu. Only pure 7-bit ASCII characters are allowed. -- When displayed in the main menu screen, the picture is overwritten -- with game and engine version strings and keybindings. -- The keybindings overwrite places marked with left curly brace signs. -- This sign is forbidden anywhere else in the picture. -- The picture and the whole main menu is displayed dull white on black. -- The glyphs, or at least the character cells, are perfect squares. -- The picture should be exactly 45 rows by 80 columns. -- For larger screen sizes, the picture is centered and padded with spaces, -- so it makes sense for some or all of the picture borders to be spaces. , rmainMenuArt = $(do let path = "GameDefinition/MainMenu.ascii" qAddDependentFile path x <- qRunIO (readFile path) lift x) , rintroScreen = $(do let path = "GameDefinition/PLAYING.md" qAddDependentFile path x <- qRunIO (readFile path) let paragraphs :: [String] -> [String] -> [[String]] paragraphs [] rows = [reverse rows] paragraphs (l : ls) rows = if null l then reverse rows : paragraphs ls [] else paragraphs ls (l : rows) intro = case paragraphs (lines x) [] of _title : _blurb : par1 : par2 : _rest -> par1 ++ [""] ++ par2 _ -> error "not enough paragraphs in intro screen text" lift intro) , rfirstDeathEnds = False , rwriteSaveClips = 1000 , rleadLevelClips = 50 , rscoresFile = "Allure.scores" , rnearby = 20 } Allure-0.8.3.0/GameDefinition/Content/ItemKind.hs0000644000000000000000000017725513315545742017660 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Item definitions. module Content.ItemKind ( content, items, otherItemContent ) where import Prelude () import Game.LambdaHack.Common.Prelude import Content.ItemKindActor import Content.ItemKindBlast import Content.ItemKindEmbed import Content.ItemKindOrgan import Content.ItemKindTemporary import Game.LambdaHack.Common.Ability import Game.LambdaHack.Common.Color import Game.LambdaHack.Common.Dice import Game.LambdaHack.Common.Flavour import Game.LambdaHack.Common.ItemAspect (Aspect (..), EqpSlot (..)) import Game.LambdaHack.Common.Misc import Game.LambdaHack.Content.ItemKind content :: [ItemKind] content = items ++ otherItemContent otherItemContent :: [ItemKind] otherItemContent = embeds ++ actors ++ organs ++ blasts ++ temporaries items :: [ItemKind] items = [sandstoneRock, dart, spike, spike2, slingStone, slingBullet, paralizingProj, harpoon, harpoon2, net, light1, light2, light3, blanket, flaskTemplate, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, flask18, flask19, flask20, potionTemplate, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, fragmentationBomb, concussionBomb, flashBomb, firecrackerBomb, scrollTemplate, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, jumpingPole, sharpeningTool, seeingItem, motionScanner, gorget, necklaceTemplate, necklace1, necklace2, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, imageItensifier, sightSharpening, ringTemplate, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, armorLeather, armorMail, gloveFencing, gloveGauntlet, gloveJousting, buckler, shield, shield2, shield3, dagger, daggerDropBestWeapon, hammer, hammer2, hammer3, hammerParalyze, hammerSpark, sword, swordImpress, swordNullify, halberd, halberd2, halberd3, halberdPushActor, wandTemplate, wand1, gemTemplate, gem1, gem2, gem3, gem4, gem5, currencyTemplate, currency] -- Allure-specific ++ [needle, constructionHooter, scroll14] sandstoneRock, dart, spike, spike2, slingStone, slingBullet, paralizingProj, harpoon, harpoon2, net, light1, light2, light3, blanket, flaskTemplate, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, flask18, flask19, flask20, potionTemplate, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, fragmentationBomb, concussionBomb, flashBomb, firecrackerBomb, scrollTemplate, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, jumpingPole, sharpeningTool, seeingItem, motionScanner, gorget, necklaceTemplate, necklace1, necklace2, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, imageItensifier, sightSharpening, ringTemplate, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, armorLeather, armorMail, gloveFencing, gloveGauntlet, gloveJousting, buckler, shield, shield2, shield3, dagger, daggerDropBestWeapon, hammer, hammer2, hammer3, hammerParalyze, hammerSpark, sword, swordImpress, swordNullify, halberd, halberd2, halberd3, halberdPushActor, wandTemplate, wand1, gemTemplate, gem1, gem2, gem3, gem4, gem5, currencyTemplate, currency :: ItemKind -- Allure-specific needle, constructionHooter, scroll14 :: ItemKind -- Keep the dice rolls and sides in aspects small so that not too many -- distinct items are generated (for display in item lore and for narrative -- impact ("oh, I found the more powerful of the two variants of the item!", -- instead of "hmm, I found one of the countless variants, a decent one"). -- In particular, for unique items, unless they inherit aspects from -- a standard item, permit only a couple possible variants. -- This is especially important if an item kind has mulitple random aspects. -- Instead multiply dice results, e.g., (1 `d` 3) * 5 instead of 1 `d` 15. -- -- Beware of non-periodic non-weapon durable items with beneficial effects -- and low timeout -- AI will starve applying such an item incessantly. -- * Item group symbols, from Angband and variants symbolProjectile, _symbolLauncher, symbolLight, symbolTool, symbolSpecial, symbolGold, symbolNecklace, symbolRing, symbolPotion, symbolFlask, symbolScroll, symbolTorsoArmor, symbolMiscArmor, _symbolClothes, symbolShield, symbolPolearm, symbolEdged, symbolHafted, symbolWand, _symbolStaff, symbolFood :: Char symbolProjectile = '{' _symbolLauncher = '}' symbolLight = '(' symbolTool = ')' symbolSpecial = '*' -- don't overuse, because it clashes with projectiles symbolGold = '$' -- also gems symbolNecklace = '"' symbolRing = '=' symbolPotion = '!' -- concoction, bottle, jar, vial, canister symbolFlask = '!' symbolScroll = '?' -- book, note, tablet, remote, chip, card symbolTorsoArmor = '[' symbolMiscArmor = '[' _symbolClothes = '[' symbolShield = ']' symbolPolearm = '/' symbolEdged = '|' symbolHafted = '\\' symbolWand = '-' -- magical rod, transmitter, pistol, rifle, instrument _symbolStaff = '_' -- scanner symbolFood = ',' -- also body part; distinct from floor: not middle dot -- * Thrown weapons sandstoneRock = ItemKind { isymbol = symbolProjectile , iname = "ceramic foam splinter" , ifreq = [("sandstone rock", 1), ("weak arrow", 10)] , iflavour = zipPlain [Green] , icount = 1 `d` 2 , irarity = [(1, 50), (10, 1)] , iverbHit = "hit" , iweight = 300 , idamage = 1 `d` 1 , iaspects = [AddHurtMelee $ -16 * 5] , ieffects = [] , ifeature = [toVelocity 70, Fragile] -- not dense, irregular , idesc = "A light, irregular lump of ceramic foam used in construction." , ikit = [] } dart = ItemKind { isymbol = symbolProjectile , iname = "billiard ball" , ifreq = [("common item", 100), ("any arrow", 50), ("weak arrow", 50)] , iflavour = zipPlain [BrWhite] , icount = 4 `dL` 5 , irarity = [(1, 20), (10, 10)] , iverbHit = "strike" , iweight = 170 , idamage = 1 `d` 1 , iaspects = [AddHurtMelee $ (-15 + 1 `d` 2 + 1 `dL` 3) * 5] -- only leather-piercing , ieffects = [] , ifeature = [] , idesc = "Ideal shape, size and weight for throwing." , ikit = [] } spike = ItemKind { isymbol = symbolProjectile , iname = "steak knife" , ifreq = [("common item", 100), ("any arrow", 50), ("weak arrow", 50)] , iflavour = zipPlain [Cyan] , icount = 4 `dL` 5 , irarity = [(1, 10), (10, 15)] , iverbHit = "nick" , iweight = 100 , idamage = 2 `d` 1 , iaspects = [AddHurtMelee $ (-10 + 1 `d` 2 + 1 `dL` 3) * 5] -- heavy vs armor , ieffects = [ Explode "single spark" -- when hitting enemy , OnSmash (Explode "single spark") ] -- at wall hit -- this results in a wordy item synopsis, but it's OK, the spark really -- is useful in some situations, not just a flavour , ifeature = [MinorEffects, toVelocity 70] -- hitting with tip costs speed , idesc = "Not particularly well balanced, but with a laser-sharpened titanium tip and blade." , ikit = [] } spike2 = spike { ifreq = [("common item", 2), ("any arrow", 1), ("weak arrow", 1)] , iweight = 150 , idamage = 4 `d` 1 -- , idesc = "" } slingStone = ItemKind { isymbol = symbolProjectile , iname = "steel hex nut" , ifreq = [("common item", 5), ("any arrow", 100)] , iflavour = zipPlain [Blue] , icount = 3 `dL` 4 , irarity = [(1, 1), (10, 20)] , iverbHit = "hit" , iweight = 200 , idamage = 1 `d` 1 , iaspects = [AddHurtMelee $ (-10 + 1 `d` 2 + 1 `dL` 3) * 5] -- heavy vs armor , ieffects = [ Explode "single spark" -- when hitting enemy , OnSmash (Explode "single spark") ] -- at wall hit , ifeature = [MinorEffects, toVelocity 150] , idesc = "A large hexagonal fastening nut; due to its angular shape, securely lodging in the pouch of a makeshift string and cloth sling." , ikit = [] } slingBullet = ItemKind { isymbol = symbolProjectile , iname = "bearing ball" , ifreq = [("common item", 5), ("any arrow", 100)] , iflavour = zipPlain [White] , icount = 6 `dL` 4 , irarity = [(1, 1), (10, 15)] , iverbHit = "hit" , iweight = 28 , idamage = 1 `d` 1 , iaspects = [AddHurtMelee $ (-17 + 1 `d` 2 + 1 `dL` 3) * 5] -- not armor-piercing , ieffects = [] , ifeature = [toVelocity 200] , idesc = "Small but heavy bearing ball. Thanks to its size and shape, it doesn't snag when released from the makeshift sling's pouch." , ikit = [] } -- * Exotic thrown weapons -- Identified, because shape (and name) says it all. Detailed stats id by use. paralizingProj = ItemKind { isymbol = symbolProjectile , iname = "can" , ifreq = [("common item", 100), ("can of sticky foam", 1)] , iflavour = zipPlain [Magenta] , icount = 1 `dL` 4 , irarity = [(5, 5), (10, 20)] , iverbHit = "glue" , iweight = 1000 , idamage = 1 `d` 1 , iaspects = [AddHurtMelee $ -14 * 5] , ieffects = [Paralyze 15, OnSmash (Explode "glue") ] , ifeature = [ ELabel "of sticky foam" , toVelocity 70, Lobable, Fragile ] -- unwieldy , idesc = "A can of liquid, fast-setting construction foam." , ikit = [] } harpoon = ItemKind { isymbol = symbolProjectile , iname = "harpoon" , ifreq = [("curious item", 100), ("harpoon", 100)] , iflavour = zipPlain [Brown] , icount = 1 `dL` 5 , irarity = [(5, 5), (10, 5)] , iverbHit = "hook" , iweight = 750 , idamage = 5 `d` 1 , iaspects = [AddHurtMelee $ (-10 + 1 `d` 2 + 1 `dL` 3) * 5] , ieffects = [PullActor (ThrowMod 200 50)] , ifeature = [] , idesc = "A display piece harking back to the Earth's oceanic tourism heyday. The cruel, barbed head lodges in its victim so painfully that the weakest tug of the thin line sends the victim flying." , ikit = [] } harpoon2 = harpoon { ifreq = [("curious item", 2), ("harpoon", 2)] , iweight = 1000 , idamage = 10 `d` 1 -- , idesc = "" -- perhaps something modern for a change? some sharpened cargo hook? } net = ItemKind { isymbol = symbolProjectile , iname = "net" , ifreq = [("common item", 100)] , iflavour = zipPlain [White] , icount = 1 `dL` 3 , irarity = [(3, 5), (10, 4)] , iverbHit = "entangle" , iweight = 1000 , idamage = 2 `d` 1 , iaspects = [AddHurtMelee $ -14 * 5] , ieffects = [ toOrganBad "slowed" (3 + 1 `d` 3) , DropItem maxBound 1 CEqp "torso armor" , SendFlying (ThrowMod 100 50) ] -- make the drop painful -- only one of each kind is dropped, because no rubbish in this group , ifeature = [] , idesc = "A large synthetic fibre net with weights affixed along the edges. Entangles armor and restricts movement." , ikit = [] } -- * Lights light1 = ItemKind { isymbol = symbolLight , iname = "torch" , ifreq = [ ("common item", 100), ("light source", 100) , ("wooden torch", 1) ] , iflavour = zipPlain [Brown] , icount = 1 `dL` 4 , irarity = [(1, 15)] , iverbHit = "scorch" , iweight = 1000 , idamage = 0 , iaspects = [ AddShine 3 -- not only flashes, but also sparks, , AddSight (-2) ] -- so unused by AI due to the mixed blessing , ieffects = [Burn 1] , ifeature = [Lobable, Equipable, EqpSlot EqpSlotLightSource] -- not Fragile; reusable flare , idesc = "A puttering torch improvised with polymer sheets soaked in lubricant on a stick." , ikit = [] } light2 = ItemKind { isymbol = symbolLight , iname = "oil lamp" , ifreq = [("common item", 100), ("light source", 100)] , iflavour = zipPlain [BrYellow] , icount = 1 `dL` 2 , irarity = [(6, 8)] , iverbHit = "burn" , iweight = 1500 , idamage = 1 `d` 1 , iaspects = [AddShine 3, AddSight (-1)] , ieffects = [Burn 1, Paralyze 6, OnSmash (Explode "burning oil 3")] , ifeature = [Lobable, Fragile, Equipable, EqpSlot EqpSlotLightSource ] , idesc = "A sizable restaurant glass lamp filled with plant oil feeding a slender wick." , ikit = [] } light3 = ItemKind { isymbol = symbolLight , iname = "crank spotlight" , ifreq = [("common item", 100), ("light source", 100)] , iflavour = zipPlain [BrWhite] , icount = 1 , irarity = [(10, 4)] , iverbHit = "snag" , iweight = 3000 , idamage = 0 , iaspects = [ AddShine 4 , AddArmorRanged $ - 1 `d` 3 ] -- noise and distraction , ieffects = [] , ifeature = [Equipable, EqpSlot EqpSlotLightSource] , idesc = "Powerful, wide-beam spotlight, powered by a hand-crank. Requires noisy two-handed recharging every few minutes." , ikit = [] } blanket = ItemKind { isymbol = symbolLight , iname = "mineral fibre blanket" , ifreq = [("common item", 100), ("light source", 100), ("blanket", 1)] , iflavour = zipPlain [BrBlack] , icount = 1 , irarity = [(1, 3)] , iverbHit = "swoosh" , iweight = 1000 , idamage = 0 , iaspects = [ AddShine (-10) -- douses torch, lamp and lantern in one action , AddArmorMelee 1, AddMaxCalm 2 ] , ieffects = [] , ifeature = [Lobable, Equipable] -- not Fragile; reusable douse , idesc = "" , ikit = [] } -- * Exploding consumables, often intended to be thrown. -- Not identified, because they are perfect for the id-by-use fun, -- due to effects. They are fragile and upon hitting the ground explode -- for effects roughly corresponding to their normal effects. -- Whether to hit with them or explode them close to the tartget -- is intended to be an interesting tactical decision. -- -- Flasks are often not natural; maths, magic, distillery. -- In reality, they just cover all conditions, which in turn matches -- all aspects. -- -- There is no flask nor temporary organ of Calm depletion, -- because Calm reduced often via combat, etc.. flaskTemplate = ItemKind { isymbol = symbolFlask , iname = "flask" , ifreq = [("flask unknown", 1)] , iflavour = zipLiquid darkCol ++ zipPlain darkCol ++ zipFancy darkCol ++ zipLiquid brightCol , icount = 1 `dL` 3 , irarity = [(1, 7), (10, 3)] , iverbHit = "splash" , iweight = 500 , idamage = 0 , iaspects = [] , ieffects = [] , ifeature = [ HideAs "flask unknown", Lobable, Fragile , toVelocity 50 ] -- oily, bad grip , idesc = "A flask of oily liquid of a suspect color. Something seems to be moving inside." , ikit = [] } flask1 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , irarity = [(10, 5)] , ieffects = [ toOrganGood "strengthened" (20 + 1 `d` 5) , toOrganNoTimer "regenerating" , OnSmash (Explode "dense shower") ] , ifeature = [ELabel "of strength renewal brew"] ++ ifeature flaskTemplate } flask2 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , ieffects = [ toOrganBad "weakened" (20 + 1 `d` 5) , OnSmash (Explode "sparse shower") ] , ifeature = [ELabel "of weakness brew"] ++ ifeature flaskTemplate } flask3 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , ieffects = [ toOrganGood "protected from melee" (20 + 1 `d` 5) , OnSmash (Explode "melee protective balm") ] , ifeature = [ELabel "of melee protective balm"] ++ ifeature flaskTemplate } flask4 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , ieffects = [ toOrganGood "protected from ranged" (20 + 1 `d` 5) , OnSmash (Explode "ranged protective balm") ] , ifeature = [ELabel "of ranged protective balm"] ++ ifeature flaskTemplate } flask5 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , ieffects = [ toOrganBad "painted red" (20 + 1 `d` 5) , OnSmash (Explode "red paint") ] , ifeature = [ELabel "of red paint"] ++ ifeature flaskTemplate } flask6 = flaskTemplate { ifreq = [] } flask7 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , icount = 1 -- too poweful en masse , ieffects = [ toOrganGood "hasted" (20 + 1 `d` 5) , OnSmash (Explode "haste spray") ] , ifeature = [ELabel "of haste brew"] ++ ifeature flaskTemplate } flask8 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , irarity = [(1, 12), (10, 2)] , ieffects = [ toOrganBad "slowed" (20 + 1 `d` 5) , toOrganNoTimer "regenerating", toOrganNoTimer "regenerating" -- x2 , RefillCalm 5 , OnSmash (Explode "slowness mist") , OnSmash (Explode "youth sprinkle") ] , ifeature = [ELabel "of lethargy brew"] ++ ifeature flaskTemplate } flask9 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , ieffects = [ toOrganGood "far-sighted" (40 + 1 `d` 10) , OnSmash (Explode "eye drop") ] , ifeature = [ELabel "of eye drops"] ++ ifeature flaskTemplate } flask10 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , irarity = [(10, 2)] -- not very useful right now , ieffects = [ toOrganGood "keen-smelling" (40 + 1 `d` 10) , Detect DetectActor 10 , OnSmash (Explode "smelly droplet") ] , ifeature = [ELabel "of smelly concoction"] ++ ifeature flaskTemplate } flask11 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , irarity = [(10, 2)] -- not very useful right now , ieffects = [ toOrganGood "shiny-eyed" (40 + 1 `d` 10) , OnSmash (Explode "eye shine") ] , ifeature = [ELabel "of cat tears"] ++ ifeature flaskTemplate } flask12 = flaskTemplate { iname = "bottle" , ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , icount = 1 `d` 3 -- the only one sometimes giving away its identity , ieffects = [ toOrganGood "drunk" (20 + 1 `d` 5) , Burn 1, RefillHP 3 , OnSmash (Explode "whiskey spray") ] , ifeature = [ELabel "of whiskey"] ++ ifeature flaskTemplate } flask13 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , icount = 1 , ieffects = [ toOrganGood "drunk" (20 + 1 `d` 5) , Burn 1, RefillHP 3 , Summon "mobile animal" 1 , OnSmash (Summon "mobile animal" 1) , OnSmash Impress , OnSmash (Explode "waste") ] , ifeature = [ELabel "of bait cocktail"] ++ ifeature flaskTemplate } -- The player has full control over throwing the flask at his party, -- so he can milk the explosion, so it has to be much weaker, so a weak -- healing effect is enough. OTOH, throwing a harmful flask at many enemies -- at once is not easy to arrange, so these explostions can stay powerful. flask14 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , irarity = [(1, 2), (10, 10)] , ieffects = [ toOrganNoTimer "regenerating", toOrganNoTimer "regenerating" -- x2 , OnSmash (Explode "youth sprinkle") ] , ifeature = [ELabel "of regeneration brew"] ++ ifeature flaskTemplate } flask15 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , ieffects = [ toOrganNoTimer "poisoned", toOrganNoTimer "poisoned" -- x2 , OnSmash (Explode "poison cloud") ] , ifeature = [ELabel "of poison"] ++ ifeature flaskTemplate } flask16 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , ieffects = [ toOrganNoTimer "poisoned" , OnSmash (Explode "poison cloud") ] , ifeature = [ELabel "of weak poison"] ++ ifeature flaskTemplate } flask17 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , irarity = [(10, 3)] , ieffects = [ toOrganNoTimer "slow resistant" , OnSmash (Explode "anti-slow mist") ] , ifeature = [ELabel "of slow resistance"] ++ ifeature flaskTemplate } flask18 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , irarity = [(10, 3)] , ieffects = [ toOrganNoTimer "poison resistant" , OnSmash (Explode "antidote mist") ] , ifeature = [ELabel "of poison resistance"] ++ ifeature flaskTemplate } flask19 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , ieffects = [ toOrganBad "blind" (40 + 1 `d` 10) , OnSmash (Explode "iron filing") ] , ifeature = [ELabel "of blindness"] ++ ifeature flaskTemplate } flask20 = flaskTemplate { ifreq = [("common item", 100), ("explosive", 100), ("any vial", 100)] , ieffects = [ toOrganNoTimer "poisoned" , toOrganBad "weakened" (20 + 1 `d` 5) , toOrganBad "painted red" (20 + 1 `d` 5) , OnSmash (Explode "glass hail") ] -- enough glass to cause that , ifeature = [ELabel "of calamity"] ++ ifeature flaskTemplate } -- Potions are often natural. Appear deeper than most flasks. Various -- configurations of effects. A different class of effects is on scrolls -- and mechanical items. Some are shared. potionTemplate = ItemKind { isymbol = symbolPotion , iname = "vial" , ifreq = [("potion unknown", 1)] , iflavour = zipLiquid brightCol ++ zipPlain brightCol ++ zipFancy brightCol , icount = 1 `dL` 3 , irarity = [(1, 10), (10, 6)] , iverbHit = "splash" , iweight = 200 , idamage = 0 , iaspects = [] , ieffects = [] , ifeature = [ HideAs "potion unknown", Lobable, Fragile , toVelocity 50 ] -- oily, bad grip , idesc = "A vial of bright, frothing concoction. The best that nature has to offer." , ikit = [] } potion1 = potionTemplate { iname = "vial" , ifreq = [("common item", 100), ("potion", 100), ("any vial", 100)] , icount = 3 `dL` 1 -- very useful, despite appearances , ieffects = [ Impress, RefillCalm (-5) , OnSmash ApplyPerfume, OnSmash (Explode "fragrance") ] , ifeature = [ELabel "of rose water"] ++ ifeature potionTemplate } potion2 = potionTemplate { ifreq = [("curious item", 100)] , icount = 1 , irarity = [(5, 8), (10, 8)] , ieffects = [ Impress, RefillCalm (-20) , OnSmash (Explode "pheromone") ] , ifeature = [Unique, ELabel "of Attraction"] ++ [ Lobable, Fragile -- identified , toVelocity 50 ] -- , idesc = "" } potion3 = potionTemplate { ifreq = [("common item", 100), ("potion", 100), ("any vial", 100)] , ieffects = [ RefillHP 5, DropItem 1 maxBound COrgan "poisoned" , OnSmash (Explode "healing mist") ] } potion4 = potionTemplate { ifreq = [("common item", 100), ("potion", 100), ("any vial", 100)] , irarity = [(1, 6), (10, 9)] , ieffects = [ RefillHP 10, DropItem 1 maxBound COrgan "poisoned" , OnSmash (Explode "healing mist 2") ] } potion5 = potionTemplate -- needs to be common to show at least a portion of effects { ifreq = [("common item", 100), ("potion", 100), ("any vial", 100)] , icount = 3 `dL` 1 -- always as many as possible on this level -- without giving away potion identity , irarity = [(1, 10)] , ieffects = [ OneOf [ RefillHP 10, RefillHP 5, Burn 5 , DropItem 1 maxBound COrgan "poisoned" , toOrganGood "strengthened" (20 + 1 `d` 5) ] , OnSmash (OneOf [ Explode "dense shower" , Explode "sparse shower" , Explode "melee protective balm" , Explode "ranged protective balm" , Explode "red paint" ]) ] } potion6 = potionTemplate -- needs to be common to show at least a portion of effects { ifreq = [("common item", 100), ("potion", 100), ("any vial", 100)] , icount = 3 `dL` 1 , irarity = [(10, 8)] , ieffects = [ Impress , OneOf [ RefillCalm (-60) , RefillHP 20, RefillHP 10, Burn 10 , DropItem 1 maxBound COrgan "poisoned" , toOrganGood "hasted" (20 + 1 `d` 5) ] , OnSmash (OneOf [ Explode "healing mist 2" , Explode "wounding mist" , Explode "distressing odor" , Explode "haste spray" , Explode "slowness mist" , Explode "fragrance" , Explode "violent flash" ]) ] } potion7 = potionTemplate { ifreq = [("common item", 100), ("potion", 100), ("any vial", 100)] , ieffects = [ DropItem 1 maxBound COrgan "poisoned" , OnSmash (Explode "antidote mist") ] } potion8 = potionTemplate { iname = "ampoule" -- filled with semi-stabilized high explosive liquid , ifreq = [("common item", 100), ("potion", 100), ("any vial", 100)] , icount = 3 `dL` 1 , ieffects = [ DropItem 1 maxBound COrgan "condition" , OnSmash (Explode "violent concussion") ] -- not fragmentation nor glass hail, because not enough glass } potion9 = potionTemplate { ifreq = [("curious item", 100)] , icount = 1 , irarity = [(10, 5)] , ieffects = [ RefillHP 60, Impress, RefillCalm (-60) , OnSmash (Explode "healing mist 2") , OnSmash (Explode "pheromone") ] , ifeature = [Unique, ELabel "of Love"] ++ [ Lobable, Fragile -- identified , toVelocity 50 ] -- , idesc = "" } -- * Explosives, with the only effect being @Explode@ fragmentationBomb = ItemKind { isymbol = symbolProjectile , iname = "hand bomb" -- improvised bomb filled with iron pellets, nuts, cut nails; -- deflagration, not detonation, so large mass and hard container -- required not to burn harmlessly; improvised short fuze; -- can't be more powerful or would fracture the spaceship's hull , ifreq = [("common item", 100), ("explosive", 200)] , iflavour = zipPlain [Red] , icount = 1 `dL` 4 -- many, because not very intricate , irarity = [(5, 5), (10, 5)] , iverbHit = "thud" , iweight = 3000 -- low velocity due to weight , idamage = 1 `d` 1 -- heavy and hard , iaspects = [] , ieffects = [ Explode "focused fragmentation" , OnSmash (Explode "violent fragmentation") ] , ifeature = [Lobable, Fragile] , idesc = "" -- given that we now have several kinds of explosives, tell something -- related to 'fragmentation', e.g., mention flying metal bits , ikit = [] } concussionBomb = fragmentationBomb { iname = "canister" -- slightly stabilized liquid explosive in a soft container, hence -- no fragmentation, but huge shock wave despite small size and lack -- of strong container to build up pressure; indoors help the shock wave; -- unstable enough that no fuze required (or simple electric fuse?); -- that's the most potent explosive (a detonating one) to be found -- and only in small quantities, due to depressurization hazard , iflavour = zipPlain [Magenta] , iverbHit = "flap" , iweight = 400 , idamage = 0 , ieffects = [ Explode "focused concussion" , OnSmash (Explode "violent concussion") ] , ifeature = [ Lobable, Fragile , toVelocity 70 ] -- flappy and so slow , idesc = "" } -- Not flashbang, because powerful bang without fragmentation is harder -- to manufacture (requires an oxidizer and steel canister with holes) -- and because we don't model hearing adequately yet. The bang would also -- paralyze and/or lower the movement skill (out of balance due to ear trauma). flashBomb = fragmentationBomb { iname = "powder tube" -- filled with magnesium flash powder , iflavour = zipPlain [BrWhite] , iverbHit = "flash" , iweight = 400 , idamage = 0 , ieffects = [Explode "focused flash", OnSmash (Explode "violent flash")] , ifeature = [Lobable, Fragile, toVelocity 70] -- bad shape for throwing , idesc = "" } firecrackerBomb = fragmentationBomb { iname = "roll" -- not fireworks, as they require outdoors , iflavour = zipPlain [BrMagenta] , irarity = [(1, 5), (5, 5)] -- a toy, if deadly , iverbHit = "crack" -- a pun, matches the verb from "ItemKindBlast" , iweight = 1000 , idamage = 0 , ieffects = [Explode "firecracker", OnSmash (Explode "firecracker")] , ifeature = [Lobable, Fragile] , idesc = "String and paper, concealing a deadly surprise." } -- * Non-exploding consumables, not specifically designed for throwing scrollTemplate = ItemKind { isymbol = symbolScroll , iname = "chip" , ifreq = [("scroll unknown", 1)] , iflavour = zipFancy stdCol ++ zipPlain darkCol -- arcane and old , icount = 1 `dL` 3 , irarity = [(1, 14), (10, 7)] , iverbHit = "thump" , iweight = 20 , idamage = 0 , iaspects = [] , ieffects = [] , ifeature = [ HideAs "scroll unknown" , toVelocity 30 ] -- too small , idesc = "A generic, disposable chip, capable of a one-time holo-display. Some of these also contain a one-time password authorizing a particular spaceship's infrastructure transition. It is unknown how the infrastructure might respond after so many years." , ikit = [] } scroll1 = scrollTemplate { ifreq = [("curious item", 100), ("any scroll", 100)] , icount = 1 , irarity = [(5, 9), (10, 9)] -- mixed blessing, so available early, often , ieffects = [Summon "hero" 1, Summon "mobile animal" (2 + 1 `d` 2)] , ifeature = [Unique, ELabel "of Reckless Beacon"] ++ ifeature scrollTemplate , idesc = "This industrial, wide-spectrum alarm broadcaster, if over-amped for a single powerful blast, should be able to cut through the interference and reach any lost crew members, giving them enough positional information to locate us." } scroll2 = scrollTemplate { ifreq = [("common item", 100), ("any scroll", 100)] , irarity = [(1, 2)] -- mixed blessing , ieffects = [Detect DetectItem 20, Teleport 20, RefillCalm (-100)] , ifeature = [ELabel "of greed"] ++ ifeature scrollTemplate } scroll3 = scrollTemplate { ifreq = [("curious item", 100), ("any scroll", 100)] , irarity = [(1, 4), (10, 2)] , ieffects = [Ascend True] } scroll4 = scrollTemplate -- needs to be common to show at least a portion of effects { ifreq = [("common item", 100), ("any scroll", 100)] , icount = 3 `dL` 1 , irarity = [(1, 14)] , ieffects = [OneOf [ Teleport 5, Paralyze 10, InsertMove 10 , Detect DetectEmbed 12, Detect DetectItem 20 ]] } scroll5 = scrollTemplate -- needs to be common to show at least a portion of effects { ifreq = [("common item", 100), ("any scroll", 100)] , icount = 3 `dL` 1 , irarity = [(10, 11)] , ieffects = [ Impress , OneOf [ Teleport 20, Ascend False, Ascend True , Summon "hero" 1, Summon "mobile animal" $ 1 `d` 2 , Detect DetectAll 40, RefillCalm (-100) , CreateItem CGround "common item" timerNone ] ] } scroll6 = scrollTemplate { ifreq = [("common item", 100), ("any scroll", 100)] , ieffects = [Teleport 5] } scroll7 = scrollTemplate { ifreq = [("common item", 100), ("any scroll", 100)] , ieffects = [Teleport 20] } scroll8 = scrollTemplate { ifreq = [("common item", 100), ("any scroll", 100)] , icount = 1 -- too poweful en masse , irarity = [(10, 4)] , ieffects = [InsertMove $ 1 + 1 `d` 2 + 1 `dL` 2] } scroll9 = scrollTemplate { ifreq = [("common item", 100), ("any scroll", 100)] , icount = 3 `dL` 1 , irarity = [(1, 14)] -- uncommon deep down, where all is known , ieffects = [Composite [Identify, RefillCalm 10]] , ifeature = [ELabel "of scientific explanation"] ++ ifeature scrollTemplate , idesc = "The most pressing existential concerns are met with a deeply satisfying scientific answer." } scroll10 = scrollTemplate { ifreq = [("common item", 100), ("any scroll", 100)] , irarity = [(10, 20)] -- at endgame a crucial item may be missing , ieffects = [Composite [PolyItem, Explode "firecracker"]] , ifeature = [ELabel "of molecular reconfiguration"] ++ ifeature scrollTemplate } scroll11 = scrollTemplate { ifreq = [("curious item", 100), ("any scroll", 100)] , icount = 1 , irarity = [(5, 8), (10, 8)] , ieffects = [Summon "hero" 1] , ifeature = [Unique, ELabel "of Rescue Proclamation"] ++ ifeature scrollTemplate , idesc = "This lock chip opens a nearby closet containing one of our lost crew members." } scroll12 = scrollTemplate { ifreq = [("common item", 100), ("any scroll", 100)] , irarity = [(1, 9), (10, 4)] , ieffects = [Detect DetectHidden 20] } scroll13 = scrollTemplate { ifreq = [("common item", 100), ("any scroll", 100)] , ieffects = [Detect DetectActor 20] , ifeature = [ELabel "of acute hearing"] ++ ifeature scrollTemplate } -- * Assorted tools jumpingPole = ItemKind { isymbol = symbolWand , iname = "jumping pole" , ifreq = [("common item", 100)] , iflavour = zipPlain [White] , icount = 1 , irarity = [(1, 3)] , iverbHit = "prod" , iweight = 10000 , idamage = 0 , iaspects = [Timeout $ (2 + 1 `d` 2 - 1 `dL` 2) * 5] , ieffects = [Recharging (toOrganGood "hasted" 1)] -- safe for AI, because it speeds up, so when AI applies it -- again and again, it gets its time back and is not stuck; -- in total, the explorations speed is unchanged, -- but it's useful when fleeing in the dark to make distance -- and when initiating combat, so it's OK that AI uses it , ifeature = [Durable] , idesc = "Makes you vulnerable at take-off, but then you are free like a bird." , ikit = [] } sharpeningTool = ItemKind { isymbol = symbolTool , iname = "honing steel" , ifreq = [("common item", 100)] , iflavour = zipPlain [Blue] , icount = 1 , irarity = [(10, 10)] , iverbHit = "smack" , iweight = 400 , idamage = 0 , iaspects = [AddHurtMelee $ (1 `dL` 5) * 5] , ieffects = [] , ifeature = [Equipable, EqpSlot EqpSlotAddHurtMelee] , idesc = "Originally used for realigning the chipped or buckled edges of kitchen knives in the local bars. Now it saves lives by letting you fix your weapons between or even during fights, without the need to set up camp, fish out tools and assemble a proper sharpening workshop." , ikit = [] } seeingItem = ItemKind { isymbol = symbolFood , iname = "visual sensor" , ifreq = [("common item", 100)] , iflavour = zipPlain [Red] , icount = 1 , irarity = [(1, 1)] , iverbHit = "gaze at" , iweight = 500 , idamage = 0 , iaspects = [ AddSight 10, AddMaxCalm 30, AddShine 2 , Timeout $ 1 + 1 `d` 2 ] , ieffects = [ Recharging (toOrganNoTimer "poisoned") , Recharging (Summon "mobile robot" 1) ] , ifeature = [Periodic] , idesc = "A functioning visual sensor torn out from some sizable robot. The circuitry seem too large for basic signal processing alone. Watch out for the sharp edges and the seeping coolant liquid." , ikit = [] } motionScanner = ItemKind { isymbol = symbolTool , iname = "handhelp sonar" , ifreq = [("common item", 100), ("add nocto 1", 20)] , iflavour = zipPlain [Green] , icount = 1 , irarity = [(5, 2)] , iverbHit = "ping" , iweight = 1000 , idamage = 0 , iaspects = [ AddNocto 1 , AddArmorMelee (-10 + 1 `dL` 5) , AddArmorRanged (-10 + 1 `dL` 5) ] , ieffects = [] , ifeature = [Equipable, EqpSlot EqpSlotMiscBonus] , idesc = "Handheld underwater echolocator overdriven to scan dark corridors at the cost of emitting loud pings." , ikit = [] } -- * Periodic jewelry gorget = necklaceTemplate { iname = "Old Gorget" , ifreq = [("common item", 25), ("treasure", 25)] , iflavour = zipFancy [BrCyan] -- looks exactly the same as on of necklaces, -- but it's OK, it's an artifact , irarity = [(4, 3), (10, 3)] -- weak, shallow , iaspects = [ Timeout $ (1 `d` 2) * 2 , AddArmorMelee 3 , AddArmorRanged 2 ] , ieffects = [Recharging (RefillCalm 1)] , ifeature = [Unique, Durable, EqpSlot EqpSlotMiscBonus] ++ ifeature necklaceTemplate , idesc = "Highly ornamental, cold, large, steel medallion on a chain. Unlikely to offer much protection as an armor piece, but the old, worn engraving reassures you." } -- Not idenfified, because id by use, e.g., via periodic activations. Fun. necklaceTemplate = ItemKind { isymbol = symbolNecklace , iname = "necklace" , ifreq = [("necklace unknown", 1)] , iflavour = zipFancy stdCol ++ zipPlain brightCol , icount = 1 , irarity = [(10, 2)] , iverbHit = "whip" , iweight = 30 , idamage = 0 , iaspects = [Timeout 1] -- fake, but won't be displayed , ieffects = [] , ifeature = [ Periodic, HideAs "necklace unknown", Precious, Equipable , toVelocity 50 ] -- not dense enough , idesc = "Tingling, rattling chain of flat encrusted links. Eccentric millionaires are known to hide their highly personalized body augmentation packs in such large jewelry pieces." , ikit = [] } necklace1 = necklaceTemplate { ifreq = [("curious item", 100), ("any jewelry", 100)] , irarity = [(3, 0), (4, 1), (10, 2)] -- prevents camping on lvl 3 , iaspects = [Timeout $ (1 `d` 2) * 20] , ieffects = [Recharging (RefillHP 1)] ++ ieffects necklaceTemplate , ifeature = [ Unique, ELabel "of Trickle Life", Durable , EqpSlot EqpSlotMiscBonus ] ++ ifeature necklaceTemplate -- , idesc = "" } necklace2 = necklaceTemplate { ifreq = [("treasure", 100), ("any jewelry", 100)] -- too nasty to call it just a "common item" , iaspects = [Timeout 30] , ieffects = [ Recharging (Summon "mobile animal" $ 1 `d` 2) , Recharging (Explode "waste") , Recharging Impress , Recharging (DropItem 1 maxBound COrgan "condition") ] ++ ieffects necklaceTemplate , ifeature = [Unique, ELabel "of Live Bait", Durable] ++ ifeature necklaceTemplate -- , idesc = "" } necklace3 = necklaceTemplate { ifreq = [("common item", 100), ("any jewelry", 100)] , iaspects = [Timeout $ (1 `d` 2) * 20] , ieffects = [ Recharging (Detect DetectActor 10) , Recharging (RefillCalm (-20)) ] ++ ieffects necklaceTemplate , ifeature = [ELabel "of fearful listening"] ++ ifeature necklaceTemplate } necklace4 = necklaceTemplate { ifreq = [("common item", 100), ("any jewelry", 100)] , iaspects = [Timeout $ (3 + 1 `d` 3 - 1 `dL` 3) * 2] , ieffects = [Recharging (Teleport $ 3 `d` 2)] ++ ieffects necklaceTemplate } necklace5 = necklaceTemplate { ifreq = [("common item", 100), ("any jewelry", 100)] , iaspects = [Timeout $ (7 - 1 `dL` 5) * 10] , ieffects = [ Recharging (Teleport $ 14 + 3 `d` 3) , Recharging (Detect DetectExit 20) , Recharging (RefillHP (-2)) ] -- prevent micromanagement ++ ieffects necklaceTemplate , ifeature = [ELabel "of escape"] ++ ifeature necklaceTemplate } necklace6 = necklaceTemplate { ifreq = [("common item", 100), ("any jewelry", 100)] , iaspects = [Timeout $ 1 + (1 `d` 3) * 2] , ieffects = [Recharging (PushActor (ThrowMod 100 50))] -- 1 step, slow -- the @50@ is only for the case of very light actor, etc. ++ ieffects necklaceTemplate } necklace7 = necklaceTemplate { ifreq = [("curious item", 100), ("any jewelry", 100)] , iaspects = [AddMaxHP 15, AddArmorMelee 20, AddArmorRanged 10, Timeout 4] , ieffects = [ Recharging (InsertMove $ 1 `d` 3) -- unpredictable , Recharging (RefillCalm (-1)) -- fake "hears something" :) , Recharging (RefillHP (-1)) ] ++ ieffects necklaceTemplate , ifeature = [Unique, ELabel "of Overdrive", Durable, EqpSlot EqpSlotAddSpeed] ++ ifeature necklaceTemplate -- , idesc = "" } necklace8 = necklaceTemplate { ifreq = [("common item", 100), ("any jewelry", 100)] , iaspects = [Timeout $ (1 + 1 `d` 3) * 5] , ieffects = [Recharging $ Explode "spark"] ++ ieffects necklaceTemplate } necklace9 = necklaceTemplate { ifreq = [("common item", 100), ("any jewelry", 100)] , iaspects = [Timeout $ (1 + 1 `d` 3) * 5] , ieffects = [Recharging $ Explode "fragrance"] ++ ieffects necklaceTemplate } -- * Non-periodic jewelry imageItensifier = ItemKind { isymbol = symbolRing , iname = "noctovisor" , ifreq = [("treasure", 100), ("add nocto 1", 80)] , iflavour = zipFancy [BrGreen] , icount = 1 , irarity = [(5, 2)] , iverbHit = "rattle" , iweight = 700 , idamage = 0 , iaspects = [AddNocto 1, AddSight (-1), AddArmorMelee $ (1 `dL` 3) * 3] , ieffects = [] , ifeature = [Precious, Durable, Equipable, EqpSlot EqpSlotMiscBonus] , idesc = "Sturdy antique night vision goggles of unknown origin. Wired to run on modern micro-cells." , ikit = [] } sightSharpening = ringTemplate -- small and round, so mistaken for a ring { iname = "Autozoom Contact Lens" , ifreq = [("treasure", 10), ("add sight", 1)] -- it's has to be very rare, because it's powerful and not unique, -- and also because it looks exactly as one of necklaces, so it would -- be misleading when seen on the map , irarity = [(7, 1), (10, 5)] -- low @ifreq@ , iweight = 50 -- heavier that it looks, due to glass , iaspects = [AddSight $ 1 + 1 `d` 2, AddHurtMelee $ (1 `d` 2) * 3] , ifeature = [EqpSlot EqpSlotAddSight] ++ ifeature ringTemplate , idesc = "Zooms on any movement, distant or close. Requires some getting used to. Never needs to be taken off." } -- Don't add standard effects to rings, because they go in and out -- of eqp and so activating them would require UI tedium: looking for -- them in eqp and inv or even activating a wrong item by mistake. -- -- By general mechanisms, due to not having effects that could identify -- them by observing the effect, rings are identified on pickup. -- That's unlike necklaces, which provide the fun of id-by-use, because they -- have effects and when the effects are triggered, they get identified. ringTemplate = ItemKind { isymbol = symbolRing , iname = "ring" , ifreq = [("ring unknown", 1)] , iflavour = zipPlain stdCol ++ zipFancy darkCol , icount = 1 , irarity = [(10, 1)] -- the default very low , iverbHit = "knock" , iweight = 15 , idamage = 0 , iaspects = [] , ieffects = [] , ifeature = [HideAs "ring unknown", Precious, Equipable] , idesc = "A sturdy ring with a softly shining eye. If it contains a body booster unit, beware of the side-effects." , ikit = [] } ring1 = ringTemplate { ifreq = [("common item", 100), ("any jewelry", 100)] , irarity = [(10, 4)] , iaspects = [AddSpeed $ 1 `d` 3, AddMaxHP (-15)] , ieffects = [OnSmash (Explode "distortion")] -- high power , ifeature = [EqpSlot EqpSlotAddSpeed] ++ ifeature ringTemplate } ring2 = ringTemplate { ifreq = [("curious item", 100), ("any jewelry", 100)] , irarity = [(10, 2)] , iaspects = [AddSpeed $ (1 `d` 2) * 3, AddMaxCalm (-40), AddMaxHP (-20)] , ieffects = [OnSmash (Explode "distortion")] -- high power , ifeature = [Unique, ELabel "of Rush", Durable, EqpSlot EqpSlotAddSpeed] ++ ifeature ringTemplate -- , idesc = "" } ring3 = ringTemplate { ifreq = [("common item", 100), ("any jewelry", 100)] , irarity = [(10, 11)] , iaspects = [ AddMaxHP $ 10 + (1 `dL` 5) * 2 , AddMaxCalm $ -20 + (1 `dL` 5) * 2 ] , ifeature = [EqpSlot EqpSlotAddMaxHP] ++ ifeature ringTemplate } ring4 = ringTemplate { ifreq = [("common item", 100), ("any jewelry", 100)] , irarity = [(5, 1), (10, 14)] -- needed after other rings drop Calm , iaspects = [AddMaxCalm $ 25 + (1 `dL` 4) * 5] , ifeature = [EqpSlot EqpSlotMiscBonus] ++ ifeature ringTemplate , idesc = "Cold, solid to the touch, perfectly round, engraved with solemn, strangely comforting, worn out words." } ring5 = ringTemplate { ifreq = [("common item", 100), ("any jewelry", 100)] , irarity = [(3, 3), (10, 6)] , iaspects = [ AddHurtMelee $ (2 + 1 `d` 2 + (1 `dL` 2) * 2 ) * 3 , AddMaxHP $ (-2 - (1 `d` 2) + (1 `dL` 2) * 2) * 3 ] -- !!! , ifeature = [EqpSlot EqpSlotAddHurtMelee] ++ ifeature ringTemplate } ring6 = ringTemplate -- by the time it's found, probably no space in eqp { ifreq = [("common item", 100), ("any jewelry", 100)] , irarity = [(5, 0), (10, 4)] , iaspects = [AddShine $ 1 `d` 2] , ifeature = [EqpSlot EqpSlotLightSource] ++ ifeature ringTemplate , idesc = "A sturdy ring with a large, shining stone." } ring7 = ringTemplate { ifreq = [("common item", 10), ("ring of opportunity sniper", 1) ] , irarity = [(10, 5)] -- low @ifreq@ , iaspects = [AddAbility AbProject 8] , ieffects = [OnSmash (Explode "distortion")] -- high power , ifeature = [ELabel "of opportunity sniper", EqpSlot EqpSlotAbProject] ++ ifeature ringTemplate } ring8 = ringTemplate { ifreq = [("common item", 1), ("ring of opportunity grenadier", 1) ] , irarity = [(1, 1)] , iaspects = [AddAbility AbProject 11] , ieffects = [OnSmash (Explode "distortion")] -- high power , ifeature = [ELabel "of opportunity grenadier", EqpSlot EqpSlotAbProject] ++ ifeature ringTemplate } -- * Armor armorLeather = ItemKind { isymbol = symbolTorsoArmor , iname = "spacesuit breastplate" , ifreq = [("common item", 100), ("torso armor", 1)] , iflavour = zipPlain [Brown] , icount = 1 , irarity = [(1, 9), (10, 3)] , iverbHit = "thud" , iweight = 7000 , idamage = 0 , iaspects = [ AddHurtMelee (-2) , AddArmorMelee $ (2 + 1 `dL` 4) * 5 , AddArmorRanged $ (1 + 1 `dL` 2) * 3 ] , ieffects = [] , ifeature = [Durable, Equipable, EqpSlot EqpSlotAddArmorMelee] , idesc = "A hard-shell torso segment cut from a disposed off spacesuit." , ikit = [] } armorMail = armorLeather { iname = "bulletproof vest" , ifreq = [("common item", 100), ("torso armor", 1), ("armor ranged", 50) ] , iflavour = zipPlain [Cyan] , irarity = [(6, 9), (10, 3)] , iweight = 12000 , idamage = 0 , iaspects = [ AddHurtMelee (-3) , AddArmorMelee $ (2 + 1 `dL` 4) * 5 , AddArmorRanged $ (4 + 1 `dL` 2) * 3 ] , ieffects = [] , ifeature = [Durable, Equipable, EqpSlot EqpSlotAddArmorRanged] , idesc = "A civilian bulletproof vest. Discourages foes from attacking your torso, making it harder for them to land a blow." } gloveFencing = ItemKind { isymbol = symbolMiscArmor , iname = "construction glove" , ifreq = [("common item", 100), ("misc armor", 1), ("armor ranged", 50)] , iflavour = zipPlain [BrYellow] , icount = 1 , irarity = [(5, 9), (10, 9)] , iverbHit = "flap" , iweight = 100 , idamage = 1 `d` 1 , iaspects = [ AddHurtMelee $ (2 + 1 `d` 2 + 1 `dL` 2) * 3 , AddArmorRanged $ (1 `dL` 2) * 3 ] , ieffects = [] , ifeature = [ toVelocity 50 -- flaps and flutters , Durable, Equipable, EqpSlot EqpSlotAddHurtMelee ] , idesc = "A flexible construction glove from rough leather ensuring a good grip. Also, quite effective in deflecting or even catching slow projectiles." , ikit = [] } gloveGauntlet = gloveFencing { iname = "spacesuit glove" , ifreq = [("common item", 100), ("misc armor", 1)] , iflavour = zipPlain [BrCyan] , irarity = [(1, 9), (10, 3)] , iweight = 300 , idamage = 2 `d` 1 , iaspects = [AddArmorMelee $ (1 + 1 `dL` 4) * 5] , ifeature = [Durable, Equipable, EqpSlot EqpSlotAddArmorMelee] , idesc = "A piece of a hull maintenance spacesuit, padded and reinforced with carbon fibre." } gloveJousting = gloveFencing { iname = "Welding Handgear" , ifreq = [("common item", 100), ("misc armor", 1)] , iflavour = zipFancy [BrRed] , irarity = [(1, 3), (10, 3)] , iweight = 3000 , idamage = 3 `d` 1 , iaspects = [ AddHurtMelee $ (-7 + 1 `dL` 5) * 3 , AddArmorMelee $ (2 + 1 `d` 2 + 1 `dL` 2) * 5 , AddArmorRanged $ (1 + 1 `dL` 2) * 3 ] -- very random on purpose and can even be good on occasion , ifeature = [ toVelocity 50 -- flaps and flutters , Unique, Durable, Equipable, EqpSlot EqpSlotAddArmorMelee ] , idesc = "Rigid, bulky handgear embedding a welding equipment, complete with an affixed small shield and a darkened visor. Awe-inspiring." } -- * Shields -- Shield doesn't protect against ranged attacks to prevent -- micromanagement: walking with shield, melee without. -- Note that AI will pick them up but never wear and will use them at most -- as a way to push itself (but they won't recharge, not being in eqp). -- Being @Meleeable@ they will not be use as weapons either. -- This is OK, using shields smartly is totally beyond AI. buckler = ItemKind { isymbol = symbolShield , iname = "buckler" , ifreq = [("common item", 100)] , iflavour = zipPlain [Blue] , icount = 1 , irarity = [(4, 5)] , iverbHit = "bash" , iweight = 2000 , idamage = 2 `d` 1 , iaspects = [ AddArmorMelee 40 -- not enough to compensate; won't be in eqp , AddHurtMelee (-30) -- too harmful; won't be wielded as weapon , Timeout $ (3 + 1 `d` 3 - 1 `dL` 3) * 2 ] , ieffects = [Recharging (PushActor (ThrowMod 100 50))] -- 1 step, slow , ifeature = [ toVelocity 50 -- unwieldy to throw , MinorEffects, Durable, Meleeable , EqpSlot EqpSlotAddArmorMelee ] , idesc = "Heavy and unwieldy arm protection made from an outer airlock panel. Absorbs a percentage of melee damage, both dealt and sustained. Too small to intercept projectiles with." , ikit = [] } shield = buckler { iname = "shield" , irarity = [(8, 4)] -- the stronger variants add to total probability , iflavour = zipPlain [Green] , iweight = 4000 , idamage = 4 `d` 1 , iaspects = [ AddArmorMelee 80 -- not enough to compensate; won't be in eqp , AddHurtMelee (-70) -- too harmful; won't be wielded as weapon , Timeout $ (3 + 1 `d` 3 - 1 `dL` 3) * 4 ] , ieffects = [Recharging (PushActor (ThrowMod 400 25))] -- 1 step, fast , ifeature = [ toVelocity 50 -- unwieldy to throw , MinorEffects, Durable, Meleeable , EqpSlot EqpSlotAddArmorMelee ] , idesc = "Large and unwieldy rectangle made of anti-meteorite ceramic sheet. Absorbs a percentage of melee damage, both dealt and sustained. Too heavy to intercept projectiles with." } shield2 = shield { ifreq = [("common item", 3 * 3)] -- very low base rarity , iweight = 5000 , idamage = 8 `d` 1 -- , idesc = "" } shield3 = shield { ifreq = [("common item", 1 * 3)] -- very low base rarity , iweight = 6000 , idamage = 12 `d` 1 -- , idesc = "" } -- * Weapons dagger = ItemKind { isymbol = symbolEdged , iname = "cleaver" , ifreq = [("common item", 100), ("starting weapon", 100)] , iflavour = zipPlain [BrCyan] , icount = 1 , irarity = [(3 * 10/12, 40), (4 * 10/12, 1)] -- no weapons brought by aliens, initially, so cleaver common , iverbHit = "stab" , iweight = 1000 , idamage = 6 `d` 1 , iaspects = [ AddHurtMelee $ (-1 + 1 `d` 2 + 1 `dL` 2) * 3 , AddArmorMelee $ (1 `d` 2) * 5 ] -- very common, so don't make too random , ieffects = [] , ifeature = [ toVelocity 40 -- ensuring it hits with the tip costs speed , Durable, Meleeable, EqpSlot EqpSlotWeapon ] , idesc = "A heavy professional kitchen blade. Will do fine cutting any kind of meat and bone, as well as parrying blows. Does not penetrate deeply, but is hard to block. Especially useful in conjunction with a larger weapon." , ikit = [] } daggerDropBestWeapon = dagger { iname = "Double Dagger" , ifreq = [("treasure", 20)] , irarity = [(1, 3), (10, 3)] -- Here timeout has to be small, if the player is to count on the effect -- occuring consistently in any longer fight. Otherwise, the effect will be -- absent in some important fights, leading to the feeling of bad luck, -- but will manifest sometimes in fights where it doesn't matter, -- leading to the feeling of wasted power. -- If the effect is very powerful and so the timeout has to be significant, -- let's make it really large, for the effect to occur only once in a fight: -- as soon as the item is equipped, or just on the first strike. -- Here the timeout is either very small or very large, randomly. -- In the latter case the weapon is best swapped for a stronger one -- later on in the game, but provides some variety at the start. , iaspects = iaspects dagger ++ [Timeout $ (1 `d` 2) * 20 - 16] , ieffects = ieffects dagger ++ [ Recharging DropBestWeapon, Recharging $ RefillCalm (-3) ] , ifeature = [Unique] ++ ifeature dagger , idesc = "A knife with a forked blade that a focused fencer can use to catch and twist an opponent's weapon occasionally." } hammer = ItemKind { isymbol = symbolHafted , iname = "demolition hammer" , ifreq = [ ("common item", 100), ("starting weapon", 100) , ("hammer unknown", 1) ] , iflavour = zipFancy [BrMagenta] -- avoid "pink" , icount = 1 , irarity = [(3 * 10/12, 3), (5, 20), (8, 1)] -- don't make it too common on lvl 3 , iverbHit = "club" , iweight = 1600 , idamage = 8 `d` 1 -- we are lying about the dice here, but the dungeon -- is too small and the extra-dice hammers too rare -- to subdivide this identification class by dice , iaspects = [AddHurtMelee $ (-1 + 1 `d` 2 + 1 `dL` 2) * 3] , ieffects = [] , ifeature = [ HideAs "hammer unknown" , toVelocity 40 -- ensuring it hits with the tip costs speed , Durable, Meleeable, EqpSlot EqpSlotWeapon ] , idesc = "One of many kinds of hammers employed in construction work. The ones with completely blunt heads don't cause grave wounds, but any fitted with a long enough handle can shake and bruise even most armored foes. This one looks average at a quick glance." -- if it's really the average, the weak kind, the description stays; if not, it's replaced with one of the descriptions below at identification time , ikit = [] } hammer2 = hammer { ifreq = [("common item", 3), ("starting weapon", 1)] , iweight = 2000 , idamage = 12 `d` 1 , idesc = "Upon closer inspection, this hammer tuns out particularly deadly, with a narrowing, sharpened head and a long handle." } hammer3 = hammer { ifreq = [("common item", 1)] , iweight = 2400 , idamage = 16 `d` 1 , idesc = "This hammer sports a sharp point instead of a head. No armor can withstand a full strength swing." } hammerParalyze = hammer { iname = "Concussion Hammer" , ifreq = [("treasure", 20)] , irarity = [(5, 1), (10, 6)] , idamage = 8 `d` 1 , iaspects = iaspects hammer ++ [Timeout 7] , ieffects = ieffects hammer ++ [Recharging $ Paralyze 10] , ifeature = [Unique] ++ ifeature hammer , idesc = "This exceptionally large demolition hammer leaves no wall and no body standing." } hammerSpark = hammer { iname = "Grand Smithhammer" , ifreq = [("treasure", 20)] , irarity = [(5, 1), (10, 6)] , idamage = 12 `d` 1 , iaspects = iaspects hammer ++ [AddShine 3, Timeout 10] , ieffects = ieffects hammer ++ [Recharging $ Explode "spark"] , ifeature = [Unique] ++ ifeature hammer , idesc = "High carbon steel of this old hammer doesn't yield even to the newest alloys and produces fountains of sparks in defiance." } sword = ItemKind { isymbol = symbolPolearm , iname = "sharpened pipe" , ifreq = [("common item", 100), ("starting weapon", 10)] , iflavour = zipPlain [BrBlue] , icount = 1 , irarity = [(4, 1), (5, 15)] , iverbHit = "slash" , iweight = 2000 , idamage = 10 `d` 1 , iaspects = [] , ieffects = [] , ifeature = [ toVelocity 40 -- ensuring it hits with the tip costs speed , Durable, Meleeable, EqpSlot EqpSlotWeapon ] , idesc = "A makeshift weapon of simple design, but great potential. Hard to master, though." , ikit = [] } swordImpress = sword { isymbol = symbolEdged , iname = "Master's Sword" , ifreq = [("treasure", 20)] , irarity = [(5, 1), (10, 6)] , iaspects = [Timeout $ (1 `d` 2) * 40 - 30] , ieffects = ieffects sword ++ [Recharging Impress] , ifeature = [Unique] ++ ifeature sword -- , idesc = "" } swordNullify = sword { isymbol = symbolEdged , iname = "Gutting Sword" , ifreq = [("treasure", 20)] , irarity = [(5, 1), (10, 6)] , iaspects = [Timeout 10] , ieffects = ieffects sword ++ [ Recharging $ DropItem 1 maxBound COrgan "condition" , Recharging $ RefillCalm (-10) ] , ifeature = [Unique] ++ ifeature sword -- , idesc = "" } halberd = ItemKind { isymbol = symbolPolearm , iname = "pole cleaver" , ifreq = [("common item", 100), ("starting weapon", 20)] , iflavour = zipPlain [BrYellow] , icount = 1 , irarity = [(7, 1), (9, 15)] , iverbHit = "impale" , iweight = 3000 , idamage = 12 `d` 1 , iaspects = [ AddHurtMelee (-20) -- useless against armor at game start , AddArmorMelee $ (1 `dL` 4) * 5 ] , ieffects = [] , ifeature = [ toVelocity 20 -- not balanced , Durable, Meleeable, EqpSlot EqpSlotWeapon ] , idesc = "An improvised but deadly weapon made of a long, sharp kitchen knife glued and bound to a long pole." , ikit = [] } halberd2 = halberd { ifreq = [("common item", 3 * 2), ("starting weapon", 1)] , iweight = 4000 , idamage = 18 `d` 1 -- , idesc = "" } halberd3 = halberd { ifreq = [("common item", 1 * 2)] -- compensating for low base rarity , iweight = 5000 , idamage = 24 `d` 1 -- , idesc = "" } halberdPushActor = halberd { iname = "Swiss Halberd" , ifreq = [("curious item", 20)] , irarity = [(8, 1), (10, 15)] , idamage = 12 `d` 1 , iaspects = iaspects halberd ++ [Timeout $ (1 `d` 2) * 10] , ieffects = ieffects halberd ++ [Recharging (PushActor (ThrowMod 400 25))] -- 1 step , ifeature = [Unique] ++ ifeature halberd , idesc = "A perfect replica made for a reenactor troupe, hardened, missing only some final sharpening. Versatile, with great reach and leverage. Foes are held at a distance." } -- * Wands wandTemplate = ItemKind { isymbol = symbolWand , iname = "injector" , ifreq = [("wand unknown", 1)] , iflavour = zipFancy brightCol , icount = 1 , irarity = [] , iverbHit = "club" , iweight = 300 , idamage = 0 , iaspects = [AddShine 1, AddSpeed (-1)] -- pulsing with power, distracts , ieffects = [] , ifeature = [ HideAs "wand unknown", Durable , toVelocity 125 ] -- sufficiently advanced tech , idesc = "Buzzing with dazzling light that shines even through appendages that handle it." , ikit = [] } wand1 = wandTemplate { ifreq = [] , ieffects = [] -- will be: emit a cone of sound shrapnel that makes enemy cover his ears and so drop '|' and '{' } -- * Treasure gemTemplate = ItemKind { isymbol = symbolGold , iname = "gem" , ifreq = [("gem unknown", 1), ("valuable", 100)] , iflavour = zipPlain $ delete BrYellow brightCol -- natural, so not fancy , icount = 1 , irarity = [(3, 0), (10, 24)] , iverbHit = "tap" , iweight = 50 , idamage = 0 , iaspects = [] , ieffects = [] , ifeature = [HideAs "gem unknown", Precious] , idesc = "Precious, though useless. Worth around 100 gold grains." , ikit = [] } gem1 = gemTemplate { ifreq = [ ("treasure", 100), ("gem", 100), ("any jewelry", 100) , ("valuable", 100) ] , irarity = [(3, 0), (10, 24)] , iaspects = [AddShine 1, AddSpeed (-1)] -- reflects strongly, distracts; so it glows in the dark, -- is visible on dark floor, but not too tempting to wear , ieffects = [RefillCalm (-1)] -- minor effect to ensure no id-on-pickup } gem2 = gem1 { ifreq = [ ("treasure", 100), ("gem", 100), ("any jewelry", 100) , ("valuable", 100) ] , irarity = [(5, 0), (10, 28)] } gem3 = gem1 { ifreq = [ ("treasure", 100), ("gem", 100), ("any jewelry", 100) , ("valuable", 100) ] , irarity = [(7, 0), (10, 32)] } gem4 = gem1 { ifreq = [ ("treasure", 100), ("gem", 100), ("any jewelry", 100) , ("valuable", 100) ] , irarity = [(9, 0), (10, 100)] } gem5 = gem1 { isymbol = symbolSpecial , iname = "stimpack" , ifreq = [ ("treasure", 100), ("gem", 25), ("any jewelry", 10) , ("valuable", 100) ] , iflavour = zipPlain [BrYellow] , irarity = [(1, 40), (10, 40)] , ieffects = [RefillCalm 10, RefillHP 40] , ifeature = [ELabel "of youth", Precious] -- not hidden , idesc = "Calms, heals, invigorates and rejuvenates at the same time. No side-effects. As valuable as precious gems, at 100 gold grains each." } currencyTemplate = ItemKind { isymbol = symbolGold , iname = "gold grain" , ifreq = [("currency unknown", 1), ("valuable", 1)] , iflavour = zipPlain [BrYellow] , icount = 10 + 1 `d` 20 + 1 `dL` 20 , irarity = [(1, 25), (10, 10)] , iverbHit = "tap" , iweight = 1 , idamage = 0 , iaspects = [] , ieffects = [] , ifeature = [HideAs "currency unknown", Precious] , idesc = "Reliably valuable in every civilized place." , ikit = [] } currency = currencyTemplate { ifreq = [("treasure", 100), ("currency", 100), ("valuable", 1)] , iaspects = [AddShine 1, AddSpeed (-1)] , ieffects = [RefillCalm (-1)] } -- * Allure-specific needle = ItemKind { isymbol = symbolProjectile , iname = "needle" , ifreq = [("needle", 1)] -- special; TODO: fast when fired, not thrown , iflavour = zipPlain [BrBlue] , icount = 9 `d` 3 , irarity = [(1, 1)] , iverbHit = "prick" , iweight = 3 , idamage = 1 `d` 1 , iaspects = [AddHurtMelee $ -10 * 5] , ieffects = [] , ifeature = [toVelocity 70, Fragile] , idesc = "A long hypodermic needle ending in a dried out micro-syringe. It's too light to throw hard, but it penetrates deeply, causing intense pain on movement." , ikit = [] } constructionHooter = scrollTemplate { iname = "construction hooter" , ifreq = [("common item", 1), ("construction hooter", 1)] -- extremely rare , iflavour = zipPlain [BrRed] , irarity = [(1, 1)] , iaspects = [] , ieffects = [Summon "construction robot" $ 1 `dL` 2] , ifeature = [] -- not hidden , idesc = "The single-use electronic overdrive hooter that construction robots use to warn about danger and call help in extreme emergency." , ikit = [] } scroll14 = scrollTemplate { ifreq = [("treasure", 100)] , irarity = [(1, 2), (10, 2)] -- not every playthrough needs it , ieffects = [ toOrganGood "resolute" (500 + 1 `d` 200) -- a drawback (at least initially) due to @calmEnough@ , Explode "cruise ad hologram" ] , ifeature = [Unique, ELabel "Displaying a Happy Couple"] ++ ifeature scrollTemplate , idesc = "Biodegradable self-powered mini-projector displaying a holographic ad for an interplanetary cruise." } Allure-0.8.3.0/GameDefinition/Content/ItemKindEmbed.hs0000644000000000000000000003335313315545742020603 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Definitions of items embedded in map tiles. module Content.ItemKindEmbed ( embeds ) where import Prelude () import Game.LambdaHack.Common.Prelude import Game.LambdaHack.Common.Color import Game.LambdaHack.Common.Dice import Game.LambdaHack.Common.Flavour import Game.LambdaHack.Common.ItemAspect (Aspect (..)) import Game.LambdaHack.Common.Misc import Game.LambdaHack.Content.ItemKind embeds :: [ItemKind] embeds = [scratchOnWall, obscenePictogram, subtleFresco, treasureCache, treasureCacheTrap, signboardExit, signboardEmbed, fireSmall, fireBig, frost, rubble, doorwayTrapTemplate, doorwayTrap1, doorwayTrap2, doorwayTrap3, stairsUp, stairsDown, escape, staircaseTrapUp, staircaseTrapDown, pulpit] -- Allure-specific ++ [blackStarrySky, ruinedFirstAidKit, wall3dBillboard, jewelryDisplay, liftUp, liftDown, liftTrap, liftTrap2] scratchOnWall, obscenePictogram, subtleFresco, treasureCache, treasureCacheTrap, signboardExit, signboardEmbed, fireSmall, fireBig, frost, rubble, doorwayTrapTemplate, doorwayTrap1, doorwayTrap2, doorwayTrap3, stairsUp, stairsDown, escape, staircaseTrapUp, staircaseTrapDown, pulpit :: ItemKind -- Allure-specific blackStarrySky, ruinedFirstAidKit, wall3dBillboard, jewelryDisplay, liftUp, liftDown, liftTrap, liftTrap2 :: ItemKind -- Make sure very few walls are substantially useful, e.g., caches, -- and none that are secret. Otherwise the player will spend a lot of time -- bumping walls, which is boring compare to fights or dialogues -- and ever worse, the player will bump all secret walls, wasting time -- and foregoing the fun of guessing how to find entrance to a disjoint part -- of the level by bumping the least number of secret walls. scratchOnWall = ItemKind { isymbol = '?' , iname = "claw mark" , ifreq = [("scratch on wall", 1)] , iflavour = zipPlain [BrBlack] , icount = 1 , irarity = [(1, 1)] , iverbHit = "scratch" , iweight = 1000 , idamage = 0 , iaspects = [] , ieffects = [ Temporary "start making sense of the scratches" , Detect DetectHidden 3 ] , ifeature = [Durable] , idesc = "A seemingly random series of scratches, carved deep into the wall." , ikit = [] } obscenePictogram = ItemKind { isymbol = '*' , iname = "repulsing graffiti" , ifreq = [("obscene pictogram", 1)] , iflavour = zipPlain [BrMagenta] , icount = 1 , irarity = [(1, 1)] , iverbHit = "infuriate" , iweight = 1000 , idamage = 0 , iaspects = [Timeout 7] , ieffects = [ Recharging $ Temporary "enter unexplainable rage at a glimpse of the inscrutable graffiti" , Recharging $ RefillCalm (-20) , Recharging $ OneOf [ toOrganGood "strengthened" (3 + 1 `d` 2) , CreateItem CInv "sandstone rock" timerNone ] ] , ifeature = [Durable] , idesc = "" , ikit = [] } subtleFresco = ItemKind { isymbol = '*' , iname = "subtle mural" , ifreq = [("subtle fresco", 1)] , iflavour = zipPlain [BrGreen] , icount = 1 , irarity = [(1, 1)] , iverbHit = "sooth" , iweight = 1000 , idamage = 0 , iaspects = [Timeout 7] , ieffects = [ Temporary "be entranced by the subtle mural" , RefillCalm 2 , Recharging $ toOrganGood "far-sighted" (3 + 1 `d` 2) , Recharging $ toOrganGood "keen-smelling" (3 + 1 `d` 2) ] , ifeature = [Durable] , idesc = "Expensive yet tasteful." , ikit = [] } treasureCache = stairsUp { isymbol = 'O' , iname = "intact deposit box" , ifreq = [("treasure cache", 1)] , iflavour = zipPlain [BrBlue] , ieffects = [CreateItem CGround "common item" timerNone] , idesc = "" } treasureCacheTrap = ItemKind { isymbol = '^' , iname = "anti-theft protection" , ifreq = [("treasure cache trap", 1)] , iflavour = zipPlain [Red] , icount = 1 , irarity = [(1, 1)] , iverbHit = "taint" , iweight = 1000 , idamage = 0 , iaspects = [] , ieffects = [OneOf [ toOrganBad "blind" (40 + 1 `d` 10) , RefillCalm (-99) , Explode "focused concussion" , RefillCalm (-1), RefillCalm (-1), RefillCalm (-1) , RefillCalm (-1), RefillCalm (-1), RefillCalm (-1) , RefillCalm (-1) ]] , ifeature = [] -- not Durable, springs at most once , idesc = "" , ikit = [] } signboardExit = ItemKind { isymbol = '?' , iname = "sticker" , ifreq = [("signboard", 80)] , iflavour = zipPlain [BrMagenta] , icount = 1 , irarity = [(1, 1)] , iverbHit = "whack" , iweight = 10000 , idamage = 0 , iaspects = [] , ieffects = [Detect DetectExit 100] -- low tech, hence fully operational , ifeature = [Durable] , idesc = "Mandatory emergency exit information in low-tech form." , ikit = [] } signboardEmbed = signboardExit { iname = "notice" , ifreq = [("signboard", 20)] , ieffects = [Detect DetectEmbed 12] -- low tech, hence fully operational , idesc = "Detailed schematics for the maintenance crew." } fireSmall = ItemKind { isymbol = '%' , iname = "small fire" , ifreq = [("small fire", 1)] , iflavour = zipPlain [BrRed] , icount = 1 , irarity = [(1, 1)] , iverbHit = "burn" , iweight = 10000 , idamage = 0 , iaspects = [] , ieffects = [Burn 1, Explode "single spark"] , ifeature = [Durable] , idesc = "A few small logs, burning brightly." , ikit = [] } fireBig = fireSmall { isymbol = 'O' , iname = "big fire" , ifreq = [("big fire", 1)] , ieffects = [ Burn 2, Explode "spark" , CreateItem CInv "wooden torch" timerNone ] , ifeature = [Durable] , idesc = "Glowing with light and warmth." , ikit = [] } frost = ItemKind { isymbol = '^' , iname = "frost" , ifreq = [("frost", 1)] , iflavour = zipPlain [BrBlue] , icount = 1 , irarity = [(1, 1)] , iverbHit = "burn" , iweight = 10000 , idamage = 0 , iaspects = [] , ieffects = [ Burn 1 -- sensory ambiguity between hot and cold , RefillCalm 20 -- cold reason , PushActor (ThrowMod 100 50) ] -- slippery ice, 1 step, slow , ifeature = [Durable] , idesc = "Intricate patterns of shining ice." , ikit = [] } rubble = ItemKind { isymbol = '&' , iname = "rubble" , ifreq = [("rubble", 1)] , iflavour = zipPlain [BrYellow] , icount = 1 , irarity = [(1, 1)] , iverbHit = "bury" , iweight = 100000 , idamage = 0 , iaspects = [] , ieffects = [OneOf [ Explode "focused glass hail" , Summon "animal" $ 1 `dL` 2 , toOrganNoTimer "poisoned" , CreateItem CGround "common item" timerNone , RefillCalm (-1), RefillCalm (-1), RefillCalm (-1) , RefillCalm (-1), RefillCalm (-1), RefillCalm (-1) ]] , ifeature = [Durable] , idesc = "Broken chunks of foam concrete and glass." , ikit = [] } doorwayTrapTemplate = ItemKind { isymbol = '+' , iname = "doorway trap" , ifreq = [("doorway trap unknown", 1)] , iflavour = zipPlain brightCol , icount = 1 , irarity = [(1, 1)] , iverbHit = "cripple" , iweight = 10000 , idamage = 0 , iaspects = [] , ieffects = [] , ifeature = [HideAs "doorway trap unknown"] -- not Durable, springs at most once , idesc = "Just turn the handle..." , ikit = [] } doorwayTrap1 = doorwayTrapTemplate { ifreq = [("doorway trap", 50)] , ieffects = [toOrganBad "blind" $ (1 `dL` 4) * 10] -- , idesc = "" } doorwayTrap2 = doorwayTrapTemplate { ifreq = [("doorway trap", 25)] , ieffects = [toOrganBad "slowed" $ (1 `dL` 4) * 10] -- , idesc = "" } doorwayTrap3 = doorwayTrapTemplate { ifreq = [("doorway trap", 25)] , ieffects = [toOrganBad "weakened" $ (1 `dL` 4) * 10 ] -- , idesc = "" } stairsUp = ItemKind { isymbol = '<' , iname = "flight of steps" , ifreq = [("staircase up", 1)] , iflavour = zipPlain [BrWhite] , icount = 1 , irarity = [(1, 1)] , iverbHit = "crash" -- the verb is only used when the item hits, -- not when it's applied otherwise, e.g., from tile , iweight = 100000 , idamage = 0 , iaspects = [] , ieffects = [Ascend True] , ifeature = [Durable] , idesc = "Stairs that rise towards the spaceship core." , ikit = [] } stairsDown = stairsUp { isymbol = '>' , iname = "flight of steps" , ifreq = [("staircase down", 1)] , ieffects = [Ascend False] , idesc = "Stairs that descend towards the outer ring." } escape = stairsUp { isymbol = 'E' , iname = "way" , ifreq = [("escape", 1)] , iflavour = zipPlain [BrYellow] , ieffects = [Escape] , idesc = "" -- generic: moon outdoors, in spaceship, everywhere } staircaseTrapUp = ItemKind { isymbol = '^' , iname = "staircase trap" , ifreq = [("staircase trap up", 1)] , iflavour = zipPlain [Red] , icount = 1 , irarity = [(1, 1)] , iverbHit = "buffet" , iweight = 10000 , idamage = 0 , iaspects = [] , ieffects = [ Temporary "be caught in decompression blast" , Teleport $ 3 + 1 `dL` 10 ] , ifeature = [] -- not Durable, springs at most once , idesc = "" , ikit = [] } -- Needs to be separate from staircaseTrapUp, to make sure the item is -- registered after up staircase (not only after down staircase) -- so that effects are invoked in the proper order and, e.g., teleport works. staircaseTrapDown = staircaseTrapUp { ifreq = [("staircase trap down", 1)] , iverbHit = "open up under" , ieffects = [ Temporary "tumble down the stairwell" , toOrganGood "drunk" (20 + 1 `d` 5) ] , idesc = "A treacherous slab, to teach those who are too proud." } pulpit = ItemKind { isymbol = '?' , iname = "VR harness" , ifreq = [("pulpit", 1)] , iflavour = zipFancy [BrYellow] , icount = 1 , irarity = [(1, 1)] , iverbHit = "immerse" , iweight = 10000 , idamage = 0 , iaspects = [] , ieffects = [ OneOf [ CreateItem CGround "any scroll" timerNone , Detect DetectAll 20 , Paralyze $ (1 `dL` 6) * 10 , toOrganGood "drunk" (20 + 1 `d` 5) ] , Explode "story-telling" ] , ifeature = [] -- not Durable, springs at most once , idesc = "" , ikit = [] } -- * Allure-specific blackStarrySky = ItemKind { isymbol = ' ' , iname = "black starry sky" , ifreq = [("black starry sky", 1)] , iflavour = zipPlain [Black] , icount = 1 , irarity = [(1, 1)] , iverbHit = "awe" , iweight = 1000 , idamage = 0 , iaspects = [] , ieffects = [ Temporary "look into the void and it looks back" , OneOf [RefillCalm 5, RefillCalm (-5)] ] , ifeature = [Durable] , idesc = "Occasionally a planet zips by, but is unable to disperse the blackness. The black starscape is constantly rotating. The frantic dance is silent, muted, indifferent. There is not even a hint of vibration, just the sense of heaviness and dizziness. At the outermost deck, the curvature of the floor is unnoticeable, but artificial gravity apparently stronger than on Earth." -- appears only on 100% flavour tiles, useless and trivial to notice, so the writeup can be longer , ikit = [] } ruinedFirstAidKit = ItemKind { isymbol = '?' , iname = "ruined first aid kit" , ifreq = [("ruined first aid kit", 1)] , iflavour = zipPlain [BrGreen] , icount = 1 , irarity = [(1, 1)] , iverbHit = "prick" , iweight = 1000 , idamage = 0 , iaspects = [] , ieffects = [ Temporary "inspect a tattered CPR instruction soaked in a residue of oily drugs" , OneOf [ toOrganNoTimer "poison resistant" , toOrganNoTimer "slow resistant" , toOrganGood "drunk" (20 + 1 `d` 5) ] , CreateItem CInv "needle" timerNone ] , ifeature = [] -- not Durable, springs at most once , idesc = "" -- regulations require , ikit = [] } wall3dBillboard = ItemKind { isymbol = '*' , iname = "3D display" , ifreq = [("3D display", 1)] , iflavour = zipPlain [BrGreen] , icount = 1 , irarity = [(1, 1)] , iverbHit = "push" , iweight = 1000 , idamage = 0 , iaspects = [Timeout 3] , ieffects = [ Recharging $ Temporary "make it cough up a wobbly standalone hologram once more" , Recharging $ OneOf [ Explode "advertisement" , Explode "story-telling" ] ] , ifeature = [Durable] , idesc = "One can still make out excited moves of bleached shapes." , ikit = [] } jewelryDisplay = treasureCache { iname = "jewelry case" , ifreq = [("jewelry case", 1)] , ieffects = [CreateItem CGround "any jewelry" timerNone] , idesc = "" } liftUp = stairsUp { iname = "carriage" , ifreq = [("lift up", 1)] , idesc = "" } liftDown = stairsDown { iname = "carriage" , ifreq = [("lift down", 1)] , idesc = "" } liftTrap = staircaseTrapUp { iname = "elevator trap" -- hat tip to US heroes , ifreq = [("lift trap", 100)] , iverbHit = "squeeze" , ieffects = [ Temporary "be crushed by the sliding doors" , DropBestWeapon, Paralyze 10 ] , idesc = "" } liftTrap2 = liftTrap { ifreq = [("lift trap", 50)] , iverbHit = "choke" , ieffects = [ Temporary "inhale the gas lingering inside the cab" , toOrganBad "slowed" $ (1 `dL` 4) * 10 ] , idesc = "" } Allure-0.8.3.0/GameDefinition/Content/ModeKindPlayer.hs0000644000000000000000000001315013315545742021002 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Basic players definitions. module Content.ModeKindPlayer ( playerHero, playerAntiHero, playerCivilian , playerMonster, playerAntiMonster, playerAnimal , playerHorror, playerMonsterTourist, playerHunamConvict , playerAnimalMagnificent, playerAnimalExquisite , hiHero, hiDweller, hiRaid, hiEscapist , playerRobot ) where import Prelude () import Game.LambdaHack.Common.Prelude import Game.LambdaHack.Common.Ability import Game.LambdaHack.Common.Misc import Game.LambdaHack.Content.ModeKind playerHero, playerAntiHero, playerCivilian, playerMonster, playerAntiMonster, playerAnimal, playerHorror, playerMonsterTourist, playerHunamConvict, playerAnimalMagnificent, playerAnimalExquisite :: Player playerRobot :: Player playerHero = Player { fname = "Spacefarer" , fgroups = ["hero"] , fskillsOther = meleeAdjacent , fcanEscape = True , fneverEmpty = True , fhiCondPoly = hiHero , fhasGender = True , ftactic = TExplore , fleaderMode = LeaderUI $ AutoLeader False False , fhasUI = True } playerAntiHero = playerHero { fleaderMode = LeaderAI $ AutoLeader True False , fhasUI = False } playerCivilian = Player { fname = "Civilian" , fgroups = ["hero", "civilian"] , fskillsOther = zeroSkills -- not coordinated by any leadership , fcanEscape = False , fneverEmpty = True , fhiCondPoly = hiDweller , fhasGender = True , ftactic = TPatrol , fleaderMode = LeaderNull -- unorganized , fhasUI = False } playerMonster = Player { fname = "Alien Hierarchy" , fgroups = ["monster", "mobile monster"] , fskillsOther = zeroSkills , fcanEscape = False , fneverEmpty = False , fhiCondPoly = hiDweller , fhasGender = False , ftactic = TExplore , fleaderMode = -- No point changing leader on level, since all move and they -- don't follow the leader. LeaderAI $ AutoLeader True True , fhasUI = False } playerAntiMonster = playerMonster { fhasUI = True , fleaderMode = LeaderUI $ AutoLeader True True } playerAnimal = Player { fname = "Animal Kingdom" , fgroups = ["animal", "mobile animal", "immobile animal", "scavenger"] , fskillsOther = zeroSkills , fcanEscape = False , fneverEmpty = False , fhiCondPoly = hiDweller , fhasGender = False , ftactic = TRoam -- can't pick up, so no point exploring , fleaderMode = LeaderNull , fhasUI = False } -- | A special player, for summoned actors that don't belong to any -- of the main players of a given game. E.g., animals summoned during -- a brawl game between two hero factions land in the horror faction. -- In every game, either all factions for which summoning items exist -- should be present or a horror player should be added to host them. playerHorror = Player { fname = "Horror Den" , fgroups = [nameOfHorrorFact] , fskillsOther = zeroSkills , fcanEscape = False , fneverEmpty = False , fhiCondPoly = [] , fhasGender = False , ftactic = TPatrol -- disoriented , fleaderMode = LeaderNull , fhasUI = False } playerMonsterTourist = playerAntiMonster { fname = "Alien Tourist Office" , fcanEscape = True , fneverEmpty = True -- no spawning , fhiCondPoly = hiEscapist , ftactic = TFollow -- follow-the-guide, as tourists do , fleaderMode = LeaderUI $ AutoLeader False False } playerHunamConvict = playerCivilian { fname = "Hunam Convict" , fleaderMode = LeaderAI $ AutoLeader True False } playerAnimalMagnificent = playerAnimal { fname = "Animal Magnificent Specimen Variety" , fneverEmpty = True , fleaderMode = -- False to move away from stairs LeaderAI $ AutoLeader True False } playerAnimalExquisite = playerAnimal { fname = "Animal Exquisite Herds and Packs Galore" , fneverEmpty = True } victoryOutcomes :: [Outcome] victoryOutcomes = [Conquer, Escape] hiHero, hiRaid, hiDweller, hiEscapist :: HiCondPoly -- Heroes rejoice in loot. hiHero = [ ( [(HiLoot, 1000)] -- multiplied by fraction of collected , [minBound..maxBound] ) , ( [(HiConst, 1000), (HiLoss, -1)] , victoryOutcomes ) ] hiRaid = [ ( [(HiLoot, 100)] , [minBound..maxBound] ) , ( [(HiConst, 100)] , victoryOutcomes ) ] -- Spawners or skirmishers get no points from loot, but try to kill -- all opponents fast or at least hold up for long. hiDweller = [ ( [(HiConst, 1000)] -- no loot, so big win reward , victoryOutcomes ) , ( [(HiConst, 1000), (HiLoss, -10)] , victoryOutcomes ) , ( [(HiBlitz, -100)] -- speed matters , victoryOutcomes ) , ( [(HiSurvival, 100)] , [minBound..maxBound] \\ victoryOutcomes ) ] hiEscapist = ( [(HiLoot, 200)] -- loot matters a little bit , [minBound..maxBound] ) : hiDweller -- Allure-specific playerRobot = Player { fname = "Robot Anarchy" , fgroups = ["robot", "mobile robot", "immobile robot", "construction robot"] , fskillsOther = zeroSkills , fcanEscape = False , fneverEmpty = False , fhiCondPoly = hiDweller , fhasGender = False , ftactic = TRoam -- TODO:TFollow -- coordinated via net, follow alien leader , fleaderMode = LeaderNull , fhasUI = False } Allure-0.8.3.0/GameDefinition/Content/TileKind.hs0000644000000000000000000005667313315545742017657 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Terrain tile definitions. module Content.TileKind ( content ) where import Prelude () import Game.LambdaHack.Common.Prelude import qualified Data.Text as T import Game.LambdaHack.Common.Color import Game.LambdaHack.Common.Misc import Game.LambdaHack.Content.TileKind content :: [TileKind] content = [unknown, unknownOuterFence, basicOuterFence, wall, wallSuspect, wallObscured, wallObscuredDefaced, wallObscuredFrescoed, pillar, pillarCache, lampPost, signboardUnread, signboardRead, tree, treeBurnt, treeBurning, rubble, rubbleSpice, doorTrapped, doorClosed, stairsUp, stairsTrappedUp, stairsOutdoorUp, stairsGatedUp, stairsDown, stairsTrappedDown, stairsOutdoorDown, stairsGatedDown, escapeUp, escapeDown, escapeOutdoorDown, wallGlass, wallGlassSpice, pillarIce, pulpit, bush, bushBurnt, bushBurning, floorFog, floorFogDark, floorSmoke, floorSmokeDark, doorOpen, floorCorridor, floorArena, floorNoise, floorDirt, floorDirtSpice, floorActor, floorActorItem, floorRed, floorBlue, floorGreen, floorBrown, floorArenaShade ] ++ map makeDarkColor ldarkColorable -- Allure-specific ++ [oriel, outerHullWall, doorlessWall, machineWall, rubbleSpiceBurning, wallObscuredSafety, wallObscured3dBillboard, rock, pillarCache2, stairsLiftUp, stairsLiftTrappedUp, stairsLiftDown, stairsLiftTrappedDown, escapeSpaceshipDown, floorWindow] unknown, unknownOuterFence, basicOuterFence, wall, wallSuspect, wallObscured, wallObscuredDefaced, wallObscuredFrescoed, pillar, pillarCache, lampPost, signboardUnread, signboardRead, tree, treeBurnt, treeBurning, rubble, rubbleSpice, doorTrapped, doorClosed, stairsUp, stairsTrappedUp, stairsOutdoorUp, stairsGatedUp, stairsDown, stairsTrappedDown, stairsOutdoorDown, stairsGatedDown, escapeUp, escapeDown, escapeOutdoorDown, wallGlass, wallGlassSpice, pillarIce, pulpit, bush, bushBurnt, bushBurning, floorFog, floorFogDark, floorSmoke, floorSmokeDark, doorOpen, floorCorridor, floorArena, floorNoise, floorDirt, floorDirtSpice, floorActor, floorActorItem, floorRed, floorBlue, floorGreen, floorBrown, floorArenaShade :: TileKind -- Allure-specific oriel, outerHullWall, doorlessWall, machineWall, rubbleSpiceBurning, wallObscuredSafety, wallObscured3dBillboard, rock, pillarCache2, stairsLiftUp, stairsLiftTrappedUp, stairsLiftDown, stairsLiftTrappedDown, escapeSpaceshipDown, floorWindow :: TileKind ldarkColorable :: [TileKind] ldarkColorable = [tree, bush, floorCorridor, floorArena, floorNoise, floorDirt, floorDirtSpice, floorActor, floorActorItem, floorWindow] -- Symbols to be used: -- LOS noLOS -- Walk .' :; -- noWalk %^ #O&<>+ -- -- can be opened ^&+ -- can be closed ' -- some noWalk can be changed without opening, regardless of symbol -- not used yet: -- ~ (water, acid, ect.) -- : (curtain, etc., not flowing, but solid and static) -- ` (not visible enough, would need font modification) -- Note that for AI hints and UI comfort, most multiple-use @Embed@ tiles -- should have a variant, which after first use transforms into a different -- colour tile without @ChangeTo@ and similar (which then AI no longer touches). -- If a tile is supposed to be repeatedly activated by AI (e.g., cache), -- it should keep @ChangeTo@ for the whole time. -- * Main tiles, modified for Allure; some removed -- ** Not walkable -- *** Not clear unknown = TileKind -- needs to have index 0 and alter 1 { tsymbol = ' ' , tname = "unknown space" , tfreq = [("unknown space", 1)] , tcolor = defFG , tcolor2 = defFG , talter = 1 , tfeature = [Dark] } unknownOuterFence = TileKind { tsymbol = ' ' , tname = "unknown space" , tfreq = [("unknown outer fence", 1)] , tcolor = defFG , tcolor2 = defFG , talter = maxBound -- impenetrable , tfeature = [Dark] } basicOuterFence = TileKind { tsymbol = '#' , tname = "habitat containment wall" , tfreq = [("basic outer fence", 1)] , tcolor = BrBlack , tcolor2 = BrBlack , talter = maxBound -- impenetrable , tfeature = [] } wall = TileKind { tsymbol = '#' , tname = "wall" , tfreq = [ ("fillerWall", 1), ("legendLit", 100), ("legendDark", 100) , ("cachable deposit", 80), ("cachable jewelry", 80) , ("stair terminal", 30) , ("battleSet", 250), ("rectWindowsOver_%_Lit", 80) ] , tcolor = BrWhite , tcolor2 = defFG , talter = 100 , tfeature = [BuildAs "suspect wall"] } wallSuspect = TileKind -- only on client { tsymbol = '#' , tname = "suspect wall" , tfreq = [("suspect wall", 1)] , tcolor = BrWhite , tcolor2 = defFG , talter = 2 , tfeature = [ RevealAs "trapped door" , ObscureAs "obscured wall" ] } wallObscured = TileKind { tsymbol = '#' , tname = "scratched wall" , tfreq = [("obscured wall", 100)] , tcolor = BrWhite , tcolor2 = defFG , talter = 5 , tfeature = [ Embed "scratch on wall" , HideAs "suspect wall" ] } wallObscuredDefaced = TileKind { tsymbol = '#' , tname = "defaced wall" , tfreq = [("obscured wall", 45), ("escapeSetDark", 1)] , tcolor = BrWhite , tcolor2 = defFG , talter = 5 , tfeature = [ Embed "obscene pictogram" , HideAs "suspect wall" ] } wallObscuredFrescoed = TileKind { tsymbol = '#' , tname = "subtle mural" , tfreq = [("obscured wall", 5), ("brawlSetLit", 1)] , tcolor = BrWhite , tcolor2 = defFG , talter = 5 , tfeature = [ Embed "subtle fresco" , HideAs "suspect wall" ] -- a bit beneficial, but AI would loop if allowed to trigger -- so no @ConsideredByAI@ } pillar = TileKind { tsymbol = 'O' , tname = "construction beam" , tfreq = [("legendLit", 100), ("legendDark", 100)] , tcolor = BrCyan -- not BrWhite, to tell from heroes , tcolor2 = Cyan , talter = 100 , tfeature = [] } pillarCache = TileKind { tsymbol = '#' , tname = "rack of deposit boxes" , tfreq = [ ("cachable deposit", 20), ("cache deposit", 1) , ("stair terminal", 1) ] , tcolor = BrBlue , tcolor2 = Blue , talter = 5 , tfeature = [ Embed "treasure cache", Embed "treasure cache trap" , ChangeTo "cachable deposit", ConsideredByAI ] -- Not explorable, but prominently placed, so hard to miss. -- Very beneficial, so AI eager to trigger, unless wary of traps. } lampPost = TileKind { tsymbol = 'O' , tname = "lamp post" , tfreq = [("lampPostOver_O", 1)] , tcolor = BrYellow , tcolor2 = Brown , talter = 100 , tfeature = [] } signboardUnread = TileKind -- client only, indicates never used by this faction { tsymbol = 'O' , tname = "signboard" , tfreq = [("signboard unread", 1)] , tcolor = BrCyan , tcolor2 = Cyan , talter = 5 , tfeature = [ ConsideredByAI -- changes after use, so safe for AI , RevealAs "signboard" -- to display as hidden ] } signboardRead = TileKind { tsymbol = 'O' , tname = "signboard" , tfreq = [("signboard", 1), ("escapeSetDark", 1)] , tcolor = BrCyan , tcolor2 = Cyan , talter = 5 , tfeature = [Embed "signboard", HideAs "signboard unread"] } tree = TileKind { tsymbol = 'O' , tname = "tree" , tfreq = [ ("brawlSetLit", 140), ("shootoutSetLit", 10) , ("escapeSetLit", 35), ("treeShadeOver_O_Lit", 1) ] , tcolor = BrGreen , tcolor2 = Green , talter = 50 , tfeature = [] } treeBurnt = tree { tname = "burnt tree" , tfreq = [("zooSet", 7), ("tree with fire", 30)] , tcolor = BrBlack , tcolor2 = BrBlack , tfeature = Dark : tfeature tree } treeBurning = tree { tname = "burning tree" , tfreq = [("zooSet", 40), ("tree with fire", 70)] , tcolor = BrRed , tcolor2 = Red , talter = 5 , tfeature = Embed "big fire" : ChangeTo "tree with fire" : tfeature tree -- dousing off the tree will have more sense when it periodically -- explodes, hitting and lighting up the team and so betraying it } rubble = TileKind { tsymbol = '&' , tname = "rubble pile" , tfreq = [] -- [("floorCorridorLit", 1)] -- disabled while it's all or nothing per cave and per room; -- we need a new mechanism, Spice is not enough, because -- we don't want multicolor trailLit corridors -- ("rubbleOrNot", 70) -- until we can sync change of tile and activation, it always takes 1 turn , tcolor = BrYellow , tcolor2 = Brown , talter = 4 -- boss can dig through , tfeature = [OpenTo "rubbleOrNot", Embed "rubble"] } rubbleSpice = TileKind { tsymbol = '&' , tname = "rubble pile" , tfreq = [ ("rubbleSpice", 1), ("smokeClumpOver_f_Lit", 1) , ("emptySet", 1), ("emptyExitSet", 1), ("noiseSet", 9) , ("zooSet", 100), ("ambushSet", 18), ("stair terminal", 30) ] , tcolor = BrYellow , tcolor2 = Brown , talter = 4 -- boss can dig through , tfeature = [Spice, OpenTo "rubbleSpiceOrNot", Embed "rubble"] -- It's not explorable, due to not being walkable nor clear and due -- to being a door (@OpenTo@), which is kind of OK, because getting -- the item is risky and, e.g., AI doesn't attempt it. -- Also, AI doesn't go out of its way to clear the way for heroes. } doorTrapped = TileKind { tsymbol = '+' , tname = "trapped door" , tfreq = [("trapped door", 1)] , tcolor = BrRed , tcolor2 = Red , talter = 2 , tfeature = [ Embed "doorway trap" , OpenTo "open door" , HideAs "suspect wall" ] } doorClosed = TileKind { tsymbol = '+' , tname = "closed door" , tfreq = [("legendLit", 100), ("legendDark", 100), ("closed door", 1)] , tcolor = Brown , tcolor2 = BrBlack , talter = 2 , tfeature = [OpenTo "open door"] -- never hidden } stairsUp = TileKind { tsymbol = '<' , tname = "staircase up" , tfreq = [("staircase up", 9), ("ordinary staircase up", 1)] , tcolor = BrWhite , tcolor2 = defFG , talter = 0 -- very easy stairs, unlike all others , tfeature = [Embed "staircase up", ConsideredByAI] } stairsTrappedUp = TileKind { tsymbol = '<' , tname = "windy staircase up" , tfreq = [("staircase up", 1)] , tcolor = BrRed , tcolor2 = Red , talter = talterForStairs , tfeature = [ Embed "staircase up", Embed "staircase trap up" , ConsideredByAI, ChangeTo "ordinary staircase up" ] -- AI uses despite the trap; exploration more important } stairsOutdoorUp = stairsUp { tname = "signpost pointing backward" , tfreq = [("staircase outdoor up", 1)] , talter = talterForStairs } stairsGatedUp = stairsUp { tname = "gated staircase up" , tfreq = [("gated staircase up", 1)] , talter = talterForStairs + 2 -- animals and bosses can't use } stairsDown = TileKind { tsymbol = '>' , tname = "staircase down" , tfreq = [("staircase down", 9), ("ordinary staircase down", 1)] , tcolor = BrWhite , tcolor2 = defFG , talter = 0 -- very easy stairs, unlike all others , tfeature = [Embed "staircase down", ConsideredByAI] } stairsTrappedDown = TileKind { tsymbol = '>' , tname = "crooked staircase down" , tfreq = [("staircase down", 1)] , tcolor = BrRed , tcolor2 = Red , talter = talterForStairs , tfeature = [ Embed "staircase down", Embed "staircase trap down" , ConsideredByAI, ChangeTo "ordinary staircase down" ] } stairsOutdoorDown = stairsDown { tname = "signpost pointing forward" , tfreq = [("staircase outdoor down", 1)] , talter = talterForStairs } stairsGatedDown = stairsDown { tname = "gated staircase down" , tfreq = [("gated staircase down", 1)] , talter = talterForStairs + 2 -- animals and bosses can't use } escapeUp = TileKind { tsymbol = '<' , tname = "exit hatch up" , tfreq = [("legendLit", 1), ("legendDark", 1)] , tcolor = BrYellow , tcolor2 = BrYellow , talter = 0 -- anybody can escape (or guard escape) , tfeature = [Embed "escape", ConsideredByAI] } escapeDown = TileKind { tsymbol = '>' , tname = "exit trapdoor down" , tfreq = [("legendLit", 1), ("legendDark", 1)] , tcolor = BrYellow , tcolor2 = BrYellow , talter = 0 -- anybody can escape (or guard escape) , tfeature = [Embed "escape", ConsideredByAI] } escapeOutdoorDown = escapeDown { tname = "exit back to town" , tfreq = [("escape outdoor down", 1)] } -- *** Clear wallGlass = TileKind { tsymbol = '%' , tname = "transparent polymer wall" , tfreq = [("glasshouseOver_%_Lit", 1)] , tcolor = BrBlue , tcolor2 = Blue , talter = 10 , tfeature = [BuildAs "suspect wall", Clear] } wallGlassSpice = wallGlass { tfreq = [("rectWindowsOver_%_Lit", 20)] , tfeature = Spice : tfeature wallGlass } pillarIce = TileKind { tsymbol = '^' , tname = "ice" , tfreq = [("brawlSetLit", 20)] , tcolor = BrBlue , tcolor2 = Blue , talter = 4 -- boss can dig through , tfeature = [Clear, Embed "frost", OpenTo "damp stone floor"] -- Is door, due to @OpenTo@, so is not explorable, but it's OK, because -- it doesn't generate items nor clues. This saves on the need to -- get each ice pillar into sight range when exploring level. } pulpit = TileKind { tsymbol = '%' , tname = "VR booth" , tfreq = [("pulpit", 1)] , tcolor = BrYellow , tcolor2 = Brown , talter = 5 , tfeature = [Clear, Embed "pulpit"] -- mixed blessing, so AI ignores, saved for player fun } bush = TileKind { tsymbol = '%' , tname = "bush" , tfreq = [ ("bush Lit", 1), ("shootoutSetLit", 30), ("escapeSetLit", 40) , ("arenaSetLit", 3), ("bushClumpOver_f_Lit", 1) ] , tcolor = BrGreen , tcolor2 = Green , talter = 10 , tfeature = [Clear] } bushBurnt = bush { tname = "burnt bush" , tfreq = [ ("battleSet", 30), ("ambushSet", 4), ("zooSet", 30) , ("bush with fire", 70) ] , tcolor = BrBlack , tcolor2 = BrBlack , tfeature = Dark : tfeature bush } bushBurning = bush { tname = "burning bush" , tfreq = [("ambushSet", 40), ("zooSet", 290), ("bush with fire", 30)] , tcolor = BrRed , tcolor2 = Red , talter = 5 , tfeature = Embed "small fire" : ChangeTo "bush with fire" : tfeature bush } -- ** Walkable -- *** Not clear floorFog = TileKind { tsymbol = ';' , tname = "faint fog" , tfreq = [ ("lit fog", 1), ("emptySet", 5), ("shootoutSetLit", 20) , ("emptyExitSet", 2) , ("noiseSet", 10), ("fogClumpOver_f_Lit", 60) ] -- lit fog is OK for shootout, because LOS is mutual, as opposed -- to dark fog, and so camper has little advantage, especially -- on big maps, where he doesn't know on which side of fog patch to hide , tcolor = BrCyan , tcolor2 = Cyan , talter = 0 , tfeature = [Walkable, NoItem, OftenActor] } floorFogDark = floorFog { tname = "thick fog" , tfreq = [("noiseSet", 10), ("escapeSetDark", 50)] , tfeature = Dark : tfeature floorFog } floorSmoke = TileKind { tsymbol = ';' , tname = "billowing smoke" , tfreq = [ ("lit smoke", 1), ("labTrailLit", 1), ("stair terminal", 2) , ("smokeClumpOver_f_Lit", 1), ("emptyExitSet", 2) ] , tcolor = Brown , tcolor2 = BrBlack , talter = 0 , tfeature = [Walkable, NoItem] -- not dark, embers } floorSmokeDark = floorSmoke { tname = "lingering smoke" , tfreq = [("ambushSet", 60), ("zooSet", 20), ("battleSet", 5)] , tfeature = Dark : tfeature floorSmoke } -- *** Clear doorOpen = TileKind { tsymbol = '\'' , tname = "open door" , tfreq = [("legendLit", 100), ("legendDark", 100), ("open door", 1)] , tcolor = Brown , tcolor2 = BrBlack , talter = 4 , tfeature = [ Walkable, Clear, NoItem, NoActor , CloseTo "closed door" ] } floorCorridor = TileKind { tsymbol = floorSymbol , tname = "floor" , tfreq = [("floorCorridorLit", 99), ("rubbleOrNot", 30)] , tcolor = BrWhite , tcolor2 = defFG , talter = 0 , tfeature = [Walkable, Clear] } floorArena = floorCorridor { tfreq = [ ("floorArenaLit", 1), ("rubbleSpiceOrNot", 30) , ("arenaSetLit", 96), ("emptySet", 91), ("zooSet", 600) ] } floorNoise = floorArena { tname = "oily floor" , tfreq = [("noiseSet", 60), ("emptyExitSet", 88), ("damp stone floor", 1)] } floorDirt = floorArena { tname = "dirt" , tfreq = [ ("battleSet", 1000), ("brawlSetLit", 1000) , ("shootoutSetLit", 1000), ("escapeSetLit", 1000) , ("ambushSet", 1000) ] } floorDirtSpice = floorDirt { tfreq = [ ("treeShadeOver_s_Lit", 1), ("fogClumpOver_f_Lit", 40) , ("smokeClumpOver_f_Lit", 1), ("bushClumpOver_f_Lit", 1) ] , tfeature = Spice : tfeature floorDirt } floorActor = floorArena { tfreq = [("floorActorLit", 1)] -- lit even in dark cave, so no items , tfeature = OftenActor : tfeature floorArena } floorActorItem = floorActor { tfreq = [("legendLit", 100)] , tfeature = OftenItem : tfeature floorActor } floorRed = floorCorridor { tname = "emergency walkway" , tfreq = [ ("emergency walkway", 1) , ("trailLit", 20), ("alarmingTrailLit", 70) ] , tcolor = BrRed , tcolor2 = Red , tfeature = [Trail, Walkable, Clear] } floorBlue = floorRed { tname = "transport route" , tfreq = [("transport route", 1), ("trailLit", 100)] , tcolor = BrBlue , tcolor2 = Blue } floorGreen = floorRed { tname = "greenery trail" , tfreq = [("trailLit", 100)] , tcolor = BrGreen , tcolor2 = Green } floorBrown = floorRed { tname = "overgrown path" , tfreq = [("alarmingTrailLit", 30)] , tcolor = BrMagenta , tcolor2 = Magenta } floorArenaShade = floorActor { tname = "shaded ground" , tfreq = [("shaded ground", 1), ("treeShadeOver_s_Lit", 2)] , tcolor2 = BrBlack , tfeature = Dark : NoItem : tfeature floorActor } -- * Allure-specific -- ** Not walkable -- *** Not clear oriel = TileKind { tsymbol = '%' -- story-wise it's transparent, hence the symbol , tname = "oriel" , tfreq = [("oriels fence", 5)] , tcolor = White , tcolor2 = Black , talter = 5 , tfeature = [Embed "black starry sky", Dark] } outerHullWall = basicOuterFence { tname = "outer hull wall" , tfreq = [("oriels fence", 95), ("noise fence", 1)] } doorlessWall = TileKind { tsymbol = '#' , tname = "wall" , tfreq = [("noiseSet", 60), ("doorlessWallOver_#", 20)] , tcolor = BrWhite , tcolor2 = defFG , talter = 100 , tfeature = [HideAs "fillerWall"] } rubbleSpiceBurning = TileKind { tsymbol = '&' , tname = "burning installation" , tfreq = [ ("smokeClumpOver_f_Lit", 1), ("emptySet", 1) , ("emptyExitSet", 1), ("noiseSet", 1) , ("ambushSet", 2), ("zooSet", 40), ("stair terminal", 40) ] , tcolor = BrRed , tcolor2 = Red , talter = 4 -- boss can dig through , tfeature = [Spice, OpenTo "rubbleSpice", Embed "big fire"] } wallObscuredSafety = TileKind { tsymbol = '#' , tname = "safety procedures wall" , tfreq = [("obscured wall", 5)] , tcolor = BrWhite , tcolor2 = defFG , talter = 5 , tfeature = [ Embed "ruined first aid kit" , HideAs "suspect wall" ] } wallObscured3dBillboard = TileKind { tsymbol = '#' , tname = "3D billboard" , tfreq = [("obscured wall", 45)] , tcolor = BrWhite , tcolor2 = defFG , talter = 5 , tfeature = [ Embed "3D display" , HideAs "suspect wall" ] } rock = pillar { tname = "rock" , tfreq = [("brawlSetLit", 30), ("arenaSetLit", 1), ("arenaSetDark", 1)] } pillarCache2 = pillarCache { tname = "jewelry display" , tfreq = [ ("cachable jewelry", 20), ("cache jewelry", 1) , ("escapeSetDark", 1) ] , tfeature = [ Embed "jewelry case", Embed "treasure cache trap" , ChangeTo "cachable jewelry", ConsideredByAI ] } stairsLiftUp = stairsUp { tname = "lift up" , tfreq = [("staircase lift up", 9), ("ordinary lift up", 1)] , talter = talterForStairs , tcolor = BrCyan , tcolor2 = Cyan , tfeature = [Embed "lift up", ConsideredByAI] } stairsLiftTrappedUp = stairsTrappedUp { tname = "corroded lift up" , tfreq = [("staircase lift up", 1)] , tcolor = BrBlue , tcolor2 = Blue , tfeature = [ Embed "lift up", Embed "lift trap" , ConsideredByAI, ChangeTo "ordinary lift up" ] -- AI uses despite the trap; exploration more important } stairsLiftDown = stairsDown { tname = "lift down" , tfreq = [("staircase lift down", 9), ("ordinary lift down", 1)] , talter = talterForStairs , tcolor = BrCyan , tcolor2 = Cyan , tfeature = [Embed "lift down", ConsideredByAI] } stairsLiftTrappedDown = stairsTrappedDown { tname = "corroded lift down" , tfreq = [("staircase lift down", 1)] , tcolor = BrBlue , tcolor2 = Blue , tfeature = [ Embed "lift down", Embed "lift trap" , ConsideredByAI, ChangeTo "ordinary lift down" ] } escapeSpaceshipDown = escapeDown { tname = "airlock to the shuttle" , tfreq = [("escape spaceship down", 1)] } floorWindow = floorArena { tsymbol = ' ' -- story-wise it's transparent, hence the symbol , tname = "floor window" , tfreq = [("emptySet", 3)] , tcolor = defFG , tcolor2 = defFG , tfeature = Embed "black starry sky" : tfeature floorCorridor } -- *** Clear machineWall = TileKind { tsymbol = '%' , tname = "hardware rack" , tfreq = [ ("noiseSet", 35), ("emptyExitSet", 6) , ("doorlessWallOver_#", 80) ] , tcolor = White , tcolor2 = BrBlack , talter = 100 , tfeature = [Spice, Clear] } -- ** Walkable -- none -- * Helper functions makeDark :: TileKind -> TileKind makeDark k = let darkText :: GroupName TileKind -> GroupName TileKind darkText t = maybe t (toGroupName . (<> "Dark")) $ T.stripSuffix "Lit" $ tshow t darkFrequency = map (first darkText) $ tfreq k darkFeat (OpenTo t) = Just $ OpenTo $ darkText t darkFeat (CloseTo t) = Just $ CloseTo $ darkText t darkFeat (ChangeTo t) = Just $ ChangeTo $ darkText t darkFeat (HideAs t) = Just $ HideAs $ darkText t darkFeat (BuildAs t) = Just $ BuildAs $ darkText t darkFeat (RevealAs t) = Just $ RevealAs $ darkText t darkFeat (ObscureAs t) = Just $ ObscureAs $ darkText t darkFeat OftenItem = Nothing -- items not common in the dark darkFeat feat = Just feat in k { tfreq = darkFrequency , tfeature = Dark : mapMaybe darkFeat (tfeature k) } makeDarkColor :: TileKind -> TileKind makeDarkColor k = (makeDark k) { tcolor = if tsymbol k == floorSymbol then BrYellow else tcolor k , tcolor2 = BrBlack } Allure-0.8.3.0/GameDefinition/Content/ItemKindOrgan.hs0000644000000000000000000003417113315545742020634 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Actor organ definitions. module Content.ItemKindOrgan ( organs ) where import Prelude () import Game.LambdaHack.Common.Prelude import Game.LambdaHack.Common.Ability import Game.LambdaHack.Common.Color import Game.LambdaHack.Common.Dice import Game.LambdaHack.Common.Flavour import Game.LambdaHack.Common.ItemAspect (Aspect (..)) import Game.LambdaHack.Common.Misc import Game.LambdaHack.Content.ItemKind organs :: [ItemKind] organs = [fist, foot, hookedClaw, smallClaw, snout, smallJaw, jaw, largeJaw, antler, horn, rhinoHorn, tentacle, thorn, boilingFissure, arsenicFissure, sulfurFissure, beeSting, sting, venomTooth, venomFang, screechingBeak, largeTail, armoredSkin, eye2, eye3, eye4, eye5, eye6, eye7, eye8, vision4, vision5, vision6, vision7, vision8, vision10, vision12, vision14, vision16, nostril, insectMortality, sapientBrain, animalBrain, speedGland2, speedGland4, speedGland6, speedGland8, speedGland10, scentGland, boilingVent, arsenicVent, sulfurVent, bonusHP, impressed] -- Allure-specific ++ [razor, liveWire, robotBrain, wasteContainer, spotlight] fist, foot, hookedClaw, smallClaw, snout, smallJaw, jaw, largeJaw, antler, horn, rhinoHorn, tentacle, thorn, boilingFissure, arsenicFissure, sulfurFissure, beeSting, sting, venomTooth, venomFang, screechingBeak, largeTail, armoredSkin, eye2, eye3, eye4, eye5, eye6, eye7, eye8, vision4, vision5, vision6, vision7, vision8, vision10, vision12, vision14, vision16, nostril, insectMortality, sapientBrain, animalBrain, speedGland2, speedGland4, speedGland6, speedGland8, speedGland10, scentGland, boilingVent, arsenicVent, sulfurVent, bonusHP, impressed :: ItemKind -- Allure-specific razor, liveWire, robotBrain, wasteContainer, spotlight :: ItemKind -- Weapons -- * Human weapon organs fist = ItemKind { isymbol = ',' , iname = "fist" , ifreq = [("fist", 100)] , iflavour = zipPlain [Red] , icount = 2 , irarity = [(1, 1)] , iverbHit = "punch" , iweight = 2000 , idamage = 4 `d` 1 , iaspects = [] , ieffects = [] , ifeature = [Durable, Meleeable] , idesc = "Simple but effective." , ikit = [] } foot = fist { iname = "foot" , ifreq = [("foot", 50)] , iverbHit = "kick" , idamage = 4 `d` 1 , idesc = "A weapon you can still use if disarmed." } -- * Other weapon organs hookedClaw = fist { iname = "hooked claw" , ifreq = [("hooked claw", 50)] , icount = 2 -- even if more, only the fore claws used for fighting , iverbHit = "hook" , idamage = 2 `d` 1 , iaspects = [Timeout $ 12 - 1 `dL` 3] , ieffects = [Recharging (toOrganBad "slowed" 2)] , idesc = "A curved talon." } smallClaw = fist { iname = "small claw" , ifreq = [("small claw", 50)] , iverbHit = "slash" , idamage = 2 `d` 1 , idesc = "A pearly spike." } snout = fist { iname = "snout" , ifreq = [("snout", 10)] , icount = 1 , iverbHit = "bite" , idamage = 2 `d` 1 , idesc = "Sensitive and wide-nostrilled." } smallJaw = fist { iname = "small jaw" , ifreq = [("small jaw", 20)] , icount = 1 , iverbHit = "rip" , idamage = 3 `d` 1 , idesc = "Filled with small, even teeth." } jaw = fist { iname = "jaw" , ifreq = [("jaw", 20)] , icount = 1 , iverbHit = "rip" , idamage = 5 `d` 1 , idesc = "Delivers a powerful bite." } largeJaw = fist { iname = "large jaw" , ifreq = [("large jaw", 100)] , icount = 1 , iverbHit = "crush" , idamage = 10 `d` 1 , idesc = "Enough to swallow anything in a single gulp." } antler = fist { iname = "antler" , ifreq = [("antler", 100)] , icount = 2 , iverbHit = "ram" , idamage = 4 `d` 1 , iaspects = [Timeout $ 3 + (1 `d` 3) * 3, AddArmorMelee 10] -- bonus doubled , ieffects = [Recharging (PushActor (ThrowMod 100 50))] -- 1 step, slow , idesc = "" } horn = fist { iname = "horn" , ifreq = [("horn", 100)] , icount = 2 , iverbHit = "impale" , idamage = 5 `d` 1 , iaspects = [AddHurtMelee 10, AddArmorMelee 10] -- bonus doubled , idesc = "Sharp and long, for defence or attack." } rhinoHorn = fist { iname = "ugly horn" -- made of keratin, unlike real horns , ifreq = [("rhino horn", 20)] , icount = 1 -- single, unlike real horns , iverbHit = "impale" , idamage = 5 `d` 1 , iaspects = [Timeout 7, AddHurtMelee 20] , ieffects = [Recharging Impress] -- the owner is a mid-boss, after all , idesc = "Very solid, considering it has the same composition as fingernails." } tentacle = fist { iname = "tentacle" , ifreq = [("tentacle", 50)] , icount = 4 , iverbHit = "slap" , idamage = 4 `d` 1 , idesc = "Damp and dextrous." } thorn = fist { iname = "thorn" , ifreq = [("thorn", 100)] , icount = 2 + 1 `d` 3 , iverbHit = "impale" , idamage = 2 `d` 1 , ifeature = [Meleeable] -- not Durable , idesc = "Sharp yet brittle." } boilingFissure = fist { iname = "fissure" , ifreq = [("boiling fissure", 100)] , icount = 5 + 1 `d` 5 , iverbHit = "hiss at" , idamage = 1 `d` 1 , iaspects = [AddHurtMelee 20] -- decreasing as count decreases , ieffects = [DropItem 1 1 COrgan "condition"] -- useful; limited , ifeature = [Meleeable] -- not Durable , idesc = "" } arsenicFissure = boilingFissure { iname = "fissure" , ifreq = [("biogas fissure", 100)] , icount = 3 + 1 `d` 3 , idamage = 2 `d` 1 , ieffects = [] -- nothing interesting fits the weaken/poison biological data , idesc = "" } sulfurFissure = boilingFissure { iname = "fissure" , ifreq = [("medbot fissure", 100)] , icount = 2 + 1 `d` 2 , idamage = 0 -- heal not via (negative) idamage, for armour would block it , ieffects = [RefillHP 5] , idesc = "" } beeSting = fist { iname = "bee sting" , ifreq = [("bee sting", 100)] , icount = 1 , iverbHit = "sting" , idamage = 0 , iaspects = [AddArmorMelee 200, AddArmorRanged 45] , ieffects = [Paralyze 6, RefillHP 4] , ifeature = [Meleeable] -- not Durable , idesc = "Painful, but beneficial." } sting = fist { iname = "sting" , ifreq = [("sting", 100)] , icount = 1 , iverbHit = "sting" , idamage = 1 `d` 1 , iaspects = [Timeout $ 10 - 1 `dL` 4, AddHurtMelee 40] , ieffects = [Recharging (Paralyze 4)] , idesc = "Painful, debilitating and harmful." } venomTooth = fist { iname = "venom tooth" , ifreq = [("venom tooth", 100)] , icount = 2 , iverbHit = "bite" , idamage = 2 `d` 1 , iaspects = [Timeout $ 7 - 1 `dL` 3] , ieffects = [Recharging (toOrganBad "slowed" (3 + 1 `d` 3))] , idesc = "A chilling numbness spreads from its bite." } venomFang = fist { iname = "venom fang" , ifreq = [("venom fang", 100)] , icount = 2 , iverbHit = "bite" , idamage = 2 `d` 1 , iaspects = [Timeout $ 10 - 1 `dL` 4] , ieffects = [Recharging (toOrganNoTimer "poisoned")] , idesc = "Dripping with deadly venom." } screechingBeak = fist { iname = "screeching beak" , ifreq = [("screeching beak", 100)] , icount = 1 , iverbHit = "peck" , idamage = 2 `d` 1 , iaspects = [Timeout $ 7 - 1 `dL` 3] , ieffects = [Recharging $ Summon "scavenger" $ 1 `dL` 3] , idesc = "Both a weapon and a beacon, calling more scavengers to the meal." } largeTail = fist { iname = "large tail" , ifreq = [("large tail", 50)] , icount = 1 , iverbHit = "knock" , idamage = 7 `d` 1 , iaspects = [Timeout $ 1 + 1 `d` 3, AddHurtMelee 20] , ieffects = [Recharging (PushActor (ThrowMod 400 50))] -- 2 steps , idesc = "Slow but heavy." } -- Non-weapons -- * Armor organs armoredSkin = ItemKind { isymbol = ',' , iname = "armored skin" , ifreq = [("armored skin", 100)] , iflavour = zipPlain [Red] , icount = 1 , irarity = [(1, 1)] , iverbHit = "bash" , iweight = 2000 , idamage = 0 , iaspects = [AddArmorMelee 30, AddArmorRanged 15] , ieffects = [] , ifeature = [Durable] , idesc = "Homemade armour is just as good." , ikit = [] } -- * Sense organs eye :: Int -> ItemKind eye n = armoredSkin { iname = "eye" , ifreq = [(toGroupName $ "eye" <+> tshow n, 100)] , icount = 2 , iverbHit = "glare at" , iaspects = [AddSight (intToDice n)] , idesc = "A piercing stare." } eye2 = eye 2 eye3 = eye 3 eye4 = eye 4 eye5 = eye 5 eye6 = eye 6 eye7 = eye 7 eye8 = eye 8 vision :: Int -> ItemKind vision n = armoredSkin { iname = "vision" , ifreq = [(toGroupName $ "vision" <+> tshow n, 100)] , iverbHit = "visualize" , iaspects = [AddSight (intToDice n)] , idesc = "" } vision4 = vision 4 vision5 = vision 5 vision6 = vision 6 vision7 = vision 7 vision8 = vision 8 vision10 = vision 10 vision12 = vision 12 vision14 = vision 14 vision16 = vision 16 nostril = armoredSkin { iname = "nostril" , ifreq = [("nostril", 100)] , icount = 2 , iverbHit = "snuff" , iaspects = [AddSmell 1] -- times 2, from icount , idesc = "" } -- * Assorted insectMortality = armoredSkin { iname = "insect mortality" , ifreq = [("insect mortality", 100)] , iverbHit = "age" , iaspects = [Timeout $ 30 + (1 `d` 2) * 10] , ieffects = [Recharging (RefillHP (-1))] , ifeature = [Periodic] ++ ifeature armoredSkin , idesc = "" } sapientBrain = armoredSkin { iname = "sapient brain" , ifreq = [("sapient brain", 100)] , iverbHit = "outbrain" , iaspects = [AddAbility ab 1 | ab <- [minBound..maxBound]] ++ [AddAbility AbAlter 2] -- can use stairs , idesc = "" } animalBrain = armoredSkin { iname = "animal brain" , ifreq = [("animal brain", 100)] , iverbHit = "blank" , iaspects = [AddAbility ab 1 | ab <- [minBound..maxBound]] ++ [AddAbility AbAlter 2] -- can use stairs ++ [ AddAbility ab (-1) | ab <- [AbDisplace, AbMoveItem, AbProject, AbApply] ] , idesc = "" } speedGland :: Int -> ItemKind speedGland n = armoredSkin { iname = "speed gland" , ifreq = [(toGroupName $ "speed gland" <+> tshow n, 100)] , iverbHit = "spit at" , iaspects = [ AddSpeed $ intToDice n , Timeout $ intToDice $ 100 `div` n ] , ieffects = [Recharging (RefillHP 1)] , ifeature = [Periodic] ++ ifeature armoredSkin , idesc = "" } speedGland2 = speedGland 2 speedGland4 = speedGland 4 speedGland6 = speedGland 6 speedGland8 = speedGland 8 speedGland10 = speedGland 10 scentGland = armoredSkin { iname = "scent gland" , ifreq = [("scent gland", 100)] , icount = 2 + 1 `d` 3 -- runs out , iverbHit = "spray at" , iaspects = [Timeout $ (1 `d` 3) * 10] , ieffects = [ Recharging (Temporary "look spent") , Recharging (Explode "distressing odor") , Recharging ApplyPerfume ] , ifeature = [Periodic] -- not Durable , idesc = "" } boilingVent = armoredSkin { iname = "vent" , ifreq = [("boiling vent", 100)] , iflavour = zipPlain [BrBlue] , iverbHit = "menace" , iaspects = [Timeout $ (2 + 1 `d` 2) * 5] , ieffects = [ Recharging (Explode "boiling water") , Recharging (RefillHP 2) ] , ifeature = [Periodic] ++ ifeature armoredSkin , idesc = "" } arsenicVent = armoredSkin { iname = "vent" , ifreq = [("biogas vent", 100)] , iflavour = zipPlain [BrGreen] , iverbHit = "menace" , iaspects = [Timeout $ (2 + 1 `d` 2) * 5] , ieffects = [ Recharging (Explode "sparse shower") , Recharging (RefillHP 2) ] , ifeature = [Periodic] ++ ifeature armoredSkin , idesc = "" } sulfurVent = armoredSkin { iname = "vent" , ifreq = [("medbot vent", 100)] , iflavour = zipPlain [BrYellow] , iverbHit = "menace" , iaspects = [Timeout $ (2 + 1 `d` 2) * 5] , ieffects = [ Recharging (Explode "dense shower") , Recharging (RefillHP 2) ] , ifeature = [Periodic] ++ ifeature armoredSkin , idesc = "" } -- * Special bonusHP = armoredSkin { isymbol = 'H' -- '+' reserved for conditions , iname = "bonus HP" , iflavour = zipPlain [BrBlue] , ifreq = [("bonus HP", 1)] , iverbHit = "intimidate" , iweight = 0 , iaspects = [AddMaxHP 1] , idesc = "" } impressed = armoredSkin { isymbol = '!' , iname = "impressed" , iflavour = zipPlain [BrRed] , ifreq = [("impressed", 1), ("condition", 1)] , iverbHit = "confuse" , iweight = 0 , iaspects = [AddMaxCalm (-1)] -- to help player notice on main screen -- and to count as bad condition , ieffects = [OnSmash $ tmpNoLonger "impressed"] -- not @Periodic@ , ifeature = [Fragile, Durable] -- hack: destroy on drop , idesc = "" } -- * Allure-specific razor = fist { iname = "razor" , ifreq = [("razor", 100)] , icount = 2 + 1 `d` 5 , iverbHit = "slice" , idamage = 2 `d` 1 , idesc = "" } liveWire = fist { iname = "live wire" , ifreq = [("live wire", 100)] , icount = 1 , iverbHit = "shock" , idamage = 0 , iaspects = [Timeout $ 3 + 1 `d` 2, AddHurtMelee 20] , ieffects = [ Recharging $ Paralyze 6 , Recharging $ RefillHP (-2) ] , idesc = "" } robotBrain = armoredSkin { iname = "robot brain" , ifreq = [("robot brain", 100)] , iverbHit = "outcompute" , iaspects = [AddAbility ab 1 | ab <- [minBound..maxBound]] ++ [AddAbility AbApply (-1)] , idesc = "" } wasteContainer = armoredSkin { iname = "waste container" , ifreq = [("waste container", 100)] , iverbHit = "spill over" , iaspects = [Timeout $ (1 + 1 `d` 2) * 30] , ieffects = [ Recharging (Summon "mobile animal" $ 1 `dL` 2) , Recharging (RefillHP 1) , Recharging (Explode "waste") ] , ifeature = [Periodic] ++ ifeature armoredSkin , idesc = "" } spotlight = armoredSkin { iname = "spotlight" , ifreq = [("spotlight", 100)] , iverbHit = "illuminate" , iaspects = [AddShine 3] , idesc = "" } Allure-0.8.3.0/GameDefinition/Client/0000755000000000000000000000000013315545741015402 5ustar0000000000000000Allure-0.8.3.0/GameDefinition/Client/UI/0000755000000000000000000000000013315545741015717 5ustar0000000000000000Allure-0.8.3.0/GameDefinition/Client/UI/Content/0000755000000000000000000000000013315545742017332 5ustar0000000000000000Allure-0.8.3.0/GameDefinition/Client/UI/Content/KeyKind.hs0000644000000000000000000002674013315545742021235 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | The default game key-command mapping to be used for UI. Can be overridden -- via macros in the config file. module Client.UI.Content.KeyKind ( standardKeys ) where import Prelude () import Game.LambdaHack.Common.Prelude import Game.LambdaHack.Client.UI.Content.KeyKind import Game.LambdaHack.Client.UI.HandleHelperM (ppSLore) import Game.LambdaHack.Client.UI.HumanCmd import Game.LambdaHack.Common.Misc import qualified Game.LambdaHack.Content.TileKind as TK -- | Description of default key-command bindings. -- -- In addition to these commands, mouse and keys have a standard meaning -- when navigating various menus. standardKeys :: KeyKind standardKeys = KeyKind $ map evalKeyDef $ -- All commands are defined here, except some movement and leader picking -- commands. All commands are shown on help screens except debug commands -- and macros with empty descriptions. -- The order below determines the order on the help screens. -- Remember to put commands that show information (e.g., enter aiming -- mode) first. -- Main menu [ ("e", ([CmdMainMenu], "enter challenges menu>", ChallengesMenu)) , ("s", ([CmdMainMenu], "start new game", GameRestart)) , ("x", ([CmdMainMenu], "exit to desktop", GameExit)) , ("v", ([CmdMainMenu], "visit settings menu>", SettingsMenu)) , ("a", ([CmdMainMenu], "automate faction", Automate)) , ("?", ([CmdMainMenu], "see command help", Help)) , ("F12", ([CmdMainMenu], "go to dashboard", Dashboard)) , ("Escape", ([CmdMainMenu], "back to playing", Cancel)) -- Minimal command set, in the desired presentation order , ("g", addCmdCategory CmdMinimal $ grabItems "grab item(s)") -- actually it's not necessary, ground items menu suffices , ("Escape", ( [CmdMinimal, CmdAim] , "cancel aiming/open main menu" , ByAimMode { exploration = ExecuteIfClear MainMenu , aiming = Cancel } )) , ("Return", ( [CmdMinimal, CmdAim] , "accept target/open dashboard" , ByAimMode { exploration = ExecuteIfClear Dashboard , aiming = Accept } )) , ("space", ( [CmdMinimal, CmdMeta] , "clear messages/display history" , ExecuteIfClear History )) -- not necessary, because messages available from dashboard , ("BackTab", ( [CmdMinimal, CmdMove] , "cycle among all party members" , MemberBack )) , ("KP_Multiply", ( [CmdMinimal, CmdAim] , "cycle x-hair among enemies" , AimEnemy )) -- not necessary, because flinging from item menu enters aiming mode , ("C-c", ([CmdMinimal, CmdMove], "open or close or alter", AlterDir [])) , ("+", ([CmdMinimal, CmdAim], "swerve the aiming line", EpsIncr True)) -- Item menu, first part of item use commands , ("comma", grabItems "") , ("d", dropItems "drop item(s)") , ("period", dropItems "") , ("f", addCmdCategory CmdItemMenu $ projectA flingTs) , ("C-f", addCmdCategory CmdItemMenu $ replaceDesc "fling without aiming" $ projectI flingTs) , ("a", addCmdCategory CmdItemMenu $ applyI [TriggerItem { tiverb = "apply" , tiobject = "consumable" , tisymbols = "!?-" }]) , ("C-a", addCmdCategory CmdItemMenu $ replaceDesc "apply and keep choice" $ applyIK [TriggerItem { tiverb = "apply" , tiobject = "consumable" , tisymbols = "!?-" }]) , ("p", moveItemTriple [CGround, CEqp, CSha] CInv "item" False) , ("e", moveItemTriple [CGround, CInv, CSha] CEqp "item" False) , ("s", moveItemTriple [CGround, CInv, CEqp] CSha "and share item" False) -- Terrain exploration and alteration , ("Tab", ( [CmdMove] , "cycle among party members on the level" , MemberCycle )) , ("c", ([CmdMove], descTs closeDoorTriggers, AlterDir closeDoorTriggers)) , ("=", ( [CmdMove], "select (or deselect) party member", SelectActor) ) , ("_", ([CmdMove], "deselect (or select) all on the level", SelectNone)) , ("semicolon", ( [CmdMove] , "go to x-hair for 25 steps" , Macro ["C-semicolon", "C-/", "C-V"] )) , ("colon", ( [CmdMove] , "run to x-hair collectively for 25 steps" , Macro ["C-colon", "C-/", "C-V"] )) , ("x", ( [CmdMove] , "explore nearest unknown spot" , autoexploreCmd )) , ("X", ( [CmdMove] , "autoexplore 25 times" , autoexplore25Cmd )) , ("R", ([CmdMove], "rest (wait 25 times)", Macro ["KP_5", "C-V"])) , ("C-R", ( [CmdMove], "lurk (wait 0.1 turns 100 times)" , Macro ["C-KP_5", "V"] )) -- Item use, continued , ("^", ( [CmdItem], "sort items by ownership, kind and stats", SortSlots)) , ("P", ( [CmdItem, CmdDashboard] , "manage item pack of the leader" , ChooseItemMenu (MStore CInv) )) , ("G", ( [CmdItem, CmdDashboard] , "manage items on the ground" , ChooseItemMenu (MStore CGround) )) , ("E", ( [CmdItem, CmdDashboard] , "manage equipment of the leader" , ChooseItemMenu (MStore CEqp) )) , ("S", ( [CmdItem, CmdDashboard] , "manage the shared party stash" , ChooseItemMenu (MStore CSha) )) , ("A", ( [CmdItem, CmdDashboard] , "manage all owned items" , ChooseItemMenu MOwned )) , ("@", ( [CmdItem, CmdDashboard] , "describe organs of the leader" , ChooseItemMenu MOrgans )) , ("#", ( [CmdItem, CmdDashboard] , "show stat summary of the leader" , ChooseItemMenu MStats )) , ("~", ( [CmdItem] , "display known lore" , ChooseItemMenu (MLore SItem) )) , ("q", addCmdCategory CmdItem $ applyI [TriggerItem { tiverb = "quaff" , tiobject = "liquid" , tisymbols = "!" }]) , ("r", addCmdCategory CmdItem $ applyI [TriggerItem { tiverb = "read" , tiobject = "chip" , tisymbols = "?" }]) , ("t", addCmdCategory CmdItem $ projectA [ TriggerItem { tiverb = "throw" , tiobject = "missile" , tisymbols = "{" } , TriggerItem { tiverb = "throw" , tiobject = "missile" , tisymbols = "}" } ]) -- , ("z", projectA [TriggerItem { tiverb = "zap" -- , tiobject = "wand" -- , tisymbols = "-" }]) -- Dashboard, in addition to commands marked above , ("safeD0", ([CmdInternal, CmdDashboard], "", Cancel)) -- blank line ] ++ map (\(k, slore) -> ("safeD" ++ show (k :: Int) , ( [CmdInternal, CmdDashboard] , "display" <+> ppSLore slore <+> "lore" , ChooseItemMenu (MLore slore) ))) (zip [1..] [minBound..maxBound]) ++ [ ("safeD99", ([CmdInternal, CmdDashboard], "", Cancel)) -- blank line -- Aiming , ("!", ([CmdAim], "", AimEnemy)) , ("KP_Divide", ([CmdAim], "cycle x-hair among items", AimItem)) , ("/", ([CmdAim], "", AimItem)) , ("-", ([CmdAim], "unswerve the aiming line", EpsIncr False)) , ("\\", ([CmdAim], "cycle aiming modes", AimFloor)) , ("C-?", ( [CmdAim] , "set x-hair to nearest unknown spot" , XhairUnknown )) , ("C-I", ( [CmdAim] , "set x-hair to nearest item" , XhairItem )) , ("C-{", ( [CmdAim] , "set x-hair to nearest upstairs" , XhairStair True )) , ("C-}", ( [CmdAim] , "set x-hair to nearest dnstairs" , XhairStair False )) , ("<", ([CmdAim], "move aiming one level up" , AimAscend 1)) , ("C-<", ( [CmdNoHelp], "move aiming 10 levels up" , AimAscend 10) ) , (">", ([CmdAim], "move aiming one level down", AimAscend (-1))) -- 'lower' would be misleading in some games, just as 'deeper' , ("C->", ( [CmdNoHelp], "move aiming 10 levels down" , AimAscend (-10)) ) , ("BackSpace" , ( [CmdAim] , "clear chosen item and target" , ComposeUnlessError ItemClear TgtClear )) -- Assorted , ("F12", ([CmdMeta], "open dashboard", Dashboard)) , ("?", ([CmdMeta], "display help", Hint)) , ("F1", ([CmdMeta], "", Hint)) , ("v", ([CmdMeta], "voice again the recorded commands", Repeat 1)) , ("V", repeatTriple 100) , ("C-v", repeatTriple 1000) , ("C-V", repeatTriple 25) , ("'", ([CmdMeta], "start recording commands", Record)) , ("C-P", ([CmdMeta], "print screen", PrintScreen)) -- Dashboard, in addition to commands marked above , ("safeD100", ([CmdInternal, CmdDashboard], "display help", Help)) , ("safeD101", ([CmdInternal, CmdDashboard], "display history", History)) -- Mouse , ("LeftButtonRelease", mouseLMB) , ("RightButtonRelease", mouseRMB) , ("C-LeftButtonRelease", replaceDesc "" mouseRMB) -- Mac convention , ( "C-RightButtonRelease" , ([CmdMouse], "open or close or alter at pointer", AlterWithPointer []) ) , ("MiddleButtonRelease", mouseMMB) , ("WheelNorth", ([CmdMouse], "swerve the aiming line", Macro ["+"])) , ("WheelSouth", ([CmdMouse], "unswerve the aiming line", Macro ["-"])) -- Debug and others not to display in help screens , ("C-S", ([CmdDebug], "save game", GameSave)) , ("C-semicolon", ( [CmdNoHelp] , "move one step towards the x-hair" , MoveOnceToXhair )) , ("C-colon", ( [CmdNoHelp] , "run collectively one step towards the x-hair" , RunOnceToXhair )) , ("C-/", ( [CmdNoHelp] , "continue towards the x-hair" , ContinueToXhair )) , ("C-comma", ([CmdNoHelp], "run once ahead", RunOnceAhead)) , ("safe1", ( [CmdInternal] , "go to pointer for 25 steps" , goToCmd )) , ("safe2", ( [CmdInternal] , "run to pointer collectively" , runToAllCmd )) , ("safe3", ( [CmdInternal] , "pick new leader on screen" , PickLeaderWithPointer )) , ("safe4", ( [CmdInternal] , "select party member on screen" , SelectWithPointer )) , ("safe5", ( [CmdInternal] , "set x-hair to enemy" , AimPointerEnemy )) , ("safe6", ( [CmdInternal] , "fling at enemy under pointer" , aimFlingCmd )) , ("safe7", ( [CmdInternal, CmdDashboard] , "open main menu" , MainMenu )) , ("safe8", ( [CmdInternal] , "cancel aiming" , Cancel )) , ("safe9", ( [CmdInternal] , "accept target" , Accept )) , ("safe10", ( [CmdInternal] , "wait a turn, bracing for impact" , Wait )) , ("safe11", ( [CmdInternal] , "wait 0.1 of a turn" , Wait10 )) ] ++ map defaultHeroSelect [0..6] closeDoorTriggers :: [TriggerTile] closeDoorTriggers = [ TriggerTile { ttverb = "close" , ttobject = "door" , ttfeature = TK.CloseTo "closed door" } , TriggerTile { ttverb = "close" , ttobject = "door" , ttfeature = TK.CloseTo "closed door" } ] Allure-0.8.3.0/GameDefinition/Implementation/0000755000000000000000000000000013315545742017152 5ustar0000000000000000Allure-0.8.3.0/GameDefinition/Implementation/MonadClientImplementation.hs0000644000000000000000000001400513315545742024611 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- {-# LANGUAGE GeneralizedNewtypeDeriving #-} -- | The implementation of our custom game client monads. Just as any other -- component of the library, this implementation can be substituted. module Implementation.MonadClientImplementation ( executorCli #ifdef EXPOSE_INTERNAL -- * Internal operations , CliState(..), CliImplementation(..) #endif ) where import Prelude () import Game.LambdaHack.Common.Prelude import Control.Concurrent import qualified Control.Monad.IO.Class as IO import Control.Monad.Trans.State.Strict hiding (State) import Game.LambdaHack.Atomic (MonadStateWrite (..), putState) import Game.LambdaHack.Client import Game.LambdaHack.Client.HandleAtomicM import Game.LambdaHack.Client.HandleResponseM import Game.LambdaHack.Client.LoopM import Game.LambdaHack.Client.MonadClient import Game.LambdaHack.Client.State import Game.LambdaHack.Client.UI import Game.LambdaHack.Client.UI.SessionUI import Game.LambdaHack.Common.Faction import Game.LambdaHack.Common.Kind import Game.LambdaHack.Common.MonadStateRead import qualified Game.LambdaHack.Common.Save as Save import Game.LambdaHack.Common.State import Game.LambdaHack.Server (ChanServer (..)) data CliState = CliState { cliState :: State -- ^ current global state , cliClient :: StateClient -- ^ current client state , cliSession :: Maybe SessionUI -- ^ UI state, empty for AI clients , cliDict :: ChanServer -- ^ this client connection information , cliToSave :: Save.ChanSave (StateClient, Maybe SessionUI) -- ^ connection to the save thread } -- | Client state transformation monad. newtype CliImplementation a = CliImplementation { runCliImplementation :: StateT CliState IO a } deriving (Monad, Functor, Applicative) instance MonadStateRead CliImplementation where {-# INLINE getsState #-} getsState f = CliImplementation $ gets $ f . cliState instance MonadStateWrite CliImplementation where {-# INLINE modifyState #-} modifyState f = CliImplementation $ state $ \cliS -> let !newCliState = f $ cliState cliS in ((), cliS {cliState = newCliState}) instance MonadClient CliImplementation where {-# INLINE getsClient #-} getsClient f = CliImplementation $ gets $ f . cliClient {-# INLINE modifyClient #-} modifyClient f = CliImplementation $ state $ \cliS -> let !newCliState = f $ cliClient cliS in ((), cliS {cliClient = newCliState}) liftIO = CliImplementation . IO.liftIO instance MonadClientSetup CliImplementation where saveClient = CliImplementation $ do toSave <- gets cliToSave cli <- gets cliClient msess <- gets cliSession IO.liftIO $ Save.saveToChan toSave (cli, msess) restartClient = CliImplementation $ state $ \cliS -> case cliSession cliS of Just sess -> let !newSess = (emptySessionUI (sUIOptions sess)) { schanF = schanF sess , sbinding = sbinding sess , shistory = shistory sess , sstart = sstart sess , sgstart = sgstart sess , sallTime = sallTime sess , snframes = snframes sess , sallNframes = sallNframes sess } in ((), cliS {cliSession = Just newSess}) Nothing -> ((), cliS) instance MonadClientUI CliImplementation where {-# INLINE getsSession #-} getsSession f = CliImplementation $ gets $ f . fromJust . cliSession {-# INLINE modifySession #-} modifySession f = CliImplementation $ state $ \cliS -> let !newCliSession = f $ fromJust $ cliSession cliS in ((), cliS {cliSession = Just newCliSession}) liftIO = CliImplementation . IO.liftIO instance MonadClientReadResponse CliImplementation where receiveResponse = CliImplementation $ do ChanServer{responseS} <- gets cliDict IO.liftIO $ takeMVar responseS instance MonadClientWriteRequest CliImplementation where sendRequestAI scmd = CliImplementation $ do ChanServer{requestAIS} <- gets cliDict IO.liftIO $ putMVar requestAIS scmd sendRequestUI scmd = CliImplementation $ do ChanServer{requestUIS} <- gets cliDict IO.liftIO $ putMVar (fromJust requestUIS) scmd clientHasUI = CliImplementation $ do mSession <- gets cliSession return $! isJust mSession instance MonadClientAtomic CliImplementation where {-# INLINE execUpdAtomic #-} execUpdAtomic _ = return () -- handleUpdAtomic, until needed, save resources -- Don't catch anything; assume exceptions impossible. {-# INLINE execPutState #-} execPutState = putState -- | Run the main client loop, with the given arguments and empty -- initial states, in the @IO@ monad. executorCli :: KeyKind -> UIOptions -> ClientOptions -> COps -> Bool -> FactionId -> ChanServer -> IO () executorCli copsClient sUIOptions clientOptions cops isUI fid cliDict = let cliSession | isUI = Just $ emptySessionUI sUIOptions | otherwise = Nothing stateToFileName (cli, _) = ssavePrefixCli (soptions cli) <> Save.saveNameCli cops (sside cli) totalState cliToSave = CliState { cliState = updateCOpsAndCachedData (const cops) emptyState -- state is empty, so the cached data is left empty and untouched , cliClient = emptyStateClient fid , cliDict , cliToSave , cliSession } m = loopCli copsClient sUIOptions clientOptions exe = evalStateT (runCliImplementation m) . totalState in Save.wrapInSaves cops stateToFileName exe Allure-0.8.3.0/GameDefinition/Implementation/MonadServerImplementation.hs0000644000000000000000000002200413315545742024637 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- {-# LANGUAGE GeneralizedNewtypeDeriving #-} -- | The implementation of our custom game server monads. Just as any other -- component of the library, this implementation can be substituted. module Implementation.MonadServerImplementation ( executorSer #ifdef EXPOSE_INTERNAL -- * Internal operations , SerState(..), SerImplementation(..) #endif ) where import Prelude () import Game.LambdaHack.Common.Prelude import Control.Concurrent import qualified Control.Exception as Ex import qualified Control.Monad.IO.Class as IO import Control.Monad.Trans.State.Strict hiding (State) import qualified Data.EnumMap.Strict as EM import qualified Data.Text.IO as T import Options.Applicative (defaultPrefs, execParserPure, handleParseResult) import System.Exit (ExitCode (ExitSuccess)) import System.FilePath import System.IO (hFlush, stdout) import Game.LambdaHack.Atomic import Game.LambdaHack.Client import Game.LambdaHack.Common.File import Game.LambdaHack.Common.Kind import Game.LambdaHack.Common.Misc import Game.LambdaHack.Common.MonadStateRead import qualified Game.LambdaHack.Common.Save as Save import Game.LambdaHack.Common.State import Game.LambdaHack.Common.Thread import Game.LambdaHack.Server import Game.LambdaHack.Server.BroadcastAtomic import Game.LambdaHack.Server.HandleAtomicM import Game.LambdaHack.Server.MonadServer import Game.LambdaHack.Server.ProtocolM import Game.LambdaHack.Server.ServerOptions import Game.LambdaHack.Server.State import Implementation.MonadClientImplementation (executorCli) data SerState = SerState { serState :: State -- ^ current global state , serServer :: StateServer -- ^ current server state , serDict :: ConnServerDict -- ^ client-server connection information , serToSave :: Save.ChanSave (State, StateServer) -- ^ connection to the save thread } -- | Server state transformation monad. newtype SerImplementation a = SerImplementation {runSerImplementation :: StateT SerState IO a} deriving (Monad, Functor, Applicative) instance MonadStateRead SerImplementation where {-# INLINE getsState #-} getsState f = SerImplementation $ gets $ f . serState instance MonadStateWrite SerImplementation where {-# INLINE modifyState #-} modifyState f = SerImplementation $ state $ \serS -> let !newSerState = f $ serState serS in ((), serS {serState = newSerState}) instance MonadServer SerImplementation where {-# INLINE getsServer #-} getsServer f = SerImplementation $ gets $ f . serServer {-# INLINE modifyServer #-} modifyServer f = SerImplementation $ state $ \serS -> let !newSerServer = f $ serServer serS in ((), serS {serServer = newSerServer}) chanSaveServer = SerImplementation $ gets serToSave liftIO = SerImplementation . IO.liftIO instance MonadServerComm SerImplementation where {-# INLINE getsDict #-} getsDict f = SerImplementation $ gets $ f . serDict {-# INLINE modifyDict #-} modifyDict f = SerImplementation $ state $ \serS -> let !newSerDict = f $ serDict serS in ((), serS {serDict = newSerDict}) liftIO = SerImplementation . IO.liftIO instance MonadServerAtomic SerImplementation where execUpdAtomic cmd = do oldState <- getState (ps, atomicBroken, executedOnServer) <- handleCmdAtomicServer cmd when executedOnServer $ cmdAtomicSemSer oldState cmd handleAndBroadcast ps atomicBroken (UpdAtomic cmd) execUpdAtomicSer cmd = SerImplementation $ StateT $ \cliS -> do cliSNewOrE <- Ex.try $ execStateT (runSerImplementation $ handleUpdAtomic cmd) cliS case cliSNewOrE of Left AtomicFail{} -> return (False, cliS) Right cliSNew -> -- We know @cliSNew@ differs only in @serState@. return (True, cliSNew) execUpdAtomicFid fid cmd = SerImplementation $ StateT $ \cliS -> do -- Don't catch anything; assume exceptions impossible. let sFid = sclientStates (serServer cliS) EM.! fid cliSNew <- execStateT (runSerImplementation $ handleUpdAtomic cmd) cliS {serState = sFid} -- We know @cliSNew@ differs only in @serState@. let serServerNew = (serServer cliS) {sclientStates = EM.insert fid (serState cliSNew) $ sclientStates $ serServer cliS} return $! ((), cliS {serServer = serServerNew}) execUpdAtomicFidCatch fid cmd = SerImplementation $ StateT $ \cliS -> do let sFid = sclientStates (serServer cliS) EM.! fid cliSNewOrE <- Ex.try $ execStateT (runSerImplementation $ handleUpdAtomic cmd) cliS {serState = sFid} case cliSNewOrE of Left AtomicFail{} -> return (False, cliS) Right cliSNew -> do -- We know @cliSNew@ differs only in @serState@. let serServerNew = (serServer cliS) {sclientStates = EM.insert fid (serState cliSNew) $ sclientStates $ serServer cliS} return $! (True, cliS {serServer = serServerNew}) execSfxAtomic sfx = do ps <- posSfxAtomic sfx handleAndBroadcast ps [] (SfxAtomic sfx) execSendPer = sendPer -- Don't inline this, to keep GHC hard work inside the library -- for easy access of code analysis tools. -- | Run the main server loop, with the given arguments and empty -- initial states, in the @IO@ monad. executorSer :: COps -> KeyKind -> ServerOptions -> IO () executorSer cops copsClient soptionsNxtCmdline = do -- Parse UI client configuration file. -- It is reparsed at each start of the game executable. let benchmark = sbenchmark $ sclientOptions soptionsNxtCmdline -- Fail here, not inside client code, so that savefiles are not removed, -- because they are not the source of the failure. sUIOptions <- mkUIOptions cops benchmark soptionsNxt <- case uCmdline sUIOptions of [] -> return soptionsNxtCmdline args -> handleParseResult $ execParserPure defaultPrefs serverOptionsPI args -- Options for the clients modified with the configuration file. -- The client debug inside server debug only holds the client commandline -- options and is never updated with config options, etc. let clientOptions = applyUIOptions cops sUIOptions $ sclientOptions soptionsNxt -- Partially applied main loop of the clients. executorClient = executorCli copsClient sUIOptions clientOptions cops -- Wire together game content, the main loop of game clients -- and the game server loop. let stateToFileName (_, ser) = ssavePrefixSer (soptions ser) <> Save.saveNameSer cops totalState serToSave = SerState { serState = updateCOpsAndCachedData (const cops) emptyState -- state is empty, so the cached data is left empty and untouched , serServer = emptyStateServer , serDict = EM.empty , serToSave } m = loopSer soptionsNxt executorClient exe = evalStateT (runSerImplementation m) . totalState exeWithSaves = Save.wrapInSaves cops stateToFileName exe defPrefix = ssavePrefixSer defServerOptions bkpOneSave name = do dataDir <- appDataDir let path bkp = dataDir "saves" bkp <> name b <- doesFileExist (path "") when b $ renameFile (path "") (path "bkp.") bkpAllSaves = if benchmark then return () else do T.hPutStrLn stdout "The game crashed, so savefiles are moved aside." bkpOneSave $ defPrefix <> Save.saveNameSer cops forM_ [-99..99] $ \n -> bkpOneSave $ defPrefix <> Save.saveNameCli cops (toEnum n) -- Wait for clients to exit even in case of server crash -- (or server and client crash), which gives them time to save -- and report their own inconsistencies, if any. Ex.handle (\ex -> case Ex.fromException ex of Just ExitSuccess -> -- User-forced shutdown, not crash, so the intention is -- to keep old saves and also clients may be not ready to save. Ex.throwIO ex _ -> do Ex.uninterruptibleMask_ $ threadDelay 1000000 -- let clients report their errors and save when (ssavePrefixSer soptionsNxt == defPrefix) bkpAllSaves hFlush stdout Ex.throwIO ex -- crash eventually, which kills clients ) exeWithSaves -- T.hPutStrLn stdout "Server exiting, waiting for clients." -- hFlush stdout waitForChildren childrenServer -- no crash, wait for clients indefinitely -- T.hPutStrLn stdout "Server exiting now." -- hFlush stdout Allure-0.8.3.0/GameDefinition/Implementation/TieKnot.hs0000644000000000000000000000730713315545742021072 0ustar0000000000000000-- Copyright (c) 2008--2011 Andres Loeh -- Copyright (c) 2010--2018 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Here the knot of engine code pieces, frontend and the game-specific -- content definitions is tied, resulting in an executable game. module Implementation.TieKnot ( tieKnot ) where import Prelude () import Game.LambdaHack.Common.Prelude import qualified System.Random as R import Game.LambdaHack.Common.Kind import qualified Game.LambdaHack.Common.Tile as Tile import qualified Game.LambdaHack.Content.CaveKind as CK import qualified Game.LambdaHack.Content.ItemKind as IK import qualified Game.LambdaHack.Content.ModeKind as MK import qualified Game.LambdaHack.Content.PlaceKind as PK import qualified Game.LambdaHack.Content.RuleKind as RK import qualified Game.LambdaHack.Content.TileKind as TK import Game.LambdaHack.Server import qualified Client.UI.Content.KeyKind as Content.KeyKind import qualified Content.CaveKind import qualified Content.ItemKind import qualified Content.ModeKind import qualified Content.PlaceKind import qualified Content.RuleKind import qualified Content.TileKind import Implementation.MonadServerImplementation (executorSer) -- | Tie the LambdaHack engine client, server and frontend code -- with the game-specific content definitions, and run the game. -- -- The custom monad types to be used are determined by the 'executorSer' -- call, which in turn calls 'executorCli'. If other functions are used -- in their place- the types are different and so the whole pattern -- of computation differs. Which of the frontends is run inside the UI client -- depends on the flags supplied when compiling the engine library. -- Similarly for the choice of native vs JS builds. tieKnot :: ServerOptions -> IO () tieKnot options@ServerOptions{sallClear, sboostRandomItem, sdungeonRng} = do -- This setup ensures the boosting option doesn't affect generating initial -- RNG for dungeon, etc., and also, that setting dungeon RNG on commandline -- equal to what was generated last time, ensures the same item boost. initialGen <- maybe R.getStdGen return sdungeonRng let soptionsNxt = options {sdungeonRng = Just initialGen} boostedItems = IK.boostItemKindList initialGen Content.ItemKind.items coitem = IK.makeData $ if sboostRandomItem then boostedItems ++ Content.ItemKind.otherItemContent else Content.ItemKind.content coItemSpeedup = IK.speedupItem coitem cotile = TK.makeData coitem Content.TileKind.content coTileSpeedup = Tile.speedupTile sallClear cotile coplace = PK.makeData cotile Content.PlaceKind.content cocave = CK.makeData coitem coplace cotile Content.CaveKind.content -- Common content operations, created from content definitions. -- Evaluated fully to discover errors ASAP and to free memory. -- Fail here, not inside server code, so that savefiles are not removed, -- because they are not the source of the failure. !cops = COps { cocave , coitem , comode = MK.makeData cocave coitem Content.ModeKind.content , coplace , corule = RK.makeData Content.RuleKind.content , cotile , coItemSpeedup , coTileSpeedup } -- Client content operations containing default keypresses -- and command descriptions. !copsClient = Content.KeyKind.standardKeys -- Wire together game content, the main loops of game clients -- and the game server loop. executorSer cops copsClient soptionsNxt Allure-0.8.3.0/GameDefinition/fonts/0000755000000000000000000000000013315545742015316 5ustar0000000000000000Allure-0.8.3.0/GameDefinition/fonts/LICENSE.16x16x0000644000000000000000000004325413315545742017307 0ustar0000000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. Allure-0.8.3.0/GameDefinition/fonts/16x16x.fon0000644000000000000000000002316013315545742017001 0ustar0000000000000000MZ @ !L!This Program cannot be run in DOS mode $NE+@@zP2K0PFONTDIR16x16x'FONTRES 100,96,96 : 16x16x 12 (VGA res)P$Leon Marrick, 2005. Freeware. Share and enjoy! ``X0 $~16x16x$Leon Marrick, 2005. Freeware. Share and enjoy! ``X0 $~~>^~>^~>^~>^~ > ^ ~      > ^ ~      > ^ ~      > ^ ~      > ^ ~     >^~>^~>^~>^~>^~>^~>^~>^~>^~>^~>^~>^~>^~>^~>^~>^~>^~>^~ > ^ ~     !>!^!~!!!!!">"^"~"""""#>#^#~#####$>$^$  kz{oUUUUUUUUUUUU"%"HDH @( H@A@  @@@@5jUVU?UU* VVX`ujժժժժVUUUUU`@@ըz   .pppx&2L :UjUjU.TTT谀F]:o@@@(dTT %@ ` @% H H ```` ?? ?? 0000000𘀀6"6 0`8lDl8800=p܌```````  `` 00`80000008p88p ? 80??p00p?  ?8888??```````??00?? ?88807?800888?? 8p800800888008 ?88800????0000p'.,,,,' ddddp 00``00 ??000??000??p0p88800008??00000000??p88p??000??000????000??00000800008x00000??0000080`p80088<<663311 <FNV^fnv~&.6>FNV^fnv~&.6>FNV^fnv~&.6>FNV^fnv~    & . 6 > F N V ^ f n v ~                     & . 6 > F N V ^ f n v ~                     & . 6 > F N V ^ f n v ~                     & . 6 > F N V ^ f n v ߖUUU$H@ @@<<8|||8 v|T(~ի~Zl(8(P 8lTl*"\zf(@@<<$$$lllll|||f6j8l8vv0 000  l88l000000 <00 0`f<0000 0`0 ~~0  0F>``|fff|>```>>fff>ff><``lvfff8<  L8``flxlf8<lvfffff>\2000>p<|00x000fffn6l8x0xfff><~ 0~ 0 0 02LUUUU8d`d8200x@$Z<X||~`x`~~`x`~~`x`~$~`x`~<<<<<<$<<FF(<``|f|``$ff><8X8xbAllure-0.8.3.0/GameDefinition/fonts/LICENSE.Fix15Mono-Bold0000644000000000000000000001064313315545742020731 0ustar0000000000000000Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. with Reserved Font Name < Fira >, This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Allure-0.8.3.0/GameDefinition/fonts/Fix15Mono-Bold.woff0000644000000000000000000027475413315545742020627 0ustar0000000000000000wOFFyPFFTMsGDEFwDqGPOSh};YfGSUBLL@FOS/24X`bcmap .\Ncvt $> /fpgm$ gasp*glyf*BLKheadBH66 x hheaB!$thmtxB :!TlocaN0@5!maxp\ name\^post`Q5nXprepyXm=a.xKQSulczm#{b`i/7ɯ`Қ=ꚆѴ">ZjrnMozWoyCy SNA8 n-w'«|MWC~Fãc9I,UuԜzo?j*w5k#kt.x[PUy?s=s^(,K KYABH"",a)c)1 ,uYj(Za0:qXb85Ƹu1b 8,`;Ç&̎s.?~wsZ@/ J+kf̓h@> 6&կחW/RM_MezJ뛀[oX񸬃<@R@qt i~\.6d*.vF~F1W-ϬgBHc`O|%|_M|Q S|J(JS \٤lV-JU٦Pv*=J_9R(ǔ)r^W.+W)eF* !!$ MH>)"TROI i#I'Ev d'd%c" d WTZj&+L5G]jZVjڤjڥv{}jzPT'zN^RMzWOE)>Gi MY4Mt3[h3Jt't?=@#=IO3<U:Eg-:K jD3-^KR -["TЪ^kZ6m֩vk{>m@; iǵamT;i mRnh9mee ,`,aJVjYkbu.}d(;Fiv]`v]g7vELqzYzN/Kr}YӷV}CߩA!_n{Jk~ 9|ctD~\ɆÅOߟWmd?h%d;>Χõ~ҢmO\>cDD/أ.sfUa@#?upB <ؽυge6BxP?st j~SpkT~ُ}0>=jQg99aqkb~'7-~K[x|(.x#`kzCh^>,?VijڭnMvk[:en>۾9hA.ÑxT\/.WFM:Ti}ħN}BC~R?e>Y`@cIFadyFQdFQcFfl7:]ncg !1lg11aL 1g̛D&5-3L0fc1 b̬4Zl2[v2=>ҹgR8-| Uā ٸ{硽;yAȫ.vsi8%gwW8cyűAc.1ocvr󘋇91y8\sK ]̂8]\ᵜMn8wǚ)w9~ޥ܍rq:w.- v\o:ĭ\;x~9+Cۜltc= /Yݼr9<<8J.n]9.o*g-;np-D o ?}UhP5v;; +|( q¿ yzyy  )gVXZ5@4&0, BX&-n^x x*3%"R/%IR-IRT*UHURT/5J-R]vIR4 Ұ4*Ƥ҄4)MK7Ҝ4B" ŢVL֠TP%F5VԎ:PF{>ԏAt@#4:.K ˢe&89QN,9W^'%rI,[fyM!{^y|@>$'S<._S|K b qN8|\Kq57܆N {q>(>E<'4o9<)TX%AIVV(JF)P2RVjIiUڕKV(~2UN(#irA\Q)וrF|$$NH.YG I )'fRGfl#;NCz~r"G1r"gy2N.d̐[d# *Tj1jjEjZV5jڨmvSݥV}zXRzVS/:Pos<QJ-Kh2]A3i]C h1-D[i;]h?=HQz@/+No;.֘D-EKײ\mVh&mVmњ6mSzvD;NigڸvYMi3-mV-03X gI,elY+eհzZX:.e}lfC8f,cdؼtSc=Y_g9@/JZ&Uo;.[ߣ~T?s~E_ow}C4 g$)FeB(76Aj_ȗ .~ĵa9KUŦ?_ xYg\䫛a_-U~i:B }cc گ\%0r+ܽS[V hdUae`5aoRዶ:B=e\-W23:<1ñ Ua~q/?q,oßR g;x}͓ތBξsYLy;͇[/{|5Z̞;g0Y._Í(d>eFgWK_"~d t ;`ԁs|B}|O(JMCD]̀SV@N0`өM>T_vp%q+n2Mz~g χ/kػxW{P?w=,)oDѩؙI1 " MJM65&ڇtlhv2it2ijfLD;阞{Ca%nJf2wܳw;@@Q`[W V/d'Γ _!hi qChv 'l.q%%N8̊r'\w9Uha v;UA ۨ%nп1~P@ {@!-BZph""ih*#VyZj`n65͞*xN|Eio|K_D'}D^56&ZSsWAK[e niܢ"ZVܤ9NjdH=AdHho "2TdH-.=0Ev%^^ ip/Bz^h Axq8 Yx prXE}:ukǚ y5W[sFO)ʻ{k9NkZQkК/<Ǟe>_fǚ{}sw? {2zHh:xHXg^ sG ,4@'m4oVz{'h^ctCS5R;rӏ!*TDTr2ZNTB++-4P=WV}_ a5F;a}~ưoF07Ly~O۴NOߧsz~DG 8bqq0(>/A<cx_—$i| x?;^/ƏckvCW\}|]]XUL5F+ajFRmGZYٸ߬ZhnNs)]|gx@;Qb}F?a:A:I;x|':JYOVƯUBGRMeOS f=MGpt:DkXw(ֲ9:ZzUz:Fhs:F}-2u,>:WA^o wpw\}>I:*8B]b9!Kŕ9Qz ~?^{3TL+s}T\ӓ7 ːzx0VvC@ܹ?nmttdɄ\]2O\CUș Z)M˯R}|T_a[ hf9`*f Hd^6ci2yWL1`0P0#Z'=SζξQ7<3+w11.w>9 bQY,<㝅w`5XA%#LDs(#t+x o'x9\oyN"x[SSluVW~brCn;w ݰ;-v5Pt`4-Bou>[u>OwY}A_[M{^[/j.)CA:fp 0qmgDa< =iĴA ޴+vuLwEuMt0`g3qzҖ}'H[ܓv]ݛ։z=iн>*ӖK[Ǵv'L􍴟~+ݒ.I{`Q0 (ЩԝԍTWZ*:lJ$'sɬLd(i%~ʿo7W%?~[WB+i)Dnvf|_>^/Ov---͏͊U*bi)ؠ7:w\8qTWȪ6?~gjC8d.x 2~%uo/.i*2~&u'mk66Xs̭_L"Q3Oo,>#CWQ=RD:LZiVuF~=u y)jI`m&S8OQ>{,,|OxFLŌ*QERYyjš JF*S%zXFE5R[3gPGKu^oQ%>:?7Ia_ j?EL2nSBz΋3Ÿ1ìv_A~2}sC>k$P 3'O ${1ȅb\D6H΄@pR9 h({3O}f׊$=cf4&RYwHo$<!9gE1ln5 6Ol]s&5;k5gfyr=K|Lr~5f1ǙЏN7^ 6w`?De~mΦw=(j6Sbf+fZuV6wf7?cYg;HG`u v5B9l>͌ ~;og}֏;>\ F C1 BHBKA.,1@ bx|L$H~x*9r̖}*{,2=ք].%}*Zk!6ZVX֜1xc` a C V&aJ}g:=oL0q8}G5Y2xVisV$NBuyij?B0`B,.JPZ)v}~͕ig7~Z}M I;Ôa|ϻ:zw JgVvt٢0:2Q0:o6! "!S(M{Us; ہM%ʨ"IIqV5f>oNbA FIF#+ "vpe=b ^;tiLp?$3r($$ ~_F~%zODw'k" o ts'#Xv(h+L1+bƔ?$/94;9u}7f2%k' ?>ex[GɞP/'2,Gɘ 3eXMzI٢멉S^eeX/B;OMflUԍ(3]~@!Mii5s:%.xEf4Tk7HrzLCoVI ֟U1Gfl&gg?d.b;H8yldK6V_A&D$M߄RR0%呱7MSj^wi M9)o`9Ә1\%gl]@|١E,gTeJr,RIyT2mX (Q!C% :TC*˯{]-#K>YJr>,G+*|dVsEC}"O㣸*29h82J^c@+96*DKI\!ã9WgǍ`N4zcO7d5d."ˈ/;:tEUZ/*UX*h"wzlbuELi..~[f B^WBzڋǢA9 }s+xYa=ͬdχ.lyR{(]/鼸/#A%,f@ %,4N0"mÀ#y4Tح#*j'N [(u΁&2ȁ!57 !ƸZ52`Qc7@)ؗaNJ89M uR*#os[Q|⥮k sBPQ % Iz]K;5Zu&B>a ߖ Dȋp9eǧ` t-Κ>e<}c|`2\Mihs|m0a]0cm/K0 dp9 3Wa9 0 f 0246wp{E7F?j4bϊj#/|_5bo1##>h1X=eyե'Ł8]Y!\fb_j~ ;6C25߰Mz~ ӧIOWdLf wwEIޖlÏ.1o^BwyyV[8XYk,^P)v{ !Ѧ_9g2ta*CG% ƚXgFPk#1utM1cҠR+eCCDNKS&g!HA KrhIE[U%ԇ>eW}$>ˊS$J)b: 7(:.:o6StBkP3{|زM }zE2Hyi켔dDе 4.#i3MCdiaΗKhC)nmp)5 ˿p)&:;333'3';+#=- slR̦Btz|*=`?A#@9K €u\Կ! -^ut4{#߾|;{,e0~RDÕE/i5@H"eD0k`l0 .MufIgS SYBl߀$ 3mg 6KN޷riٝi+jt몞9N_Ϫ@{5~qO)%}¿"%]ڻ)G޴ skRsNЌ.=^{!H|+Ĩ@(յχ^=#cT@q,+vA7LtZFMO 2A;˝K2~wnJFJÆ;./d/j$aGCP.-k~׷oy[Ŗn1g}}kg}>B~um@?zkkuB+b?; vS ;t[ PɠoKIϘ1Xݰt|ra"IM2P@G4 4PaϜħ N&$?.Ar=f/,7b_e'ai*ʡݾxuFsi]bґ߰`E VoT{/\~h]cEtiQ`1yKQbApÞ4/p* Sk$g)M.yo..;9$ѲB(E ?xӶo?kqmhdҁ uGߘPZw_wC?tL6'L:D"69C=x~oz6Ґ4P [7^K\ߡ‡2-;e;2-8@$ l>I -m^0+=aM^φ- ji0C D`e/EЃ/x%7k0s&?DFujA-'toP)IdCzّ[Vڴ֊7dB04#{'R 겻ֱ=S7ӽxw Gx7EhP.#tY  45oij*oY4}4bx&IvHTɽP1!$rMTOtL3rid0;*BPm {:3(1;;=551TO;;3љ谤@q0@qC"rڅGkO7tvL??ů>o<ՋvxMy5~>0l/Wds9Y H+V6P6[NS j%%](_T<(_ʠlo$y߿>rWx_g`]p2<9)+R q(a-U@ɕ8iViIt2N$ NFU D_$'4H>QUqz$e  fɿq?\5\U&ݸ*[RDح07PM%`LJݝ oW8J6 $pVNalX4 *IJ &욣 D9%x.hJ58d@@HPWhhG3Yvg~8YlҢ@q}(N$N0 qk7M ˮ\NZ~tґf 6\V=\|s?`9;DW#W-i,%.CQdX|XN/MNOuH!`5dQ pQiA * j$fwMeMK#_lW:dRI]S;pfc' - Br݋9nu>~_>r:lI}ovPzN8t5Nv/gdSc`~qPsKfN 0Ew"uHA ǽ_Y/aBeEE_;Ń] j z\,IjskˮE/ܾ5WF>V!3qĉ ӿ|V& TTpr̳f3uU.:‘sXξ Ü? szH9+JfH)ɶGpa[)VSAN<_PX@풨ZTUpʺN@JvXj#;ۖjy=Kݒ| 4Z{ZkKle9ȏ s,ˊ++ξ s`v( rC`FSY$YMJvP,'+ OD :2+~ =+nzKk]gu5'9;r6dfU_ؾsAy+.vM]`~.7RO&7(VDƴU|j)lA ԙ/ʒNtEo˅ ,(|+., K^?^^at9L9b\"Ʌ*=ZD@d%!#ت(G7-bp."a4Ӭ.TD Y`y-koMZJXPPߐ_y0z'er$\Y t5*}Ld~-2 ;ON@nggܶ(V$NwŒ vvMVP٫g@1 [-t~IAzy+Y$i!Bͨ3Eٰ2: Ric1@@稗$ݠڧ5;<:YajII4zp)B  w+ Ue慼 ~_C].,ɯ93pqfddwm_>_0Y|REm~c:5 3@;W88 D0,$ V&ОQxX` j+TT/}^/'UnziFbI"ƒna2w/3,+;Fl`^fnf1_qȒ[P5ddcXڐ_qhӵ:JvՑmM-uy9<~ x`.uͩq* Qށ0QjA6 S" pY : |D)/5†0=VSJ[UOtUΝ'OsS'`{FP3$s Yd ",]D;X(~ \.%,H-v懭|ѡ|a|BomMovuwW._;2u&%,=/>(ீa?\8I(҂IJ ̅எ5m"N(5r1LР\Ie[(5LR“NC7*ru-o}Ϟ`^=gs?sJ2Jŗڿ'j"IuyRBjs>2~;swzH#Hco~=J ,m(!5;j( t*gV-I;R3{xZik䅇XfJ>zj܀f–8xVVM>a~]g]sl G[:kpǗ :j@њb*Rb jFg"f?y>9sOع_iTI>r&7|uU︸f{Yoc5ޚZjkm#]Z?~ ȪqȽLB^c%MҰ-N']!zS"Q%A+`x#?G+z({NIkib+cdR[XK'WPHYo9CLQNP Y&`'7bfJqvɉtրZ-$-I#I"ϧ8؀z?wnGC(ޑޑU=O땯~>s~A=(WN{dG(F:BcҘ 'c&ZY.mn8nn\ p)6Ug'{}i`mΝޫES+)cͷ^% ]Su˶ ϛ5c. :go_y,|'xhkhgL',|_Ċ̓G'W m# ^4؏&S#'rcQD^$يtVI,rs~_^+z{{"?qعMW|e ◹x{8 3H)I2e+FS ,46É5(F#ºdro[/mX|d^w`a[OGZƑCYY#;ݒ~%$E.GUV#A~V,,Ƹ_gcь_kS~B Fh-23fImEء)ķnxk%4A 66_ b_!{Cmu_ EXԝ߽&J@C.>gQ(X H8xQ>Abe]"wVp»?ޕH=odH!]Ic6Hl2eQP$ʪM}[ɽ5&ʓü>"ޒ_z/{m|Ē[R'[`M"no6$>OĒ/_ жt^~λrw|KoV,Fz_~aB Me>E䞰 ΥzzUSdrsaLCsTms4}no*a^}sW(a͢*ٖ,lq^w-74Ӕig 4nһr=UW[{m_z&'c;c3ǣ#=2d˴6-TDJuR{J2#m1L)Mp JNGq6Q`F[! H1`w06f}btB_AJGy{ Kzs}a;4z!EAZtUukF7dn9=*mY)c#Gn|dU{O7<=xF,PhyE"qI9KrCB;U\؛o:s[W9|gG:}}G]y55O-fU҉Ѥ8eOL/*:Ҍee$1~  P, qa$yUd3>W=y)6 [LK&*֬vmamG(-eO׺。H[9E.,h0º:`.n 1 ZbNZ;6:$@S003n8Lg"]n<0 Fu7޴H[ozu7+Z^}]«\?][τ&2E,$VaH +^1 ls!٥@ZeMŠL/l'64X=&߅4(ģw.*im˿}~bbM#F h{a{gFXi7&RCɏ h{ 5E&g$qDgt &(3ѱxg&F#Cab0ԢFuU7ȨvrVn,t.)wFk6?4" 4P2vE0~03=3[z0nAwJrҼD~hkqhkU֊շE"m@yN-ںmjveba"Ո_=rVϖuo@8F=Cp!`UYWږ =};J;`^A[?^ZvpK~&cY"#VJ.FeRP.?g) CZ=F@"ͳP$qF{$t'`aQzl`fK$#R3Yy ."OSȧ:_޼uIH]Uk7 BP^!_'f9[♰*#gE&Aa4=e538T,F,;և\z‘a-g@ԐQ;bp USfGAމ΂^DC|@z(3,XTff\CJ8愷 <}H4ၙ_<d5u++rm>PRp1kC^(׊rNv䳀 d9%Ʌ*N~JfEZ?{ ^^84V8A 9.޾=2<:-/-X6xxMZaOw4-YfOڒSP w7p_8nfÐ(ߩ3 dHZАEmlj⹃@Q] AuHdyٸcwS_UXTa4mǶ!N]/"5JD8x9(H!by\ĸ9t*"7bڬa%72[ȍ|]YOsohXzQ_NN3A?^vD~lvtaXiͲm7_2kd=V3x1xO"8pV=z2-d%ib Ѫp`a@c<"hepil;g˽twt[N9 Qٖ΢tY@4JMx)Ji؆CK 3LAk ThO~K<_#]`~GV(uԟ,o Ppg1kK6%lS[ l3 XLU}ҽ=}mƉJ^$k+ֿcgog?<3y hߎbR p!qt@O.[q;r 5ېC77[258v H 啥TKuɴ%9(9Xo]!]}-qE &.OxmJG}.5Wо{Rl+ir=H;,)mkO2Epy<`gc F95;N#!2. ##䦉 MCwpy. t$]Y&6kf89{-Ni"I k´GjQQog~f^};+O?}#ɜL͗XڛeѥM 7h2$Z44ۙZjѪ?KXŊ 70#U~oe_5_;]Β,f0iHQH wNHq#(1S^7ӣAΚޮ@uVkm[Nf4,I D =#my\1P":Ve?P'HL/-rYBNc[-  f*{-ؼ!ҕް)֝tgg[[{{feu.lZT+3|q/VVVU">Mf_c爓5?tGi?Ӏ2]NzndE;ݞ'؍H=YS\HUH:'s3QݖY.e4;3)Ȯ UT$;!E9PúBou=+vtvj?\.gf#%!=zmaHMImi ;KK "i_==c$]Rk5B " {0F Elf[j^Vx.+kJݮTEaell30l} PØ~MQPd~0Ril1Y٘Ņy~K|$pEL 6Yت1CH~u4<0y#N?@swßZZtQ֕G+gpVܵgws"gZ}BBeGp7r{3Q'Ű-#m^o4ȧ8Q d mF#*3|~V; 0F\`0FTW.!E~on+(E\lȢ٧jܲS'0=+zҦ 'd8}']jYr*jp58F$:>\zЯЧgd,m\ܕNKLXIfN0ͱ *xB1tyt.C*+*hPIͯrӓO?e;5os%dCd;Ag3fv!eX*;4<8G#YҦS;|yt -b9/՛1 Hy |g@4LLzU Ďmp姷0k簄ssqJrr ~_JNJvﶤvjpW]r`9˱Knv䶡m55ۆ`Fv$ƱW=gp }Lpl6`<5^wKr;)R(߭_o NS0P9f { > I޵ 񟜏d$<">pDʝD#h5L?+Nd_αJ Kc>/ٜpd,Gf>Zp$A0=dGo/:w-|^|s!۲%m^ѯum/UKӞWGu˞B:1<ƈSX_)J-T-?IS8z!o0yc'U t%ܽfG p4p% $`޼yy6,)?)eVp#(.| S)l/͡:}-Ʊ%'YPo~%w͋cȔ_ oPDtI4xB6X`:"-eO6<P8|>&__^_PVPZ\id|n% Cѥ3svA0PQ`y3QoZ*}iM8 4ȧJ)kײ(&Ɣ*+vT 2r+HbpKN1tx m&B[u͛-C+)2%UǿRR}}^#[3n3t}N߹Iz}}xpV۽{hz#ȿ"NdqY!p+֜q{5'8)㈀"s'NW߱u.C*aL;J8_}֦9S}QP8Q9@hѻy.فtHqUaUs{8:,u[:6C [aU9bG  Z¥=aK$e ;pR:5R`LteR']D!RFd21L@&xzYsiiii0k0rF( \юҼuGFWDެz DwvtTSӌL kCmiGx25('Sc"g?\LBp*/th(Y`0D͓'Q2E'@?Lf<Ҝ&1m4vdS.NHA bSgb*g[df'fe7ߴ(<;8"ٓSW$=%cFqVwN+Я |qRb}O$k>?'rkm/In-B۟Km_yoڶ}Fm{ ]6G[Ӎ nQn>#fI"]J*k>]lz\x@RKؖB':JMRr[D99L"BV_{L3qTztl!Fd.qDV610ehnS 3#Η9AI{B[L*֬ W0ܾL=k"?*a-jsVFQȓ;CZ}*rKK{bV_>_W{CBeZy2ɗkr`\+VGQ)I,6 I/U,,, 0G܄)g8D;dご$|eK;J/9޽QWydkQhK>yxzkلX5D?_G7V`n~_+)\- ̑imd"m .n/f'ǎl{>yaO4:{ 5x'c*, s^{Csv!+Dx+R{bV_ |"2%=u?@P]8(OeR Kc `B, $8t4'.^GI\\3:rZL6I\Aqs?&y1t1M{֞M‚/7iON:;wAo4r[#V4y+i* o_ݕs{<e@S(@~ҧ#GCds[Sgdx# &$&7_g[O  j੊,3JyYIIcCg>8?H"͏+zo($NNԘͪslEb|S…yFt==%uZSF715O[I P4r=Iيfpy9W=xǏ0_L|HMʒ]ʙv~!Δ.5\vi![~o}e~[ZK}~ϴC9\T7Wpq&f0`עP&F!F[VBkUFGɗbk%2K-98_zD>uQzȓjfP-,[G1fi$w,w(^b8L rxUU$nez vf_gq1&dR}oțK}EhZB86lY?;ف@9:Ag~lV&3'|M;A0 b0z~ 㗋a2(gZP8pJ0~@nTIMş;w *MM+ﺫ`RW͖0&P>S=([W!b7*627{A*˃-䵟&£;˗B#(H4φ9gf΃,Ks/+jk#z'4d9͆ϑQR繝T "y ʗ!ӿ%?Ry5<o<ޓ-E[>އb𨋓Qgg{2שF_缢,^ׄE 5ϑlJz#O0OQ[<9of"Rb(k1aRѯJ~Eoydݬ39x1 9vv*eug.F|/3?vx]Ao(UnĶr挣tVb[)x6Qi s>I;WW߳xZۚ!5QPu)TrE|e쑳b8d.׈kjʔUP.^İ?T+He)ay1?G5/+H:o( y }Vo8#@n÷/2vWq_o.b{][埚 s@L΅]b|2qGm#î`1EױlpVƹc&#aPRiq.^ɸ˘iI3s}$E}Qt &#Ӟ窐zL׭pû6'& {9.%3HOQ5ǜW -u]{*wvEQY4P w`u ƍȹp%y@si!O̤.[ϭgxVdQqp`FbbF\W'b& ^<ov)t3U/lʄ 5Zj=6vhProB2 Qe mZ#- Xfx? Ts+;CZZ~"HS&xãd+6b }3N!ЗF"ڑ/>EN@I*@Ks`@\ 5G,)^m { $nP 3㊇Z9߄hܻ;_qlL,o2mk+0k߭_VcFnTިlE.S oi2  <{bU_>, > K\.?epGйEY~ޖȢk80$|hրX "[&~WiHQiK©"i\nEGB>kqRGDqIτ[F:To x~LoUUB?¿gTQxqF/ }}HUvˮǏ :nR~|bk]B' jbDdff+K$dgymEEeŏXX8(N|gg oޔ˂q ~nk&b\?*A?&?%+=/xYVl2?eSB.]n,]PE#dêA=jSyg5u_t iS\hjS)ͤ^b )7̼sHAnu& 5qHO8>ı5h#KﰿDҠZyNGb *#p,-GPbv\l,htw6RSmvB]o޴Դt}zZ?%Ab!Ytr+5?5i TXe@勊lfkT$D !-jv($NQCA5wL䗄'z}ZGÈ4BY1o}ag~53HZfo*cW'ͳR ٜʰ->&(CR5>ДҶ-GHC<OM3D}gtoj3\7 :m)7k{@\x# ,nȡgbKb@0^#֞ݼp``QN#K$dNL=JD lycDvN=:S1d-|x컚:4?b~qft`qJNVVNb Sϰv 4sDHmE?ݕtw8kH"-\*zLSR/]i)Sʧ{vGAܚ*`­@(>ΩǏlGO2ÜSR/맏C[nҐڗWX(FZEm XW^!-M}Xߝl$Trەw0 hC%d\Y:-*^ts`'H A.M|HXB,(0 7j,/>Y"MvS.vuB"=zDOWDM pZZxP40:uWJNF-#\a!V<ց6T2Mu#wgPzvO䥋Dv3Wl'"_]>ς')˒%$H66z8ES"i 8^$>%RhzD9m܎U>Osg5}Z?wlV]~p 0fsG [{/W)75%u8 xKSc>>aӥҽ|&W/Tk5y͵w**zʼsnWq],dB\N~ (SÅ ;v 1+K4j|G>^-]xjO_{/Wvֻ.xXӱq^oiw.|=OӋٷXj #L;㣸I+X,VΥ\/'Δo]taLqi!mM4ci)LID.Yg0r7d󬽡hwsSR7݋/&} [\,2 yD^KI՜9`\8]Jx'">///GV^^$?ßS`[u9ݻo63,I]rz\~?ptʿmZqUxr\թkLƉ*g:J4<Qj.Vd-IgDGxx+YqΠ0OrgI QPX̸|BICo)uQ$e]<3I&dyOBބ !B$$<@@DOP@)D|EhJ HZkm>jkѶrksΜIz_>zsd^{UK:OtZ+DièdUaAm[H.ؼvH1OXJ65lJhʃ.Ymg6|iR:MMm2^%(8P:5FcJGCD9tĄ^y.n4#pH $j=r;e ^Тj)*/Xwuw80mZCôvȢEާ9W 2CW }pRf3.>$U0@KzE3Azi>%u;Hk D m{n}Gei^g yLz4/}+.B%T̐2$\wXFBո1UWARS«*$Iťx.ůÑDe c{q#T>=byQq\VC9Omo;}aCLRMh0nzw!7[!$YBT ipVAS4q')N@"sAȓ} -]gkuY9f2Χ)ך1)Q͑-EMK*!Y$){E%c|g8Nl>oQ*+nG jزeӻشbܞo K4EƬfӪY"3IMv)4A&q7i#5_|qmr# w6 q5"hY1hnT gOДx:xo-ރdJ8SMJi5l`d}HB Ѥ0z6;NyVUsmVL}ʦ&M{\wr-\يaoPOc-ᅍk&XDgjAq1{-3مs.ݳVL%#Bvo ;Ġ[us7bƯJ,L_cYMgSU8&"uN=PQƠi5ªBk'w(%H[a]u,Z^! 8&)]^ʞ/!H=ͣ>>=Aϙ{G#@>tam{ ޣOP+AthΎkcn\gLjc/ˎ˾D c*H75#NIfFhꨱQRsA~ ߘ:V;yU{KߚUK:._6V nlץڤ O.=zS{ٜyfUj/~l܂IdQHw*R)D +D!Ջ~\V'Rh:M$FOdE R΢iHV57hosίhVݖ}2ݓ,4mεxRWWQ~̿m~I7Tdtj _V7ofM~iE? -ZE䰉䛬ҖYzYcz3=O}&Nwϙ0.FhN&cLcNh}=p Ǥb,4pw4=fimREAF9A['jE@Qp[=,) tGې_U$ ž]&xsNnk4Dt9aQJ:8SRp%T683Ӑmy=j זΞY_#[׷,<0@US?г^qbH3E\_ ݱ / 5D}7uL++m]2V ͩ) ,^K 津No: gŭ8 ^ɽfDzИEt Ž1yRJJ+i+?OvY) >;д7{#|Mװ(gtJ[Gv-onlmF*mMm%VZHK;=XBVN\,Sc'xN-m,~?%ěhO(^=0PC{޿MƴڀtMK*]q X_}Ƽr9Nl#`uuw5;64ENnZuڲh1*/k~ .' NFR`zTeSD(6˟ S8^@vv{sTlTVMQX, H؍Bk4i ?Ztg(P\tMmt]s#oi59Ok8# 1>&͢idWOFG4'G~\6*@Z\Y7e=Aˊ&R69ꙁK gn;Ԩ Ptoq`^zuw4EZUyGIvZ|a={+`Y1drˢinufnj :x0l&X3@-%yW4*J dDR sۚ=Yu:;˙v}7敖Lٵs7T78KVY9gVi&7N-p+ 1=9\֯Y~y庅OOLMPBN9QUhK.!hNܸP.;Bs:K}X}x 8ςϞVfuzɇʼn{L[r+n S` xG2#61m!3(ځXP NSx]OXxW_u>uv1d>՞U1c]Ai5<Ěgc+E ᰏމŋ>1{۶{Γ` gTtԋ%wv:Dd& ƊyқC968qziޞW -_iٷ3i;uܸvĝ:iZNȘ:.H͠ rhD,.V"⣡H}u]Ӌ笨ktǕӫ}Д%-:|awRŭUWyFmռ'AG_*$C2 - ډეB)HVW BO^fFhFeWule9O)\xRR$f³{ɹfYVU jZ,%Gw=ӞZVKPH-pXpƫQ:Ys. t?[S2q~I{FZ_M2MR#Ro,B(f/=堩61pw;5Bd4e,/24_s 5RϠ(5JہXH33bMHQFZN**LBH:MAA;~3S MN~7.6֧k;[s~nvςW߯2ET᢫@OT^pOiNǜI\l%5asXGsW/x029oE9#B{y2`LQ?NxRToRujVΣ9T`kQ k|*4]?txS޿oGy.Fg494Oh?3WC<7V?a|{E;̩po`#\&z!wK}dnbTndmIVu L&y< IL颸ymF&$iii`,fÀ-!2gd)cW6]Fu-'YӼ;n{,T^k0&j{?}-[lN5͋fs&<}(O)46!ҵpMb٪+f2jB|X <ͩLbΕp㗈50.~~ExOR_;M#ՑJ4j)6\e@~mQZ\U,CiJM\NtTqicJФf+D ! bIׇ**<)mrkgUV*1.flAwJP'w?K֋ɉd Ad̘ݣJHG=Q'IPJ #^쪋TyPuK[`KOwZ붯KM]+Kb$Rqzk$a]Rp1z{֋+7yܦۯϭ"{֕ L CAenN~C%;Jw(3RJVNf.B80wKJH2wcQElh jԩtP?J7Ֆ)s}=~GM+fk^>tpX~}W۹ex.""bkҕr Cn<.1T WOP}a,Huѩ^YM;k=!l6-2 5$46z Sb"0[ݿ,I-3Xj ʵϬ֢W]q#^7ӣDo|:-Y| XQQT!p F/Ŗ,ZJIhk JX8 ?~C: 2~/>Bkr;6TvOAegRBEӊr˧{v6 xjhLCd9$P gБ 7ȺJ7yL[wG}9҉j~C79ņV8|~5ұqY3':*\'TgYNKKWDC){%E+!Q[aa <2\zqZ9{"'cwHm9luD1j oc_cL)iS *F/7^cCG:.¥_ Dꔒ):4N%@̌jC^enB9+6S@O7)k[ɚ?KLv&n>vhU "c.%V(սz 𚾠 :k nR?Ϡj@TJ2d_C1/~v)GtPt 4AԺ[6sϙ9Ys/[?!c.O%Fr81B4r*jzUeת|:$DH\d hS$1X߬(0} !JZܰU-7:!kEgVTӿ/(KG# ybL5S"laz$V:))..Na.N3ƼpBo 0#1 \Y7noXؾ,kŲ%#i:ٴy)څ3z Z* O g/XKS5MPT<&dff\ՠT=L]UPWXzdy+nmKe~~x}Jl3 PXEk4x] uXHDe̮t&t}O}_\Mrv~TrNk@ I+BxvʇP z줎\ʁϸ SP'`V=ڇrW9 "9S=)d^1|+7g}:^ @xꥈ(WY8alj*vq0. $<~ 5_b~cNCUq41ʁ //@hLKˋJnm``y‡]WkhY痠hX=,~3x+uy{ eq3j0fEfH3EatMl{`ӆ=0=-5ZSMuUCۏW3wfȑ۵ut͝۝DOʼsrL֯]z'4L5X2fZm²Rn*pq BD29 u*PhMP^Kq3z-|=/脗^y4r:B AQo 4@l4b+qYbFHuD,apm; ll/)ccFI5S_u~.tص"K{ ƽ SSKz]"czς0"sD9P3i5T4%$A@Dze4xP#!vViZiz?0>k<}&\"TǕ0yKȔ*MTˈ'fM@Iu5&Emh[|n'(qI*?LA9D–=*oQwn R@4=9E2`p;dlhEd* 5R0KssS>}Mڥ?sBoa7Q j5y:[ܮ19+HIL+Qbݸeԅ|{͉\`Xrz8U$?$w |RO֡o*ufW3l;c-)f<2.;UV k @Z1HK%{NV5=XϙAEN/M)+i~gNVzd{mp3e37P^IuR[YbN'gxrse<9O,tnbφ6.:kǕ2ט;A9`'(6V3?~Y~=zE)ons WT8sbbǥ`VلBe1~\[š7p m`^+z-wF.󎣦4M#4E8*5h1у@w;MM3}殓箾FFWDbl%:EL{(˙ 33% ӷI_O< aAc . y)R5O{ HIk[V˕UUck7W{E?apI=S֊(u/:J 3dz:)$ur{p)sNW3$Y>K[jxUșUlje>NdÍK<(;8ŷt3C&;Բ4LMC{ڇ C }@{NQ>5__s{ߺW5"ے_:ޯIj? /s]IIbK Jg Di0dMK$]f+_dԮ&C}֯gdI2F?PA貔I[orQ=ӍN? N9%7,)xu2?~^{iUhmGwby{,;/'& :}֯gbS-ydǞgTDkІZH`N'AGg}NbE zy+Yqn I1wӲ$wU̸EKC5. 5/]kw,ab1Xk2-QɜDӽE>Kg,M&3eҝ2Rb:cI:vhjoicMOz$wxϰ7-4̬4' ^K+FtCT=e ))Jցy*a5H`z1:{ 9{YC}cW:jnzϭչX04f~Jfps| 0V́Oh/D7Gq* %y3J CgK S )֡{ N^>XX4L`~+u =PQ njv }oiܱ) T** WO@^7`]S }՛;/h9/Г0C\`W N&C"m ϛWU5lCSNLla_֭88sad"Fu-ΧAz*'DI;R=GBl|sq3ߔ+~ewnO{ Wxc;9L>mC4)h~ZAtZ>xH܃nLs&dөRru`3 `x`drR< ӥ-\5|v8C5-f[ŋneIdn k3-=a{d DҳΖ'G~ n>>QgY"{ɜ,NN`dv%uXт‰?x}pψk.s;bnZG*TQ#1db9۰-d{ƿg ˦!?G[,X)+&Ydgo 6͘,x',^$`Nbw  N[<|_-U]\v}շw%#q ၉ >6Q-ɹƠ%9VQ9j)Ilp\N8h8N.Rk)f3V\fg=^6 L̹k-ܡ'co;kVsk7 ]fŚXx޼9t41B. RQc t%כL:B1a۰K qq  `)!Zӫ-/Yپ*+Z@ŁE]WfGƉj+5QGA jT"cڡEwL6l Z'42b!N!`}1^*uEQFNdhs?OņEK-_bd9|osOwﯚ_ d~ g9W$MQðDĚh[' P/V6x.-?`U=1[JsQ\ɪbƭ[~rO?D& h|iH s{ZCgSV|CpZPE3,:c{zPiUjmhp" HkRܠP '` E^L?sӚm9kw9 o]?P!W;:^}LKk_n^sjw6ܴg^}]WwtNdAGXM8Q`SIZDMx` C29e\V+~d,]'WUJr?edQ^ŽHpM0ssּۇ:ЙY/=.-\12wA{~@mD":\5gaU67+3WonܶݞGjy_;~+/F=QD|"܆=KT\0SDj-f)6`{hKwpcNj 8 ~_|fD@cD fNж %JOà .;Rٷ,X8gf[5׶}aG?E;Z_^mʲm#gUŕn#֍Ͼ{4 Y\oԖJmz3geJӶstf2H|Fd#eϜ8+bi tx㓇NO<>w}B_d^wJuu.[p<[XeFmYPiL6̉=~N֥.3P/H(wH-E~!ԃ6W 6 IMrAYXcpp-_wh΀,AYuCm^ⵗ^Zm>nM)P7yelou 6~ )9ԮT6ZWVcam8Ą\GSBڅ,#{^HClud:ud=Oe!Vi]PK{t5*Pȝഏ! ܛsRtP$4 ^ 5];'Go[|Q1|c9F*AtkA[}cKjj+9o9[/s @4g='~[Y9e3[ގ%莦O|!?0ȿ&YXju9TJ]  T" `|0ޘ 聪\qQCC"&8S  ZFD%{奲$\`LyaES῾uݾac-:Գ>vܨ=DO' @.c g |LP E#Q v 6*frZѤUCBSat鮝%DRP\m5d_BǷWaq:nJ%U_So!gt耩*| ښ_N"ܽgK,/6!I(\u_a\PrrG$q@B]Zj!_5l4E$NhLg6ΕF@aj7*^8QWdn_䗕BЀ%\\}#܍7\ߏB=h$d#H{+7>~"n1VkqSM{fh8J_M9Ѫf^*-aKx>;;nfi~ g^U-g> ̂1y1ݬMjG}VjŦ""6Ad3hX%I[Ǽ[Qd D%qI.ڱUdI#h͗ [-Qk!YyZedLD 'A, $F+h [pl9?Vvh\ۂvOFK4bZ6wGJn$$- LJń|M7 LH$K I:H!bA?27 nZJ8 x'W=]+GFF1Cin3)`wIsQS[fc'bMHDuvd$Zc48x)=/FYsq{W ]s8c{ȝք3zYR:LYN\R&$ML.C:DhR QY<>i(NMGGK #gJP*wrP(1<ž>xF8/H"5$k@? X䡺 T"Y4T\98/b28Y"˭>ZXh97柬[?ذwO`>Jq>D64Plh):wed'@[@BzYX' $d?3nCmnعq'mƼIW*Zr[V3L79RZ(KGX_$SQbZ$`N?QSlq:Mm?*W7o=b*)/$xhjWa$2z("Q9`>eOfE4gڅ1 mVh/hC4!YQ*87RR⡍^ (Th$-RuPRQc24D>i{INߛ=ܓ|}=Z52kЬw{ ??R&$%@lpP%&4.q縷Cz y4ӗ<>cF4sà7@d2`J3.;!`gwlyOϨdԩ747S{a Fbm(:`j lh| 82Wd"(b֊({֊`~YYz[[VZŋFQ+"湑Z;JиyZSHi7Ё¥* q_چKsL-tz2eag#*D @JX'8L񍣉eniA-:0W\qJt9̜_՜l<8+F|zBM uQX V֒urpVJԱDK%Mg%ƢpM5TrR)_doa.wE:#xE,|CAaDpp簚F&\t4؋ %Fv嬲,[S3<*/"pP(-|{ѳpu:hD?Jʘjܴis}c$Sr,ՖfEKRkL{g?<Y:QdUVHa ƽ:Väu[ _$E.oZ9m*1N_w ș2^>+YRFҐ O Bll`8* j5ƚ(# i( e@j?*|")3BxcDiSX F߯ㆺrϴPo=] z}|u?WP6}{U:/U>t;6Z<\;j2#B,;ꬕV X'G^bHOOTSK/+5V Y;;`Hc1̅V/vT4n{Ѡ&޵g~[TTo fcW^jQFTF&s֨1* Ȉ˜IIl 25ZHe`PPXi4EKN M ~Rk"BH| ʯ0Y>#D bҠK4$RW4crf_>>$dpj3&gq \?ҨHqŹ'hexeQX&'O\4Ȇݟ5BDL81*}a Lq{ܓV7u\^p2Lb\㦮 hwևc}Wb:DN/{Z(.E񃝆R }]q^8**(1r{-zAa2D;Ƴz֊%:=>g8`H5`# Da_o*HPlqCGb)ؓ,/_VxQ9|^8K&'nN ,,k4"׍TK洉b9Y9&(oZlgs_[вP]ؓ#ȿz¹6OAW[5YwdhZI,",O6$p >:"P4@% y3J8X1MF]𛀸)z+lV_G W/8ߣZ{*Wנ[;wgn;gi||mdYd t$SjU7 weϮ눎OlܼXO]9.蜳XeVtN?'S.?F^@?KuܛϟW)IYoRo+8 ySSFye%୴kn*S8ehPudhBjT ]SRͮ,.h;P.. Y#Ͼ=СB=mhreO:cv䇝Kv #O,*5|2Dlr:ҫ)}`?A-na'2,AցFq#V>$^ȜiSdOoQTlx<FoYhg"wa _VUM-gkOXqYG8Hd,q0~=Z-` < O3Y;%<&ORFO{R0BܣH%.~ne\c?59-]1!]usBieW\/ h2A0-Bb5'_C^Cdy=K1\DtkʊIEmmxxGqi<+_8}M)p)myj Et;KT_S9).+-_ꆫznz01<*a>2蟓EeѯiW$(уp5Dĥr{OJO$sa&րvl@obg~8̳ԢI͟R6e?Y׷w,Yl %]FdP0I5kAtIzԐ+B kr dJ:K-6,NaK6' )FFtvn9}(/΄:io͜6mfL2p!ܩ3d@xN*FvƜGBgN> 5LV)3*6蟓f?m^z; Qu@8{s?~w^A=gO:T96A̦2?b?QN+jP{-x5N 5a_3w;i& Y` k%R~0~W wy~dowh;; LuQȁTrƯ@-;dDmpE*BKV!NK9mBo> N-'P^bLҨO=Y5)Krt56u'GψT*“Cd<vddhttVף%%J6FhIdSmfRRK6l42fȄi2 HQ^#$L5"g(o${F&;}?|SZY GޛFUd{Uu{O{'N{7I4$@BWYDEDD}WܙqtTqQQgFAf|.0<(N]v{ot~Uuԩsj9e?muuo5bĢKFY60\y˫zMrC_jׅ93Q,h#}cǀ$wtSfМǢ\XH>U"İGta,d>Cfc-qF%'[@`mR!+V^ܳ41fJi\færqyEDk=gWQ7:D]D }̿eg?7aVΓر-i+Jixqz@,yN;B.Eq&k-7;>қϹݳӟ+ƜYga{iY?B"Yg}ڼG,?;|)c dl&f#l0+kZ,:= -A *AS+]i[%nOގi]qCCԙp^5ogZhk W~G~kiXLC/= KRTA]jڬ&[Sꬸ WBS0wђuE/0#!Mgle!?Ҫ蕖5/\/HҬt!҃WȊV<%seX&Px_ǚEe9B˱Sb#qGxNXY4uoڶm6=~q'~bK6ۛQ}?NiUi;KV|/M0EޖMgOy͗n'HeϞRwy'%+zԷ/.cd-&6(3\72߈JV27aMTZ\nJUM;_U,D5g-|pGH(u9~zDf{et+`=(y@G DDѫ|¤R28ZZDUo[,& v;=/)ddKVۉV]ET{K-~位Y5c&K Y<YGU;z+z*X""T.mYxKW|5pEU cZ{Ȯ<_- yA>\|[e d9t[e NVق!*0X0,]NJTᴡr+yG=]v]ǙgLر;8v\}0P/W78w2MAT1=ib繥ȍmm|NGrȮQ#v}-* g1916TUx<%0g(b ar|=\jŖ%]gySڙϬ?8Ƹ۔߱npXCq_ރ/4kFi3ҁ%Xs𘞗*8iZP93xAt ˞εqQփZcK3rC%MXr>|*7L" ٵu-g6g>6%69טcЪw] -]}qn_6i^S{u{uzu/)Ĵ.mDK>ٻwϜGwO^ \V6ruSf@Ϋ=n}¢6{w3]lw/;T+`ʺoN2ǀmQBqa/-6v'PN‘"q^ 8~Tqxva(ZhaxəqX6U##[k:P Uq,er4{ s$Hb0m6PG%Q Yѐy&x4DvΝ7bTQ_};tWoţZz84/E1?z"ad{D/ #2Oxo(O\oOOlPa K7HxϘ:rz%,q掜='WQWm/t㶦у<_7 >߽?Ջ ;AYgCGZ2ݠ'fw54;v` .z; Te8к+lJAE5;0wQjZ!y| ^KA0I9tRr\晢MWK1|sZ::PϧTK|GAΕj>Fdb#%0.E42A¬>(k@AyɝRgmTOsW2h^~|sW2x-(K}w2`5aU^ü^׌ՠZ()V݁g>>38llwmL2 kgċ'Fw8ĥN5tBf0d4&iIE)N/SħL>îQ>lnY[Qeqwd3>w­7P Gqvs\]7dXg8opiIg:Wڅx$:pI ToZv~~j{k'f~ʼn5BLy{>ʊ.'۹vN1/)W;~g~Y-=Hfwڅ; XGù&d?A7g?~`vC*VL Fn3p}!Y0'&\0e̛Q 7pٞ9ȿ?\ff_P̣ ykh W䊘'5)#셲v_h?3O+Go~&{M«{-.ڋN̜o21#fyxfbWlG7~ J~`d*|"PI*|k_!T}m89+ѶRl;9?5} _Ƽɟ5癘AS?cƐ94ßdWe[A5!?kP;&d(Gof?N??07;xݍ%Cb{}9g œׂ7U AM AD` ]O |H>Z:{Ag*EY񇚔OoQ?TW,׃H#1{?k2C~ u{o P2|.;KNfer4v3?Tfnqɝ93 ̛Â9_;3*s( 3 ̇^.DvAEJ}]_W.]H_w*v9|]J" T!rJy~ 7+v9|Y߸a 3_u~i_rRY0?++.Vk1sx9e'Kh9SƃtxNvQxNRx2{B@ݔevثve2̎\SC`e`(þ*c},}M2;ki` O~:>gI`PCx_{aP|n{P|~SA4+FEƫW ,'vev/2Ouf`1֚=t ,ǰXfwd`ea_ȎqV1|ƽxď%*SK5T<Y⟖%J'ǃxyduR2e3LoȎd`* S`d`>1vT)X_d`e=#XQIz7Q8[U!~, bLtґ/=/7cN ed2_'ۭXMϏFMrk E8y_Ʃ@b+#@"2>R' Ԣ3h7Q((nh C&t#\@=s5_(w Oz+r_r9a/Ur01Hqȅ`V z~PPt)9RDo_0Pk]e( ˾\)P3wcdU`xcr:MFS՞4vW8{>nDը'ѩ5Ew Z=Vz\(TPP:TU6 X())) d#Q%ik^gɧ)y8j`̘WW8ˠ =_ p7+E:Ie:*:/X~u1^&aa^q/"d7،9ϤUs®Fq-Аxsڻ)H_Te9#h2{W%#KFZ.4TpHU):6%@i}TU ]+h5k5WqXֶIbyWQ᳆<)'hxvcc`MÑJ Q)zzgPe<~&R]њ~v.^ G-]ˉ_y *74Nh^h.[*Jm}JͿ2 W06V|WMó%]?&<ц'>׀9ؠΰS](tA675q 8Pi8 ^3/=e>0m$6mx[KS}J3@l1hC ])_F$w3KMXGӹ\cc_d?㵮@$5GDWq7K*9rq.o.NjDZ$U8y>6t%Ƥ:*󉖽G \;դ"DJb)}mmjhܢ'ʋVyժ\Iksxr͎+š٥BXI`*ŒыzR_YPJz fMh@ hvG7a@T _Wg'0Vr;65[Y[;&}hm}.4u3/y6:γI6-}x7[ yB=DR=UF3*wr^a0fSPzÚ@O5LG+(L8;0dn3֓3kxwS_㱃I_Ѷ3&Q4_ jqӭ~{*1|Z"1-J{h3$vj?[Q܇ڗ zK6*QBORsx}Gh~H|sܣF ciee>PWBƽ1կ Ai;FNցNUȵ-'j=1J^4|ڟgrEAbŘ,:( ʗ~I^T?q29 _aFQ}q-/*ZџWAR0y{Τmlh֎Tv$&r'Y,sc+sU|jL O8{hNw-fs&O64Qt|P(y t3:pƓ'$(B9wZ(S3 l #/1ע"ėH+GtejK;:LJR ΁rQq0y*ިhG)Cb?h1ӁFLΙaMmmY tҹ4wre6ҹ)ф}e1WQ+/b<ŧؗ+/0FأٰD5L"4\k煣Z,Ge`oi~2MF1^K|e@*_zC(1/(6(_eUFN*iVSA?YLG9h{cAvMJndP/}:j/ӻbj+;)yHRAjž *4:7{ IcgH~)e5/lCO442LFT):FI%QazH$%(M[%HTUn&U|b6芄rEYUie]k/qѵtFo= awٜdr:GYP:)ښL1i5"F2dPjǮЩEeIChAD߀?m%3,C}v>(}8T5jjߴ}@Ƚ]6ԃ= C!&\(TG$C5A &Nm1szZ =j`c:J6J ʕtȒAzD4*nLbnNjh1J$tԶն65DcњrO{.jfo)_Q/pR\ PTΫmc'LJzb"; |I1c&&vNouD`Fޖ߻woS/N?yYlG_,@L=]Z~ob9/%VEu[js_xafu⣅GOGq> BɋG}Ѹa M lLlnrĉ 6>iЅ:SsIEwLI3j M3#p HxB6AB#K4k~KLŇ/Ϛh g 6Dzf磏7ߜ-=@AzxhOo]7  QOp}jȹ0M?ꣷFB,ukN]1֥=MSEpZ>wZ!͡|S<1P`g?HvA~ 7#,cE<:1>spz 33JL7{As3j}C֨RzQA3WNHy_f>-ȚTS) iM5!=%D$9d?Z#m;EӗIHgxQrᜒ-s᫜#&9: n yo˻1S:FTnm^?e^TFz*(a% Lo9pcS:!Ak1F%hЙ07'Ck8N3y @r8ZQڵ0hvzgqUc?24:;cd߽mϓJvt?nh4ĕx_L\Հ&ͯ57>0@Bj."MXkiSX3Ѥnv]bM\gYE7 ['z<͙144Gȿ-YY5՜g1uZ(f/j0OMelȾT8GLo^W+s:WYȄ8L\w o?֫''|JފjFlh݈흡vuM5a+ؘ0槭%}cMAf( Aۇƞ4&A5,ȞH4g3PRtA<9|)`yBfET#pl~ݭov?W3̂Xx٫]!uF;G>)0q,(l֍k=e.,xmkW-nԥ.kkn8˼F+T9ֵW ,5~K_r9vRXp7doMwvfCWWCcgw}ςk!ϐ} ߉Xk@$cV3uIU*o\|V?6*q5%Fwf5< g'ОhO|g.y qNFhvuw{.Ok'_t;}Uk~őҿ}k3u,zs^|v&M c3MYG#YZ2xUxVu[|~{i,豐?)zB8g דez^QXxѣk=5ȃO$.?8 #v3|2I]qMWv<3`2^6vB<ܬ`T{սPN 99ܝJeH﷤6hSF%S4*"s97}-^__j_q-6ɵ`H_/&Mos6h&*;{5t uP`s–<0֨ 0yJ塗޽vZ;ކ}/>>{]M o8:'?h&'R0RQVǂ*ڙ)v45]Y0}L<ڰwPK A TʇE>'N`(ʨ@קTC ^ { +/[:nY=g -JU\XwZZJ4kf^][׷z% ca9d9袚'TtE0x2W&#]+M-[kC}+ ю(|O e'rsz5*Z|B):GcwKϭNBw'/9{M ϟʾ6wBt=|ߞ64*`?>Mj& rFmLv|d@ =k!G[l.:\P,!-~ 7G\W<zY^c luL_N _7fb܂Ø"Qڷ-M҆p^XP`aBC0Az ! s#Mx>9i?ŵ۸ Xx#dP JL܆m|C7R}^z.}LujB_Ǐq_FZZoc1* l݁ː=Q'G>kQHѺzW}>oT$_曋Xy*~KoKG3 򧧦t{YWbBA'R[ZP 1UcUPN BmhZ֡&Jدq+̞>>QW:jgD4"B%\9>6A;jŅ1|;_tFii1t3 uuӟsh:ci ӟj[m:rh4UŰNc+qBg|> B( 迓o/="7Ip!jPoP7r rC.NYCrk2p$RoR̔ӳ=X::o[︎lO(o89Kt0S9zv BM^z0 cě ?.Cz. 4({^\w ɸ)\H;#)d2?NʎMO8oK{@TiR;8$>.^fD;8NȎ!W9&h V~moOmǡqsDˠ`4,\9?ӠpR~Tv '_[ fd$g鸐w82I_|wܴR89?AZ0|a2'lQ<zZ؃C9P%GagYsQQ>C>|\@+§h\A:|qӦNs4ip"qE'6Oz$~h`د$SW09 uݟe.4|LX{&OnO Z~푇Tg%4 'ᇤEDX5~MW~ !'D؃i.al0&SђƟ4q9*OҤ P&=gw.]&%S؃i2.5g6xBYOѝND~8`Y<4R9Da/h.0D ͆g]R7 ~P@j~|~ݷԵ–POѦ:]¶Ȇ]hm MlI1Ζ?'|C0s *CiNcdϤl_2>-n3I+ac . :!ҜSAgXl.řZS&KJR鬙@~k02m/]) HiTIITRmɌ9C'UiWb6HrzfI0{%446YˑcJ[6669uɣIym}3cBic}ĚM`p,%޽RLcomr'1mIw3C̜(6ua=6 qb %2 =ɋ(/rW3kD&I/N6t1Y\KSrJoYz&T4 :$+NG$]HNH5 'lݭ i[nEv]:'9yj \XP }k:¨޲/ut},)p(*wR?`~NUR^INP.Xbm#SwdȾI)'5GQV\HɾFF%cgO蓓$wH?~gmRk9ljiP0z _z5BM7t2'3Gc{LAC a;Ľ2'_|&^x5>7%x_o7]>|?~?Og:~ [!ῂ_75*~ ?}x/~ ?<w;.|+^WeR _ &|^x^x6{ <Oppģ*N7ǵx8QA^\=cc 6\lXS`uA?:@?їs 7hߡߢ}+%z@WKy3Ђ{c z݁v[-GFtZ*t9 ]t!ڄ.@|yN/x ۷x#wӿY2L%af ˆ%TUF~8rwM=UuvźkPdM7 + 1{5"ހ)By-oE,EBY4tpAK{ >&ܝW c{i{𐟥?g4©])#H4uhzl4-BJt6:Ɗ aܸƐ`<-w(cs~ ^q5~ 0vƱ?g%؟g~:_[^+/³~C{n믹;_7WXdg9}'N;}T[kK#3ze,z醑Y{̉|#wGϩ(_yY*O7S$W.ؒ`)96:7[8$@6d 6` #)1MBr>Jۗ9"*L ܻ4EΗ}}}'f铚GÔ< ºśxJ`"Oϒc2Z`@Q"(p~Z5s>"/47wܱK u&0Z= oCSI&$*i$! kzN%0v!`R=+־zAh8y;#| gnD\*I0 IE Epiz+z,TOHZ@\+:UV=Zs ZtmjУ/%MQ H>ztM(C0W1I1MG4y,cn2 oGoZt}2G (=5e#"q#=Anxڛo _&6w&Gt^X}DOMK;xnzM]ȩKMe2Ks}2'Y^Ocz ]WHZ;/ XIzc.KA: ׬SBdEУ;4.C̟1u|5QMt"!Z0E,t]nj])R(FZQnO}D]jۚԝ n JWwJ;-5/Wg4iF]j68:?w-|d&u;yF:ջo:n2iF]$L c+j==1Gns#GcBI? h0r,1۫vY,PE-E"~TV7cIDzoRe쬗qhIoGy FIWt;_d-GbSSW=qU36LaƌS6ktϘ@[;\D ;SYۼya3|5-l 6{{EV/#0x_rFeE<>2ieՍ^#weӦOt4)RtMGN96P }HqYU\Q9X.uBEEsM3T M!U ^=YaZVQ=@K@M$圂lmk[}Y଱ /^|˅ 5]'+B5_}6_a' RpbOՕz5h- ކ`uCg7/DFg^ õ2! } yJb2T"y'vOY5:m o2K[|[?NϟNGQX_S: ~W'33eX.M/ė(`otGB鳈\y2P)ba4O$?B~o!y QAVx*^7‘F|>x< 7aPZJpVz΀NK\Nh Dػ5`"yWH}ļhk#u컖j ᰻@J#MhMˡŊ8`Mĩc (2>@Pj% 3KK]!V|;R؟?PG:! ӻT@K @*6^ńz`lJu30 S,~+$ 1N՗&6nάTéG]KCKzEco6Cf[S~^K 8Jƞ@D-mQ\v{T8O+߯s[nE>ʐDũ Xr5hU*fh=/Щ,!-ѩjU^QkaFMQ{݆q chs&8lqUdxMjMe2j\M#`Jrz#-zm`JBUNǕbrFMǓEނ\Ӧ5vF\ܗ1i,\7X_uXU::Pi /2ZlA5%֊dt*DU8iVaj8Gj+s<6^?w%kts؈\# }>E;aW(WB]%z#B=` dzCEOh~ dP&:YE/€ڵԠAv;Z ;ԀgS}mU_WJ~ 0.`nDaBw;PbzaFm('4c֔uP5Md<6yF]DFin7Ɯ*i@x'E#fjG.xGF#Ώ$/8h$ga4"E^B|BivSt8?'rT`5XIv%b'srCXS%%9.BĘǗj@RG LMp4Y,xݼ$CKxdBbqxk 9@ B T\dApSsE*,Hk0F<1fb@6.׋bvM(u,QC7 TX™Qq:ӨyLc.֓悥Y^U,謘&!A21~߼qcѬC"^']>s=z[),NO_+GV{8/6T,o_W<!hM8ޓ;v{n]W+ޮz~t0๑=tlΑ؄Oű{  8 \#S> !1C/X##`@(@{|NoRQ#= }g |6SDz/ Նaۀ(ՈbHtRƛg'#1dشM]nӖ=vY[=A{YH[-NxDÕTFSSqw@VET$TjQjE aJtEĕvo]74]ޙiomjHVURrZdvKJRiM/ϊ>~Л׉!!5T'H7N$iOyAHc>{nwæ5kRMo\]WN>{)E^4-sF.|զq16_v6n]O">"\xjc k̾'ypu׬IW6N$_1ý,ӜexShP2>ϏǺ2v9>pu.ĻV[;5۸ݻ+c fçO56\㊦LS**ZO,%dv^+QV elG7_שj "OWMp^ ԯA |ШdxUPEq|u/#S'1>=Ez</T}knկU뵾v_oU3C҅.UE}(?@ X gc^aQPtbP x=&a-exE*be~Q>Xdr3'o:>-_^I_G37A*/D=Շl!Ȉ 6zHUxTLWgʦ+#lD`WɈa룑_~[8_~4DλBedp'JzDh dpz|0Aک#S gЪ(CN>l%*Lz.Ov5DC DKGKr ]]FCҢܽY{bFr 7uxd0cbz:i w.E*,7iv* 6F=ͭڕ{Fltug;􏤫&[o3^մ1*Dbgբ⦁Q]Sw~3Uj C F47aAI y:<^e:4HvH' r!?}nﭷyaz&H eB:|Ja9^1Xz#^p S pj"rȢgZیSG1}c+2uoMoCS;]νm~_!{%z@Y@0",!H`~sM_ݾRu9~AƦg1)l:;z:c0#S>"C$I4 o=#+ܣ4:'ۗ6VҋHZ5̝YUL]k(|8뇮JConH+smf}Ԗ+Zsw8Fږ# ցR)Z ˭x RlJ&=37X0pJZmjY;[7r6ռVipElfqXU|ƿAW*"䉺~*m. ~d0з%"H ÑJ$A(tA> Uoͬv,t=&ݠ\*}(T,fkX^biL7 V+ pӐRl- $ ;Q) wC>BQ$@bu&VU5t|/ž>^q+ȕnN W&t=_X M d?^ud;Ra%)hHS+{f]^gm b_Pj>7?ãtin+4‘ aM2;?uwSS#V.s`%-tiZw B:j:a ȩ+A56 I cſ\L?1J9+dI Jُ~-i DovmT}c͢,b2{{LH`E Gػ3 \pѭ'qxeTo;`<({5QdUVF5RU# .t;iu;Ȳ?:=tNOgod,l2o/56w$z жۋ2-M N@SYs3 峜F!3|_ݹH5p.K8m&H{;xaͥUUxicm+Gsp.?sVWFt:~FQwyc!N:QxM;KZqwV2 - ~Binb"nT38ZLx1f,3?Aw+onެmI+^mbs,)E;Q|BWf9hEX!.-MW iDnXP)5k@99=lOoˑd22%!RJIIN]hħ1nWn2j5+c<mNm7asQAcDŰDb?ϣmڏ) Y^ZGcB~Cmr2%OTygh7̣ 4#ͣDAY-'_&Pޮl$[uPֳ{o!"ce ʋ1Lt(W%JP~mcetǹI }:v6v+{ "a-s N&5}n!b@؍{͇A4-!{!*ju♗龾m^_ͮg~?A FqF Џ鸐>} [aZhBA}>i|=2;>(aѝs(TJ!۠de /A'iD!kgm@߫&\/>V5$*n^WW?j/u|D%k _%B[T*VYq'ʉˉfϧ`|bb/=\woq0p edv3Y4H'8+m [3sd3lLGPlZN-s-0sݧzwUkkoov8n't:M{e}z|Hvr+N!ؠ'\{xZ>ijKѝ9:n44X9*k;uhW~Q꼎RQ4gpvsIQ%'6^҉s ws"(bX[j*4` AB&*+<$ بXA(D@TJ7|iwcϞ={"ӅW$@ct,%Г_;[el Ҷ:N:jK.='9Z+u ԏܧoKP0=PǤE!]eA\Bnr=ve+tOHwR21n^26Z2tz-DtVP*8|t7Em߬f$_&jA<hLC(`յ\7ot%2H>gY2^SDbt=BI ӽVnbú;Xui;_@J {LϠTwIއDbǃ`)X'O_^Q~/{EcΕ~^p)hyPkdV2 ]')Pp qԀ}zHS2cq3OS=Wf J{  mJOF!ؔnfH_G:ɞJۼ"N?&c#|*"=k~7".9Z8=^QwJ7+W/[?U_*?d.ŖM? :(׃#v̧M.+8˚wsJ_4|6~EԝӚo;dx_r~y7}A[|1g_J?LjLY6ACC_xErFrؙ/u@kuWfM1vgn-^ SDA<ϑ RwGY"dJ|L}:~l3dd|d6}BB+VLC YP&t6kn!oӖ݂ϼ(vnN3> oMO.77|jK; =-È흜Op74W-m]0Npw0ϗkit>u/?W_BwX^7n/~owkTc*.t8ލ{yd]dYF,L,^xߒTzK*簃 sn: w#9}l+NY L4 H??x8e䆓覈|7lh:z(:{#34 l.ഋ=[X477 7kl~Gb/o3ckʱѳ|9:~Ԣs1{/@['+Э:&]С&#O~(nX\%wq3ñmwE`9sW rD;!b$0 FV3qoZ9x8V*}/C e z}s(C:W0/dž%[J61zߖJ8X?ߡZk5.}O"j컒*c8ݙɴ3hsj+ {ogho;cR磝.|e:3ps֋)lQEpC\!鈟yz 4\4Ǡf=/}ǝ]#z#qS5zϼ\s_cay1͑ ş$^u^ޫe"è-]}W23}$n>$mq|3bQ2<ޖXѱ=Φ; cx|m=J/ʍ;:#Dڙ@{/.߶Cts0AgC·=rabcȯ1mȣ?&Å V5wl"eɵ~V} rMăE7S7rQ ׀6WZ,jUh)WS_5UONL ]NL|5k.: 7=ch FG|W;.omߗ x{ 8Gb>>u̦7h\GX>IIWWtPp*ikEw 7>49طKjO^krw!v7OȋKv+9{0`/9ba|u58w-F v i8~I  Xn__oM.$nD,[E Ng`& _)!&}=H 0Œl|Q2t^ì)uܷ]]h1*8"xYdžџ f`7<[ꯃ'4Np,7/NwH|Gah%7̣&[&Bhabz0P½fas=BmLPm'ɗL%AG6B{X2:ÍMRmMSڣ ^ԊݩW5%}7o.8[?9]k}^~ m!mx_"H|W/_Kp6u({Wt,&{=fzypa2W$ Ij9r{uw}D (deݑR "nuvԽ5|J*=hozȹJNjǽgEqԽ#hoA%8v#keQ sFW|z][$< z;N, ۉraUr|wG&{,*`qT&~~\0X; 6¼P{ra7]An;kxF%>?7{_qͱΎ2_6`{x]yxWUthe!!aSRE< KJUe؁$d&T@@ K,Ѳ ;)m@3y}s9[#!·U!(,%[ Tuu]Z7nB}gnWo SBvgB~עp ߭5rQuI ọCW{KChJX1Pp7i pyCh frx(Q $aM!4y7byT(nCCx'rpiۨ m[ЎWq =;ӄ/CHBkօБƎxvrS?Z]p|V!U~j$јDcNI0`'O~_{Q_R%^2'Gq8׿ ay8pp^ 7n#HivG:3ҙpF(5GhZFz3Z?Q c{scy=\XkF+gr~"}W}qtSc~4' < ?iƣ4}NS#Mt>gГoxfgxfg_g,3T'SLu2%Le/,,X^2V{_J}ZwWZWgTGk{-ί5kXG~ϧuz/zyqA_7OF3Mjmͧz?⻙ݬhJVJ5+qLg۟pbFҽ/[ mcsٮvvim/w%g46UTu3{{V3t<>HAssqP_![;b{gǜ=8oNzJSjҟӸg5fݟƟ:ϣ| ?~fKg_qY:ן35 NjDkZ}u}j\{]XW^\^5k^:ofiBi!ՄNRn+օ!յvkP\ QBTZ8_RnwFݱE Q&!ݙC0+Dwù"Dz qVPX|Dq!z@ރ#BԴeU!zhQ;GQs-\z'wt{6-m7L5Q'NΝlsP]>&Dϩ]C|z5V[+8vB 4M y#D= {ѳPx|z^Uoz;N>}aų/~}\G}W^?yO^?kI%9o%q?@Az?fd|0<#5F1šhDŽh >?c ^ĜΫ_wx9o&'d2s޽! \ZS_/s0_a=L'[Os9H-YdsH;R4v#V30z7Ls943̈́ /ϩR~*TRaH# F4>{48ipI'70aȀASMp2hʠ)eГYpfu0f1KY54 F9L8p2dɄi>`dȂ# F,z' N6l8piɦ%V6lXٰfÚ k,6{6̦۬'lɁ'N89prx˻\X].\xru.Vy䙃<癏<5;%7WS|}ɗS @N9r|Dj/įfY.7LT9s̑3G9mjUs`.O[DGw@ER$Ez[lb=)Wn1/aXb:[8ϯx/5w%Gg %pK{RT~y}E8ן/T~-c! a$VE||Z]lrKiZ9]Y^ʧƹ25`yy79W\2|[2:ѱ?VK_Οj,7G +y8Tk%} wëpX*UVӳjV[_jV_mCk\qg85/_n~r?7#s{c>F`m̬ǵ ^UQfB 3 < c?1o jli\T sUi*a}&>7ßOނu*w+}[Vw໓Ɲv݉N=o]vn:#ޕG{QkGxrLc8w'`yB NusIx<˧?Ei{i3ruΘ3o,z;+Ƭ𥆖~՘>~NsjwN?sg| fԸ5.wiErEվīK^r}-jZSeg/|e5[:UpMkNu^_{]Ͽ>|s)B71E,!=DX)v!/: b o5Ŧ#:n#ԼICC8u+CV5Y!VOn=*^R>w8wwۿGܻ<;w_FMD6FդX6h, jkYM=t5Gs:#jkz<0׸E,5^y<;`˸\WGq !#:! G0n], 2ZpS<,}#󼚫Y)} {ήӞykxuOPI /^P{.6-9P4M?ϸW$PvTLyO''Nt-5@_ˤȈӐRN~Fv6Lr OH=1Vų6Ϫ~ntɹ~iWۋ6k>.Ug^^y)mnʏcmSI$BӍ8[ zϕRZymoMRǣKI2Ps(Oz=7Enu֐5Io%lpxf[`+M5zbLNcWJ6=yy8TYYhu(2Y;Nm[V$ݑks)k7WUtA "t[VJ궷`n sY2qL&*8J}: s^Xۅ-9UGFRLF]RhKWo)t}90ʨi6l7?Bxmxֆg9c*33^P^LӤIhkf.pm{g{?3yϑFsRMYg^hE(ZTZBvա:(5ZQc8ZJ-Q˪jZIVQj ZKQjHm6QզK;9zM{yݣ]ݫt.\]e=B]zzG7GzW>To }>DӇ#(}>F$}>EOӧ3,}>G "}D_/ӗ+*}F_&}Eߪoӷ;.}G߫!~D?ӏ')~F?%~E_ӯ7-:GCX?՟KZG/oYտO[C41 jVZڨ:FhCci-IKҴ -KH+ʴ JIkڴKmHƴ MBdQlr(BQQ6i ڒiJPR eimKi;Di{BSi@iͤY#ͦhgCЮN{ОnSK}OڋRDeBUQ|Z@ iC~?@At0Bat8AGQt4Cqt<@'It2Bit:AgYt6Cyt>]@Et1]Bet9]AWUt5]Cut=@7Mt3Bmt;Aw]t7C}t?=@C0=Bc8=AOS4=Cs<@/K2Bk:Ao[6C{>}@G1}Bg9}A_W5}Cw=@?ҿ~_+F'E?X31n%۹;y1<//+̫kơxޔ8f#y3ޜ-y+ޚ'9ip'/| !|(Ƈ|$G1|, |"')|*Ƨ|&g9|.|!_%|)_Ɨ|%_W5|-_ |#7-|+Ʒ|'w=|/ ?#(?Ə$?O3,? "/+*Ư&o;.!')Ɵ%_7-#g˿o;_7#JiVYBڤ]:SFh#ce,)KҲ,+򲂬(+ʲ*fu!kQuM=V#5quz@+kڲ+l(ƲMKBbIXlq$"QI\6e ReIHRRdձ2ALɲL2Mv2Cf,QfN̑]dWMv=dOIՙluAzUWP'H:U&Rde@JRA[*Re̗P>'r$!r&r%G1r' r$')r&r%g9r'r\$%r\&r\%W5r\' r$7-r&r%w=r'<$#<&<%O3<' $/+&%o;'|$'|&|%_7|'K-gE+o!_cц 1ƴVi3tQfcƚqfIY,cUwfYɬlV1f Yˬm1fll61ͦ˄e6ln0[f0I2i1Y3lk&$3loAfv0 32;f'cv1fən7=55E3`Jlަbffofk3́ s9j3#̑(s9k3Ǜ̉$s9ŜjN33̙,s9ǜk3 ̅"s\j.3+̕*s\k3כ̍&sjn3;̝.sk3̃!yŸ,Ÿ,Ÿ,Ÿ,Ÿ,Ÿ,]]Ԃ6ԁFQh &Ih fM?~Zmlv!C[[[[[[c3aMFʰ? ~0amm66m|||||||||||||||||||||||||Q#G?~#GF,m$KF,m$K; ~1c?~8q?~8qJv 'O?~ UN? ~|xUN? >J7%? ~)SO?~ )SO?>|h#ȇ6|h#ȇ6|h#ȇ6|h#ȇ6|h#3gπ?~ 3gπ?>k#ȿ6k#ȿ6k#ȿ6k#ȿ :ȿ :ȿ :ȿ :ȿ :ȿ :ȿ :ȿ :ȿ :ȿ :ȿ :ȿ :VeN2?|_fmZ-;ҰwԂ6ԁFQh &Ih f@C?~x!B ς} -ط`߂} -g!> < < < < ԁFQh &%-;kjGoaPsKl[R)/VUlҘ 痂Vw߆i=DZZg0.>; ~|P.u{ rEw[{5brꎪB>W,k퍓Xnv?[aƠ BoƷP[-jt 5BnFP[-jt 5BnFP[-jt 5BnFP[-jt 5BnFP[-jtO? ~0a? ~0aǦ;;;vT;x<jOqwwwww?~#G?~Q؏~;l6uM`SwC6uvAM6R8ԝX4-bo81нR Ƌj5֞=6EۢmA#8b=m-^&-/$}'pho!MjB>hZ D A-[DCf /ixi\4̦mҼfiĐUI㪤@43 f@̀]ɀ$ 9HB7Y~,gYPgY:Pk.zimpRK;%6i1Ao)IX6nλ!=@1P"ŹCU!ti껐 !C*lK%z8:2[i'xww,m+'/>xxxwFZڻWH_ͶrW< ~5L,`0`4`T~o4o4xjy ?/O~g7FF3`ß` Dhq䋹F]7>7Po MjB_pFwWEۨa뗈~l?׭ O#SF*niĂÍ<7 4 7@ķF  No7 #o-~|K[@y$xOJ}ŧF{s]Ƴ6v7kW` ^GOXK78! Nh^4N&;N<[Xbŏ*z BmaW]j(` ¸q%` 0?&WOa^i}{ |p!n,F =2} b0X9|Xo- bO7mЭ ߝ^xȕzoFޅ:+/j}{m`X^k҉\,ohޖ?|_wdklm oI'`*?qN8JJo,,WpJ|ӋK9tc)mlS~a'?xcp"(b##c_Ɲ  ؜03h99ٙ9 FNV^fnv~&.6>FNV^fnv~&.6>FNV^fnv~&.6>FNV^fnv~    & . 6 > F N V ^ f n v ~                     & . 6 > F N V ^ f n v ~                     & . 6 > F N V ^ f n v ~                     & . 6 > F N V ^ f n v ߖUUU$H@ @@<<8|||8 v|T(~ի~Zl(8(P 8lTl*"\zf(@@$$$(((((|||DH$JD0Hh0JL60    l88l|0 <00 @@B< ~~  8D8D@<$BB~BB|BB|BB|F:@@\bBB|<@@@<:FBB><@@\bBBB0< $@@DX`XD08쒒\bBBBBB>\" >@<|< BBBF:DD((DD((DBBF:<| |   2LUUUU"x x" 88V (T8T(D(|| $8X400  80@@08 (T|T( ljl||l @D8F:<>F:<>F:(<>F:$<>F:<>F:~ n<@@@<