| author | Tampax86 |
| Thu, 17 Jul 2025 22:03:19 -0400 | |
| changeset 0 | b2e3aa63e96c |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,11 @@ +syntax: glob +bin +obj +DeviousLicks + +*.layout +*.depend +*.zip +*.exe +*.dll +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ISOL.CBP Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<CodeBlocks_project_file> + <FileVersion major="1" minor="6" /> + <Project> + <Option title="ISOL" /> + <Option pch_mode="2" /> + <Option compiler="gcc" /> + <Build> + <Target title="Debug"> + <Option output="bin/Debug/ISOL" prefix_auto="1" extension_auto="1" /> + <Option object_output="obj/Debug/" /> + <Option type="1" /> + <Option compiler="gcc" /> + <Compiler> + <Add option="-g" /> + </Compiler> + </Target> + <Target title="Release"> + <Option output="bin/Release/ISOL" prefix_auto="1" extension_auto="1" /> + <Option object_output="obj/Release/" /> + <Option type="1" /> + <Option compiler="gcc" /> + <Compiler> + <Add option="-O2" /> + </Compiler> + <Linker> + <Add option="-s" /> + </Linker> + </Target> + </Build> + <Compiler> + <Add option="-Wall" /> + <Add option="-std=c99" /> + </Compiler> + <Linker> + <Add option="-lmingw32 -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf" /> + </Linker> + <Unit filename="src/common.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/common.h" /> + <Unit filename="src/devlicks.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/draw.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/draw.h" /> + <Unit filename="src/effects.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/effects.h" /> + <Unit filename="src/fade.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/fade.h" /> + <Unit filename="src/firealrm.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/firealrm.h" /> + <Unit filename="src/fontcche.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/fontcche.h" /> + <Unit filename="src/icon.rc"> + <Option compilerVar="WINDRES" /> + </Unit> + <Unit filename="src/intro.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/intro.h" /> + <Unit filename="src/kbdinput.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/kbdinput.h" /> + <Unit filename="src/level.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/level.h" /> + <Unit filename="src/localztn.h" /> + <Unit filename="src/missions.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/missions.h" /> + <Unit filename="src/store.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/texture.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/texture.h" /> + <Unit filename="src/theft.c"> + <Option compilerVar="CC" /> + </Unit> + <Unit filename="src/theft.h" /> + <Unit filename="src/title.c"> + <Option compilerVar="CC" /> + </Unit> + <Extensions> + <lib_finder disable_auto="1" /> + </Extensions> + </Project> +</CodeBlocks_project_file>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE.TXT Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,253 @@ +Devious Licks Version 0.9 | +Released on January 28, 2025 | +______________________________| + +The main code of the code (all code excluding used libraries) was written by MCL Software +Copyright (c) 2023-2025 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +=========================================================================================== +All images and sounds (*.png, *.xcf, *.wav, *.ogg) are by MCL Software and licensed under the Creative Commons Attribution 4.0 International Public License. +The license can be accessed on-line throught the World Wide Web at https://creativecommons.org/licenses/by/4.0/legalcode +The full text of the license is included below: + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. + +Section 1 – Definitions. + + Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. + Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. + Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. + Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. + Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. + Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. + Licensor means the individual(s) or entity(ies) granting rights under this Public License. + Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. + Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. + You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + License grant. + Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: + reproduce and Share the Licensed Material, in whole or in part; and + produce, reproduce, and Share Adapted Material. + Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. + Term. The term of this Public License is specified in Section 6(a). + Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. + Downstream recipients. + Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. + No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. + No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). + + Other rights. + Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. + Patent and trademark rights are not licensed under this Public License. + To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following conditions. + + Attribution. + + If You Share the Licensed Material (including in modified form), You must: + retain the following if it is supplied by the Licensor with the Licensed Material: + identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); + a copyright notice; + a notice that refers to this Public License; + a notice that refers to the disclaimer of warranties; + a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + indicate if You modified the Licensed Material and retain an indication of any previous modifications; and + indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. + You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. + If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. + If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: + + for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; + if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and + You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + + Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. + To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. + + The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + + This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. + + Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: + automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or + upon express reinstatement by the Licensor. + For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. + For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. + Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + + The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. + Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. + +Section 8 – Interpretation. + + For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. + To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. + No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. + Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. + + + +===================================================================================================== +The SDL2, SDL_ttf, SDL-image, and SDL-mixer libraries that this program makes use of is licensed under the zlib license: + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + + +========================================================= +The SDL_FontCache library by Jonathan Dearborn (fontcche.c and fontcche.h) is licensed under the following terms and conditions: + +Copyright (c) 2015 Jonathan Dearborn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + +=============================================================================================== +The Montserrat and Noto Color Emoji Fonts used in this software is licensed under the SIL Open Font License: + 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. + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.TXT Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,12 @@ +Devious Licks 0.9 (2025-01-28) + +Copyright (c) 2023-2025 MCL Software + +This program is free software, please +see LICENSE.TXT for detailed licensing information. + +Read the Itch page for controls and info on how to play. + +Originally made for the 2023 Linux Game Jam. + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/changelog.txt Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,71 @@ +0.1 (2023-05-28) Changelog: + - Initial release + - Very early prototype + +0.2 (2023-06-01) Changelog: + - First public release + + +0.3 (2023-06-04) Changelog: + - Now using SDL_FontCache for drawing text more efficiently. + - Added an intro sequence + - Textures are now flipped + - Improved title screen + - Code cleanup + - Added an indicator for showing what tile the player is currently standing on + +0.4 (2023-06-07) + - New mission lookup table (not fully functional yet) + - First official GNU/Linux build + - Other minor changes + +0.5 (2023-06-09) + - Improved mission screen + - Fixed an issue that caused the mission description not being displayed + - New main menu background + - Added support for translations + - The "Currently @" HUD now displays the proper name of the object instead of the one character code + - First public source release + - New fire alarm sound by Looki2000 + - General bug fixes and other small changes + +0.5.1 (2023-06-09) + - Re-enabled the intro sequence + +0.6 (2023-06-09) + - New theme + - Changed "Currently @" to "Currently Selected:" + - Added a background for the selected object indicator + - Changed the text color of the selected object indicator + - Changed the color of the "Stealing" progress bar + - Added several new debug indicators + - Changed the sky color + - Revamped code for the stealing mechanic to make it easier to implement changes in the future. + - Modified code to not use strcpy_s for better cross-compatibility + - Removed the text mode debug feature as it is no longer needed + - Other minor changes + +0.7 (2023-06-14) + - The player has to go out one of the doors after completing a mission. + - Player cannot move before starting missions + - Other minor changes + +0.8 (2024-08-26) + - New level loader + - New tiles + - New music + - Minor changes to title menu + - Fixed zoom glitch + - Other minor changes + +0.8.1 (2024-09-04) + - Changes to some textures + - Reenabled the intro + - One of the new songs now plays during the intro + - The songs that plays in-game has been changed to Class 1 (in release 0.8 it was Class 3) + - Other minor changes + +0.9 (2025-01-28) + - Added new music + - Different tracks now randomly play in-game. + - Changed copyright date \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,447 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "common.h" + +int zoomFactor = 1; +int orientation = 0; +bool gameRunning = 0; +SDL_TimerID fireAlarmTimer; +SDL_Texture* logo = 0; +Mix_Chunk* alarmSound = 0; +Mix_Music* themeSong = 0; +Mix_Music* bgm1 = 0; +Mix_Music* bgm2 = 0; +Mix_Music* bgm3 = 0; +Mix_Music* bgm4 = 0; +Mix_Music* bgm5 = 0; +Mix_Music* bgm6 = 0; +Mix_Music* bgm7 = 0; +Mix_Music* bgm8 = 0; +Mix_Music* bgm9 = 0; +Mix_Music* bgm10 = 0; +Mix_Music* bgm11 = 0; +Mix_Music* bgm12 = 0; +Mix_Music* bgm13 = 0; +Mix_Music* bgm14 = 0; + +int selectedMenuItem = -1; + + +SDL_Point pixelToTileCoordinates(int x, int y) +{ + SDL_Point tileCoords; + tileCoords.x = (((y * 2) / TILE_HEIGHT) - 2) + ((x / TILE_WIDTH) - 6); + tileCoords.y = (((y * 2) / TILE_HEIGHT) - 2) - ((x / TILE_WIDTH) - 6); + return tileCoords; +} + +SDL_Point tileToPixelCoordinates(int x, int y) +{ + SDL_Point pixelCoords; + pixelCoords.x = (6 * TILE_WIDTH) + (x - y) * (TILE_WIDTH / 2); + pixelCoords.y = (2 * TILE_HEIGHT) + (x + y) * (TILE_HEIGHT / 4); + return pixelCoords; +} + +SDL_Point getTransformation(int x, int y) +{ + SDL_Point trans; + if(orientation == 0) + { + trans = (SDL_Point){x, y}; + } + + else if(orientation == 1) + { + trans = (SDL_Point){y, -x}; + } + + else if(orientation == 2) + { + trans = (SDL_Point){-x, -y}; + } + + else // 300 grads + { + trans = (SDL_Point){-y, x}; + } + + return trans; +} + +SDL_Point getLevelTransformation(int i, int j) +{ + if(orientation == 0) + { + return (SDL_Point){i, j}; + } + + else if(orientation == 1) + { + return (SDL_Point){LEVEL_WIDTH - j - 1, i}; + } + + else if(orientation == 2) + { + return (SDL_Point){LEVEL_WIDTH - i - 1, LEVEL_WIDTH - j - 1}; + } + + else // 300 grads + { + return (SDL_Point){j, LEVEL_WIDTH - i - 1}; + } +} + +Object getObjectFromIdent(char ident) // Deprecated | DO NOT USE +{ + Object object = {0, 1, 0, 0, 0, 0, 1, 0, 0, 0}; + switch(ident) + { + + case '#': + object.textureIndex = 3; + object.passable = 0; + object.height = 4; + break; + + case 'W': + object.textureIndex = 4; + object.passable = 0; + object.height = 4; + break; + + case '-': + object.textureIndex = 5; + object.passable = 0; + object.height = 2; + object.placementHeight = 2; + object.renderUnder = 1; + break; + + case '|': + object.textureIndex = 5; + object.passable = 0; + object.height = 2; + object.placementHeight = 2; + object.renderUnder = 1; + object.flipTexture = 1; + break; + + case 'D': + object.textureIndex = 50; + object.textureHeight = 2; + object.placementHeight = 1; + object.height = 2; + object.renderAbove = 1; + object.passable = 0; + break; + + case 'd': + object.textureIndex = 50; + object.textureHeight = 2; + object.placementHeight = 1; + object.height = 2; + object.renderAbove = 1; + break; + + case ' ': + object.textureIndex = 1; + break; + + case 'G': + object.textureIndex = 36; + break; + + case 'A': + object.textureIndex = 16; + break; + + // Decor + case 'F': + object.textureIndex = 6; + object.placementHeight = 1; + object.stealable = 1; + object.stealTime = 6; + object.renderUnder = 1; + break; + + case '^': + object.textureIndex = 21; + object.textureHeight = 1; + object.placementHeight = 1; + object.renderUnder = 1; + object.passable = 0; + break; + + case '&': + object.textureIndex = 22; + object.textureHeight = 1; + object.placementHeight = 2; + object.stealable = 1; + object.stealTime = 4; + object.renderUnder = 1; + break; + + case '~': + object.textureIndex = !fireAlarmSystemActivated ? 23 : 23 + 16; + object.textureHeight = 1; + object.placementHeight = 3; + object.stealable = 1; + object.stealTime = 3; + object.renderUnder = 1; + break; + + case 'S': + object.textureIndex = 24; + object.textureHeight = 1; + object.placementHeight = 2; + object.renderUnder = 1; + object.stealable = 1; + object.stealTime = 1; + break; + + case 'T': + object.textureIndex = 25; + object.textureHeight = 1; + object.stealable = 1; + object.stealTime = 3; + object.placementHeight = 2; + object.renderUnder = 1; + break; + + case 'l': + object.textureIndex = 26; + object.textureHeight = 1; + object.placementHeight = 1; + object.passable = 0; + object.renderUnder = 1; + break; + + case 'L': + object.textureIndex = 26; + object.textureHeight = 1; + object.placementHeight = 1; + object.passable = 0; + object.renderUnder = 1; + object.flipTexture = 1; + break; + + case '`': + object.textureIndex = 40; + object.placementHeight = 1; + object.passable = 0; + object.renderUnder = 1; + break; + + case '=': + object.textureIndex = 49; + object.placementHeight = 1; + object.height = 2; + object.renderAbove = 1; + break; + + case 'c': + object.textureIndex = 27; + object.textureHeight = 1; + object.placementHeight = 1; + object.passable = 0; + object.renderUnder = 1; + } + return object; +} + +void ISOL_Terminate() +{ + SDL_DestroyRenderer(renderer); + + Mix_FreeMusic(themeSong); + Mix_FreeMusic(bgm1); + Mix_FreeMusic(bgm2); + Mix_FreeMusic(bgm3); + Mix_FreeMusic(bgm4); + Mix_FreeMusic(bgm5); + Mix_FreeMusic(bgm6); + Mix_FreeMusic(bgm7); + Mix_FreeMusic(bgm8); + Mix_FreeMusic(bgm9); + Mix_FreeMusic(bgm10); + Mix_FreeMusic(bgm11); + Mix_FreeMusic(bgm12); + Mix_FreeMusic(bgm13); + Mix_FreeMusic(bgm14); + + Mix_FreeChunk(alarmSound); + + SDL_Quit(); + IMG_Quit(); + Mix_Quit(); + return 0; +} + + +// SPACE = Floor +// G = Grass +// A = Asphalt +// d = Classroom Door +// D = Exit Door +// W = White Wall +// # = White Tiles +// - = Stall Divider +// | = Stall Divider (flipped) +// ~ = Strobe Light +// & = Hand Dryer +// ^ = Desk/Chair Combo +// l = Table +// L = Table (flipped) +// T = Fire Alarm T-Bar Pull Station +// S = Soap Dispenser +// F = Drinking Fountain +// ` = Toilet +// = = Shelf + +char levelTiles[LEVEL_WIDTH * LEVEL_DEPTH]; + +/*char levelTiles[LEVEL_WIDTH * LEVEL_DEPTH] = +{ + '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', 'W', 'D', 'D', 'D', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', + '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', '&', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', '~', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', '^', '^', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', 'T', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', '^', '^', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', '-', '-', '-', '-', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', '^', '^', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', 'S', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', 'F', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', '^', '^', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', 'F', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', '-', '-', '-', '-', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', '#', ' ', ' ', ' ', ' ', 'W', ' ', ' ', '^', ' ', ' ', ' ', ' ', ' ', ' ', 'l', 'l', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', ' ', ' ', ' ', ' ', 'W', + '#', '`', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + '#', 'S', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + '#', 'S', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', ' ', '^', ' ', '^', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', ' ', '^', ' ', '^', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', '~', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', ' ', '^', ' ', '^', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', '=', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', ' ', '^', ' ', '^', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', '=', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'l', 'W', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', '^', ' ', '^', ' ', '^', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', '~', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', '=', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', '^', '^', '^', '^', '^', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', '^', '^', '^', '^', '^', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', '^', '^', '^', '^', '^', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', '^', '^', '^', '^', '^', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', '~', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', '^', '^', '^', '^', '^', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', 'l', 'L', 'L', 'L', 'L', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', 'l', ' ', ' ', ' ', 'l', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', 'l', ' ', ' ', ' ', 'l', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'd', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', 'L', 'L', 'L', 'L', 'l', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'D', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'D', + 'D', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'D', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'd', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', + 'W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'T', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', '~', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', '^', '^', 'W', ' ', ' ', ' ', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', '^', '^', 'W', ' ', ' ', ' ', '^', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', '^', '^', 'W', 'W', 'W', ' ', '^', '^', '^', ' ', ' ', 'W', '~', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', '^', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', '~', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', 'W', '^', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', '^', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'W', ' ', ' ', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', + 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', ' ', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'W', 'W', '^', '^', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', 'd', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', '~', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'D', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', + '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', 'W', 'D', 'D', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', + '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', '&', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', '~', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', '^', '^', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', 'T', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', '^', '^', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', '-', '-', '-', '-', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', '^', '^', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', 'S', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', 'F', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', '^', '^', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', 'W', 'F', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', + '#', '-', '-', '-', '-', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', '#', ' ', ' ', ' ', ' ', 'W', ' ', ' ', '^', ' ', ' ', ' ', ' ', ' ', ' ', 'l', 'l', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', ' ', ' ', ' ', ' ', 'W', + '#', '`', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + '#', 'S', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + '#', 'S', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + '#', ' ', ' ', ' ', '|', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', ' ', '^', ' ', '^', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', ' ', '^', ' ', '^', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', '~', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', ' ', '^', ' ', '^', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', '=', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', ' ', '^', ' ', '^', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', '=', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'l', 'W', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', '^', ' ', '^', ' ', '^', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', '~', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', '=', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', '^', '^', '^', '^', '^', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', '^', '^', '^', '^', '^', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', '^', '^', '^', '^', '^', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', '^', '^', '^', '^', '^', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', '~', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', '^', '^', '^', '^', '^', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', 'l', 'L', 'L', 'L', 'L', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', 'l', ' ', ' ', ' ', 'l', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', 'l', ' ', ' ', ' ', 'l', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'd', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', 'L', 'L', 'L', 'L', 'l', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'D', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'D', + 'D', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'D', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'd', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', + 'W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'T', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', '~', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', '^', '^', 'W', ' ', ' ', ' ', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', '^', '^', 'W', ' ', ' ', ' ', '^', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', '^', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', '^', '^', 'W', 'W', 'W', ' ', '^', '^', '^', ' ', ' ', 'W', '~', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', '^', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', '^', '^', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', '~', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', 'W', '^', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', '^', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'W', ' ', ' ', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'd', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', + 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', ' ', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'W', 'W', '^', '^', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', 'd', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', 'W', 'W', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', 'W', 'W', 'W', 'W', 'W', 'd', 'W', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'l', 'c', 'l', 'c', 'l', 'c', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'd', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', '~', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', + 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'D', 'D', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W', 'W' +}; +*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,120 @@ +/* +Copyright (c) 2023 MCL Software +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef COMMON +#define COMMON + +#define TILEMAP_DIMS 16 + +#include <SDL2/SDL.h> +#include <SDL2/SDL_mixer.h> +#include "fontcche.h" +#include <SDL2/SDL_image.h> +#include <stdbool.h> + +#define SCREEN_WIDTH 1280 +#define SCREEN_HEIGHT 960 + +// Level width and height in tiles +#define LEVEL_WIDTH 256 // X +#define LEVEL_HEIGHT 5 // Y +#define LEVEL_DEPTH 256 // Z + +// Tile width and height in pixels +#define TILE_WIDTH 89 +#define TILE_HEIGHT 90 + +extern SDL_Texture* logo; +extern bool gameRunning; +extern int zoomFactor; +extern int orientation; +extern int stealProgress; +extern bool fireAlarmSystemActivated; +extern SDL_TimerID fireAlarmTimer; +extern Mix_Chunk* alarmSound; +extern Mix_Music* themeSong; +extern Mix_Music* bgm1; +extern Mix_Music* bgm2; +extern Mix_Music* bgm3; +extern Mix_Music* bgm4; +extern Mix_Music* bgm5; +extern Mix_Music* bgm6; +extern Mix_Music* bgm7; +extern Mix_Music* bgm8; +extern Mix_Music* bgm9; +extern Mix_Music* bgm10; +extern Mix_Music* bgm11; +extern Mix_Music* bgm12; +extern Mix_Music* bgm13; +extern Mix_Music* bgm14; +extern int selectedMenuItem; + +extern FC_Font* font; + +typedef struct +{ + int x, y; + char objectsStolen[8]; + bool canMove; + bool canLeaveCampus; + bool leftCampus; +} +Player; + +typedef struct +{ + int ident; +} +Interactions; + +typedef struct +{ + int textureIndex; + bool passable; + int stealable; + int stealTime; + int interactable; // Different numbers mean different actions (e.g. 0 = sit in chair, 1 = activate fire alarm sys, 2 = flush toilet, etc.) + int placementHeight; + int height; + int textureHeight; + bool renderUnder; + bool renderAbove; + bool flipTexture; +} +Object; + +typedef struct +{ + Object level[LEVEL_WIDTH * LEVEL_HEIGHT * LEVEL_DEPTH]; +} +Level; + +Object getObjectFromIdent(char ident); + +SDL_Point getTransformation(int x, int y); + +SDL_Point getLevelTransformation(int i, int j); + +SDL_Point pixelToTileCoordinates(int x, int y); + +SDL_Point tileToPixelCoordinates(int x, int y); + +extern char levelTiles[LEVEL_WIDTH * LEVEL_DEPTH]; + +extern Player player; +extern SDL_Point offset; +extern SDL_Rect clip; +extern SDL_Rect destRect; +extern SDL_Renderer* renderer; +extern SDL_Texture* tilemap; +extern SDL_Surface* tilemapSurface; +extern double frameDelta; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/devlicks.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,309 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "kbdinput.h" +#include "draw.h" +#include "texture.h" +#include "intro.h" +#include "level.h" +#include "common.h" +#include <stdio.h> + +SDL_Point offset; +Player player; + +int stealProgress = 0; +Uint64 now = 0; +Uint64 then = 0; +bool fireAlarmSystemActivated = 0; +bool missionScreenShown = 1; + +float applyZoomFactor(float i) +{ + if(zoomFactor > 0) + { + i *= zoomFactor; + } + else + { + i /= -zoomFactor; + } + + return i; +} + +void updateZoomFactor() +{ + destRect.w = applyZoomFactor(TILE_WIDTH); + destRect.h = applyZoomFactor(TILE_HEIGHT); +} + +void drawTiles() +{ + + for(int y = 0; y < 4; y ++) + { + + for(int i = (player.x < 64 ? 0 : player.x - 63); i < (player.x + 64 < LEVEL_WIDTH ? player.x + 64 : LEVEL_WIDTH); i ++) + { + for(int j = (player.y < 64 ? 0 : player.y - 63); j < (player.y + 64 < LEVEL_DEPTH ? player.y + 64 : LEVEL_DEPTH); j ++) + { + SDL_Point coords = tileToPixelCoordinates(i, j); + destRect.x = applyZoomFactor(coords.x + offset.x) + applyZoomFactor(LEVEL_WIDTH); + destRect.y = applyZoomFactor(coords.y + offset.y) - applyZoomFactor(LEVEL_HEIGHT);; + destRect.y -= applyZoomFactor(TILE_HEIGHT / 2 * y); + + // Transform coordinates according to rotation formula + SDL_Point trans = getLevelTransformation(i, j); + + // Check what tile is currently being drawn; this will later be needed + // to determine how it should be drawn. + Object object = getObjectFromIdent(levelTiles[trans.x * LEVEL_WIDTH + trans.y]); + + SDL_Point textureClip; + + // Get the tile's texture + if(object.textureHeight == 2 && y == object.placementHeight + 1) + // For tiles with a texture higher than TILE_HEIGHT + textureClip = getTextureFromIndex(object.textureIndex - 16); + else + textureClip = getTextureFromIndex(object.textureIndex); + + // Multiply coordinates by tile width and height to get the real coordinates on the tilemap + clip.x = textureClip.x * 89; + clip.y = textureClip.y * 90; + + // This sets the destination rectangle's width and height + // TODO: figure out a more efficient way to do this without calling this function for every tile + updateZoomFactor(); + + if(y < object.placementHeight + object.height && y >= object.placementHeight) + SDL_RenderCopyEx(renderer, tilemap, &clip, &destRect, 0, 0, SDL_FLIP_HORIZONTAL ? orientation % 2 == 0 ? object.flipTexture : !object.flipTexture : 0); + else if(y == 0 && object.renderUnder) + // Render tile at Y: 0 for tiles that are transparent and/or above the ground + SDL_RenderCopy(renderer, tilemap, &(SDL_Rect){89, 0, 89, 90}, &destRect); + else if(y == 3 && object.renderAbove) + { + SDL_RenderCopy(renderer, tilemap, &(SDL_Rect){356, 0, 89, 90}, &destRect); + } + + //SDL_Point transPlayer = getTransformation(player.x, player.y); + + // Render player + if(y == 1 && trans.x == player.x && trans.y == player.y) + { + textureClip = getTextureFromIndex(33); + clip.x = textureClip.x * 89; + clip.y = textureClip.y * 90; + clip.w = 89; + clip.h = 90; + + //SDL_Point newDestRect = getTransformation(applyZoomFactor(coords.x + offset.x) + applyZoomFactor(LEVEL_WIDTH), applyZoomFactor(coords.y + offset.y) - applyZoomFactor(LEVEL_HEIGHT)); + + //destRect.x = newDestRect.x; + //destRect.y = newDestRect.y; + //destRect.y -= applyZoomFactor(TILE_HEIGHT / 2 * y); + SDL_RenderCopy(renderer, tilemap, &clip, &destRect); + } + } + } + } +} + + + +Object getObjectProperties(char object) +{ + Object objectProps; + objectProps.textureIndex = 0; + objectProps.stealable = true; + objectProps.height = 2; + objectProps.interactable = -1;; + objectProps.passable = false; + + return objectProps; +} + +int main(int argc, char *argv[]) +{ + loadLevelFromFile(); + + initMissions(); + + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO); + + Mix_Init(MIX_INIT_OGG); + + Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048); + + alarmSound = Mix_LoadWAV("alarm.wav"); + + themeSong = Mix_LoadMUS("audio/music/DeviousLicks.ogg"); + bgm1 = Mix_LoadMUS("audio/music/Class1.ogg"); + bgm2 = Mix_LoadMUS("audio/music/Class2.ogg"); + bgm3 = Mix_LoadMUS("audio/music/Class3.ogg"); + bgm4 = Mix_LoadMUS("audio/music/ANewReality.ogg"); // todo: move this track further down + bgm5 = Mix_LoadMUS("audio/music/Recollection.ogg"); + bgm6 = Mix_LoadMUS("audio/music/TheSadReality1.ogg"); + bgm7 = Mix_LoadMUS("audio/music/TheSadReality2.ogg"); + bgm8 = Mix_LoadMUS("audio/music/TheXXXReality.ogg"); + bgm9 = Mix_LoadMUS("audio/music/FirstDay.ogg"); + bgm10 = Mix_LoadMUS("audio/music/FirstDayAlternateVersion.ogg"); + bgm11 = Mix_LoadMUS("audio/music/Hallway1.ogg"); + bgm12 = Mix_LoadMUS("audio/music/Hallway2.ogg"); + bgm13 = Mix_LoadMUS("audio/music/Hallway3.ogg"); + bgm14 = Mix_LoadMUS("audio/music/Hallway4.ogg"); + //creditsTheme = Mix_LoadMUS("audio/music/Credits.ogg"); + + Mix_PlayMusic(themeSong, -1); + + SDL_Window* window = SDL_CreateWindow("Devious Licks", 170, 70, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + + initGraphics(); + + SDL_Event event; + bool quit = false; + + // Title Screen + while(!gameRunning) + { + drawTitleScreen(); + while(SDL_PollEvent(&event)) + { + handleKeyboard(event); + + if(!performMenuKeyActions() || event.type == SDL_QUIT) + { + gameRunning = 1; + quit = true; + } + } + } + + // Intro + // 0 - enabled + // 1 - disabled (skip) + gameRunning = 0; + + Mix_PlayMusic(bgm7, -1); + + while(!gameRunning && !quit) + { + if(INTR_DrawIntro()) + gameRunning = 1; + + while(SDL_PollEvent(&event)) + { + handleKeyboard(event); + + if(event.type == SDL_QUIT) + { + gameRunning = 1; + quit = true; + } + } + } + + Mix_HaltMusic(); + + srand(time(0)); // set seed for pseudoRNG with RTC + + /*int nextTrack = rand() % 16; + + Mix_PlayMusic(nextTrack == 0 ? bgm1 : nextTrack == 1 ? bgm2 : nextTrack == 2 ? bgm3 : nextTrack == 3 ? bgm4 : nextTrack == 4 ? bgm5 : nextTrack == 5 ? bgm6 : nextTrack == 6 ? bgm7 : nextTrack == 7 ? bgm8 : nextTrack == 8 ? bgm9 : nextTrack == 9 ? bgm10 : nextTrack == 10 ? bgm11 : nextTrack == 11 ? bgm12 : nextTrack == 12 ? bgm13 : bgm14, 1); + */ + clip = (SDL_Rect){89, 0, 89, 90}; + + destRect.x = 0; + destRect.y = 0; + updateZoomFactor(); + + offset.x = -2420; + offset.y = -1870; + + player.x = 75; + player.y = 25; + + while(!quit) + { + now = SDL_GetTicks(); + + SDL_RenderClear(renderer); + + //SDL_GetMouseState(&mouseX, &mouseY); + + drawTiles(); + + if(levelTiles[(player.x + 1) * LEVEL_WIDTH + player.y] == 'D' || levelTiles[(player.x - 1) * LEVEL_WIDTH + player.y] == 'D' || levelTiles[player.x * LEVEL_WIDTH + player.y + 1] == 'D' || levelTiles[player.x * LEVEL_WIDTH + player.y - 1] == 'D') + { + drawLeaveSchool(); + player.leftCampus = performYesNo() ? true : false; + } + + drawHUD(); + + if(missionScreenShown && drawMissionScreen()) + { + missionScreenShown = 0; + } + + if(checkForMissionComplete(currentMission)) + { + drawGoToExit(); + if(player.leftCampus) + { + offset.x = -2420; + offset.y = -1870; + player.x = 75; + player.y = 25; + if(drawMissionComplete()) + { + missionScreenShown = 1; + } + } + } + SDL_SetRenderDrawColor(renderer, 10, 180, 255, 255); + SDL_RenderPresent(renderer); + + // Cycle music + if(!Mix_PlayingMusic()) + { + int nextTrack = rand() % 16; + + Mix_PlayMusic(nextTrack == 0 ? bgm1 : nextTrack == 1 ? bgm2 : nextTrack == 2 ? bgm3 : nextTrack == 3 ? bgm4 : nextTrack == 4 ? bgm5 : nextTrack == 5 ? bgm6 : nextTrack == 6 ? bgm7 : nextTrack == 7 ? bgm8 : nextTrack == 8 ? bgm9 : nextTrack == 9 ? bgm10 : nextTrack == 10 ? bgm11 : nextTrack == 11 ? bgm12 : nextTrack == 12 ? bgm13 : bgm14, 1); + } + + + // Handle all queued events + while(SDL_PollEvent(&event)) + { + handleKeyboard(event); + + if(event.type == SDL_QUIT) + quit = true; + } + + if(now - then > 100) + { + if(performPlayerMovement()) + then = now; + } + } + + SDL_RemoveTimer(fireAlarmTimer); + SDL_DestroyWindow(window); + + return ISOL_Terminate(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/draw.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,113 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "draw.h" +#include "common.h" +#include "missions.h" +#include "localztn.h" +#include "SDL2/SDL_ttf.h" + +SDL_Rect clip; +SDL_Rect destRect; +SDL_Renderer* renderer = 0; +SDL_Texture* tilemap = 0; + +FC_Font* font = 0; + + +void initGraphics() +{ + IMG_Init(IMG_INIT_PNG); + SDL_Surface* tilemapSurface = IMG_Load("./img/tilemap.png"); + SDL_Surface* logoSurface = IMG_Load("./img/logo.png"); + + tilemap = SDL_CreateTextureFromSurface(renderer, tilemapSurface); + logo = SDL_CreateTextureFromSurface(renderer, logoSurface); + + + SDL_FreeSurface(tilemapSurface); + SDL_FreeSurface(logoSurface); + + initTitleScreenGraphics(); + + // Create font cache + font = FC_CreateFont(); + FC_LoadFont(font, renderer, "./Montserrat-ExtraBold.ttf", 24, FC_MakeColor(255, 255, 255, 255), TTF_STYLE_NORMAL); + +} + + +void freeGraphics() +{ + SDL_DestroyTexture(tilemap); + SDL_DestroyTexture(logo); + FC_FreeFont(font); +} + +void drawLeaveSchool() +{ + SDL_SetRenderDrawColor(renderer, 10, 69, 127, 199); + SDL_RenderFillRect(renderer, &(SDL_Rect){SCREEN_WIDTH / 3, SCREEN_HEIGHT / 3, SCREEN_WIDTH / 3, SCREEN_HEIGHT / 3}); + FC_DrawAlign(font, renderer, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, FC_ALIGN_CENTER, checkForMissionComplete(currentMission) ? "Do you want to leave \nthe campus? (Y/N)" : "You can't leave right now."); +} + +void drawHUD() +{ + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + + // Progress bar background + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 127); + SDL_RenderFillRect(renderer, &(SDL_Rect){50, 50, 128 * 1.5, 40}); + + FC_SetDefaultColor(font, (SDL_Color){255, 255, 255, 255}); + + // Selected object indicator + SDL_RenderFillRect(renderer, &(SDL_Rect){250, 50, FC_GetBounds(font, 250, 50, FC_ALIGN_LEFT, (FC_Scale){1, 1}, "Selected object: %s", getTranslationForObject(levelTiles[player.x * LEVEL_WIDTH + player.y])).w, 40}); + FC_Draw(font, renderer, 250, 55, "Selected object: %s", getTranslationForObject(levelTiles[player.x * LEVEL_WIDTH + player.y])); + + // Camera offset + SDL_Rect bounds = FC_GetBounds(font, 50, 200, FC_ALIGN_LEFT, (FC_Scale){1, 1}, "Cam offset: %i, %i", offset.x, offset.y); + bounds.h += 5; + SDL_RenderFillRect(renderer, &bounds); + FC_Draw(font, renderer, 50, 205, "Cam offset: %i, %i", offset.x, offset.y); + + // Player pos + SDL_RenderFillRect(renderer, &(SDL_Rect){50, 100, FC_GetBounds(font, 50, 50, FC_ALIGN_LEFT, (FC_Scale){1, 1}, "Player pos: %i, %i\nObjects stolen: %s", player.x, player.y, player.objectsStolen).w, 75}); + FC_Draw(font, renderer, 50, 105, "Player pos: %i, %i\nObjects stolen: %s", player.x, player.y, player.objectsStolen); + + // Fill progress bar + SDL_SetRenderDrawColor(renderer, 200, 0, 0, 200); + SDL_RenderFillRect(renderer, &(SDL_Rect){50, 50, stealProgress * 1.5, 40}); + FC_DrawAlign(font, renderer, 50 + 64 * 1.5, 55, FC_ALIGN_CENTER, "Stealing (%i)", stealProgress); + + if(fireAlarmSystemActivated) + { + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 220); + SDL_RenderFillRect(renderer, &(SDL_Rect){0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}); + } +} + +void drawGoToExit() +{ + SDL_SetRenderDrawColor(renderer, 0, 0, 255, 200); + SDL_RenderFillRect(renderer, &(SDL_Rect){300, 400, 128, 40}); + FC_Draw(font, renderer, 300, 400, "Go to exit"); +} + +void drawTitleMenuText() +{ + FC_DrawAlign(font, renderer, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 1.1, FC_ALIGN_CENTER, "Version 0.9 (2025-01-28)\nDevious Licks is currently in beta. Bugs may occur."); + FC_Draw(font, renderer, 0, SCREEN_HEIGHT - 30, "Copyright (c) 2023 - 2025 MCL Software"); + FC_DrawAlign(font, renderer, SCREEN_WIDTH, SCREEN_HEIGHT - 30, FC_ALIGN_RIGHT, "Free-as-in-freedom, please copy and share!"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/draw.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,26 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef DRAW +#define DRAW + +#include "missions.h" + + +void drawTiles(); +void drawPlayer(); +void drawHUD(); +void drawTitleMenuText(); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/effects.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,57 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "common.h" +#include "effects.h" +SDL_TimerID textTimer = 0; +bool typingEffectInProgress = 0; + +static int placesFromLeft = 0; + +Uint32 animateText(Uint32 interval, Uint16 sizeOfText) +{ + placesFromLeft += 1; + if(placesFromLeft >= sizeOfText) + { + SDL_RemoveTimer(textTimer); + typingEffectInProgress = 0; + textTimer = 0; + return 0; + } + + return 50; +} + +Uint16 ISOL_TypingEffect(Uint16 sizeOfText) +{ + if(typingEffectInProgress) + { + return placesFromLeft; + } + + else if(placesFromLeft >= sizeOfText) + { + placesFromLeft = 0; + return -1; + } + + else + { + placesFromLeft = 0; + typingEffectInProgress = 1; + textTimer = SDL_AddTimer(400, animateText, sizeOfText); + } + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/effects.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,13 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fade.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,95 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "common.h" + +Uint8 alpha; +bool fadeInProgress = 0; +SDL_TimerID fadeTimer = 0; + +Uint32 fadeIn(Uint32 interval) +{ + alpha -= 1; + SDL_SetRenderDrawColor(renderer, 0, 0, 0, alpha); + + if(alpha == 0) + { + SDL_RemoveTimer(fadeTimer); + return 0; + } + + return interval; +} + +Uint32 fadeOut(Uint32 interval) +{ + alpha += 1; + SDL_SetRenderDrawColor(renderer, 0, 0, 0, alpha); + + if(alpha == 255) + { + SDL_RemoveTimer(fadeTimer); + return 0; + } + + return interval; +} + +bool ISOL_FadeIn(int interval) +{ + if(fadeInProgress && alpha > 0) + { + SDL_RenderFillRect(renderer, &(SDL_Rect){0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}); + //SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); + return 0; + } + + else if(fadeInProgress) + { + fadeInProgress = 0; + return 1; + } + + else + { + fadeInProgress = 1; + alpha = 255; + fadeTimer = SDL_AddTimer(interval, fadeIn, 0); + return 0; + } +} + +bool ISOL_FadeOut(int interval) +{ + if(fadeInProgress && alpha < 255) + { + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_RenderFillRect(renderer, &(SDL_Rect){0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}); + return 0; + } + + else if(fadeInProgress) + { + fadeInProgress = 0; + return 1; + } + + else + { + fadeInProgress = 1; + alpha = 0; + fadeTimer = SDL_AddTimer(interval, fadeOut, 0); + return 0; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fade.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,18 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +bool ISOL_FadeOut(); + +bool ISOL_FadeIn(); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/firealrm.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,25 @@ +#include "firealrm.h" + +int counter = 0; + +Uint32 ALRM_soundFireAlarm(Uint32 interval) +{ + if(!fireAlarmSystemActivated) + { + Mix_PlayChannel(-1, alarmSound, 0); + fireAlarmSystemActivated = 1; + counter += 1; + return 100; + } + + else + { + fireAlarmSystemActivated = 0; + if(counter > 2) + { + counter = 0; + return 1800; + } + return 1000; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/firealrm.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,2 @@ +#include "common.h" +Uint32 ALRM_soundFireAlarm(Uint32 interval);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fontcche.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,2925 @@ +/* +SDL_FontCache: A font cache for SDL and SDL_ttf +by Jonathan Dearborn + +See fontcche.h for license info. +*/ + +#include "fontcche.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// Visual C does not support static inline +#ifndef static_inline + #ifdef _MSC_VER + #define static_inline static + #else + #define static_inline static inline + #endif +#endif + +#if SDL_VERSION_ATLEAST(2,0,0) + #define FC_GET_ALPHA(sdl_color) ((sdl_color).a) +#else + #define FC_GET_ALPHA(sdl_color) ((sdl_color).unused) +#endif + +// Need SDL_RenderIsClipEnabled() for proper clipping support +#if SDL_VERSION_ATLEAST(2,0,4) + #define ENABLE_SDL_CLIPPING +#endif + +#define FC_MIN(a,b) ((a) < (b)? (a) : (b)) +#define FC_MAX(a,b) ((a) > (b)? (a) : (b)) + + +// vsnprintf replacement from Valentin Milea: +// http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 +#if defined(_MSC_VER) && _MSC_VER < 1900 + +#define snprintf c99_snprintf +#define vsnprintf c99_vsnprintf + +__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) +{ + int count = -1; + + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} + +__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...) +{ + int count; + va_list ap; + + va_start(ap, format); + count = c99_vsnprintf(outBuf, size, format, ap); + va_end(ap); + + return count; +} + +#endif + + +#define FC_EXTRACT_VARARGS(buffer, start_args) \ +{ \ + va_list lst; \ + va_start(lst, start_args); \ + vsnprintf(buffer, fc_buffer_size, start_args, lst); \ + va_end(lst); \ +} + +// Extra pixels of padding around each glyph to avoid linear filtering artifacts +#define FC_CACHE_PADDING 1 + + + +static Uint8 has_clip(FC_Target* dest) +{ + #ifdef FC_USE_SDL_GPU + return dest->use_clip_rect; + #elif defined(ENABLE_SDL_CLIPPING) + return SDL_RenderIsClipEnabled(dest); + #else + return 0; + #endif +} + +static FC_Rect get_clip(FC_Target* dest) +{ + #ifdef FC_USE_SDL_GPU + return dest->clip_rect; + #elif defined(ENABLE_SDL_CLIPPING) + SDL_Rect r; + SDL_RenderGetClipRect(dest, &r); + return r; + #else + SDL_Rect r = {0, 0, 0, 0}; + return r; + #endif +} + +static void set_clip(FC_Target* dest, FC_Rect* rect) +{ + #ifdef FC_USE_SDL_GPU + if(rect != NULL) + GPU_SetClipRect(dest, *rect); + else + GPU_UnsetClip(dest); + #elif defined(ENABLE_SDL_CLIPPING) + SDL_RenderSetClipRect(dest, rect); + #endif +} + +static void set_color(FC_Image* src, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + #ifdef FC_USE_SDL_GPU + GPU_SetRGBA(src, r, g, b, a); + #else + SDL_SetTextureColorMod(src, r, g, b); + SDL_SetTextureAlphaMod(src, a); + #endif +} + + + +static char* new_concat(const char* a, const char* b) +{ + // Create new buffer + unsigned int size = strlen(a) + strlen(b); + char* new_string = (char*)malloc(size+1); + + // Concatenate strings in the new buffer + strcpy(new_string, a); + strcat(new_string, b); + + return new_string; +} + +static char* replace_concat(char** a, const char* b) +{ + char* new_string = new_concat(*a, b); + free(*a); + *a = new_string; + return *a; +} + + +// Width of a tab in units of the space width (sorry, no tab alignment!) +static unsigned int fc_tab_width = 4; + +// Shared buffer for variadic text +static char* fc_buffer = NULL; +static unsigned int fc_buffer_size = 1024; + +static Uint8 fc_has_render_target_support = 0; + +// The number of fonts that has been created but not freed +static int NUM_EXISTING_FONTS = 0; + +// Globals for GetString functions +static char* ASCII_STRING = NULL; +static char* LATIN_1_STRING = NULL; +static char* ASCII_LATIN_1_STRING = NULL; + +char* FC_GetStringASCII(void) +{ + if(ASCII_STRING == NULL) + { + int i; + char c; + ASCII_STRING = (char*)malloc(512); + memset(ASCII_STRING, 0, 512); + i = 0; + c = 32; + while(1) + { + ASCII_STRING[i] = c; + if(c == 126) + break; + ++i; + ++c; + } + } + return U8_strdup(ASCII_STRING); +} + +char* FC_GetStringLatin1(void) +{ + if(LATIN_1_STRING == NULL) + { + int i; + unsigned char c; + LATIN_1_STRING = (char*)malloc(512); + memset(LATIN_1_STRING, 0, 512); + i = 0; + c = 0xA0; + while(1) + { + LATIN_1_STRING[i] = 0xC2; + LATIN_1_STRING[i+1] = c; + if(c == 0xBF) + break; + i += 2; + ++c; + } + i += 2; + c = 0x80; + while(1) + { + LATIN_1_STRING[i] = 0xC3; + LATIN_1_STRING[i+1] = c; + if(c == 0xBF) + break; + i += 2; + ++c; + } + } + return U8_strdup(LATIN_1_STRING); +} + +char* FC_GetStringASCII_Latin1(void) +{ + if(ASCII_LATIN_1_STRING == NULL) + ASCII_LATIN_1_STRING = new_concat(FC_GetStringASCII(), FC_GetStringLatin1()); + + return U8_strdup(ASCII_LATIN_1_STRING); +} + +FC_Rect FC_MakeRect(float x, float y, float w, float h) +{ + FC_Rect r = {x, y, w, h}; + return r; +} + +FC_Scale FC_MakeScale(float x, float y) +{ + FC_Scale s = {x, y}; + + return s; +} + +SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + SDL_Color c = {r, g, b, a}; + + return c; +} + +FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color) +{ + FC_Effect e; + + e.alignment = alignment; + e.scale = scale; + e.color = color; + + return e; +} + +FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h) +{ + FC_GlyphData gd; + + gd.rect.x = x; + gd.rect.y = y; + gd.rect.w = w; + gd.rect.h = h; + gd.cache_level = cache_level; + + return gd; +} + +// Enough to hold all of the ascii characters and some. +#define FC_DEFAULT_NUM_BUCKETS 300 + +typedef struct FC_MapNode +{ + Uint32 key; + FC_GlyphData value; + struct FC_MapNode* next; + +} FC_MapNode; + +typedef struct FC_Map +{ + int num_buckets; + FC_MapNode** buckets; +} FC_Map; + + + +static FC_Map* FC_MapCreate(int num_buckets) +{ + int i; + FC_Map* map = (FC_Map*)malloc(sizeof(FC_Map)); + + map->num_buckets = num_buckets; + map->buckets = (FC_MapNode**)malloc(num_buckets * sizeof(FC_MapNode*)); + + for(i = 0; i < num_buckets; ++i) + { + map->buckets[i] = NULL; + } + + return map; +} + +/*static void FC_MapClear(FC_Map* map) +{ + int i; + if(map == NULL) + return; + + // Go through each bucket + for(i = 0; i < map->num_buckets; ++i) + { + // Delete the nodes in order + FC_MapNode* node = map->buckets[i]; + while(node != NULL) + { + FC_MapNode* last = node; + node = node->next; + free(last); + } + // Set the bucket to empty + map->buckets[i] = NULL; + } +}*/ + +static void FC_MapFree(FC_Map* map) +{ + int i; + if(map == NULL) + return; + + // Go through each bucket + for(i = 0; i < map->num_buckets; ++i) + { + // Delete the nodes in order + FC_MapNode* node = map->buckets[i]; + while(node != NULL) + { + FC_MapNode* last = node; + node = node->next; + free(last); + } + } + + free(map->buckets); + free(map); +} + +// Note: Does not handle duplicates in any special way. +static FC_GlyphData* FC_MapInsert(FC_Map* map, Uint32 codepoint, FC_GlyphData glyph) +{ + Uint32 index; + FC_MapNode* node; + if(map == NULL) + return NULL; + + // Get index for bucket + index = codepoint % map->num_buckets; + + // If this bucket is empty, create a node and return its value + if(map->buckets[index] == NULL) + { + node = map->buckets[index] = (FC_MapNode*)malloc(sizeof(FC_MapNode)); + node->key = codepoint; + node->value = glyph; + node->next = NULL; + return &node->value; + } + + for(node = map->buckets[index]; node != NULL; node = node->next) + { + // Find empty node and add a new one on. + if(node->next == NULL) + { + node->next = (FC_MapNode*)malloc(sizeof(FC_MapNode)); + node = node->next; + + node->key = codepoint; + node->value = glyph; + node->next = NULL; + return &node->value; + } + } + + return NULL; +} + +static FC_GlyphData* FC_MapFind(FC_Map* map, Uint32 codepoint) +{ + Uint32 index; + FC_MapNode* node; + if(map == NULL) + return NULL; + + // Get index for bucket + index = codepoint % map->num_buckets; + + // Go through list until we find a match + for(node = map->buckets[index]; node != NULL; node = node->next) + { + if(node->key == codepoint) + return &node->value; + } + + return NULL; +} + + + +struct FC_Font +{ + #ifndef FC_USE_SDL_GPU + SDL_Renderer* renderer; + #endif + + TTF_Font* ttf_source; // TTF_Font source of characters + Uint8 owns_ttf_source; // Can we delete the TTF_Font ourselves? + + FC_FilterEnum filter; + + SDL_Color default_color; + Uint16 height; + + Uint16 maxWidth; + Uint16 baseline; + int ascent; + int descent; + + int lineSpacing; + int letterSpacing; + + // Uses 32-bit (4-byte) Unicode codepoints to refer to each glyph + // Codepoints are little endian (reversed from UTF-8) so that something like 0x00000005 is ASCII 5 and the map can be indexed by ASCII values + FC_Map* glyphs; + + FC_GlyphData last_glyph; // Texture packing cursor + int glyph_cache_size; + int glyph_cache_count; + FC_Image** glyph_cache; + + char* loading_string; + +}; + +// Private +static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight); + + +static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); +static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); +static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); + + +static_inline SDL_Surface* FC_CreateSurface32(Uint32 width, Uint32 height) +{ + #if SDL_BYTEORDER == SDL_BIG_ENDIAN + return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + #else + return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); + #endif +} + + +char* U8_alloc(unsigned int size) +{ + char* result; + if(size == 0) + return NULL; + + result = (char*)malloc(size); + result[0] = '\0'; + + return result; +} + +void U8_free(char* string) +{ + free(string); +} + +char* U8_strdup(const char* string) +{ + char* result; + if(string == NULL) + return NULL; + + result = (char*)malloc(strlen(string)+1); + strcpy(result, string); + + return result; +} + +int U8_strlen(const char* string) +{ + int length = 0; + if(string == NULL) + return 0; + + while(*string != '\0') + { + string = U8_next(string); + ++length; + } + + return length; +} + +int U8_charsize(const char* character) +{ + if(character == NULL) + return 0; + + if((unsigned char)*character <= 0x7F) + return 1; + else if((unsigned char)*character < 0xE0) + return 2; + else if((unsigned char)*character < 0xF0) + return 3; + else + return 4; + return 1; +} + +int U8_charcpy(char* buffer, const char* source, int buffer_size) +{ + int charsize; + if(buffer == NULL || source == NULL || buffer_size < 1) + return 0; + + charsize = U8_charsize(source); + if(charsize > buffer_size) + return 0; + + memcpy(buffer, source, charsize); + return charsize; +} + +const char* U8_next(const char* string) +{ + return string + U8_charsize(string); +} + +int U8_strinsert(char* string, int position, const char* source, int max_bytes) +{ + int pos_u8char; + int len; + int add_len; + int ulen; + const char* string_start = string; + + if(string == NULL || source == NULL) + return 0; + + len = strlen(string); + add_len = strlen(source); + ulen = U8_strlen(string); + + if(position == -1) + position = ulen; + + if(position < 0 || position > ulen || len + add_len + 1 > max_bytes) + return 0; + + // Move string pointer to the proper position + pos_u8char = 0; + while(*string != '\0' && pos_u8char < position) + { + string = (char*)U8_next(string); + ++pos_u8char; + } + + // Move the rest of the string out of the way + memmove(string + add_len, string, len - (string - string_start) + 1); + + // Copy in the new characters + memcpy(string, source, add_len); + + return 1; +} + +void U8_strdel(char* string, int position) +{ + if(string == NULL || position < 0) + return; + + while(*string != '\0') + { + if(position == 0) + { + int chars_to_erase = U8_charsize(string); + int remaining_bytes = strlen(string) + 1; + memmove(string, string + chars_to_erase, remaining_bytes); + break; + } + + string = (char*)U8_next(string); + --position; + } +} + + + + + +static_inline FC_Rect FC_RectUnion(FC_Rect A, FC_Rect B) +{ + float x,x2,y,y2; + x = FC_MIN(A.x, B.x); + y = FC_MIN(A.y, B.y); + x2 = FC_MAX(A.x+A.w, B.x+B.w); + y2 = FC_MAX(A.y+A.h, B.y+B.h); + { + FC_Rect result = {x, y, FC_MAX(0, x2 - x), FC_MAX(0, y2 - y)}; + return result; + } +} + +// Adapted from SDL_IntersectRect +static_inline FC_Rect FC_RectIntersect(FC_Rect A, FC_Rect B) +{ + FC_Rect result; + float Amin, Amax, Bmin, Bmax; + + // Horizontal intersection + Amin = A.x; + Amax = Amin + A.w; + Bmin = B.x; + Bmax = Bmin + B.w; + if(Bmin > Amin) + Amin = Bmin; + result.x = Amin; + if(Bmax < Amax) + Amax = Bmax; + result.w = Amax - Amin > 0 ? Amax - Amin : 0; + + // Vertical intersection + Amin = A.y; + Amax = Amin + A.h; + Bmin = B.y; + Bmax = Bmin + B.h; + if(Bmin > Amin) + Amin = Bmin; + result.y = Amin; + if(Bmax < Amax) + Amax = Bmax; + result.h = Amax - Amin > 0 ? Amax - Amin : 0; + + return result; +} + + + + + + + + + + + + + + +FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale) +{ + float w = srcrect->w * xscale; + float h = srcrect->h * yscale; + FC_Rect result; + + // FIXME: Why does the scaled offset look so wrong? + #ifdef FC_USE_SDL_GPU + { + GPU_Rect r = *srcrect; + GPU_BlitScale(src, &r, dest, x + xscale*r.w/2.0f, y + r.h/2.0f, xscale, yscale); + } + #else + { + SDL_RendererFlip flip = SDL_FLIP_NONE; + if(xscale < 0) + { + xscale = -xscale; + flip = (SDL_RendererFlip) ((int)flip | (int)SDL_FLIP_HORIZONTAL); + } + if(yscale < 0) + { + yscale = -yscale; + flip = (SDL_RendererFlip) ((int)flip | (int)SDL_FLIP_VERTICAL); + } + + SDL_Rect r = *srcrect; + SDL_Rect dr = {(int)x, (int)y, (int)(xscale*r.w), (int)(yscale*r.h)}; + SDL_RenderCopyEx(dest, src, &r, &dr, 0, NULL, flip); + } + #endif + + result.x = x; + result.y = y; + result.w = w; + result.h = h; + return result; +} + +static FC_Rect (*fc_render_callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale) = &FC_DefaultRenderCallback; + +void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale)) +{ + if(callback == NULL) + fc_render_callback = &FC_DefaultRenderCallback; + else + fc_render_callback = callback; +} + +void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint) +{ + char a, b, c, d; + + if(result == NULL) + return; + + a = (codepoint >> 24) & 0xFF; + b = (codepoint >> 16) & 0xFF; + c = (codepoint >> 8) & 0xFF; + d = codepoint & 0xFF; + + if(a == 0) + { + if(b == 0) + { + if(c == 0) + { + result[0] = d; + result[1] = '\0'; + } + else + { + result[0] = c; + result[1] = d; + result[2] = '\0'; + } + } + else + { + result[0] = b; + result[1] = c; + result[2] = d; + result[3] = '\0'; + } + } + else + { + result[0] = a; + result[1] = b; + result[2] = c; + result[3] = d; + result[4] = '\0'; + } +} + +Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer) +{ + Uint32 result = 0; + const char* str; + if(c == NULL || *c == NULL) + return 0; + + str = *c; + if((unsigned char)*str <= 0x7F) + result = *str; + else if((unsigned char)*str < 0xE0) + { + result |= (unsigned char)(*str) << 8; + result |= (unsigned char)(*(str+1)); + if(advance_pointer) + *c += 1; + } + else if((unsigned char)*str < 0xF0) + { + result |= (unsigned char)(*str) << 16; + result |= (unsigned char)(*(str+1)) << 8; + result |= (unsigned char)(*(str+2)); + if(advance_pointer) + *c += 2; + } + else + { + result |= (unsigned char)(*str) << 24; + result |= (unsigned char)(*(str+1)) << 16; + result |= (unsigned char)(*(str+2)) << 8; + result |= (unsigned char)(*(str+3)); + if(advance_pointer) + *c += 3; + } + return result; +} + + +void FC_SetLoadingString(FC_Font* font, const char* string) +{ + if(font == NULL) + return; + + free(font->loading_string); + font->loading_string = U8_strdup(string); +} + + +unsigned int FC_GetBufferSize(void) +{ + return fc_buffer_size; +} + +void FC_SetBufferSize(unsigned int size) +{ + free(fc_buffer); + if(size > 0) + { + fc_buffer_size = size; + fc_buffer = (char*)malloc(fc_buffer_size); + } + else + fc_buffer = (char*)malloc(fc_buffer_size); +} + + +unsigned int FC_GetTabWidth(void) +{ + return fc_tab_width; +} + +void FC_SetTabWidth(unsigned int width_in_spaces) +{ + fc_tab_width = width_in_spaces; +} + + + + + +// Constructors + +static void FC_Init(FC_Font* font) +{ + if(font == NULL) + return; + + #ifndef FC_USE_SDL_GPU + font->renderer = NULL; + #endif + + font->ttf_source = NULL; + font->owns_ttf_source = 0; + + font->filter = FC_FILTER_NEAREST; + + font->default_color.r = 0; + font->default_color.g = 0; + font->default_color.b = 0; + FC_GET_ALPHA(font->default_color) = 255; + + font->height = 0; // ascent+descent + + font->maxWidth = 0; + font->baseline = 0; + font->ascent = 0; + font->descent = 0; + + font->lineSpacing = 0; + font->letterSpacing = 0; + + // Give a little offset for when filtering/mipmaps are used. Depending on mipmap level, this will still not be enough. + font->last_glyph.rect.x = FC_CACHE_PADDING; + font->last_glyph.rect.y = FC_CACHE_PADDING; + font->last_glyph.rect.w = 0; + font->last_glyph.rect.h = 0; + font->last_glyph.cache_level = 0; + + if(font->glyphs != NULL) + FC_MapFree(font->glyphs); + + font->glyphs = FC_MapCreate(FC_DEFAULT_NUM_BUCKETS); + + font->glyph_cache_size = 3; + font->glyph_cache_count = 0; + + + font->glyph_cache = (FC_Image**)malloc(font->glyph_cache_size * sizeof(FC_Image*)); + + if (font->loading_string == NULL) + font->loading_string = FC_GetStringASCII(); + + if(fc_buffer == NULL) + fc_buffer = (char*)malloc(fc_buffer_size); +} + +static Uint8 FC_GrowGlyphCache(FC_Font* font) +{ + if(font == NULL) + return 0; + #ifdef FC_USE_SDL_GPU + GPU_Image* new_level = GPU_CreateImage(font->height * 12, font->height * 12, GPU_FORMAT_RGBA); + GPU_SetAnchor(new_level, 0.5f, 0.5f); // Just in case the default is different + #else + SDL_Texture* new_level = SDL_CreateTexture(font->renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, font->height * 12, font->height * 12); + #endif + if(new_level == NULL || !FC_SetGlyphCacheLevel(font, font->glyph_cache_count, new_level)) + { + FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n"); + #ifdef FC_USE_SDL_GPU + GPU_FreeImage(new_level); + #else + SDL_DestroyTexture(new_level); + #endif + return 0; + } + // bug: we do not have the correct color here, this might be the wrong color! + // , most functions use set_color_for_all_caches() + // - for evading this bug, you must use FC_SetDefaultColor(), before using any draw functions + set_color(new_level, font->default_color.r, font->default_color.g, font->default_color.b, FC_GET_ALPHA(font->default_color)); +#ifndef FC_USE_SDL_GPU + { + Uint8 r, g, b, a; + SDL_Texture* prev_target = SDL_GetRenderTarget(font->renderer); + SDL_Rect prev_clip, prev_viewport; + int prev_logicalw, prev_logicalh; + Uint8 prev_clip_enabled; + float prev_scalex, prev_scaley; + // only backup if previous target existed (SDL will preserve them for the default target) + if (prev_target) { + prev_clip_enabled = has_clip(font->renderer); + if (prev_clip_enabled) + prev_clip = get_clip(font->renderer); + SDL_RenderGetViewport(font->renderer, &prev_viewport); + SDL_RenderGetScale(font->renderer, &prev_scalex, &prev_scaley); + SDL_RenderGetLogicalSize(font->renderer, &prev_logicalw, &prev_logicalh); + } + SDL_SetTextureBlendMode(new_level, SDL_BLENDMODE_BLEND); + SDL_SetRenderTarget(font->renderer, new_level); + SDL_GetRenderDrawColor(font->renderer, &r, &g, &b, &a); + SDL_SetRenderDrawColor(font->renderer, 0, 0, 0, 0); + SDL_RenderClear(font->renderer); + SDL_SetRenderDrawColor(font->renderer, r, g, b, a); + SDL_SetRenderTarget(font->renderer, prev_target); + if (prev_target) { + if (prev_clip_enabled) + set_clip(font->renderer, &prev_clip); + if (prev_logicalw && prev_logicalh) + SDL_RenderSetLogicalSize(font->renderer, prev_logicalw, prev_logicalh); + else { + SDL_RenderSetViewport(font->renderer, &prev_viewport); + SDL_RenderSetScale(font->renderer, prev_scalex, prev_scaley); + } + } + } +#endif + return 1; +} + +Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface) +{ + if(font == NULL || data_surface == NULL) + return 0; + #ifdef FC_USE_SDL_GPU + GPU_Image* new_level = GPU_CopyImageFromSurface(data_surface); + GPU_SetAnchor(new_level, 0.5f, 0.5f); // Just in case the default is different + if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) + GPU_SetImageFilter(new_level, GPU_FILTER_LINEAR); + else + GPU_SetImageFilter(new_level, GPU_FILTER_NEAREST); + #else + SDL_Texture* new_level; + if(!fc_has_render_target_support) + new_level = SDL_CreateTextureFromSurface(font->renderer, data_surface); + else + { + // Must upload with render target enabled so we can put more glyphs on later + SDL_Renderer* renderer = font->renderer; + + // Set filter mode for new texture + char old_filter_mode[16]; // Save it so we can change the hint value in the meantime + const char* old_filter_hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY); + if(!old_filter_hint) + old_filter_hint = "nearest"; + snprintf(old_filter_mode, 16, "%s", old_filter_hint); + + if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + else + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + + new_level = SDL_CreateTexture(renderer, data_surface->format->format, SDL_TEXTUREACCESS_TARGET, data_surface->w, data_surface->h); + SDL_SetTextureBlendMode(new_level, SDL_BLENDMODE_BLEND); + + // Reset filter mode for the temp texture + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + + { + Uint8 r, g, b, a; + SDL_Texture* temp = SDL_CreateTextureFromSurface(renderer, data_surface); + SDL_Texture* prev_target = SDL_GetRenderTarget(renderer); + SDL_Rect prev_clip, prev_viewport; + int prev_logicalw, prev_logicalh; + Uint8 prev_clip_enabled; + float prev_scalex, prev_scaley; + // only backup if previous target existed (SDL will preserve them for the default target) + if (prev_target) { + prev_clip_enabled = has_clip(renderer); + if (prev_clip_enabled) + prev_clip = get_clip(renderer); + SDL_RenderGetViewport(renderer, &prev_viewport); + SDL_RenderGetScale(renderer, &prev_scalex, &prev_scaley); + SDL_RenderGetLogicalSize(renderer, &prev_logicalw, &prev_logicalh); + } + SDL_SetTextureBlendMode(temp, SDL_BLENDMODE_NONE); + SDL_SetRenderTarget(renderer, new_level); + + SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + SDL_SetRenderDrawColor(renderer, r, g, b, a); + + SDL_RenderCopy(renderer, temp, NULL, NULL); + SDL_SetRenderTarget(renderer, prev_target); + if (prev_target) { + if (prev_clip_enabled) + set_clip(renderer, &prev_clip); + if (prev_logicalw && prev_logicalh) + SDL_RenderSetLogicalSize(renderer, prev_logicalw, prev_logicalh); + else { + SDL_RenderSetViewport(renderer, &prev_viewport); + SDL_RenderSetScale(renderer, prev_scalex, prev_scaley); + } + } + + SDL_DestroyTexture(temp); + } + + // Reset to the old filter value + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, old_filter_mode); + + } + #endif + if(new_level == NULL || !FC_SetGlyphCacheLevel(font, cache_level, new_level)) + { + FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n"); + #ifdef FC_USE_SDL_GPU + GPU_FreeImage(new_level); + #else + SDL_DestroyTexture(new_level); + #endif + return 0; + } + return 1; +} + +static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight) +{ + FC_Map* glyphs = font->glyphs; + FC_GlyphData* last_glyph = &font->last_glyph; + Uint16 height = font->height + FC_CACHE_PADDING; + + // TAB is special! + if(codepoint == '\t') + { + FC_GlyphData spaceGlyph; + FC_GetGlyphData(font, &spaceGlyph, ' '); + width = fc_tab_width * spaceGlyph.rect.w; + } + + if(last_glyph->rect.x + last_glyph->rect.w + width >= maxWidth - FC_CACHE_PADDING) + { + if(last_glyph->rect.y + height + height >= maxHeight - FC_CACHE_PADDING) + { + // Get ready to pack on the next cache level when it is ready + last_glyph->cache_level = font->glyph_cache_count; + last_glyph->rect.x = FC_CACHE_PADDING; + last_glyph->rect.y = FC_CACHE_PADDING; + last_glyph->rect.w = 0; + return NULL; + } + else + { + // Go to next row + last_glyph->rect.x = FC_CACHE_PADDING; + last_glyph->rect.y += height; + last_glyph->rect.w = 0; + } + } + + // Move to next space + last_glyph->rect.x += last_glyph->rect.w + 1 + FC_CACHE_PADDING; + last_glyph->rect.w = width; + + return FC_MapInsert(glyphs, codepoint, FC_MakeGlyphData(last_glyph->cache_level, last_glyph->rect.x, last_glyph->rect.y, last_glyph->rect.w, last_glyph->rect.h)); +} + + +FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level) +{ + if(font == NULL || cache_level < 0 || cache_level > font->glyph_cache_count) + return NULL; + + return font->glyph_cache[cache_level]; +} + +Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture) +{ + if(font == NULL || cache_level < 0) + return 0; + + // Must be sequentially added + if(cache_level > font->glyph_cache_count + 1) + return 0; + + if(cache_level == font->glyph_cache_count) + { + font->glyph_cache_count++; + + // Grow cache? + if(font->glyph_cache_count > font->glyph_cache_size) + { + // Copy old cache to new one + int i; + FC_Image** new_cache; + new_cache = (FC_Image**)malloc(font->glyph_cache_count * sizeof(FC_Image*)); + for(i = 0; i < font->glyph_cache_size; ++i) + new_cache[i] = font->glyph_cache[i]; + + // Save new cache + free(font->glyph_cache); + font->glyph_cache_size = font->glyph_cache_count; + font->glyph_cache = new_cache; + } + } + + font->glyph_cache[cache_level] = cache_texture; + return 1; +} + + +FC_Font* FC_CreateFont(void) +{ + FC_Font* font; + + font = (FC_Font*)malloc(sizeof(FC_Font)); + memset(font, 0, sizeof(FC_Font)); + + FC_Init(font); + ++NUM_EXISTING_FONTS; + + return font; +} + + +// Assume this many will be enough... +#define FC_LOAD_MAX_SURFACES 10 + +#ifdef FC_USE_SDL_GPU +Uint8 FC_LoadFontFromTTF(FC_Font* font, TTF_Font* ttf, SDL_Color color) +#else +Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, SDL_Color color) +#endif +{ + if(font == NULL || ttf == NULL) + return 0; + #ifndef FC_USE_SDL_GPU + if(renderer == NULL) + return 0; + #endif + + FC_ClearFont(font); + + + // Might as well check render target support here + #ifdef FC_USE_SDL_GPU + fc_has_render_target_support = GPU_IsFeatureEnabled(GPU_FEATURE_RENDER_TARGETS); + #else + SDL_RendererInfo info; + SDL_GetRendererInfo(renderer, &info); + fc_has_render_target_support = (info.flags & SDL_RENDERER_TARGETTEXTURE); + + font->renderer = renderer; + #endif + + font->ttf_source = ttf; + + //font->line_height = TTF_FontLineSkip(ttf); + font->height = TTF_FontHeight(ttf); + font->ascent = TTF_FontAscent(ttf); + font->descent = -TTF_FontDescent(ttf); + + // Some bug for certain fonts can result in an incorrect height. + if(font->height < font->ascent - font->descent) + font->height = font->ascent - font->descent; + + font->baseline = font->height - font->descent; + + font->default_color = color; + + { + SDL_Color white = {255, 255, 255, 255}; + SDL_Surface* glyph_surf; + char buff[5]; + const char* buff_ptr = buff; + const char* source_string; + Uint8 packed = 0; + + // Copy glyphs from the surface to the font texture and store the position data + // Pack row by row into a square texture + // Try figuring out dimensions that make sense for the font size. + unsigned int w = font->height*12; + unsigned int h = font->height*12; + SDL_Surface* surfaces[FC_LOAD_MAX_SURFACES]; + int num_surfaces = 1; + surfaces[0] = FC_CreateSurface32(w, h); + font->last_glyph.rect.x = FC_CACHE_PADDING; + font->last_glyph.rect.y = FC_CACHE_PADDING; + font->last_glyph.rect.w = 0; + font->last_glyph.rect.h = font->height; + + source_string = font->loading_string; + for(; *source_string != '\0'; source_string = U8_next(source_string)) + { + memset(buff, 0, 5); + if(!U8_charcpy(buff, source_string, 5)) + continue; + glyph_surf = TTF_RenderUTF8_Blended(ttf, buff, white); + if(glyph_surf == NULL) + continue; + + // Try packing. If it fails, create a new surface for the next cache level. + packed = (FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL); + if(!packed) + { + int i = num_surfaces-1; + if(num_surfaces >= FC_LOAD_MAX_SURFACES) + { + // Can't do any more! + FC_Log("SDL_FontCache error: Could not create enough cache surfaces to fit all of the loading string!\n"); + SDL_FreeSurface(glyph_surf); + break; + } + + // Upload the current surface to the glyph cache now so we can keep the cache level packing cursor up to date as we go. + FC_UploadGlyphCache(font, i, surfaces[i]); + SDL_FreeSurface(surfaces[i]); + #ifndef FC_USE_SDL_GPU + SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND); + #endif + // Update the glyph cursor to the new cache level. We need to do this here because the actual cache lags behind our use of the packing above. + font->last_glyph.cache_level = num_surfaces; + + + surfaces[num_surfaces] = FC_CreateSurface32(w, h); + num_surfaces++; + } + + // Try packing for the new surface, then blit onto it. + if(packed || FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL) + { + SDL_SetSurfaceBlendMode(glyph_surf, SDL_BLENDMODE_NONE); + SDL_Rect srcRect = {0, 0, glyph_surf->w, glyph_surf->h}; + SDL_Rect destrect = font->last_glyph.rect; + SDL_BlitSurface(glyph_surf, &srcRect, surfaces[num_surfaces-1], &destrect); + } + + SDL_FreeSurface(glyph_surf); + } + + { + int i = num_surfaces-1; + FC_UploadGlyphCache(font, i, surfaces[i]); + SDL_FreeSurface(surfaces[i]); + #ifndef FC_USE_SDL_GPU + SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND); + #endif + } + } + + return 1; +} + + +#ifdef FC_USE_SDL_GPU +Uint8 FC_LoadFont(FC_Font* font, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style) +#else +Uint8 FC_LoadFont(FC_Font* font, FC_Target* renderer, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style) +#endif +{ + SDL_RWops* rwops; + + if(font == NULL) + return 0; + + rwops = SDL_RWFromFile(filename_ttf, "rb"); + + if(rwops == NULL) + { + FC_Log("Unable to open file for reading: %s \n", SDL_GetError()); + return 0; + } + + #ifdef FC_USE_SDL_GPU + return FC_LoadFont_RW(font, rwops, 1, pointSize, color, style); + #else + return FC_LoadFont_RW(font, renderer, rwops, 1, pointSize, color, style); + #endif +} + +#ifdef FC_USE_SDL_GPU +Uint8 FC_LoadFont_RW(FC_Font* font, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style) +#else +Uint8 FC_LoadFont_RW(FC_Font* font, FC_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style) +#endif +{ + Uint8 result; + TTF_Font* ttf; + Uint8 outline; + + if(font == NULL) + return 0; + + if(!TTF_WasInit() && TTF_Init() < 0) + { + FC_Log("Unable to initialize SDL_ttf: %s \n", TTF_GetError()); + if(own_rwops) + SDL_RWclose(file_rwops_ttf); + return 0; + } + + ttf = TTF_OpenFontRW(file_rwops_ttf, own_rwops, pointSize); + + if(ttf == NULL) + { + FC_Log("Unable to load TrueType font: %s \n", TTF_GetError()); + if(own_rwops) + SDL_RWclose(file_rwops_ttf); + return 0; + } + + outline = (style & TTF_STYLE_OUTLINE); + if(outline) + { + style &= ~TTF_STYLE_OUTLINE; + TTF_SetFontOutline(ttf, 1); + } + TTF_SetFontStyle(ttf, style); + + #ifdef FC_USE_SDL_GPU + result = FC_LoadFontFromTTF(font, ttf, color); + #else + result = FC_LoadFontFromTTF(font, renderer, ttf, color); + #endif + + // Can only load new (uncached) glyphs if we can keep the SDL_RWops open. + font->owns_ttf_source = own_rwops; + if(!own_rwops) + { + TTF_CloseFont(font->ttf_source); + font->ttf_source = NULL; + } + + return result; +} + + +#ifndef FC_USE_SDL_GPU +void FC_ResetFontFromRendererReset(FC_Font* font, SDL_Renderer* renderer, Uint32 evType) +{ + TTF_Font* ttf; + SDL_Color col; + Uint8 owns_ttf; + if (font == NULL) + return; + + // Destroy glyph cache + if (evType == SDL_RENDER_TARGETS_RESET) { + int i; + for (i = 0; i < font->glyph_cache_count; ++i) + SDL_DestroyTexture(font->glyph_cache[i]); + } + free(font->glyph_cache); + + ttf = font->ttf_source; + col = font->default_color; + owns_ttf = font->owns_ttf_source; + FC_Init(font); + + // Can only reload glyphs if we own the SDL_RWops. + if (owns_ttf) + FC_LoadFontFromTTF(font, renderer, ttf, col); + font->owns_ttf_source = owns_ttf; +} +#endif + +void FC_ClearFont(FC_Font* font) +{ + int i; + if(font == NULL) + return; + + // Release resources + if(font->owns_ttf_source) + TTF_CloseFont(font->ttf_source); + + font->owns_ttf_source = 0; + font->ttf_source = NULL; + + // Delete glyph map + FC_MapFree(font->glyphs); + font->glyphs = NULL; + + // Delete glyph cache + for(i = 0; i < font->glyph_cache_count; ++i) + { + #ifdef FC_USE_SDL_GPU + GPU_FreeImage(font->glyph_cache[i]); + #else + SDL_DestroyTexture(font->glyph_cache[i]); + #endif + } + free(font->glyph_cache); + font->glyph_cache = NULL; + + // Reset font + FC_Init(font); +} + + +void FC_FreeFont(FC_Font* font) +{ + int i; + if(font == NULL) + return; + + // Release resources + if(font->owns_ttf_source) + TTF_CloseFont(font->ttf_source); + + // Delete glyph map + FC_MapFree(font->glyphs); + + // Delete glyph cache + for(i = 0; i < font->glyph_cache_count; ++i) + { + #ifdef FC_USE_SDL_GPU + GPU_FreeImage(font->glyph_cache[i]); + #else + SDL_DestroyTexture(font->glyph_cache[i]); + #endif + } + free(font->glyph_cache); + + free(font->loading_string); + + free(font); + + // If the last font has been freed; assume shutdown and free the global variables + if (--NUM_EXISTING_FONTS <= 0) + { + free(ASCII_STRING); + ASCII_STRING = NULL; + + free(LATIN_1_STRING); + LATIN_1_STRING = NULL; + + free(ASCII_LATIN_1_STRING); + ASCII_LATIN_1_STRING = NULL; + + free(fc_buffer); + fc_buffer = NULL; + } +} + +int FC_GetNumCacheLevels(FC_Font* font) +{ + return font->glyph_cache_count; +} + +Uint8 FC_AddGlyphToCache(FC_Font* font, SDL_Surface* glyph_surface) +{ + if(font == NULL || glyph_surface == NULL) + return 0; + + SDL_SetSurfaceBlendMode(glyph_surface, SDL_BLENDMODE_NONE); + FC_Image* dest = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level); + if(dest == NULL) + return 0; + + #ifdef FC_USE_SDL_GPU + { + GPU_Target* target = GPU_LoadTarget(dest); + if(target == NULL) + return 0; + GPU_Image* img = GPU_CopyImageFromSurface(glyph_surface); + GPU_SetAnchor(img, 0.5f, 0.5f); // Just in case the default is different + GPU_SetImageFilter(img, GPU_FILTER_NEAREST); + GPU_SetBlendMode(img, GPU_BLEND_SET); + + SDL_Rect destrect = font->last_glyph.rect; + GPU_Blit(img, NULL, target, destrect.x + destrect.w/2, destrect.y + destrect.h/2); + + GPU_FreeImage(img); + GPU_FreeTarget(target); + } + #else + { + SDL_Renderer* renderer = font->renderer; + SDL_Texture* img; + SDL_Rect destrect; + SDL_Texture* prev_target = SDL_GetRenderTarget(renderer); + SDL_Rect prev_clip, prev_viewport; + int prev_logicalw, prev_logicalh; + Uint8 prev_clip_enabled; + float prev_scalex, prev_scaley; + // only backup if previous target existed (SDL will preserve them for the default target) + if (prev_target) { + prev_clip_enabled = has_clip(renderer); + if (prev_clip_enabled) + prev_clip = get_clip(renderer); + SDL_RenderGetViewport(renderer, &prev_viewport); + SDL_RenderGetScale(renderer, &prev_scalex, &prev_scaley); + SDL_RenderGetLogicalSize(renderer, &prev_logicalw, &prev_logicalh); + } + + img = SDL_CreateTextureFromSurface(renderer, glyph_surface); + + destrect = font->last_glyph.rect; + SDL_SetRenderTarget(renderer, dest); + SDL_RenderCopy(renderer, img, NULL, &destrect); + SDL_SetRenderTarget(renderer, prev_target); + if (prev_target) { + if (prev_clip_enabled) + set_clip(renderer, &prev_clip); + if (prev_logicalw && prev_logicalh) + SDL_RenderSetLogicalSize(renderer, prev_logicalw, prev_logicalh); + else { + SDL_RenderSetViewport(renderer, &prev_viewport); + SDL_RenderSetScale(renderer, prev_scalex, prev_scaley); + } + } + + SDL_DestroyTexture(img); + } + #endif + + return 1; +} + + +unsigned int FC_GetNumCodepoints(FC_Font* font) +{ + FC_Map* glyphs; + int i; + unsigned int result = 0; + if(font == NULL || font->glyphs == NULL) + return 0; + + glyphs = font->glyphs; + + for(i = 0; i < glyphs->num_buckets; ++i) + { + FC_MapNode* node; + for(node = glyphs->buckets[i]; node != NULL; node = node->next) + { + result++; + } + } + + return result; +} + +void FC_GetCodepoints(FC_Font* font, Uint32* result) +{ + FC_Map* glyphs; + int i; + unsigned int count = 0; + if(font == NULL || font->glyphs == NULL) + return; + + glyphs = font->glyphs; + + for(i = 0; i < glyphs->num_buckets; ++i) + { + FC_MapNode* node; + for(node = glyphs->buckets[i]; node != NULL; node = node->next) + { + result[count] = node->key; + count++; + } + } +} + +Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint) +{ + FC_GlyphData* e = FC_MapFind(font->glyphs, codepoint); + if(e == NULL) + { + char buff[5]; + int w, h; + SDL_Color white = {255, 255, 255, 255}; + SDL_Surface* surf; + FC_Image* cache_image; + + if(font->ttf_source == NULL) + return 0; + + FC_GetUTF8FromCodepoint(buff, codepoint); + + cache_image = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level); + if(cache_image == NULL) + { + FC_Log("SDL_FontCache: Failed to load cache image, so cannot add new glyphs!\n"); + return 0; + } + + #ifdef FC_USE_SDL_GPU + w = cache_image->w; + h = cache_image->h; + #else + SDL_QueryTexture(cache_image, NULL, NULL, &w, &h); + #endif + + surf = TTF_RenderUTF8_Blended(font->ttf_source, buff, white); + if(surf == NULL) + { + return 0; + } + + e = FC_PackGlyphData(font, codepoint, surf->w, w, h); + if(e == NULL) + { + // Grow the cache + FC_GrowGlyphCache(font); + + // Try packing again + e = FC_PackGlyphData(font, codepoint, surf->w, w, h); + if(e == NULL) + { + SDL_FreeSurface(surf); + return 0; + } + } + + // Render onto the cache texture + FC_AddGlyphToCache(font, surf); + + SDL_FreeSurface(surf); + } + + if(result != NULL && e != NULL) + *result = *e; + + return 1; +} + + +FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data) +{ + return FC_MapInsert(font->glyphs, codepoint, glyph_data); +} + + + +// Drawing +static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) +{ + const char* c = text; + FC_Rect srcRect; + FC_Rect dstRect; + FC_Rect dirtyRect = FC_MakeRect(x, y, 0, 0); + + FC_GlyphData glyph; + Uint32 codepoint; + + float destX = x; + float destY = y; + float destH; + float destLineSpacing; + float destLetterSpacing; + + if(font == NULL) + return dirtyRect; + + destH = font->height * scale.y; + destLineSpacing = font->lineSpacing*scale.y; + destLetterSpacing = font->letterSpacing*scale.x; + + if(c == NULL || font->glyph_cache_count == 0 || dest == NULL) + return dirtyRect; + + int newlineX = x; + + for(; *c != '\0'; c++) + { + if(*c == '\n') + { + destX = newlineX; + destY += destH + destLineSpacing; + continue; + } + + codepoint = FC_GetCodepointFromUTF8(&c, 1); // Increments 'c' to skip the extra UTF-8 bytes + if(!FC_GetGlyphData(font, &glyph, codepoint)) + { + codepoint = ' '; + if(!FC_GetGlyphData(font, &glyph, codepoint)) + continue; // Skip bad characters + } + + if (codepoint == ' ') + { + destX += glyph.rect.w*scale.x + destLetterSpacing; + continue; + } + /*if(destX >= dest->w) + continue; + if(destY >= dest->h) + continue;*/ + + #ifdef FC_USE_SDL_GPU + srcRect.x = glyph.rect.x; + srcRect.y = glyph.rect.y; + srcRect.w = glyph.rect.w; + srcRect.h = glyph.rect.h; + #else + srcRect = glyph.rect; + #endif + dstRect = fc_render_callback(FC_GetGlyphCacheLevel(font, glyph.cache_level), &srcRect, dest, destX, destY, scale.x, scale.y); + if(dirtyRect.w == 0 || dirtyRect.h == 0) + dirtyRect = dstRect; + else + dirtyRect = FC_RectUnion(dirtyRect, dstRect); + + destX += glyph.rect.w*scale.x + destLetterSpacing; + } + + return dirtyRect; +} + +static void set_color_for_all_caches(FC_Font* font, SDL_Color color) +{ + // TODO: How can I predict which glyph caches are to be used? + FC_Image* img; + int i; + int num_levels = FC_GetNumCacheLevels(font); + for(i = 0; i < num_levels; ++i) + { + img = FC_GetGlyphCacheLevel(font, i); + set_color(img, color.r, color.g, color.b, FC_GET_ALPHA(color)); + } +} + +FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...) +{ + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(x, y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); +} + + + +typedef struct FC_StringList +{ + char* value; + struct FC_StringList* next; +} FC_StringList; + +void FC_StringListFree(FC_StringList* node) +{ + // Delete the nodes in order + while(node != NULL) + { + FC_StringList* last = node; + node = node->next; + + free(last->value); + free(last); + } +} + +FC_StringList** FC_StringListPushBack(FC_StringList** node, char* value, Uint8 copy) +{ + if(node == NULL) + { + return NULL; + } + + // Get to the last node + while(*node != NULL) + { + node = &(*node)->next; + } + + *node = (FC_StringList*)malloc(sizeof(FC_StringList)); + + (*node)->value = (copy? U8_strdup(value) : value); + (*node)->next = NULL; + + return node; +} + +FC_StringList** FC_StringListPushBackBytes(FC_StringList** node, const char* data, int num_bytes) +{ + if(node == NULL) + { + return node; + } + + // Get to the last node + while(*node != NULL) + { + node = &(*node)->next; + } + + *node = (FC_StringList*)malloc(sizeof(FC_StringList)); + + (*node)->value = (char*)malloc(num_bytes + 1); + memcpy((*node)->value, data, num_bytes); + (*node)->value[num_bytes] = '\0'; + (*node)->next = NULL; + + return node; +} + +static FC_StringList* FC_Explode(const char* text, char delimiter) +{ + FC_StringList* head; + FC_StringList* new_node; + FC_StringList** node; + const char* start; + const char* end; + unsigned int size; + if(text == NULL) + return NULL; + + head = NULL; + node = &head; + + // Doesn't technically support UTF-8, but it's probably fine, right? + size = 0; + start = end = text; + while(1) + { + if(*end == delimiter || *end == '\0') + { + *node = (FC_StringList*)malloc(sizeof(FC_StringList)); + new_node = *node; + + new_node->value = (char*)malloc(size + 1); + memcpy(new_node->value, start, size); + new_node->value[size] = '\0'; + + new_node->next = NULL; + + if(*end == '\0') + break; + + node = &((*node)->next); + start = end+1; + size = 0; + } + else + ++size; + + ++end; + } + + return head; +} + +static FC_StringList* FC_ExplodeBreakingSpace(const char* text, FC_StringList** spaces) +{ + FC_StringList* head; + FC_StringList** node; + const char* start; + const char* end; + unsigned int size; + if(text == NULL) + return NULL; + + head = NULL; + node = &head; + + // Warning: spaces must not be initialized before this function + *spaces = NULL; + + // Doesn't technically support UTF-8, but it's probably fine, right? + size = 0; + start = end = text; + while(1) + { + // Add any characters here that should make separate words (except for \n?) + if(*end == ' ' || *end == '\t' || *end == '\0') + { + FC_StringListPushBackBytes(node, start, size); + FC_StringListPushBackBytes(spaces, end, 1); + + if(*end == '\0') + break; + + node = &((*node)->next); + start = end+1; + size = 0; + } + else + ++size; + + ++end; + } + + return head; +} + +static FC_StringList* FC_ExplodeAndKeep(const char* text, char delimiter) +{ + FC_StringList* head; + FC_StringList** node; + const char* start; + const char* end; + unsigned int size; + if(text == NULL) + return NULL; + + head = NULL; + node = &head; + + // Doesn't technically support UTF-8, but it's probably fine, right? + size = 0; + start = end = text; + while(1) + { + if(*end == delimiter || *end == '\0') + { + FC_StringListPushBackBytes(node, start, size); + + if(*end == '\0') + break; + + node = &((*node)->next); + start = end; + size = 1; + } + else + ++size; + + ++end; + } + + return head; +} + +static void FC_RenderAlign(FC_Font* font, FC_Target* dest, float x, float y, int width, FC_Scale scale, FC_AlignEnum align, const char* text) +{ + switch(align) + { + case FC_ALIGN_LEFT: + FC_RenderLeft(font, dest, x, y, scale, text); + break; + case FC_ALIGN_CENTER: + FC_RenderCenter(font, dest, x + width/2, y, scale, text); + break; + case FC_ALIGN_RIGHT: + FC_RenderRight(font, dest, x + width, y, scale, text); + break; + } +} + +static FC_StringList* FC_GetBufferFitToColumn(FC_Font* font, int width, FC_Scale scale, Uint8 keep_newlines) +{ + FC_StringList* result = NULL; + FC_StringList** current = &result; + + FC_StringList *ls, *iter; + + ls = (keep_newlines? FC_ExplodeAndKeep(fc_buffer, '\n') : FC_Explode(fc_buffer, '\n')); + for(iter = ls; iter != NULL; iter = iter->next) + { + char* line = iter->value; + + // If line is too long, then add words one at a time until we go over. + if(width > 0 && FC_GetWidth(font, "%s", line) > width) + { + FC_StringList *words, *word_iter, *spaces, *spaces_iter; + + words = FC_ExplodeBreakingSpace(line, &spaces); + // Skip the first word for the iterator, so there will always be at least one word per line + line = new_concat(words->value, spaces->value); + for(word_iter = words->next, spaces_iter = spaces->next; word_iter != NULL && spaces_iter != NULL; word_iter = word_iter->next, spaces_iter = spaces_iter->next) + { + char* line_plus_word = new_concat(line, word_iter->value); + char* word_plus_space = new_concat(word_iter->value, spaces_iter->value); + if(FC_GetWidth(font, "%s", line_plus_word) > width) + { + current = FC_StringListPushBack(current, line, 0); + + line = word_plus_space; + } + else + { + replace_concat(&line, word_plus_space); + free(word_plus_space); + } + free(line_plus_word); + } + current = FC_StringListPushBack(current, line, 0); + FC_StringListFree(words); + FC_StringListFree(spaces); + } + else + { + current = FC_StringListPushBack(current, line, 0); + iter->value = NULL; + } + } + FC_StringListFree(ls); + + return result; +} + +static void FC_DrawColumnFromBuffer(FC_Font* font, FC_Target* dest, FC_Rect box, int* total_height, FC_Scale scale, FC_AlignEnum align) +{ + int y = box.y; + FC_StringList *ls, *iter; + + ls = FC_GetBufferFitToColumn(font, box.w, scale, 0); + for(iter = ls; iter != NULL; iter = iter->next) + { + FC_RenderAlign(font, dest, box.x, y, box.w, scale, align, iter->value); + y += FC_GetLineHeight(font); + } + FC_StringListFree(ls); + + if(total_height != NULL) + *total_height = y - box.y; +} + +FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...) +{ + Uint8 useClip; + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(box.x, box.y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + useClip = has_clip(dest); + FC_Rect oldclip, newclip; + if(useClip) + { + oldclip = get_clip(dest); + newclip = FC_RectIntersect(oldclip, box); + } + else + newclip = box; + + set_clip(dest, &newclip); + + set_color_for_all_caches(font, font->default_color); + + FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT); + + if(useClip) + set_clip(dest, &oldclip); + else + set_clip(dest, NULL); + + return box; +} + +FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...) +{ + Uint8 useClip; + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(box.x, box.y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + useClip = has_clip(dest); + FC_Rect oldclip, newclip; + if(useClip) + { + oldclip = get_clip(dest); + newclip = FC_RectIntersect(oldclip, box); + } + else + newclip = box; + set_clip(dest, &newclip); + + set_color_for_all_caches(font, font->default_color); + + FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), align); + + if(useClip) + set_clip(dest, &oldclip); + else + set_clip(dest, NULL); + + return box; +} + +FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...) +{ + Uint8 useClip; + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(box.x, box.y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + useClip = has_clip(dest); + FC_Rect oldclip, newclip; + if(useClip) + { + oldclip = get_clip(dest); + newclip = FC_RectIntersect(oldclip, box); + } + else + newclip = box; + set_clip(dest, &newclip); + + set_color_for_all_caches(font, font->default_color); + + FC_DrawColumnFromBuffer(font, dest, box, NULL, scale, FC_ALIGN_LEFT); + + if(useClip) + set_clip(dest, &oldclip); + else + set_clip(dest, NULL); + + return box; +} + +FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...) +{ + Uint8 useClip; + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(box.x, box.y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + useClip = has_clip(dest); + FC_Rect oldclip, newclip; + if(useClip) + { + oldclip = get_clip(dest); + newclip = FC_RectIntersect(oldclip, box); + } + else + newclip = box; + set_clip(dest, &newclip); + + set_color_for_all_caches(font, color); + + FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT); + + if(useClip) + set_clip(dest, &oldclip); + else + set_clip(dest, NULL); + + return box; +} + +FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...) +{ + Uint8 useClip; + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(box.x, box.y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + useClip = has_clip(dest); + FC_Rect oldclip, newclip; + if(useClip) + { + oldclip = get_clip(dest); + newclip = FC_RectIntersect(oldclip, box); + } + else + newclip = box; + set_clip(dest, &newclip); + + set_color_for_all_caches(font, effect.color); + + FC_DrawColumnFromBuffer(font, dest, box, NULL, effect.scale, effect.alignment); + + if(useClip) + set_clip(dest, &oldclip); + else + set_clip(dest, NULL); + + return box; +} + +FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...) +{ + FC_Rect box = {x, y, width, 0}; + int total_height; + + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(x, y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT); + + return FC_MakeRect(box.x, box.y, width, total_height); +} + +FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...) +{ + FC_Rect box = {x, y, width, 0}; + int total_height; + + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(x, y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + switch(align) + { + case FC_ALIGN_CENTER: + box.x -= width/2; + break; + case FC_ALIGN_RIGHT: + box.x -= width; + break; + default: + break; + } + + FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), align); + + return FC_MakeRect(box.x, box.y, width, total_height); +} + +FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...) +{ + FC_Rect box = {x, y, width, 0}; + int total_height; + + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(x, y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + FC_DrawColumnFromBuffer(font, dest, box, &total_height, scale, FC_ALIGN_LEFT); + + return FC_MakeRect(box.x, box.y, width, total_height); +} + +FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...) +{ + FC_Rect box = {x, y, width, 0}; + int total_height; + + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(x, y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, color); + + FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT); + + return FC_MakeRect(box.x, box.y, width, total_height); +} + +FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...) +{ + FC_Rect box = {x, y, width, 0}; + int total_height; + + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(x, y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, effect.color); + + switch(effect.alignment) + { + case FC_ALIGN_CENTER: + box.x -= width/2; + break; + case FC_ALIGN_RIGHT: + box.x -= width; + break; + default: + break; + } + + FC_DrawColumnFromBuffer(font, dest, box, &total_height, effect.scale, effect.alignment); + + return FC_MakeRect(box.x, box.y, width, total_height); +} + +static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) +{ + FC_Rect result = {x, y, 0, 0}; + if(text == NULL || font == NULL) + return result; + + char* str = U8_strdup(text); + char* del = str; + char* c; + + // Go through str, when you find a \n, replace it with \0 and print it + // then move down, back, and continue. + for(c = str; *c != '\0';) + { + if(*c == '\n') + { + *c = '\0'; + result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result); + *c = '\n'; + c++; + str = c; + y += scale.y*font->height; + } + else + c++; + } + + result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result); + + free(del); + return result; +} + +static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) +{ + FC_Rect result = {x, y, 0, 0}; + if(text == NULL || font == NULL) + return result; + + char* str = U8_strdup(text); + char* del = str; + char* c; + + for(c = str; *c != '\0';) + { + if(*c == '\n') + { + *c = '\0'; + result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result); + *c = '\n'; + c++; + str = c; + y += scale.y*font->height; + } + else + c++; + } + + result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result); + + free(del); + return result; +} + + + +FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...) +{ + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(x, y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + return FC_RenderLeft(font, dest, x, y, scale, fc_buffer); +} + +FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...) +{ + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(x, y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, font->default_color); + + FC_Rect result; + switch(align) + { + case FC_ALIGN_LEFT: + result = FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); + break; + case FC_ALIGN_CENTER: + result = FC_RenderCenter(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); + break; + case FC_ALIGN_RIGHT: + result = FC_RenderRight(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); + break; + default: + result = FC_MakeRect(x, y, 0, 0); + break; + } + + return result; +} + +FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...) +{ + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(x, y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, color); + + return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); +} + + +FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...) +{ + if(formatted_text == NULL || font == NULL) + return FC_MakeRect(x, y, 0, 0); + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + set_color_for_all_caches(font, effect.color); + + FC_Rect result; + switch(effect.alignment) + { + case FC_ALIGN_LEFT: + result = FC_RenderLeft(font, dest, x, y, effect.scale, fc_buffer); + break; + case FC_ALIGN_CENTER: + result = FC_RenderCenter(font, dest, x, y, effect.scale, fc_buffer); + break; + case FC_ALIGN_RIGHT: + result = FC_RenderRight(font, dest, x, y, effect.scale, fc_buffer); + break; + default: + result = FC_MakeRect(x, y, 0, 0); + break; + } + + return result; +} + + + + +// Getters + + +FC_FilterEnum FC_GetFilterMode(FC_Font* font) +{ + if(font == NULL) + return FC_FILTER_NEAREST; + + return font->filter; +} + +Uint16 FC_GetLineHeight(FC_Font* font) +{ + if(font == NULL) + return 0; + + return font->height; +} + +Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...) +{ + if(formatted_text == NULL || font == NULL) + return 0; + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + Uint16 numLines = 1; + const char* c; + + for (c = fc_buffer; *c != '\0'; c++) + { + if(*c == '\n') + numLines++; + } + + // Actual height of letter region + line spacing + return font->height*numLines + font->lineSpacing*(numLines - 1); //height*numLines; +} + +Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...) +{ + if(formatted_text == NULL || font == NULL) + return 0; + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + const char* c; + Uint16 width = 0; + Uint16 bigWidth = 0; // Allows for multi-line strings + + for (c = fc_buffer; *c != '\0'; c++) + { + if(*c == '\n') + { + bigWidth = bigWidth >= width? bigWidth : width; + width = 0; + continue; + } + + FC_GlyphData glyph; + Uint32 codepoint = FC_GetCodepointFromUTF8(&c, 1); + if(FC_GetGlyphData(font, &glyph, codepoint) || FC_GetGlyphData(font, &glyph, ' ')) + width += glyph.rect.w; + } + bigWidth = bigWidth >= width? bigWidth : width; + + return bigWidth; +} + +// If width == -1, use no width limit +FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...) +{ + FC_Rect result = {0, 0, 1, FC_GetLineHeight(font)}; + FC_StringList *ls, *iter; + int num_lines = 0; + Uint8 done = 0; + + if(formatted_text == NULL || column_width == 0 || position_index == 0 || font == NULL) + return result; + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1); + for(iter = ls; iter != NULL;) + { + char* line; + int i = 0; + FC_StringList* next_iter = iter->next; + + ++num_lines; + for(line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line)) + { + ++i; + --position_index; + if(position_index == 0) + { + // FIXME: Doesn't handle box-wrapped newlines correctly + line = (char*)U8_next(line); + line[0] = '\0'; + result.x = FC_GetWidth(font, "%s", iter->value); + done = 1; + break; + } + } + if(done) + break; + + // Prevent line wrapping if there are no more lines + if(next_iter == NULL && !done) + result.x = FC_GetWidth(font, "%s", iter->value); + iter = next_iter; + } + FC_StringListFree(ls); + + if(num_lines > 1) + { + result.y = (num_lines - 1) * FC_GetLineHeight(font); + } + + return result; +} + + +Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...) +{ + int y = 0; + + FC_StringList *ls, *iter; + + if(font == NULL) + return 0; + + if(formatted_text == NULL || width == 0) + return font->height; + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + ls = FC_GetBufferFitToColumn(font, width, FC_MakeScale(1,1), 0); + for(iter = ls; iter != NULL; iter = iter->next) + { + y += FC_GetLineHeight(font); + } + FC_StringListFree(ls); + + return y; +} + +static int FC_GetAscentFromCodepoint(FC_Font* font, Uint32 codepoint) +{ + FC_GlyphData glyph; + + if(font == NULL) + return 0; + + // FIXME: Store ascent so we can return it here + FC_GetGlyphData(font, &glyph, codepoint); + return glyph.rect.h; +} + +static int FC_GetDescentFromCodepoint(FC_Font* font, Uint32 codepoint) +{ + FC_GlyphData glyph; + + if(font == NULL) + return 0; + + // FIXME: Store descent so we can return it here + FC_GetGlyphData(font, &glyph, codepoint); + return glyph.rect.h; +} + +int FC_GetAscent(FC_Font* font, const char* formatted_text, ...) +{ + Uint32 codepoint; + int max, ascent; + const char* c; + + if(font == NULL) + return 0; + + if(formatted_text == NULL) + return font->ascent; + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + max = 0; + c = fc_buffer; + + while(*c != '\0') + { + codepoint = FC_GetCodepointFromUTF8(&c, 1); + if(codepoint != 0) + { + ascent = FC_GetAscentFromCodepoint(font, codepoint); + if(ascent > max) + max = ascent; + } + ++c; + } + return max; +} + +int FC_GetDescent(FC_Font* font, const char* formatted_text, ...) +{ + Uint32 codepoint; + int max, descent; + const char* c; + + if(font == NULL) + return 0; + + if(formatted_text == NULL) + return font->descent; + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + max = 0; + c = fc_buffer; + + while(*c != '\0') + { + codepoint = FC_GetCodepointFromUTF8(&c, 1); + if(codepoint != 0) + { + descent = FC_GetDescentFromCodepoint(font, codepoint); + if(descent > max) + max = descent; + } + ++c; + } + return max; +} + +int FC_GetBaseline(FC_Font* font) +{ + if(font == NULL) + return 0; + + return font->baseline; +} + +int FC_GetSpacing(FC_Font* font) +{ + if(font == NULL) + return 0; + + return font->letterSpacing; +} + +int FC_GetLineSpacing(FC_Font* font) +{ + if(font == NULL) + return 0; + + return font->lineSpacing; +} + +Uint16 FC_GetMaxWidth(FC_Font* font) +{ + if(font == NULL) + return 0; + + return font->maxWidth; +} + +SDL_Color FC_GetDefaultColor(FC_Font* font) +{ + if(font == NULL) + { + SDL_Color c = {0,0,0,255}; + return c; + } + + return font->default_color; +} + +FC_Rect FC_GetBounds(FC_Font* font, float x, float y, FC_AlignEnum align, FC_Scale scale, const char* formatted_text, ...) +{ + FC_Rect result = {x, y, 0, 0}; + + if(formatted_text == NULL) + return result; + + // Create a temp buffer while GetWidth and GetHeight use fc_buffer. + char* temp = (char*)malloc(fc_buffer_size); + FC_EXTRACT_VARARGS(temp, formatted_text); + + result.w = FC_GetWidth(font, "%s", temp) * scale.x; + result.h = FC_GetHeight(font, "%s", temp) * scale.y; + + switch(align) + { + case FC_ALIGN_LEFT: + break; + case FC_ALIGN_CENTER: + result.x -= result.w/2; + break; + case FC_ALIGN_RIGHT: + result.x -= result.w; + break; + default: + break; + } + + free(temp); + + return result; +} + +Uint8 FC_InRect(float x, float y, FC_Rect input_rect) +{ + return (input_rect.x <= x && x <= input_rect.x + input_rect.w && input_rect.y <= y && y <= input_rect.y + input_rect.h); +} + +// TODO: Make it work with alignment +Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...) +{ + FC_StringList *ls, *iter; + Uint8 done = 0; + int height = FC_GetLineHeight(font); + Uint16 position = 0; + int current_x = 0; + int current_y = 0; + FC_GlyphData glyph_data; + + if(formatted_text == NULL || column_width == 0 || font == NULL) + return 0; + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1); + for(iter = ls; iter != NULL; iter = iter->next) + { + char* line; + + for(line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line)) + { + if(FC_GetGlyphData(font, &glyph_data, FC_GetCodepointFromUTF8((const char**)&line, 0))) + { + if(FC_InRect(x, y, FC_MakeRect(current_x, current_y, glyph_data.rect.w, glyph_data.rect.h))) + { + done = 1; + break; + } + + current_x += glyph_data.rect.w; + } + position++; + } + if(done) + break; + + current_x = 0; + current_y += height; + if(y < current_y) + break; + } + FC_StringListFree(ls); + + return position; +} + +int FC_GetWrappedText(FC_Font* font, char* result, int max_result_size, Uint16 width, const char* formatted_text, ...) +{ + FC_StringList *ls, *iter; + + if(font == NULL) + return 0; + + if(formatted_text == NULL || width == 0) + return 0; + + FC_EXTRACT_VARARGS(fc_buffer, formatted_text); + + ls = FC_GetBufferFitToColumn(font, width, FC_MakeScale(1,1), 0); + int size_so_far = 0; + int size_remaining = max_result_size-1; // reserve for \0 + for(iter = ls; iter != NULL && size_remaining > 0; iter = iter->next) + { + // Copy as much of this line as we can + int len = strlen(iter->value); + int num_bytes = FC_MIN(len, size_remaining); + memcpy(&result[size_so_far], iter->value, num_bytes); + size_so_far += num_bytes; + + // If there's another line, add newline character + if(size_remaining > 0 && iter->next != NULL) + { + --size_remaining; + result[size_so_far] = '\n'; + ++size_so_far; + } + } + FC_StringListFree(ls); + + result[size_so_far] = '\0'; + + return size_so_far; +} + + + +// Setters + + +void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter) +{ + if(font == NULL) + return; + + if(font->filter != filter) + { + font->filter = filter; + + #ifdef FC_USE_SDL_GPU + // Update each texture to use this filter mode + { + int i; + GPU_FilterEnum gpu_filter = GPU_FILTER_NEAREST; + if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) + gpu_filter = GPU_FILTER_LINEAR; + + for(i = 0; i < font->glyph_cache_count; ++i) + { + GPU_SetImageFilter(font->glyph_cache[i], gpu_filter); + } + } + #endif + } +} + + +void FC_SetSpacing(FC_Font* font, int LetterSpacing) +{ + if(font == NULL) + return; + + font->letterSpacing = LetterSpacing; +} + +void FC_SetLineSpacing(FC_Font* font, int LineSpacing) +{ + if(font == NULL) + return; + + font->lineSpacing = LineSpacing; +} + +void FC_SetDefaultColor(FC_Font* font, SDL_Color color) +{ + if(font == NULL) + return; + + font->default_color = color; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fontcche.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,328 @@ +/* +SDL_FontCache v0.10.0: A font cache for SDL and SDL_ttf +by Jonathan Dearborn +(minor changes have been made by Migdyn for use in Devious Licks) +Dedicated to the memory of Florian Hufsky + +License: + The short: + Use it however you'd like, but keep the copyright and license notice + whenever these files or parts of them are distributed in uncompiled form. + + The long: +Copyright (c) 2019 Jonathan Dearborn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef _SDL_FONTCACHE_H__ +#define _SDL_FONTCACHE_H__ + +#include "SDL2/SDL.h" +#include "SDL2/SDL_ttf.h" + +#ifdef FC_USE_SDL_GPU + #include "SDL_gpu.h" +#endif + + +#include <stdarg.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +// Let's pretend this exists... +#define TTF_STYLE_OUTLINE 16 + + + +// Differences between SDL_Renderer and SDL_gpu +#ifdef FC_USE_SDL_GPU +#define FC_Rect GPU_Rect +#define FC_Target GPU_Target +#define FC_Image GPU_Image +#define FC_Log GPU_LogError +#else +#define FC_Rect SDL_Rect +#define FC_Target SDL_Renderer +#define FC_Image SDL_Texture +#define FC_Log SDL_Log +#endif + + +// SDL_FontCache types + +typedef enum +{ + FC_ALIGN_LEFT, + FC_ALIGN_CENTER, + FC_ALIGN_RIGHT +} FC_AlignEnum; + +typedef enum +{ + FC_FILTER_NEAREST, + FC_FILTER_LINEAR +} FC_FilterEnum; + +typedef struct FC_Scale +{ + float x; + float y; + +} FC_Scale; + +typedef struct FC_Effect +{ + FC_AlignEnum alignment; + FC_Scale scale; + SDL_Color color; + +} FC_Effect; + +// Opaque type +typedef struct FC_Font FC_Font; + + +typedef struct FC_GlyphData +{ + SDL_Rect rect; + int cache_level; + +} FC_GlyphData; + + + + +// Object creation + +FC_Rect FC_MakeRect(float x, float y, float w, float h); + +FC_Scale FC_MakeScale(float x, float y); + +SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color); + +FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h); + + + +// Font object + +FC_Font* FC_CreateFont(void); + +#ifdef FC_USE_SDL_GPU +Uint8 FC_LoadFont(FC_Font* font, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style); + +Uint8 FC_LoadFontFromTTF(FC_Font* font, TTF_Font* ttf, SDL_Color color); + +Uint8 FC_LoadFont_RW(FC_Font* font, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style); +#else +Uint8 FC_LoadFont(FC_Font* font, SDL_Renderer* renderer, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style); + +Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, SDL_Color color); + +Uint8 FC_LoadFont_RW(FC_Font* font, SDL_Renderer* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style); +#endif + +#ifndef FC_USE_SDL_GPU +// note: handle SDL event types SDL_RENDER_TARGETS_RESET(>= SDL 2.0.2) and SDL_RENDER_DEVICE_RESET(>= SDL 2.0.4) +void FC_ResetFontFromRendererReset(FC_Font* font, SDL_Renderer* renderer, Uint32 evType); +#endif + +void FC_ClearFont(FC_Font* font); + +void FC_FreeFont(FC_Font* font); + + + +// Built-in loading strings + +char* FC_GetStringASCII(void); + +char* FC_GetStringLatin1(void); + +char* FC_GetStringASCII_Latin1(void); + + +// UTF-8 to SDL_FontCache codepoint conversion + +/*! +Returns the Uint32 codepoint (not UTF-32) parsed from the given UTF-8 string. +\param c A pointer to a string of proper UTF-8 character values. +\param advance_pointer If true, the source pointer will be incremented to skip the extra bytes from multibyte codepoints. +*/ +Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer); + +/*! +Parses the given codepoint and stores the UTF-8 bytes in 'result'. The result is NULL terminated. +\param result A memory buffer for the UTF-8 values. Must be at least 5 bytes long. +\param codepoint The Uint32 codepoint to parse (not UTF-32). +*/ +void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint); + + +// UTF-8 string operations + +/*! Allocates a new string of 'size' bytes that is already NULL-terminated. The NULL byte counts toward the size limit, as usual. Returns NULL if size is 0. */ +char* U8_alloc(unsigned int size); + +/*! Deallocates the given string. */ +void U8_free(char* string); + +/*! Allocates a copy of the given string. */ +char* U8_strdup(const char* string); + +/*! Returns the number of UTF-8 characters in the given string. */ +int U8_strlen(const char* string); + +/*! Returns the number of bytes in the UTF-8 multibyte character pointed at by 'character'. */ +int U8_charsize(const char* character); + +/*! Copies the source multibyte character into the given buffer without overrunning it. Returns 0 on failure. */ +int U8_charcpy(char* buffer, const char* source, int buffer_size); + +/*! Returns a pointer to the next UTF-8 character. */ +const char* U8_next(const char* string); + +/*! Inserts a UTF-8 string into 'string' at the given position. Use a position of -1 to append. Returns 0 when unable to insert the string. */ +int U8_strinsert(char* string, int position, const char* source, int max_bytes); + +/*! Erases the UTF-8 character at the given position, moving the subsequent characters down. */ +void U8_strdel(char* string, int position); + + +// Internal settings + +/*! Sets the string from which to load the initial glyphs. Use this if you need upfront loading for any reason (such as lack of render-target support). */ +void FC_SetLoadingString(FC_Font* font, const char* string); + +/*! Returns the size of the internal buffer which is used for unpacking variadic text data. This buffer is shared by all FC_Fonts. */ +unsigned int FC_GetBufferSize(void); + +/*! Changes the size of the internal buffer which is used for unpacking variadic text data. This buffer is shared by all FC_Fonts. */ +void FC_SetBufferSize(unsigned int size); + +/*! Returns the width of a single horizontal tab in multiples of the width of a space (default: 4) */ +unsigned int FC_GetTabWidth(void); + +/*! Changes the width of a horizontal tab in multiples of the width of a space (default: 4) */ +void FC_SetTabWidth(unsigned int width_in_spaces); + +void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale)); + +FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale); + + +// Custom caching + +/*! Returns the number of cache levels that are active. */ +int FC_GetNumCacheLevels(FC_Font* font); + +/*! Returns the cache source texture at the given cache level. */ +FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level); + +// TODO: Specify ownership of the texture (should be shareable) +/*! Sets a cache source texture for rendering. New cache levels must be sequential. */ +Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture); + +/*! Copies the given surface to the given cache level as a texture. New cache levels must be sequential. */ +Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface); + + +/*! Returns the number of codepoints that are stored in the font's glyph data map. */ +unsigned int FC_GetNumCodepoints(FC_Font* font); + +/*! Copies the stored codepoints into the given array. */ +void FC_GetCodepoints(FC_Font* font, Uint32* result); + +/*! Stores the glyph data for the given codepoint in 'result'. Returns 0 if the codepoint was not found in the cache. */ +Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint); + +/*! Sets the glyph data for the given codepoint. Duplicates are not checked. Returns a pointer to the stored data. */ +FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data); + + +// Rendering + +FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...); +FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...); +FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...); +FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...); +FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...); + +FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...); +FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...); +FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...); +FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...); +FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...); + +FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...); +FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...); +FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...); +FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...); +FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...); + + +// Getters + +FC_FilterEnum FC_GetFilterMode(FC_Font* font); +Uint16 FC_GetLineHeight(FC_Font* font); +Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...); +Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...); + +// Returns a 1-pixel wide box in front of the character in the given position (index) +FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...); +Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...); + +int FC_GetAscent(FC_Font* font, const char* formatted_text, ...); +int FC_GetDescent(FC_Font* font, const char* formatted_text, ...); +int FC_GetBaseline(FC_Font* font); +int FC_GetSpacing(FC_Font* font); +int FC_GetLineSpacing(FC_Font* font); +Uint16 FC_GetMaxWidth(FC_Font* font); +SDL_Color FC_GetDefaultColor(FC_Font* font); + +FC_Rect FC_GetBounds(FC_Font* font, float x, float y, FC_AlignEnum align, FC_Scale scale, const char* formatted_text, ...); + +Uint8 FC_InRect(float x, float y, FC_Rect input_rect); +// Given an offset (x,y) from the text draw position (the upper-left corner), returns the character position (UTF-8 index) +Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...); + +// Returns the number of characters in the new wrapped text written into `result`. +int FC_GetWrappedText(FC_Font* font, char* result, int max_result_size, Uint16 width, const char* formatted_text, ...); + +// Setters + +void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter); +void FC_SetSpacing(FC_Font* font, int LetterSpacing); +void FC_SetLineSpacing(FC_Font* font, int LineSpacing); +void FC_SetDefaultColor(FC_Font* font, SDL_Color color); + + +#ifdef __cplusplus +} +#endif + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/icon.rc Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,1 @@ +DEVLICKS ICON "icon.ico"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/intro.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,63 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "intro.h" +#include "effects.h" + +static int placesFromLeft = 0; +bool introInProgress = 0; +char introText[] = "It's the first week of September, 2021.\nYou started your freshman year at NH High School.\nA new trend has emerged on your favorite video sharing site\nin which people steal school property, including soap dispensers and masks.\n \nYou decide to take part in the trend yourself.\n Good luck. \n "; +char introTextDisplayed[sizeof introText]; + + +bool INTR_DrawIntro() +{ + if(introInProgress) + { + SDL_RenderClear(renderer); + + if(placesFromLeft != -1) + { + strncpy(introTextDisplayed, introText, placesFromLeft); + placesFromLeft = ISOL_TypingEffect(sizeof introText); + } + + FC_DrawAlign(font, renderer, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 3, FC_ALIGN_CENTER, "%s", introTextDisplayed); + + if(placesFromLeft == -1 && ISOL_FadeOut(10)) + { + introInProgress = 0; + return 1; + } + + SDL_RenderPresent(renderer); + } + + else + { + introInProgress = 1; + + } + + return 0; +} + + + +void INTR_EndIntro() +{ + +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/intro.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,3 @@ +#include "common.h" + +bool INTR_DrawIntro();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/kbdinput.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,203 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "kbdinput.h" + +bool keysHeldDown[128]; + +Uint8 performYesNo() +{ + if(keysHeldDown[28]) + { + return 1; + } + + else if(keysHeldDown[17]) + { + return 2; + } + + return 0; +} + +bool performMenuKeyActions() +{ + /*if(keysHeldDown[44]) + { + gameRunning = 1; + }*/ + + if(keysHeldDown[81]) + { + selectedMenuItem += selectedMenuItem < 3 ? 1 : -3; + } + + else if(keysHeldDown[82]) + { + selectedMenuItem -= selectedMenuItem > 0 ? 1 : -3; + } + + else if(keysHeldDown[40]) + { + if(selectedMenuItem == 0) + { + gameRunning = 1; + } + + else if(selectedMenuItem == 3) + { + return 0; + } + } + + return 1; +} + +bool performPlayerMovement() +{ + SDL_Point trans = {0, 0}; + /*for(int i = 0; i < 128; i++) + { + if(keysHeldDown[i] == 1) + { + printf("\n% is pressed", i); + } + }*/ + + if(!player.canMove) + { + return false; + } + + if(keysHeldDown[79]) + { + if(getObjectFromIdent(levelTiles[(player.x + 1) * LEVEL_WIDTH + player.y]).passable) + { + trans = getTransformation(1, 0); + offset.x -= TILE_WIDTH / 2; + offset.y -= TILE_HEIGHT / 4; + } + } + + else if(keysHeldDown[80]) + { + if(getObjectFromIdent(levelTiles[(player.x - 1) * LEVEL_WIDTH + player.y]).passable) + { + trans = getTransformation(-1, 0); + offset.y += TILE_HEIGHT / 4; + offset.x += TILE_WIDTH / 2; + } + } + + if(keysHeldDown[81]) + { + if(getObjectFromIdent(levelTiles[player.x * LEVEL_WIDTH + player.y + 1]).passable) + { + trans = (SDL_Point){trans.x + getTransformation(0, 1).x, trans.y + getTransformation(0, 1).y}; + offset.y -= TILE_HEIGHT / 4; + offset.x += TILE_WIDTH / 2; + } + } + + else if(keysHeldDown[82]) + { + if(getObjectFromIdent(levelTiles[player.x * LEVEL_WIDTH + player.y - 1]).passable) + { + trans = (SDL_Point){trans.x + getTransformation(0, -1).x, trans.y + getTransformation(0, -1).y}; + offset.y += TILE_HEIGHT / 4; + offset.x -= TILE_WIDTH / 2; + } + } + + // Cam movm't + if(keysHeldDown[7]) + { + offset.x -= TILE_WIDTH / 2; + offset.y -= TILE_HEIGHT / 4; + } + + else if(keysHeldDown[4]) + { + offset.y += TILE_HEIGHT / 4; + offset.x += TILE_WIDTH / 2; + } + + if(keysHeldDown[26]) + { + offset.y -= TILE_HEIGHT / 4; + offset.x += TILE_WIDTH / 2; + } + + else if(keysHeldDown[22]) + { + offset.y += TILE_HEIGHT / 4; + offset.x -= TILE_WIDTH / 2; + } + + // G - steal + if(keysHeldDown[10]) + { + if(getObjectFromIdent(levelTiles[player.x * LEVEL_WIDTH + player.y]).stealable) + { + stealItem(levelTiles[player.x * LEVEL_WIDTH + player.y]); + } + + return true; + } + + + player.x += trans.x; + player.y += trans.y; + + if(keysHeldDown[79] || keysHeldDown[80] || keysHeldDown[81] || keysHeldDown[82]) + { + return true; + } + return false; +} + + +void handleKeyboard(SDL_Event event) +{ + if(event.type == SDL_KEYDOWN) + { + keysHeldDown[event.key.keysym.scancode] = 1; + + + //printf("%i", event.key.keysym.scancode); + if(event.key.keysym.scancode == 9 && !event.key.repeat) + { + orientation += 1; + if(orientation == 4) + orientation = 0; + } + + if(event.key.keysym.scancode == 46 && !event.key.repeat) + { + zoomFactor += zoomFactor == -2 ? 3 : 1; + updateZoomFactor(); + } + + else if(event.key.keysym.scancode == 45 && !event.key.repeat) + { + zoomFactor -= zoomFactor == 1 ? 3 : 1; + updateZoomFactor(); + } + } + + else if(event.type == SDL_KEYUP) + { + keysHeldDown[event.key.keysym.scancode] = 0; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/kbdinput.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,5 @@ +#include "common.h" + +extern bool keysHeldDown[128]; + +void handleKeyboard(SDL_Event event);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/level.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,52 @@ +#include <stdio.h> +#include "common.h" +#include <SDL2/SDL.h> + +typedef struct +{ + +} +Image; + +void loadLevelFromFile() +{ + Image img; + FILE* imageFile; + + imageFile = fopen("img/nhhs.ppm", "rb"); + + int whiteSpaces = 0; + int lineCount = 0; + int pixelCount = 0; + if(imageFile) + { + unsigned char byte; + SDL_Color color = {0, 0, 0, 255}; + + while(fread(&byte, 1, 1, imageFile)) + { + if(whiteSpaces == 4) + { + if(lineCount == 0) color.r = byte; + if(lineCount == 1) color.g = byte; + if(lineCount == 2) color.b = byte; + lineCount ++; + if(lineCount >= 3) + { + lineCount = 0; + levelTiles[pixelCount] = (color.g == 255) ? (color.r == 0 ? 'G' : ' ') : (color.r == 255) ? ' ' : (color.r == 85) ? 'A' : (color.b == 191) ? 'S' : '#'; + pixelCount ++; + /*printf("Color: %02x, %02x, %02x\n", color.r, color.g, color.b);*/ + } + } + + if(byte == 10) whiteSpaces ++; + } + + + putchar('\n'); + + } + + fclose(imageFile); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/level.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,2 @@ + +void loadLevelFromFile();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/localztn.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,95 @@ +// localztn.h +// Devious Licks Localization + +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + + +Uint8 language = 0; +// 0 - US English +// 1 - Russian +// 2 - South American Spanish +// 3 - German +// 4 - Italian +// 5 - Polish +// 6 - Brazilian + + + +char* getTranslationForObject(char object) +{ + if(language == 0) + { + switch(object) + { + case ' ': + return "Floor"; + + case 'D': + return "Exit Door"; + + case 'd': + return "Classroom Door"; + + case 'W': + return "White Wall"; + + case '#': + return "Tiles"; + + case '-': + return "Stall Divider"; + + case '|': + return "Stall Divider"; + + case '~': + return "Fire Alarm Strobe Light"; + + case '&': + return "Hand Dryer"; + + case '^': + return "Desk/Chair Combo"; + + case 'l': + return "Table"; + + case 'L': + return "Table"; + + case 'T': + return "Fire Alarm Pull Station (T-Bar)"; + + case 'S': + return "Soap Dispenser"; + + case 'F': + return "Drinking Fountain"; + + case '`': + return "Toilet"; + + case '=': + return "Shelf"; + + case '@': + return "Fire Extinguisher"; + + case 'G': + return "Grass"; + } + return "Undefined Object"; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/missions.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,138 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "missions.h" +#include "effects.h" +#include "kbdinput.h" + +Mission* missions; +Uint8 currentMission = 0; +static int missionInProgress = 0; +static char* missionTextDisplayed[900]; +static int placesFromLeft = 0; +static char* text[200]; + +bool checkForMissionComplete(Uint8 mission) +{ + bool flag = 0; + int occurences = 0; + for(int i = 0; i < strlen(missions[mission].requirements); i++) + { + for(int j = 0; j < sizeof player.objectsStolen; j++) + { + if(player.objectsStolen[j] == missions[mission].requirements[i]) + { + flag = 1; + } + } + } + + return flag; +} + +void initMissions() +{ + missions = malloc(sizeof(Mission) * 6); + missions[0].desc = "Steal a soap dispenser"; + missions[0].requirements = "S"; + missions[0].requiredTools = 0; + + missions[1].desc = "Steal a soap dispenser and a hand dryer."; + missions[1].requirements = "S&"; + missions[1].requiredTools = 0; + + missions[2].desc = "Steal two hand dryers"; + missions[2].requirements = "&&"; + missions[2].requiredTools = 1; + + missions[3].desc = "Steal a fire alarm strobe light"; + missions[3].requirements = "~"; + missions[3].requiredTools = 1; + + missions[4].desc = "Steal a fire alarm pull station* and two strobe lights\n\n*Removing the pull station will likely set off the fire alarm system!"; + missions[4].requirements = "T~~"; + missions[4].requiredTools = 1; + + missions[5].desc = "Steal a drinking fountain"; + missions[5].requirements = "F"; + missions[5].requiredTools = 2; +} + +bool drawMissionScreen() +{ + if(missionInProgress) + { + if(placesFromLeft != -1) + { + strncpy(missionTextDisplayed, text, placesFromLeft); + placesFromLeft = ISOL_TypingEffect(strlen(text)); + } + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 180); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_RenderFillRect(renderer, &(SDL_Rect){0, SCREEN_HEIGHT / 4, SCREEN_WIDTH, SCREEN_HEIGHT - SCREEN_HEIGHT / 2}); + FC_SetDefaultColor(font, (SDL_Color){255, 255, 255, 255}); + FC_DrawAlign(font, renderer, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 3, FC_ALIGN_CENTER, "%s", missionTextDisplayed); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); + + if(placesFromLeft == -1 && keysHeldDown[40]) + { + placesFromLeft = 0; + missionInProgress = 0; + memset(missionTextDisplayed, 0, strlen(missionTextDisplayed)); // Clear the string to prevent future issues + player.canMove = true; + return 1; + } + } + + else + { + player.canMove = false; + sprintf(text, "MISSION #%i\n%s\nTools required: %s\nPress ENTER to proceed. ", currentMission + 1, missions[currentMission].desc, missions[currentMission].requiredTools == 0 ? "None" : missions[currentMission].requiredTools == 1 ? "Screwdriver" : "Screwdriver and wrench"); + missionInProgress = 1; + } + + return 0; +} + +bool drawMissionComplete() +{ + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 180); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_RenderFillRect(renderer, &(SDL_Rect){0, SCREEN_HEIGHT / 4, SCREEN_WIDTH, SCREEN_HEIGHT - SCREEN_HEIGHT / 2}); + FC_SetDefaultColor(font, (SDL_Color){0, 255, 40, 255}); + FC_DrawAlign(font, renderer, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, FC_ALIGN_CENTER, "Mission #%i complete!\nPress ENTER to acknowledge.", currentMission + 1); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); + + if(keysHeldDown[40]) + { + memset(player.objectsStolen, 0, strlen(player.objectsStolen)); + player.leftCampus = false; + currentMission += 1; + return 1; + } + + return 0; +} + +/* +missions[0] = {"S", "Steal a soap dispenser", 0}; +missions[1] = {"SS", "Steal a soap dispenser and a ", 0}; +missions[2] = {"&&", "Steal two hand dryers", 1}; +missions[3] = {"~", "Steal a fire alarm strobe light", 1}; +missions[4] = {"T~~", "Steal a fire alarm pull station* and two strobe lights\n\n*Removing the pull station will likely set off the fire alarm system!", 1}; +missions[5] = {"F", "Steal a drinking fountain", 2};*/ + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/missions.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,33 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MISSIONS +#define MISSIONS + +#include "common.h" + +typedef struct Mission +{ + char* requirements; + char* desc; + int requiredTools; +} +Mission; + +extern Uint8 currentMission; +extern Mission* missions; + +void initMissions() + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/store.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,1 @@ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texture.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,7 @@ +#include "texture.h" + +SDL_Point getTextureFromIndex(int index) +{ + SDL_Point texture = {index - 16 * floor(index / TILEMAP_DIMS), floor(index / TILEMAP_DIMS)}; + return texture; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texture.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,3 @@ +#include "common.h" + +SDL_Point getTextureFromIndex(int index);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/theft.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,45 @@ +#include "theft.h" +#include "firealrm.h" +#include "common.h" + +static bool theftInProgress = false; +/* +Uint32 progressSteal(Uint32 interval) +{ + if(!keysHeldDown[10]) + { + SDL_RemoveTimer(fadeTimer); + theftInProgress = false; + return 0; + } + else + { + + } + + return interval; +}*/ + +void stealItem(int levelIndex) +{ + if(stealProgress < 128) + stealProgress += 1; + else + { + for(int i = 0; i < sizeof player.objectsStolen; i++) + { + if(!player.objectsStolen[i]) + { + if(levelTiles[player.x * LEVEL_WIDTH + player.y] == 'T') + { + fireAlarmTimer = SDL_AddTimer(1000, ALRM_soundFireAlarm, 0); + } + player.objectsStolen[i] = levelTiles[player.x * LEVEL_WIDTH + player.y]; + break; + } + } + + levelTiles[player.x * LEVEL_WIDTH + player.y] = ' '; + stealProgress = 0; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/theft.h Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,1 @@ +void stealItem(int levelIndex);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/title.c Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,57 @@ +/* +Copyright (c) 2023 MCL Software + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "common.h" + +SDL_Texture* titleBackground = 0; +SDL_Texture* buttons = 0; + +void initTitleScreenGraphics() +{ + SDL_Surface* titleBackgroundSurface = IMG_Load("./img/titlebg.png"); + SDL_Surface* buttonsSurface = IMG_Load("./img/buttons.png"); + + titleBackground = SDL_CreateTextureFromSurface(renderer, titleBackgroundSurface); + buttons = SDL_CreateTextureFromSurface(renderer, buttonsSurface); + + SDL_FreeSurface(buttonsSurface); + SDL_FreeSurface(titleBackgroundSurface); +} + +void freeTitleScreenGraphics() +{ + SDL_DestroyTexture(titleBackground); + SDL_DestroyTexture(buttons); +} + +void drawTitleScreen(int selectedItem) +{ + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, titleBackground, 0, 0); + drawTitleMenuText(); + SDL_RenderCopy(renderer, logo, 0, &(SDL_Rect){SCREEN_WIDTH / 2 - 300, 100, 300 * 2, 36 * 2}); + + for(int i = 0; i < 4; i++) + { + SDL_RenderCopy(renderer, buttons, &(SDL_Rect){0, 65 * i, 377, 65}, &(SDL_Rect){SCREEN_WIDTH / 2 - (selectedMenuItem != i ? 377 : 400) / 2, 200 + 70 * i, selectedMenuItem != i ? 377 : 400, 65}); + } + + SDL_RenderPresent(renderer); +} + +void drawCreditsScreen() +{ + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/title.txt Thu Jul 17 22:03:19 2025 -0400 @@ -0,0 +1,27 @@ +ohio game💀 +L + ratio + cope + who asked + kys + stfu + rekt + touch grass +which women do u like? from behind! +gigachad from ohio 💪💯💯 +brain laundry🧺🧺👚🧠🧠 +-999999 social credit😨😨😨 +rip bozo +mf be like +chubby bun challeng when +drink detergant challeng +sigma males🚫😳🚫👎👎 +cope and seethe +this game is sus +🏳️🌈🐱💔: 11 +camel piss bottle flip challenge 2021 (real) +i played this game when i was 7 now im 9 +1 chad vs 100 virgin (who would win??) +you're body full of soy🤢🤢 +authright alpha chad +wh don't we 8 letters 🗿 +that's disgustang pervert +momo from twice🐔🧒🏻🔪🩸 +[CANDIDATE FOR REMOVAL] caling momo from twice at 3am??!?!!? +county humans dating sim????? +me at 15 when no first kiss 😥😭🔪 +cuntry balls: soviet counter attack ☭☭💪🏿🔥卐😳😳 +cant even play devious licks in ohio \ No newline at end of file