├── LICENSE ├── README.md ├── assets ├── bgm │ ├── D_DM2INT.it │ ├── SHADOW(LOOP).XM │ ├── aftertouch(loop).mod │ ├── aryx(T67).s3m │ ├── credits.txt │ ├── crema_lubricante_v3.mod │ ├── fod_ohclatenightearlymorningjam.it │ └── thinking_of_tit.xm ├── font │ ├── exampleblockgame.ttf │ ├── license.txt │ └── readme.txt ├── logo.svg ├── sfx │ ├── classic_bonus.ogg │ ├── classic_clear.ogg │ ├── classic_levelup.ogg │ ├── classic_lock.ogg │ ├── classic_move.ogg │ ├── classic_rotate.ogg │ └── perfectclear3.ogg ├── splashscreen │ ├── splashbg.png │ ├── splashlogo.png │ └── splashtext.png └── texture │ ├── ldem_4_uint.png │ └── lroc_color_poles_2k.png ├── conf.lua ├── config.lua ├── credits.lua ├── engine.lua ├── highscores.lua ├── linux └── MakeAppImage.sh ├── main.lua ├── menu.lua ├── pause.lua ├── piece.lua ├── shaders ├── bg1.glsl ├── bg10.glsl ├── bgbeginner.glsl ├── bgclassic.glsl ├── bgdeath.glsl ├── bgmenu.glsl ├── bgpractice.glsl ├── bgpractice2.glsl ├── bgstandard.glsl ├── blur.glsl ├── chroma-misalign.glsl ├── creditball.glsl ├── displacementnormals.glsl ├── infinity.glsl ├── nop.glsl ├── rainbow.glsl ├── wave.glsl └── wave2.glsl ├── speedcurve.lua ├── splash.lua ├── title.lua └── util.lua /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub](https://img.shields.io/github/license/Oshisaure/example-block-game)](https://github.com/Oshisaure/example-block-game/blob/master/LICENSE) 2 | [![Love2D version](https://img.shields.io/badge/Love2D-v11.3-blue)](https://github.com/love2d/love/releases/tag/11.3) 3 | [![GitHub all releases](https://img.shields.io/github/downloads/Oshisaure/example-block-game/total)](https://github.com/Oshisaure/example-block-game/releases) 4 | 5 | # example block game 6 | A simple open source block game to show a working implementation of my block game engine 7 | 8 | Powered by [Lua](https://www.lua.org/about.html) and [Love2D](https://love2d.org/) 9 | 10 | ### ok cool how do I play 11 | If you want something that works, go to [the releases page](https://github.com/Oshisaure/example-block-game/releases) and get the build for your platform. 12 | If you are on linux you get the appropriate [Love2D build](https://github.com/love2d/love/releases/tag/11.3) for your distro, and run the .love file from the latest example block game release with it. 13 | 14 | However if you would rather be up to date with all the lastest tiniest changes that may or may not function you can clone the repository and follow [these instructions](https://love2d.org/wiki/Getting_Started) to set it up. 15 | Be careful though because I may be dumb and render the whole thing unusable sometimes due to being bad at programming. 16 | 17 | ### do you mind if I mod this? 18 | No, this is open source for a reason. Go ahead and fork this to your heart's contents, as long as you keep in mind the license. 19 | Although do keep in mind that I am still working on this so things like score system, sound effects and more settings will be in this at some point. 20 | 21 | ### credits and stuff 22 | BGM credits listed in /assets/bgm/credits.txt along with links to the original downloads 23 | Font made by [MarkGamed7794](https://github.com/MarkGamed7794) using Fontstruct, more info in /assets/font/readme.txt and /assets/font/license.txt 24 | Moon rendered using [texture and displacement map from NASA's Scientific Visualization Studio]( 25 | https://svs.gsfc.nasa.gov/4720) 26 | Shoutouts to the cool block game nerds who make up the communities I've been hanging around in for the past few years 27 | And of course many thanks to you for checking out my example block game <3 28 | -------------------------------------------------------------------------------- /assets/bgm/D_DM2INT.it: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/bgm/D_DM2INT.it -------------------------------------------------------------------------------- /assets/bgm/SHADOW(LOOP).XM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/bgm/SHADOW(LOOP).XM -------------------------------------------------------------------------------- /assets/bgm/aftertouch(loop).mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/bgm/aftertouch(loop).mod -------------------------------------------------------------------------------- /assets/bgm/aryx(T67).s3m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/bgm/aryx(T67).s3m -------------------------------------------------------------------------------- /assets/bgm/credits.txt: -------------------------------------------------------------------------------- 1 | -------------------------- BGM CREDITS -------------------------- 2 | -- shoutouts to the people down there for making these bangers -- 3 | ----------------------------------------------------------------- 4 | 5 | - MENU: D_DM2INT.it 6 | edit of beside yourself by emma "msx" essex 7 | bandcamp link - https://halleylabs.com/track/beside-yourself 8 | 9 | - PRACTICE/BEGINNER: aftertouch(loop).mod 10 | loop edit of aftertouch by substance 11 | modarchive link - https://modarchive.org/module.php?126990 12 | 13 | - LEVEL 01-10: fod_ohclatenightearlymorningjam.it 14 | late night early morning jam by FearofDark 15 | modarchive link - https://modarchive.org/module.php?173929 16 | 17 | - LEVEL 11-20: crema_lubricante_v3.mod 18 | Crema Lubricante by Tempest 19 | modarchive link - https://modarchive.org/module.php?188911 20 | 21 | - LEVEL M01-M10: thinking_of_tit.xm 22 | Thinking of TIT by Jazzcat 23 | modarchive link - https://modarchive.org/module.php?181530 24 | 25 | - LEVEL M11-M20: SHADOW(LOOP).XM 26 | loop edit of Into the Shadow by Elwood 27 | modarchive link - https://modarchive.org/module.php?54298 28 | 29 | - LEVEL M21-M30: aryx(T67).s3m 30 | faster edit of aryx by Karsten Koch 31 | modarchive link - https://modarchive.org/module.php?34036 -------------------------------------------------------------------------------- /assets/bgm/crema_lubricante_v3.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/bgm/crema_lubricante_v3.mod -------------------------------------------------------------------------------- /assets/bgm/fod_ohclatenightearlymorningjam.it: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/bgm/fod_ohclatenightearlymorningjam.it -------------------------------------------------------------------------------- /assets/bgm/thinking_of_tit.xm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/bgm/thinking_of_tit.xm -------------------------------------------------------------------------------- /assets/font/exampleblockgame.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/font/exampleblockgame.ttf -------------------------------------------------------------------------------- /assets/font/license.txt: -------------------------------------------------------------------------------- 1 | FontStruct Non-Commercial Software End User License Agreement 1.0 2 | 3 | This software end user license agreement (hereinafter “Agreement”) is made 4 | between you or, if you represent a legal entity, that legal entity (hereinafter 5 | “You”) and “MarkGamed7794”, the copyright holder and designer 6 | (hereinafter “Font Designer”) of the font software named 7 | “ExampleBlockGame” (hereinafter “Font Software”). 8 | 9 | 1. BINDING AGREEMENT 10 | This Agreement is a binding agreement between You and the Font Designer and 11 | shall set forth the terms and conditions under which You can use the Font 12 | Software. For the purposes of this Agreement, Font Software shall mean the Font 13 | Software, and any parts or copies of it. 14 | 15 | 2. GRANT OF NON-EXCLUSIVE AND LICENCE 16 | The Font Designer hereby grants You a non-exclusive, non-transferable licence to 17 | use the Font Software provided that You comply with all terms and conditions of 18 | this Agreement. 19 | 20 | 2.1 PERSONAL USE 21 | You may use the Fonts Software only for Your own Personal Purposes and for Your 22 | Work. For the purposes of this Agreement, “Personal Purposes” shall mean any 23 | private use of the Font Software by you. “Work” shall mean any work or 24 | result created by You or on Your behalf with the Font Software. For the 25 | avoidance of doubt, the use for Personal Purposes does not allow any use of the 26 | Font Software and/or any Work for commercial purposes (see Section 2.2) and/or 27 | as a web-font (see Section 2.6). 28 | 29 | 2.2 NO COMMERCIAL USE 30 | Neither the Font Software, nor any of its individual components or any Work, may 31 | be used by You for any form of profit-making or otherwise commercial projects, 32 | without express prior written permission from the Font Designer. 33 | 34 | 2.3 NO SALE OR DISTRIBUTION 35 | You may not sell, rent, license, sublicense, distribute, redistribute, give-away 36 | or make available (in any other way) the Font Software alone or as part of any 37 | collection, product or service to any third party. 38 | 39 | 2.4 NO MODIFICATION 40 | You may not modify, adapt, rename, translate, reverse engineer, decompile, 41 | disassemble, alter, or attempt to discover the source code of the Font Software. 42 | 43 | 2.5 EMBEDDING 44 | You may embed the Font Software in documents, applications or devices either as 45 | a rasterized representation of the Font Software (e.g., a GIF or JPEG) or as a 46 | subset of the Font Software as long as the document, application or device is 47 | distributed in a secure format that permits only the viewing and printing but 48 | not the editing of the text. 49 | 50 | 2.6 NO USE AS A WEBFONT 51 | You may not make the Font Software accessible on a web server in order to enable 52 | a web browser to render the content of Websites using the respective Font 53 | Software. 54 | 55 | 2.7 BACKUP 56 | You may make backup copies of the Font Software for archival purposes only, 57 | provided that You retain exclusive custody and control over such copies. Any 58 | backup copy of the Font Software must contain the same copyright, trademark, and 59 | other proprietary information as the original. 60 | 61 | 2.8 PRINT SHOPS AND SERVICE BUREAUS 62 | You may provide a copy of the Font Software used in Your Work to a print shop, 63 | service bureau, etc. for printing or otherwise outputting Your Work. Afterwards, 64 | the print shop, service bureau, etc. must destroy and/or delete all copies of 65 | the Font Software. 66 | 67 | 3. ATTRIBUTION 68 | If You make Your Work publicly accessible in any form, You must keep intact all 69 | copyright notices for the Font Software and display, reasonable to the medium or 70 | means You are utilizing for Your Work, an attribution notice (hereinafter “The 71 | Attribution Notice”) comprising of: 72 | 73 | the name of the Font Designer or pseudonym, if applicable (“MarkGamed7794”); 74 | the title of the Font Software (“ExampleBlockGame”); and 75 | to the extent reasonably practicable, the URI 76 | (https://fontstruct.com/fontstructions/show/1931679) from where the Font was 77 | originally downloaded. 78 | 79 | The Attribution Notice may be implemented in any reasonable manner. For the 80 | avoidance of doubt, You may only use the credit required by this Section for the 81 | purpose of attribution in the manner set out above and, by exercising Your 82 | rights under this License, You may not implicitly or explicitly assert or imply 83 | any connection with, sponsorship or endorsement by the Font Designer of You or 84 | Your Work. 85 | 86 | 4. YOUR REPRESENTATIONS AND INDEMNIFICATIONS 87 | You represent, warrant, and covenant that You shall use the Font Software only 88 | in accordance with the terms of this Agreement and keep the Font Designer fully 89 | indemnified against all actions, claims, costs, proceedings and damages 90 | whatsoever incurred by any breach of any of the aforesaid representations, 91 | warranties of this Agreement. 92 | 93 | 5. OWNERSHIP AND RIGHTS TO THE FONT SOFTWARE 94 | The Font Designer reserves all rights not expressly granted to You in this 95 | Agreement (hereinafter “Reserved Rights”). The Font Software is protected by 96 | copyright and other intellectual property laws and treaties. You acknowledge 97 | that the Font Designer shall be the sole owner of the Font Software, of the 98 | copyright and all Reserved Rights of whatever kind and nature in the Font 99 | Software. 100 | 101 | 6. WARRANTY 102 | The Font Software is made available “as is” WITHOUT WARRANTY OF ANY KIND, 103 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 104 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF 105 | COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT.THE FONT DESIGNER DOES NOT AND 106 | CANNOT GUARANTEE THE PERFORMANCE OR RESULTS YOU MAY OBTAIN BY USING THE FONT 107 | SOFTWARE. 108 | THE FONT DESIGNER SHALL IN NO EVENT BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, 109 | INCIDENTAL OR SPECIAL DAMAGES, INCLUDING ANY LOST PROFITS OR LOST SAVINGS, OR 110 | FOR ANY CLAIM BY ANY THIRD PARTY, ARISING OUT OF THE USE OR INABILITY TO USE THE 111 | FONT SOFTWARE. 112 | 113 | 7. TERMINATION / CONDITION PRECEDENT 114 | You may use the Font Software only as set forth in this Agreement. Any use of 115 | the Font Software that is not expressly allowed in this Agreement will 116 | automatically terminate Your rights to use the Font Software under this 117 | Agreement. If Your right to use the Font Software is terminated You shall 118 | immediately stop using the Font Software, call back or change any Work that is 119 | used in the public, and delete/destroy any copies of the Font Software. The 120 | termination of the Agreement shall not affect the right of the Font Designer to 121 | claim damages or other rights. 122 | 123 | 8. WAIVER 124 | A waiver by either Party of any term or condition of this Agreement shall not be 125 | deemed or construed to be a waiver of such term or condition for the future or 126 | any subsequent breach thereof. All remedies, rights, undertakings, obligations 127 | and agreements contained in this Agreement shall be cumulative and none of them 128 | shall be in limitation of any other remedy, right, undertaking, obligation or 129 | agreement of either Party. 130 | 131 | 9. GOVERNING LAW, PLACE OF JURISDICTION 132 | This Agreement shall be construed and shall take effect in accordance with the 133 | laws of the Federal Republic of Germany excluding the United Nations Convention 134 | on Contracts for the International Sales of Goods (CISG). To the extent 135 | permitted by law, the Courts of Berlin, Germany shall have exclusive 136 | jurisdiction to resolve any dispute which may arise. 137 | 138 | 10. ENTIRE AGREEMENT 139 | This Agreement contains the entire agreement between the Parties. No variation 140 | of any of the terms or conditions of this Agreement may be made unless such 141 | variation is agreed in writing and signed by You and the Font Designer. 142 | 143 | 11. SEVERABILITY 144 | If any provision of this Agreement is adjudged by a court to be invalid or 145 | unenforceable, such provision shall in no way affect any other provision of this 146 | Agreement, the application of such provision in any other circumstance or the 147 | validity or enforceability of this Agreement. The Parties will negotiate in 148 | good faith about a provision that will replace the invalid or unenforceable 149 | provision. 150 | 151 | FontStruct Notice: FontStruct no Party to this Agreement 152 | 153 | Please note that FontStruct (www.fontstruct.com) is not a party to this 154 | Agreement between You and the Font Designer. 155 | 156 | FontStruct makes no warranty of any kind in connection with the Font Software, 157 | its use or the results that You may obtain by using the Font Software. 158 | 159 | FontStruct shall in no event be liable to You or any third party for any damages 160 | whatsoever arising out of or relating to this Agreement or the use of the Font 161 | Software, including but not limited to incidental, special damages, any lost 162 | profits or lost savings or any claim made by a third party. 163 | 164 | -------------------------------------------------------------------------------- /assets/font/readme.txt: -------------------------------------------------------------------------------- 1 | The font file in this archive was created using Fontstruct the free, online 2 | font-building tool. 3 | This font was created by “MarkGamed7794”. 4 | This font has a homepage where this archive and other versions may be found: 5 | https://fontstruct.com/fontstructions/show/1931679 6 | 7 | Try Fontstruct at https://fontstruct.com 8 | It’s easy and it’s fun. 9 | 10 | Fontstruct is copyright ©2021 Rob Meek 11 | 12 | LEGAL NOTICE: 13 | In using this font you must comply with the licensing terms described in the 14 | file “license.txt” included with this archive. 15 | If you redistribute the font file in this archive, it must be accompanied by all 16 | the other files from this archive, including this one. 17 | -------------------------------------------------------------------------------- /assets/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/sfx/classic_bonus.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/sfx/classic_bonus.ogg -------------------------------------------------------------------------------- /assets/sfx/classic_clear.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/sfx/classic_clear.ogg -------------------------------------------------------------------------------- /assets/sfx/classic_levelup.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/sfx/classic_levelup.ogg -------------------------------------------------------------------------------- /assets/sfx/classic_lock.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/sfx/classic_lock.ogg -------------------------------------------------------------------------------- /assets/sfx/classic_move.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/sfx/classic_move.ogg -------------------------------------------------------------------------------- /assets/sfx/classic_rotate.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/sfx/classic_rotate.ogg -------------------------------------------------------------------------------- /assets/sfx/perfectclear3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/sfx/perfectclear3.ogg -------------------------------------------------------------------------------- /assets/splashscreen/splashbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/splashscreen/splashbg.png -------------------------------------------------------------------------------- /assets/splashscreen/splashlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/splashscreen/splashlogo.png -------------------------------------------------------------------------------- /assets/splashscreen/splashtext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/splashscreen/splashtext.png -------------------------------------------------------------------------------- /assets/texture/ldem_4_uint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/texture/ldem_4_uint.png -------------------------------------------------------------------------------- /assets/texture/lroc_color_poles_2k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Oshisaure/example-block-game/44df131d899ca7ebd085494ed6e4ed016b97bc24/assets/texture/lroc_color_poles_2k.png -------------------------------------------------------------------------------- /conf.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | Configuration file for example block game 3 | Copyright (C) 2021 Lilla Oshisaure 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | ]==] 18 | 19 | function love.conf(t) 20 | t.identity = "example block game" 21 | t.window.width = 1280 22 | t.window.height = 720 23 | t.window.title = "EXAMPLE BLOCK GAME" 24 | t.window.resizable = true 25 | end -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | Configurations file saving and loading for example block game 3 | Copyright (C) 2021 Lilla Oshisaure 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | ]==] 18 | 19 | Bindlist = { 20 | {"left", "Move left"}, 21 | {"right", "Move right"}, 22 | {"softdrop", "Soft Drop"}, 23 | {"sonicdrop", "Sonic Drop"}, 24 | {"cw1", "Rotate Right #1"}, 25 | {"cw2", "Rotate Right #2"}, 26 | {"cw3", "Rotate Right #3"}, 27 | {"cw4", "Rotate Right #4"}, 28 | {"ccw1", "Rotate Left #1"}, 29 | {"ccw2", "Rotate Left #2"}, 30 | {"ccw3", "Rotate Left #3"}, 31 | {"ccw4", "Rotate Left #4"}, 32 | {"hold", "Hold"}, 33 | {"pause", "Pause"}, 34 | } 35 | 36 | 37 | MenuPadControls = { 38 | ["dpleft"] = "left", 39 | ["dpright"] = "right", 40 | ["dpup"] = "up", 41 | ["dpdown"] = "down", 42 | ["a"] = "return", 43 | ["b"] = "escape", 44 | } 45 | 46 | KeyDefaults = { 47 | left = "left", 48 | right = "right", 49 | cw1 = "c", 50 | cw2 = "b", 51 | cw3 = "d", 52 | cw4 = "g", 53 | ccw1 = "x", 54 | ccw2 = "v", 55 | ccw3 = "s", 56 | ccw4 = "f", 57 | sonicdrop = "up", 58 | softdrop = "down", 59 | hold = "space", 60 | } 61 | 62 | PadDefaults = { 63 | left = "leftx-", 64 | right = "leftx+", 65 | cw1 = "b", 66 | cw2 = "y", 67 | cw3 = "triggerright+", 68 | cw4 = "", 69 | ccw1 = "a", 70 | ccw2 = "x", 71 | ccw3 = "triggerleft+", 72 | ccw4 = "leftshoulder", 73 | sonicdrop = "lefty-", 74 | softdrop = "lefty+", 75 | hold = "rightshoulder", 76 | } 77 | 78 | ConfigDefaults = { 79 | key_left = "left", 80 | key_right = "right", 81 | key_cw1 = "c", 82 | key_cw2 = "b", 83 | key_cw3 = "d", 84 | key_cw4 = "g", 85 | key_ccw1 = "x", 86 | key_ccw2 = "v", 87 | key_ccw3 = "s", 88 | key_ccw4 = "f", 89 | key_sonicdrop = "up", 90 | key_softdrop = "down", 91 | key_hold = "space", 92 | 93 | pad_left = "leftx-", 94 | pad_right = "leftx+", 95 | pad_cw1 = "b", 96 | pad_cw2 = "y", 97 | pad_cw3 = "triggerright+", 98 | pad_cw4 = "", 99 | pad_ccw1 = "a", 100 | pad_ccw2 = "x", 101 | pad_ccw3 = "triggerleft+", 102 | pad_ccw4 = "leftshoulder", 103 | pad_sonicdrop = "lefty-", 104 | pad_softdrop = "lefty+", 105 | pad_hold = "rightshoulder", 106 | 107 | pad_deadzone = "70", 108 | bgm_volume = "50", 109 | sfx_volume = "50", 110 | 111 | sway_bounciness = "5", 112 | sway_amplitude = "5", 113 | sway_speed = "5", 114 | 115 | bg_brightness = "40", 116 | bg_framerate = "240", 117 | dynamic_bg = "O", 118 | blur_spread = "5", 119 | trail_duration = "5", 120 | 121 | window_width = "1280", 122 | window_height = "720", 123 | window_display = "1", 124 | fullscreen = "X", 125 | vsync = "O", 126 | } 127 | 128 | KeyBindings = {} 129 | PadBindings = {} 130 | Config = {} 131 | 132 | function LoadConfig() 133 | Config = {} 134 | KeyBindings = {} 135 | PadBindings = {} 136 | local confmachinebroke 137 | 138 | if love.filesystem.getInfo("keys.conf") then 139 | for line in love.filesystem.lines("keys.conf") do 140 | key, param = line:match("(.*)=(.+)") 141 | --print(key, param) 142 | if key then 143 | Config[key] = param 144 | if key:sub(1, 4) == "key_" then KeyBindings[key:sub(5,-1)] = param end 145 | if key:sub(1, 4) == "pad_" then PadBindings[key:sub(5,-1)] = param end 146 | elseif #line > 0 then 147 | confmachinebroke = true 148 | end 149 | end 150 | else 151 | confmachinebroke = true 152 | end 153 | 154 | if confmachinebroke then 155 | Config = Deepcopy(ConfigDefaults) 156 | KeyBindings = Deepcopy(KeyDefaults) 157 | PadBindings = Deepcopy(PadDefaults) 158 | else 159 | for k, v in pairs(ConfigDefaults) do Config[k] = Config[k] or v end 160 | end 161 | end 162 | 163 | function SaveConfig() 164 | local str = "" 165 | for key, param in pairs(Config) do 166 | str = str..key.."="..param.."\n" 167 | end 168 | local success, message = love.filesystem.write("keys.conf", str:sub(1,-2)) 169 | ConfigBroke = false 170 | end 171 | 172 | LoadConfig() -------------------------------------------------------------------------------- /credits.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | Credits screen for example block game 3 | Copyright (C) 2021 Lilla Oshisaure 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | ]==] 18 | 19 | local backbutton = { 20 | x = 0.0, y = 0.8, label = "BACK", action_e = function(button) 21 | button.parent.highlight = 1 22 | Credits.screen = 1 23 | STATE = "menu" 24 | end, 25 | } 26 | 27 | local splashcanvas = love.graphics.newCanvas() 28 | Credits = { 29 | screen = 1, 30 | menus = { 31 | Menu.new("Menu", { 32 | {x = 0.0, y = 0.7, label = "ASSETS ATTRIBUTIONS", 33 | action_e = function(button) Credits.screen = 2; button.parent.highlight = 1 end, 34 | }, 35 | backbutton, 36 | {x = 0.0, y = -0.7, label = "A GAME BY OSHISAURE", 37 | action_e = function(button) love.system.openURL("https://oshisaure.itch.io/") end, 38 | }, 39 | {x = 0.4, y = -0.4, label = "LOVE2D v11.3", 40 | action_e = function(button) love.system.openURL("http://love2d.org/") end, 41 | }, 42 | {x = 0.0, y = -0.2, label = "GITHUB CONTRIBUTORS", 43 | action_e = function(button) love.system.openURL("https://github.com/Oshisaure/example-block-game/graphs/contributors") end, 44 | }, 45 | {x = 0.0, y = 0.3, label = "THE CAMBRIDGE GAME DISCORD SERVER", 46 | action_e = function(button) love.system.openURL("https://t-sp.in/cambridge") end, 47 | }, 48 | }), 49 | Menu.new("HUD", { 50 | {x = 0.0, y = 0.7, label = "GENERAL CREDITS", 51 | action_e = function(button) Credits.screen = 1; button.parent.highlight = 1; ResetSplashScreen() end, 52 | }, 53 | backbutton, 54 | {x = 0.00, y = -0.50, label = "CGI MOON KIT FROM NASA'S SCIENTIFIC VISUALIZATION STUDIO", 55 | action_e = function(button) love.system.openURL("https://svs.gsfc.nasa.gov/4720") end, 56 | }, 57 | {x = 0.00, y = -0.20, label = "MADE BY MARKGAMED7794", 58 | action_e = function(button) love.system.openURL("https://github.com/MarkGamed7794") end, 59 | }, 60 | {x = 0.00, y = 0.10, label = "emma \"msx\" essex - beside yourself", 61 | action_e = function(button) 62 | love.system.openURL("https://halleylabs.com/track/beside-yourself") 63 | end, 64 | }, 65 | {x = 0.00, y = 0.15, label = "substance - aftertouch", 66 | action_e = function(button) 67 | love.system.openURL("https://modarchive.org/module.php?126990") 68 | end, 69 | }, 70 | {x = 0.00, y = 0.20, label = "FearofDark - late night early morning jam", 71 | action_e = function(button) 72 | love.system.openURL("https://modarchive.org/module.php?173929") 73 | end, 74 | }, 75 | {x = 0.00, y = 0.25, label = "Tempest - Crema Lubricante", 76 | action_e = function(button) 77 | love.system.openURL("https://modarchive.org/module.php?188911") 78 | end, 79 | }, 80 | {x = 0.00, y = 0.30, label = "Jazzcat - Thinking of TIT", 81 | action_e = function(button) 82 | love.system.openURL("https://modarchive.org/module.php?181530") 83 | end, 84 | }, 85 | {x = 0.00, y = 0.35, label = "Elwood - Into the Shadow", 86 | action_e = function(button) 87 | love.system.openURL("https://modarchive.org/module.php?54298") 88 | end, 89 | }, 90 | {x = 0.00, y = 0.40, label = "Karsten Koch - Aryx", 91 | action_e = function(button) 92 | love.system.openURL("https://modarchive.org/module.php?34036") 93 | end, 94 | }, 95 | }), 96 | }, 97 | labels = { 98 | { 99 | {x = -0.4, y = -0.40, font = "Menu", label = "POWERED BY"}, 100 | {x = -0.0, y = -0.10, font = "HUD", label = "ZLY_U - GENERAL CODE AND REFACTOR HELP"}, 101 | {x = -0.0, y = -0.05, font = "HUD", label = "MILLABASSET - ADDITIONAL SPEED CURVES"}, 102 | {x = -0.0, y = 0.00, font = "HUD", label = "MARKGAMED7794 - SCORING AND LEADERBOARD HELP"}, 103 | {x = -0.0, y = 0.20, font = "Menu", label = "SPECIAL THANKS TO"}, 104 | {x = -0.0, y = 0.40, font = "Menu", label = "LANTERNS AND SHELLELOCH"}, 105 | }, 106 | { 107 | {x = 0, y = -0.6, font = "Menu", label = "MOON TEXTURE/DISPLACEMENT MAP"}, 108 | {x = 0, y = -0.3, font = "Menu", label = "TEXT FONT"}, 109 | {x = 0, y = -0.0, font = "Menu", label = "MUSIC CREDITS"}, 110 | } 111 | }, 112 | resize = function(self) 113 | self.menus[1]:updateSelected() 114 | self.menus[2]:updateSelected() 115 | splashcanvas:release() 116 | splashcanvas = love.graphics.newCanvas(Width, Height) 117 | end, 118 | draw = function(self) 119 | RenderBG() 120 | love.graphics.setShader() 121 | love.graphics.setCanvas(CanvasBG) 122 | love.graphics.setColor(.5,.5,.5) 123 | love.graphics.draw(self.menus[self.screen].text) 124 | for _, item in pairs(self.labels[self.screen]) do 125 | love.graphics.setFont(Font[item.font]) 126 | love.graphics.printf( 127 | item.label, 128 | math.floor(item.x*Width/2), 129 | math.floor(item.y*Height/2)+Height/2, 130 | Width, "center" 131 | ) 132 | end 133 | 134 | if self.screen == 1 then 135 | local w, h = TitleText:getDimensions() 136 | love.graphics.draw(TitleText, Width*0.5, Height*0.1, 0, 1, 1, w/2, h/2, -math.cos(os.clock()*0.5)*0.4, 0) 137 | end 138 | 139 | love.graphics.setCanvas() 140 | local b = tonumber(Config.bg_brightness)/100 141 | love.graphics.setColor(b, b, b) 142 | love.graphics.draw(CanvasBG) 143 | 144 | love.graphics.setColor(1,1,1) 145 | if self.screen == 1 then 146 | local w, h = TitleText:getDimensions() 147 | love.graphics.draw(TitleText, Width*0.5, Height*0.1, 0, 1, 1, w/2, h/2, -math.cos(os.clock()*0.5)*0.4, 0) 148 | 149 | love.graphics.setCanvas(splashcanvas) 150 | love.graphics.clear() 151 | DrawSplashScreen() 152 | love.graphics.setCanvas() 153 | love.graphics.draw(splashcanvas, Width*0.8, Height*0.2, -0.2+math.sin(os.clock()*0.5)*0.1, 0.3, 0.3, splashcanvas:getWidth()/2, splashcanvas:getHeight()/2) 154 | else 155 | love.graphics.setShader(ShaderBG.credits) 156 | love.graphics.draw(CanvasBG) 157 | love.graphics.setShader() 158 | end 159 | 160 | love.graphics.draw(self.menus[self.screen].text) 161 | for _, item in pairs(self.labels[self.screen]) do 162 | love.graphics.setFont(Font[item.font]) 163 | love.graphics.printf( 164 | item.label, 165 | math.floor(item.x*Width/2), 166 | math.floor(item.y*Height/2)+Height/2, 167 | Width, "center" 168 | ) 169 | end 170 | end, 171 | } 172 | -------------------------------------------------------------------------------- /highscores.lua: -------------------------------------------------------------------------------- 1 | HighScores = {} 2 | 3 | function LoadHighScores() 4 | HighScores = {} 5 | local no_scores_file = false 6 | if love.filesystem.getInfo("highscores.sav") then 7 | local current_mode = "" 8 | for line in love.filesystem.lines("highscores.sav") do 9 | local header = line:match("%[(.+)%]") 10 | if header then 11 | current_mode = header 12 | HighScores[current_mode] = {} 13 | else 14 | local score, clear_score, lines, start_level, stop_level, time, clear_time = line:match("(.+),(.+),(.+),(.+),(.+),(.+),(.+)") 15 | if start_level and score and lines and stop_level then 16 | table.insert(HighScores[current_mode],{ 17 | ["start_level"] = tonumber(start_level), 18 | ["score"] = score, 19 | ["lines"] = lines, 20 | ["final_level"] = tonumber(stop_level), 21 | ["clear_score"] = clear_score ~= "none" and clear_score, 22 | ["time"] = tonumber(time), 23 | ["clear_time"] = clear_time ~= "none" and tonumber(clear_time) 24 | }) 25 | elseif #line > 0 then 26 | no_scores_file = true 27 | end 28 | end 29 | end 30 | else 31 | no_scores_file = true 32 | end 33 | 34 | if no_scores_file then 35 | print("Nevermind, it broke, resetting high scores to default!") 36 | -- make some default high scores 37 | for _, mode in pairs(Levels) do 38 | HighScores[mode.name] = {} 39 | end 40 | SaveHighScores() 41 | end 42 | end 43 | 44 | function SaveHighScores() 45 | local str = "" 46 | for mode_id, _ in pairs(HighScores) do 47 | --first things first, we have to sort them by score, of course! 48 | table.sort(HighScores[mode_id], function(a, b) return tonumber(a.score) > tonumber(b.score) end) 49 | 50 | local mode_str = ("[%s]\n"):format(mode_id) 51 | for _, score in ipairs(HighScores[mode_id]) do 52 | local score_str = ("%s,%s,%s,%s,%s,%s,%s"):format( 53 | score.score, score.clear_score or "none", score.lines, 54 | score.start_level, score.final_level, score.time, 55 | score.clear_time or "none" 56 | ) 57 | mode_str = mode_str..score_str.."\n" 58 | end 59 | str = str..mode_str.."\n" 60 | end 61 | local success, message = love.filesystem.write("highscores.sav", str:sub(1,-2)) 62 | end 63 | 64 | LoadHighScores() -------------------------------------------------------------------------------- /linux/MakeAppImage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # [2022/04/11] [06:16:44 (JST)] 3 | # Dependancies: curl, AppImageKit 4 | 5 | if ! [[ "$1" == *".love"* ]]; then 6 | echo "Invalid Input!" 7 | echo "Usage: ./MakeAppImage.sh [.love file]" 8 | exit 2 9 | fi 10 | 11 | FILE=$1 # Path to game file 12 | NAME=$(basename -s .love $1) # File name without extension 13 | 14 | # Clears the directory except for the script itself 15 | find . ! -name '*.sh' -delete 16 | 17 | # Pulls the latest generic official appimage 18 | curl -LJO https://api.github.com/repos/love2d/love/releases/latest 19 | LOVE_APPIMAGE_URL=($( \ 20 | grep browser_download_url latest \ 21 | | grep -i appimage \ 22 | | cut -d '"' -f 4 \ 23 | )) 24 | curl -LJO "$LOVE_APPIMAGE_URL" 25 | if ! [ -f *.AppImage ]; then echo "Failed to get LOVE appimage!"; exit 2; fi 26 | 27 | # Inserts the .love file and customises the AppImage 28 | chmod +x *.AppImage 29 | ./*.AppImage --appimage-extract 30 | cat squashfs-root/bin/love $FILE > squashfs-root/bin/$NAME 31 | chmod +x squashfs-root/bin/$NAME 32 | cp ../assets/logo.svg squashfs-root/ 33 | ln -sf logo.svg squashfs-root/.DirIcon 34 | sed -i 's/\(Name=\)\(.*\)/\1Example Block Game/' squashfs-root/love.desktop 35 | sed -i 's/\(Comment=\)\(.*\)/\1Open source simple block game/' squashfs-root/love.desktop 36 | sed -i "s/\(Exec=\)\(.*\)/\1$NAME %f/" squashfs-root/love.desktop 37 | sed -i "s/\(Categories=\)\(.*\)/\1Game;/" squashfs-root/love.desktop 38 | sed -i "s/\(Icon=\)\(.*\)/\1logo/" squashfs-root/love.desktop 39 | rm squashfs-root/love.svg 40 | rm squashfs-root/*.txt 41 | 42 | # Makes the thing 43 | appimagetool squashfs-root $NAME.AppImage 44 | -------------------------------------------------------------------------------- /menu.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | Copyright (C) 2021 Lilla Oshisaure 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU Affero General Public License as published 6 | by the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | 14 | You should have received a copy of the GNU Affero General Public License 15 | along with this program. If not, see . 16 | ]==] 17 | 18 | Menu = { 19 | updateSelected = function(self, key) 20 | if key == "up" then self.highlight = (self.highlight - 2) % #self.items + 1 21 | elseif key == "down" then self.highlight = (self.highlight - 0) % #self.items + 1 22 | elseif key == "return" then self.items[self.highlight]:action_e(); self.items[self.highlight]:update() 23 | elseif key == "right" then self.items[self.highlight]:action_r(); self.items[self.highlight]:update() 24 | elseif key == "left" then self.items[self.highlight]:action_l(); self.items[self.highlight]:update() 25 | end 26 | 27 | self.text:setFont(Font[self.font]) 28 | self.text:set("") 29 | for k, item in ipairs(self.items) do 30 | self.text:addf({k == self.highlight and {1, 0.7, 0.5} or {1,1,1}, item.label}, 31 | Width, "center", 32 | math.floor(item.x*Width/2), 33 | math.floor(item.y*Height/2)+Height/2) 34 | end 35 | end, 36 | 37 | reload = function(self) 38 | -- local fontsize = self.font 39 | -- local font 40 | -- if fontsize == "big" then font = HUDFontBig 41 | -- elseif fontsize == "small" then font = HUDFontSmall 42 | -- else font = HUDFont 43 | -- end 44 | -- self.text:setFont(font) 45 | self:updateSelected("kek") 46 | end, 47 | 48 | new = function(font, items) 49 | for k = 1, #items do 50 | if not items[k].action_e then items[k].action_e = NADA end 51 | if not items[k].action_l then items[k].action_l = NADA end 52 | if not items[k].action_r then items[k].action_r = NADA end 53 | if not items[k].update then items[k].update = NADA end 54 | items[k].index = k 55 | end 56 | --[[ 57 | local font 58 | if fontsize == "big" then font = HUDFontBig 59 | elseif fontsize == "small" then font = HUDFontSmall 60 | else font = HUDFont 61 | end]] 62 | local menu = { 63 | items = items, 64 | font = font, 65 | text = love.graphics.newText(Font[font]), 66 | highlight = 1, 67 | updateSelected = Menu.updateSelected, 68 | reload = Menu.reload, 69 | } 70 | for k = 1, #menu.items do menu.items[k].parent = menu end 71 | menu:updateSelected("kek") 72 | return menu 73 | end, 74 | } -------------------------------------------------------------------------------- /pause.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | Pause screen for example block game 3 | Copyright (C) 2021 Lilla Oshisaure 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | ]==] 18 | 19 | Pause = Menu.new("Menu", { 20 | {x = 0, y = -0.2, label = "RESUME", action_e = function(button) 21 | STATE = "ingame" 22 | button.parent.highlight = 1 23 | end}, 24 | {x = 0, y = 0.0, label = "RESTART", action_e = function(button) 25 | Game:reset(os.time()) 26 | SetBGM(Game.BGM) 27 | STATE = "ingame" 28 | button.parent.highlight = 1 29 | end}, 30 | {x = 0, y = 0.2, label = "QUIT", action_e = function(button) 31 | if Config.dynamic_bg == "X" then PrerenderBG() end 32 | STATE = "menu" 33 | SetBGM("menu") 34 | button.parent.highlight = 1 35 | end}, 36 | }) 37 | 38 | local pausetext = love.graphics.newText(Font.Title, "GAME PAUSED") 39 | function DrawPause() 40 | local _c = love.graphics.getCanvas() 41 | love.graphics.setCanvas(CanvasRainbow) 42 | love.graphics.clear(0,0,0,0) 43 | local w, h = pausetext:getDimensions() 44 | love.graphics.draw(pausetext, Width*0.5, Height*0.2, 0, 1, 1, w/2, h/2, math.cos(os.clock()*math.pi/2)*0.4, 0) 45 | 46 | love.graphics.setCanvas(_c) 47 | love.graphics.setColor(0,0,0,0.5) 48 | love.graphics.rectangle("fill", -Width, -Height, 2*Width, 2*Height) 49 | love.graphics.setColor(1,1,1,1) 50 | love.graphics.draw(Pause.text) 51 | DrawRainbow(CanvasRainbow) 52 | end 53 | 54 | function UpdatePauseMenuFonts() 55 | pausetext:setFont(Font.Title) 56 | Pause:updateSelected() 57 | end -------------------------------------------------------------------------------- /piece.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | Piece object class for example block game 3 | Copyright (C) 2021 Lilla Oshisaure 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | ]==] 18 | Piece = { 19 | donut_pieces = 3, 20 | 21 | -- main piece IDs 22 | IDs = {"I", "L", "J", "T", "S", "Z", "O"}, 23 | 24 | -- altered piece IDs 25 | parents = { 26 | -- regular 27 | I = "I", 28 | S = "S", 29 | Z = "Z", 30 | L = "L", 31 | J = "J", 32 | T = "T", 33 | O = "O", 34 | 35 | -- Pentomino 36 | F5l = "T", 37 | F5r = "T", 38 | I5 = "I", 39 | L5l = "I", 40 | L5r = "I", 41 | N5l = "Z", 42 | N5r = "S", 43 | P5l = "J", 44 | P5r = "L", 45 | T5 = "T", 46 | U5 = "L", 47 | V5 = "J", 48 | W5 = "Z", 49 | X5 = "T", 50 | Y5l = "I", 51 | Y5r = "I", 52 | Z5l = "L", 53 | Z5r = "J", 54 | 55 | -- 45 56 | i = "I", 57 | s = "S", 58 | z = "Z", 59 | l = "L", 60 | j = "J", 61 | t = "T", 62 | o = "O", 63 | 64 | O1 = "O", 65 | O2 = "O", 66 | O3 = "O", 67 | O4 = "O", 68 | O5 = "O", 69 | O6 = "O", 70 | 71 | 72 | -- kek 73 | -- h = "I", 74 | 75 | ue = "T", 76 | hara = "O", 77 | }, 78 | 79 | definitions = { 80 | -- My positive directions are right and up. Sorry if that bugs any of you out there. 81 | I = { 82 | {{ 1, 0},{ 0, 0},{-1, 0},{ 2, 0}}, 83 | {{ 1, 2},{ 1, 1},{ 1, 0},{ 1,-1}} 84 | }, 85 | S = { 86 | {{-1, 0},{ 0, 0},{ 0, 1},{ 1, 1}}, 87 | {{ 0, 1},{ 0, 0},{ 1, 0},{ 1,-1}} 88 | }, 89 | Z = { 90 | {{-1, 1},{ 0, 1},{ 0, 0},{ 1, 0}}, 91 | {{ 1, 1},{ 1, 0},{ 0, 0},{ 0,-1}} 92 | }, 93 | T = { 94 | {{ 1, 0},{ 0, 0},{-1, 0},{ 0, 1}}, 95 | {{ 0, 1},{ 0, 0},{ 0,-1},{ 1, 0}}, 96 | {{ 1, 0},{ 0, 0},{-1, 0},{ 0,-1}}, 97 | {{ 0, 1},{ 0, 0},{ 0,-1},{-1, 0}}, 98 | }, 99 | J = { 100 | {{ 1, 0},{ 0, 0},{-1, 0},{-1, 1}}, 101 | {{ 0, 1},{ 0, 0},{ 0,-1},{ 1, 1}}, 102 | {{ 1, 0},{ 0, 0},{-1, 0},{ 1,-1}}, 103 | {{ 0, 1},{ 0, 0},{ 0,-1},{-1,-1}}, 104 | }, 105 | L = { 106 | {{ 1, 0},{ 0, 0},{-1, 0},{ 1, 1}}, 107 | {{ 0, 1},{ 0, 0},{ 0,-1},{ 1,-1}}, 108 | {{ 1, 0},{ 0, 0},{-1, 0},{-1,-1}}, 109 | {{ 0, 1},{ 0, 0},{ 0,-1},{-1, 1}}, 110 | }, 111 | O = { 112 | {{ 0, 0},{ 1, 0},{ 1, 1},{ 0, 1}} 113 | }, 114 | 115 | -- Pentomino 116 | 117 | I5 = { 118 | {{-2, 0},{-1, 0},{ 0, 0},{ 1, 0},{ 2, 0}}, 119 | {{ 0,-2},{ 0,-1},{ 0, 0},{ 0, 1},{ 0, 2}}, 120 | }, 121 | F5l = { 122 | {{-1,-1},{ 0,-1},{ 0, 0},{ 1, 0},{ 0, 1}}, 123 | {{-1, 1},{-1, 0},{ 0, 0},{ 0,-1},{ 1, 0}}, 124 | {{ 1, 1},{ 0, 1},{ 0, 0},{-1, 0},{ 0,-1}}, 125 | {{ 1,-1},{ 1, 0},{ 0, 0},{ 0, 1},{-1, 0}}, 126 | }, 127 | F5r = { 128 | {{ 1,-1},{ 0,-1},{ 0, 0},{-1, 0},{ 0, 1}}, 129 | {{-1,-1},{-1, 0},{ 0, 0},{ 0, 1},{ 1, 0}}, 130 | {{-1, 1},{ 0, 1},{ 0, 0},{ 1, 0},{ 0,-1}}, 131 | {{ 1, 1},{ 1, 0},{ 0, 0},{ 0,-1},{-1, 0}}, 132 | }, 133 | L5l = { 134 | {{ 2, 0},{ 1, 0},{ 0, 0},{-1, 0},{-1, 1}}, 135 | {{ 0,-1},{ 0, 0},{ 0, 1},{ 0, 2},{ 1, 2}}, 136 | {{-1, 1},{ 0, 1},{ 1, 1},{ 2, 1},{ 2, 0}}, 137 | {{ 1, 2},{ 1, 1},{ 1, 0},{ 1,-1},{ 0,-1}}, 138 | }, 139 | L5r = { 140 | {{ 2, 0},{ 1, 0},{ 0, 0},{-1, 0},{ 2, 1}}, 141 | {{ 0,-1},{ 0, 0},{ 0, 1},{ 0, 2},{ 1,-1}}, 142 | {{-1, 1},{ 0, 1},{ 1, 1},{ 2, 1},{-1, 0}}, 143 | {{ 1, 2},{ 1, 1},{ 1, 0},{ 1,-1},{ 0, 2}}, 144 | }, 145 | N5l = { 146 | {{ 2, 0},{ 1, 0},{ 0, 0},{ 0, 1},{-1, 1}}, 147 | {{ 0,-1},{ 0, 0},{ 0, 1},{ 1, 1},{ 1, 2}}, 148 | {{-1, 1},{ 0, 1},{ 1, 1},{ 1, 0},{ 2, 0}}, 149 | {{ 1, 2},{ 1, 1},{ 1, 0},{ 0, 0},{ 0,-1}}, 150 | }, 151 | N5r = { 152 | {{-1, 0},{ 0, 0},{ 1, 0},{ 1, 1},{ 2, 1}}, 153 | {{ 0, 2},{ 0, 1},{ 0, 0},{ 1, 0},{ 1,-1}}, 154 | {{ 2, 1},{ 1, 1},{ 0, 1},{ 0, 0},{-1, 0}}, 155 | {{ 1,-1},{ 1, 0},{ 1, 1},{ 0, 1},{ 0, 2}}, 156 | }, 157 | P5l = { 158 | {{ 1, 0},{ 0, 0},{-1, 0},{ 0, 1},{-1, 1}}, 159 | {{ 0, 1},{ 0, 0},{ 0,-1},{ 1, 0},{ 1, 1}}, 160 | {{ 1, 0},{ 0, 0},{-1, 0},{ 0,-1},{ 1,-1}}, 161 | {{ 0, 1},{ 0, 0},{ 0,-1},{-1, 0},{-1,-1}}, 162 | }, 163 | P5r = { 164 | {{ 1, 0},{ 0, 0},{-1, 0},{ 0, 1},{ 1, 1}}, 165 | {{ 0, 1},{ 0, 0},{ 0,-1},{ 1, 0},{ 1,-1}}, 166 | {{ 1, 0},{ 0, 0},{-1, 0},{ 0,-1},{-1,-1}}, 167 | {{ 0, 1},{ 0, 0},{ 0,-1},{-1, 0},{-1, 1}}, 168 | }, 169 | T5 = { 170 | {{-1,-1},{ 0,-1},{ 1,-1},{ 0, 0},{ 0, 1}}, 171 | {{-1, 1},{-1, 0},{-1,-1},{ 0, 0},{ 1, 0}}, 172 | {{ 1, 1},{ 0, 1},{-1, 1},{ 0, 0},{ 0,-1}}, 173 | {{ 1,-1},{ 1, 0},{ 1, 1},{ 0, 0},{-1, 0}}, 174 | }, 175 | U5 = { 176 | {{ 1, 0},{ 0, 0},{-1, 0},{ 1, 1},{-1, 1}}, 177 | {{ 0, 1},{ 0, 0},{ 0,-1},{ 1,-1},{ 1, 1}}, 178 | {{ 1, 0},{ 0, 0},{-1, 0},{-1,-1},{ 1,-1}}, 179 | {{ 0, 1},{ 0, 0},{ 0,-1},{-1, 1},{-1,-1}}, 180 | }, 181 | V5 = { 182 | {{-1,-1},{ 0,-1},{ 1,-1},{ 1, 0},{ 1, 1}}, 183 | {{-1, 1},{-1, 0},{-1,-1},{ 0,-1},{ 1,-1}}, 184 | {{ 1, 1},{ 0, 1},{-1, 1},{-1, 0},{-1,-1}}, 185 | {{ 1,-1},{ 1, 0},{ 1, 1},{ 0, 1},{-1, 1}}, 186 | }, 187 | W5 = { 188 | {{-1, 1},{-1, 0},{ 0, 0},{ 0,-1},{ 1,-1}}, 189 | {{ 1, 1},{ 0, 1},{ 0, 0},{-1, 0},{-1,-1}}, 190 | {{ 1,-1},{ 1, 0},{ 0, 0},{ 0, 1},{-1, 1}}, 191 | {{-1,-1},{ 0,-1},{ 0, 0},{ 1, 0},{ 1, 1}}, 192 | }, 193 | X5 = { 194 | {{ 0, 0},{ 0,-1},{-1, 0},{ 0, 1},{ 1, 0}}, 195 | }, 196 | Y5l = { 197 | {{ 2, 0},{ 1, 0},{ 0, 0},{-1, 0},{ 0, 1}}, 198 | {{ 0,-1},{ 0, 0},{ 0, 1},{ 0, 2},{ 1, 1}}, 199 | {{-1, 1},{ 0, 1},{ 1, 1},{ 2, 1},{ 1, 0}}, 200 | {{ 1, 2},{ 1, 1},{ 1, 0},{ 1,-1},{ 0, 0}}, 201 | }, 202 | Y5r = { 203 | {{ 2, 0},{ 1, 0},{ 0, 0},{-1, 0},{ 1, 1}}, 204 | {{ 0,-1},{ 0, 0},{ 0, 1},{ 0, 2},{ 1, 0}}, 205 | {{-1, 1},{ 0, 1},{ 1, 1},{ 2, 1},{ 0, 0}}, 206 | {{ 1, 2},{ 1, 1},{ 1, 0},{ 1,-1},{ 0, 1}}, 207 | }, 208 | Z5l = { 209 | {{-1,-1},{ 0,-1},{ 0, 0},{ 0, 1},{ 1, 1}}, 210 | {{-1, 1},{-1, 0},{ 0, 0},{ 1, 0},{ 1,-1}}, 211 | {{ 1, 1},{ 0, 1},{ 0, 0},{ 0,-1},{-1,-1}}, 212 | {{ 1,-1},{ 1, 0},{ 0, 0},{-1, 0},{-1, 1}}, 213 | }, 214 | Z5r = { 215 | {{ 1,-1},{ 0,-1},{ 0, 0},{ 0, 1},{-1, 1}}, 216 | {{-1,-1},{-1, 0},{ 0, 0},{ 1, 0},{ 1, 1}}, 217 | {{-1, 1},{ 0, 1},{ 0, 0},{ 0,-1},{ 1,-1}}, 218 | {{ 1, 1},{ 1, 0},{ 0, 0},{-1, 0},{-1,-1}}, 219 | }, 220 | 221 | 222 | 223 | -- 45° rotations 224 | i = { 225 | {{-1, 0},{ 0, 0},{ 1, 0},{ 2, 0}}, 226 | {{-1, 2},{ 0, 1},{ 1, 0},{ 2,-1}}, 227 | {{ 1, 2},{ 1, 1},{ 1, 0},{ 1,-1}}, 228 | {{-1,-1},{ 0, 0},{ 1, 1},{ 2, 2}}, 229 | }, 230 | t = { 231 | {{ 1, 0},{ 0, 0},{-1, 0},{ 0, 1}}, 232 | {{ 1,-1},{ 0, 0},{-1, 1},{ 1, 1}}, 233 | {{ 0,-1},{ 0, 0},{ 0, 1},{ 1, 0}}, 234 | {{-1,-1},{ 0, 0},{ 1, 1},{ 1,-1}}, 235 | {{-1, 0},{ 0, 0},{ 1, 0},{ 0,-1}}, 236 | {{-1, 1},{ 0, 0},{ 1,-1},{-1,-1}}, 237 | {{ 0, 1},{ 0, 0},{ 0,-1},{-1, 0}}, 238 | {{ 1, 1},{ 0, 0},{-1,-1},{-1, 1}}, 239 | }, 240 | j = { 241 | {{ 1, 0},{ 0, 0},{-1, 0},{-1, 1}}, 242 | {{ 1,-1},{ 0, 0},{-1, 1},{ 0, 1}}, 243 | {{ 0,-1},{ 0, 0},{ 0, 1},{ 1, 1}}, 244 | {{-1,-1},{ 0, 0},{ 1, 1},{ 1, 0}}, 245 | {{-1, 0},{ 0, 0},{ 1, 0},{ 1,-1}}, 246 | {{-1, 1},{ 0, 0},{ 1,-1},{ 0,-1}}, 247 | {{ 0, 1},{ 0, 0},{ 0,-1},{-1,-1}}, 248 | {{ 1, 1},{ 0, 0},{-1,-1},{-1, 0}}, 249 | }, 250 | l = { 251 | {{ 1, 0},{ 0, 0},{-1, 0},{ 1, 1}}, 252 | {{ 1,-1},{ 0, 0},{-1, 1},{ 1, 0}}, 253 | {{ 0,-1},{ 0, 0},{ 0, 1},{ 1,-1}}, 254 | {{-1,-1},{ 0, 0},{ 1, 1},{ 0,-1}}, 255 | {{-1, 0},{ 0, 0},{ 1, 0},{-1,-1}}, 256 | {{-1, 1},{ 0, 0},{ 1,-1},{-1, 0}}, 257 | {{ 0, 1},{ 0, 0},{ 0,-1},{-1, 1}}, 258 | {{ 1, 1},{ 0, 0},{-1,-1},{ 0, 1}}, 259 | }, 260 | 261 | z = { 262 | {{-1, 1},{ 0, 1},{ 0, 0},{ 1, 0}}, 263 | {{-1, 2},{ 0, 1},{ 0, 0},{ 1,-1}}, 264 | {{ 0, 1},{ 0, 0},{-1, 0},{-1,-1}}, 265 | {{-2,-1},{-1, 0},{ 0, 0},{ 1, 1}}, 266 | }, 267 | 268 | s = { 269 | {{ 1, 1},{ 0, 1},{ 0, 0},{-1, 0}}, 270 | {{ 2,-1},{ 1, 0},{ 0, 0},{-1, 1}}, 271 | {{ 0, 1},{ 0, 0},{ 1, 0},{ 1,-1}}, 272 | {{ 1, 2},{ 0, 1},{ 0, 0},{-1,-1}}, 273 | }, 274 | 275 | o = { 276 | {{ 0, 0},{ 1, 0},{ 1, 1},{ 0, 1}}, 277 | {{ 0, 0},{ 1, 1},{ 2, 0},{ 1,-1}}, 278 | }, 279 | 280 | -- extend square 281 | h = { 282 | {{0,0}}, 283 | {{0,0},{1,0}}, 284 | {{0,0},{1,0},{2,0}}, 285 | {{0,0},{1,0},{2,0},{3,0}}, 286 | {{0,0},{1,0},{2,0},{3,0},{4,0}}, 287 | {{0,0},{1,0},{2,0},{3,0},{4,0},{5,0}}, 288 | {{0,0},{1,0},{2,0},{3,0},{4,0},{5,0},{6,0}}, 289 | {{0,0},{1,0},{2,0},{3,0},{4,0},{5,0},{6,0},{7,0}}, 290 | {{0,0},{1,0},{2,0},{3,0},{4,0},{5,0},{6,0},{7,0},{8,0}}, 291 | {{0,0},{1,0},{2,0},{3,0},{4,0},{5,0},{6,0},{7,0},{8,0},{9,0}}, 292 | {{0,0},{1,0},{2,0},{3,0},{4,0},{5,0},{6,0},{7,0},{8,0},{9,0},{10,0}}, -- should fail no matter what 293 | }, 294 | 295 | -- donut pieces 296 | O1 = {{{-1, 0},{ 0,-1},{ 1, 0},{ 0, 1}}}, 297 | O2 = {{{-1, 0},{ 0,-1},{ 1, 0},{ 0, 1},{ 1, 1},{-1, 1},{ 1,-1},{-1,-1}}}, 298 | O3 = {{{-1, 0},{ 0,-1},{ 1, 0},{ 0, 1},{ 1, 1},{-1, 1},{ 1,-1},{-1,-1},{-2, 0},{ 0,-2},{ 2, 0},{ 0, 2}}}, 299 | O4 = {{{ 1, 1},{-1, 1},{ 1,-1},{-1,-1},{-2, 0},{ 0,-2},{ 2, 0},{ 0, 2}}}, 300 | O5 = {{{-1,-1},{ 0,-1},{ 1,-1},{ 2,-1},{ 2, 0},{ 2, 1},{ 2, 2},{ 1, 2},{ 0, 2},{-1, 2},{-1, 1},{-1, 0}}}, 301 | O6 = {{{-2, 0},{-2,-1},{-1,-1},{-1,-2},{ 0,-2},{ 1,-2},{ 1,-1},{ 2,-1},{ 2, 0},{ 2, 1},{ 1, 1},{ 1, 2},{ 0, 2},{-1, 2},{-1, 1},{-2, 1}}}, 302 | 303 | -- misc 304 | test = {{ 305 | {-4, 0},{-3, 0},{-2, 0},{-1, 0},{ 0, 0},{ 1, 0},{ 2, 0},{ 3, 0},{ 4, 0},{ 5, 0}, 306 | {-4, 1},{-3, 1},{-2, 1},{-1, 1},{ 0, 1},{ 1, 1},{ 2, 1},{ 3, 1},{ 4, 1},{ 5, 1}, 307 | {-4, 2},{-3, 2},{-2, 2},{-1, 2},{ 0, 2},{ 1, 2},{ 2, 2},{ 3, 2},{ 4, 2},{ 5, 2}, 308 | {-4, 3},{-3, 3},{-2, 3},{-1, 3},{ 0, 3},{ 1, 3},{ 2, 3},{ 3, 3},{ 4, 3},{ 5, 3}, 309 | {-4, 4},{-3, 4},{-2, 4},{-1, 4},{ 0, 4},{ 1, 4},{ 2, 4},{ 3, 4},{ 4, 4},{ 5, 4}, 310 | }}, 311 | 312 | w6 = { 313 | {{-1, 1},{-1, 0},{ 0, 0},{ 0,-1},{ 1,-1},{-1,-1},}, 314 | {{ 1, 1},{ 0, 1},{ 0, 0},{-1, 0},{-1,-1},{-1, 1},}, 315 | {{ 1,-1},{ 1, 0},{ 0, 0},{ 0, 1},{-1, 1},{ 1, 1},}, 316 | {{-1,-1},{ 0,-1},{ 0, 0},{ 1, 0},{ 1, 1},{ 1,-1},}, 317 | }, 318 | 319 | ue = { 320 | {{ 0, 1},{ 0, 0},{ 1, 0},{ 0,-1},{-2,-2},{-1,-2},{ 0,-2},{ 1,-2},{ 2,-2}}, 321 | {{ 1, 0},{ 0, 0},{ 0,-1},{-1, 0},{-2, 2},{-2, 1},{-2, 0},{-2,-1},{-2,-2}}, 322 | {{ 0,-1},{ 0, 0},{-1, 0},{ 0, 1},{ 2, 2},{ 1, 2},{ 0, 2},{-1, 2},{-2, 2}}, 323 | {{-1, 0},{ 0, 0},{ 0, 1},{ 1, 0},{ 2,-2},{ 2,-1},{ 2, 0},{ 2, 1},{ 2, 2}}, 324 | }, 325 | 326 | --[==[ 327 | [][][][][][][] 328 | [] [] 329 | [] [][][][][] 330 | [] [] [] 331 | [] [][]><[][] 332 | [] [] [] 333 | [] [][][][][] 334 | [] [] 335 | [] [] [] [] 336 | ]==] 337 | hara = { 338 | {{-4, 4},{-3, 4},{-2, 4},{-1, 4},{ 0, 4},{ 1, 4},{ 2, 4},{-4, 3},{ 0, 3},{-4, 2},{-2, 2},{-1, 2},{ 0, 2},{ 1, 2},{ 2, 2},{-4, 1},{-2, 1},{ 2, 1},{-4, 0},{-2, 0},{-1, 0},{ 0, 0},{ 1, 0},{ 2, 0},{-4,-1},{-2,-1},{ 2,-1},{-4,-2},{-2,-2},{-1,-2},{ 0,-2},{ 1,-2},{ 2,-2},{-4,-3},{ 0,-3},{-4,-4},{-2,-4},{ 0,-4},{ 2,-4}}, 339 | {{ 4, 4},{ 4, 3},{ 4, 2},{ 4, 1},{ 4,-0},{ 4,-1},{ 4,-2},{ 3, 4},{ 3,-0},{ 2, 4},{ 2, 2},{ 2, 1},{ 2,-0},{ 2,-1},{ 2,-2},{ 1, 4},{ 1, 2},{ 1,-2},{ 0, 4},{ 0, 2},{ 0, 1},{ 0,-0},{ 0,-1},{ 0,-2},{-1, 4},{-1, 2},{-1,-2},{-2, 4},{-2, 2},{-2, 1},{-2,-0},{-2,-1},{-2,-2},{-3, 4},{-3,-0},{-4, 4},{-4, 2},{-4,-0},{-4,-2}}, 340 | {{ 4,-4},{ 3,-4},{ 2,-4},{ 1,-4},{-0,-4},{-1,-4},{-2,-4},{ 4,-3},{-0,-3},{ 4,-2},{ 2,-2},{ 1,-2},{-0,-2},{-1,-2},{-2,-2},{ 4,-1},{ 2,-1},{-2,-1},{ 4,-0},{ 2,-0},{ 1,-0},{-0,-0},{-1,-0},{-2,-0},{ 4, 1},{ 2, 1},{-2, 1},{ 4, 2},{ 2, 2},{ 1, 2},{-0, 2},{-1, 2},{-2, 2},{ 4, 3},{-0, 3},{ 4, 4},{ 2, 4},{-0, 4},{-2, 4}}, 341 | {{-4,-4},{-4,-3},{-4,-2},{-4,-1},{-4, 0},{-4, 1},{-4, 2},{-3,-4},{-3, 0},{-2,-4},{-2,-2},{-2,-1},{-2, 0},{-2, 1},{-2, 2},{-1,-4},{-1,-2},{-1, 2},{-0,-4},{-0,-2},{-0,-1},{-0, 0},{-0, 1},{-0, 2},{ 1,-4},{ 1,-2},{ 1, 2},{ 2,-4},{ 2,-2},{ 2,-1},{ 2, 0},{ 2, 1},{ 2, 2},{ 3,-4},{ 3, 0},{ 4,-4},{ 4,-2},{ 4, 0},{ 4, 2}}, 342 | } 343 | }, 344 | orientations = { 345 | I = 2, 346 | S = 2, 347 | Z = 2, 348 | T = 4, 349 | J = 4, 350 | L = 4, 351 | O = 1, 352 | 353 | -- Pentomino 354 | F5l = 4, 355 | F5r = 4, 356 | I5 = 2, 357 | L5l = 4, 358 | L5r = 4, 359 | N5l = 4, 360 | N5r = 4, 361 | P5l = 4, 362 | P5r = 4, 363 | T5 = 4, 364 | U5 = 4, 365 | V5 = 4, 366 | W5 = 4, 367 | X5 = 1, 368 | Y5l = 4, 369 | Y5r = 4, 370 | Z5l = 2, 371 | Z5r = 2, 372 | 373 | i = 4, 374 | s = 4, 375 | z = 4, 376 | t = 8, 377 | j = 8, 378 | l = 8, 379 | o = 2, 380 | 381 | O1 = 1, 382 | O2 = 1, 383 | O3 = 1, 384 | O4 = 1, 385 | O5 = 1, 386 | O6 = 1, 387 | 388 | h = 11, 389 | 390 | w6 = 4, 391 | test = 1, 392 | 393 | ue = 4, 394 | hara = 4, 395 | }, 396 | -- there are the rotation points from the end orientation, 397 | -- i.e. the rotation point I[1] in the ccw table represents 398 | -- the point relative to the definition of orientation 2 of 399 | -- the I piece assiming it rotated CCW 2->1 with no offset 400 | -- the logic to do the animation essentially does a 401 | -- counter-rotation around this point (adjusted for kicks) 402 | -- coordinates are also doubled for easier definition 403 | rotation_points = { 404 | ccw = { 405 | I = {{ 2, 0}, { 1, 1}}, 406 | S = {{ 0, 0}, { 1, 1}}, 407 | Z = {{ 0, 0}, { 1, 1}}, 408 | J = {{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}}, 409 | L = {{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}}, 410 | T = {{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}}, 411 | O = {{ 1, 1}}, 412 | ue= {{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}}, 413 | hara= {{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}}, 414 | }, 415 | cw = { 416 | I = {{ 1, 1}, { 2, 0}}, 417 | S = {{ 1, 1}, { 0, 0}}, 418 | Z = {{ 1, 1}, { 0, 0}}, 419 | J = {{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}}, 420 | L = {{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}}, 421 | T = {{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}}, 422 | O = {{ 1, 1}}, 423 | ue= {{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}}, 424 | hara= {{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}}, 425 | }, 426 | }, 427 | 428 | colours = { 429 | I = HSVA(270, 0.7, 1), 430 | J = HSVA(180, 0.7, 1), 431 | L = HSVA( 0, 0.7, 1), 432 | T = HSVA(150, 0.7, 1), 433 | S = HSVA( 60, 0.7, 1), 434 | Z = HSVA(240, 0.7, 1), 435 | O = HSVA( 30, 0.7, 1), 436 | 437 | F5l = AvgArrays(HSVA(180, 0.7, 1), HSVA(150, 0.7, 1), HSVA( 60, 0.7, 1)), 438 | F5r = AvgArrays(HSVA( 0, 0.7, 1), HSVA(150, 0.7, 1), HSVA(240, 0.7, 1)), 439 | I5 = AvgArrays(HSVA(270, 0.7, 1), HSVA( 30, 0.7, 1)), 440 | L5l = AvgArrays(HSVA(270, 0.7, 1), HSVA(180, 0.7, 1)), 441 | L5r = AvgArrays(HSVA(270, 0.7, 1), HSVA( 0, 0.7, 1)), 442 | N5l = AvgArrays(HSVA(240, 0.7, 1), HSVA(180, 0.7, 1)), 443 | N5r = AvgArrays(HSVA( 60, 0.7, 1), HSVA( 0, 0.7, 1)), 444 | P5l = AvgArrays(HSVA(180, 0.7, 1), HSVA(150, 0.7, 1), HSVA( 30, 0.7, 1)), 445 | P5r = AvgArrays(HSVA( 0, 0.7, 1), HSVA(150, 0.7, 1), HSVA( 30, 0.7, 1)), 446 | T5 = AvgArrays(HSVA( 0, 0.7, 1), HSVA(180, 0.7, 1), HSVA(150, 0.7, 1)), 447 | U5 = AvgArrays(HSVA(180, 0.7, 1), HSVA( 0, 0.7, 1), HSVA( 0, 0.7, 1)), 448 | V5 = AvgArrays(HSVA(180, 0.7, 1), HSVA( 0, 0.7, 1), HSVA(180, 0.7, 1)), 449 | W5 = AvgArrays(HSVA(240, 0.7, 1), HSVA( 60, 0.7, 1), HSVA( 30, 0.7, 1)), 450 | X5 = AvgArrays(HSVA(150, 0.7, 1), HSVA( 30, 0.7, 1)), 451 | Y5l = AvgArrays(HSVA(270, 0.7, 1), HSVA(150, 0.7, 1), HSVA(180, 0.7, 1)), 452 | Y5r = AvgArrays(HSVA(270, 0.7, 1), HSVA(150, 0.7, 1), HSVA( 0, 0.7, 1)), 453 | Z5l = AvgArrays(HSVA(180, 0.7, 1), HSVA( 30, 0.7, 1)), 454 | Z5r = AvgArrays(HSVA( 0, 0.7, 1), HSVA( 30, 0.7, 1)), 455 | 456 | i = HSVA(270, 0.7, 0.5), 457 | j = HSVA(180, 0.7, 0.5), 458 | l = HSVA( 0, 0.7, 0.5), 459 | t = HSVA(150, 0.7, 0.5), 460 | s = HSVA( 60, 0.7, 0.5), 461 | z = HSVA(240, 0.7, 0.5), 462 | o = HSVA( 30, 0.7, 0.5), 463 | 464 | 465 | O1 = HSVA( 30, 0.7, 1), 466 | O2 = HSVA( 30, 0.7, 1), 467 | O3 = HSVA( 30, 0.7, 1), 468 | O4 = HSVA( 30, 0.7, 1), 469 | O5 = HSVA( 30, 0.7, 1), 470 | O6 = HSVA( 30, 0.7, 1), 471 | 472 | h = {205/255,155/255,255/255,1}, 473 | 474 | w6 = AvgArrays(HSVA(240, 0.7, 1), HSVA( 60, 0.7, 1), HSVA( 30, 0.7, 1)), 475 | test = {1,1,1,1}, 476 | 477 | ue = {0.7, 0.7, 0.7, 1}, 478 | hara = {0.7, 0.5, 0.3, 1}, 479 | }, 480 | kicktables = { 481 | I = "I", 482 | J = "TLJ", 483 | L = "TLJ", 484 | T = "TLJ", 485 | S = "SZ", 486 | Z = "SZ", 487 | O = "O", 488 | 489 | F5l = "default", 490 | F5r = "default", 491 | I5 = "default", 492 | L5l = "default", 493 | L5r = "default", 494 | N5l = "default", 495 | N5r = "default", 496 | P5l = "default", 497 | P5r = "default", 498 | T5 = "default", 499 | U5 = "default", 500 | V5 = "default", 501 | W5 = "default", 502 | X5 = "default", 503 | Y5l = "default", 504 | Y5r = "default", 505 | Z5l = "default", 506 | Z5r = "default", 507 | 508 | i = "i45", 509 | j = "default", 510 | l = "default", 511 | t = "default", 512 | s = "default", 513 | z = "default", 514 | o = "SZ", 515 | 516 | 517 | O1 = "O", 518 | O2 = "O", 519 | O3 = "O", 520 | O4 = "O", 521 | O5 = "O", 522 | O6 = "O", 523 | 524 | h = "h", 525 | 526 | w6 = "default", 527 | test = "O", 528 | ue = "default", 529 | hara = "default", 530 | }, 531 | kicks_ccw = { 532 | I = { 533 | -- flat -> standing 534 | {{-1, 0}, {-1,-1}, {-2, 0}, {-2,-1}, {-1, 1}, {-2, 1}, {-1,-2}, {-2,-2}, { 0, 0}, { 0,-1}, { 1, 0}, { 1,-1}, { 0, 1}, { 1, 1}, { 0,-2}, { 1,-2}}, 535 | -- standing -> flat 536 | {{ 0, 0}, { 0,-1}, {-1, 0}, {-1,-1}, { 0, 1}, {-1, 1}, { 0, 2}, {-1, 2}, { 1, 0}, { 1,-1}, { 2, 0}, { 2,-1}, { 1, 1}, { 2, 1}, { 1, 2}, { 2, 2}}, 537 | }, 538 | 539 | TLJ = { 540 | -- U -> L (right) 541 | {{ 0, 0}, { 0,-1}, { 1, 0}, { 1,-1}, {-1, 0}, {-1,-1}, { 0, 1}, { 1, 1}, {-1, 1}, { 0,-2}, { 0, 2}}, 542 | -- R -> U (right) 543 | {{ 0, 0}, { 0,-1}, { 1, 0}, { 1,-1}, {-1, 0}, {-1,-1}, { 0, 1}, { 1, 1}, {-1, 1}, { 0,-2}, { 0, 2}}, 544 | -- D -> R (left) 545 | {{ 0, 0}, { 0,-1}, {-1, 0}, {-1,-1}, { 1, 0}, { 1,-1}, { 0, 1}, {-1, 1}, { 1, 1}, { 0,-2}, { 0, 2}}, 546 | -- L -> D (left) 547 | {{ 0, 0}, { 0,-1}, {-1, 0}, {-1,-1}, { 1, 0}, { 1,-1}, { 0, 1}, {-1, 1}, { 1, 1}, { 0,-2}, { 0, 2}}, 548 | }, 549 | 550 | SZ = { 551 | -- H -> V (left with offset) 552 | {{-1, 0}, {-1,-1}, {-2, 0}, {-2,-1}, {-1, 1}, {-2, 1}, { 0, 0}, { 0,-1}, { 0, 1}, {-1,-2}, {-1, 2}}, 553 | -- V -> H (left no offset) 554 | {{ 0, 0}, { 0,-1}, {-1, 0}, {-1,-1}, { 0, 1}, {-1, 1}, { 1, 0}, { 1,-1}, { 1, 1}, { 0,-2}, { 0, 2}}, 555 | }, 556 | 557 | O = { 558 | -- lol 559 | {{ 0, 0}, { 0, 1}, { 0, 2}} 560 | }, 561 | 562 | default = { 563 | -- all 564 | {{ 0, 0}, { 0,-1}, {-1, 0}, {-1,-1}, { 1, 0}, { 1,-1}, { 0, 1}, {-1, 1}, { 1, 1}, { 0,-2}, { 0, 2}}, 565 | }, 566 | 567 | -- special cases 568 | i45 = { 569 | -- _ -> / 570 | {{ 0, 0}, {-1,-1}, {-1, 0}, {-2,-1}, { 1, 1}, { 0, 1}, {-2,-2}, {-3,-2}, { 1, 0}, { 0,-1}, { 2, 0}, { 1,-1}, { 2, 1}, { 3, 1}, {-1,-2}, { 0,-2}}, 571 | -- \ -> _ 572 | {{ 0, 0}, { 1,-1}, {-1, 0}, { 0,-1}, {-1, 1}, {-2, 1}, {-2, 2}, {-3, 2}, { 1, 0}, { 2,-1}, { 2, 0}, { 3,-1}, { 0, 1}, { 1, 1}, {-1, 2}, { 0, 2}}, 573 | -- | -> \ 574 | {{ 0, 0}, { 0,-1}, {-1, 1}, {-1, 0}, { 0, 1}, {-1, 2}, { 0, 2}, {-1, 3}, { 1,-1}, { 1,-2}, { 2,-2}, { 2,-3}, { 1, 0}, { 2,-1}, { 1, 1}, { 2, 0}}, 575 | -- / -> | 576 | {{-1, 0}, {-1,-1}, {-2,-1}, {-2,-2}, {-1,-2}, {-2,-3}, {-1, 1}, {-2, 0}, { 0, 1}, { 0, 0}, { 1, 2}, { 1, 1}, { 0,-1}, { 1, 0}, { 0, 2}, { 1, 3}}, 577 | }, 578 | h = {{ 0, 0}, {-1, 0}}, 579 | 580 | 581 | --[[ 582 | flat_I = {{-1, 0}, {-1,-1}, {-2, 0}, {-2,-1}, {-1, 1}, {-2, 1}, {-1,-2}, {-2,-2}, { 0, 0}, { 0,-1}, { 1, 0}, { 1,-1}, { 0, 1}, { 1, 1}, { 0,-2}, { 1,-2}}, 583 | standing_I = {{ 0, 0}, { 0,-1}, {-1, 0}, {-1,-1}, { 0, 1}, {-1, 1}, { 0, 2}, {-1, 2}, { 1, 0}, { 1,-1}, { 2, 0}, { 2,-1}, { 1, 1}, { 2, 1}, { 1, 2}, { 2, 2}}, 584 | 585 | -- not_I = {{ 0, 0}, { 0,-1}, {-1, 0}, { 1, 0}, {-1,-1}, { 1,-1}, { 0, 1}, {-1, 1}, { 1, 1}, { 0,-2}, { 0, 2}} 586 | default = {{ 0, 0}, { 0,-1}, { 1, 0}, {-1, 0}, { 1,-1}, {-1,-1}, { 0, 1}, { 1, 1}, {-1, 1}, { 0,-2}, { 0, 2}}, 587 | 588 | o45_diamond = {{-1, 0}, {-1,-1}, { 0, 0}, {-2, 0}, { 0,-1}, {-2,-1}, {-1, 1}, { 0, 1}, {-2, 1}, {-1,-2}, {-1, 2}}, 589 | o45_square = {{ 0, 0}, { 0,-1}, { 1, 0}, {-1, 0}, { 1,-1}, {-1,-1}, { 0, 1}, { 1, 1}, {-1, 1}, { 0,-2}, { 0, 2}}, 590 | 591 | i45_1 = {{ 0, 0}, {-1,-1}, {-1, 0}, {-2,-1}, { 1, 1}, { 0, 1}, {-2,-2}, {-3,-2}, { 1, 0}, { 0,-1}, { 2, 0}, { 1,-1}, { 2, 1}, { 3, 1}, {-1,-2}, { 0,-2}}, 592 | i45_2 = {{ 0, 0}, { 1,-1}, {-1, 0}, { 0,-1}, {-1, 1}, {-2, 1}, {-2, 2}, {-3, 2}, { 1, 0}, { 2,-1}, { 2, 0}, { 3,-1}, { 0, 1}, { 1, 1}, {-1, 2}, { 0, 2}}, 593 | i45_3 = {{ 0, 0}, { 0,-1}, {-1, 1}, {-1, 0}, { 0, 1}, {-1, 2}, { 0, 2}, {-1, 3}, { 1,-1}, { 1,-2}, { 2,-2}, { 2,-3}, { 1, 0}, { 2,-1}, { 1, 1}, { 2, 0}}, 594 | i45_4 = {{-1, 0}, {-1,-1}, {-2,-1}, {-2,-2}, {-1,-2}, {-2,-3}, {-1, 1}, {-2, 0}, { 0, 1}, { 0, 0}, { 1, 2}, { 1, 1}, { 0,-1}, { 1, 0}, { 0, 2}, { 1, 3}}, 595 | h = {{ 0, 0}, {-1, 0}}, 596 | --]] 597 | }, 598 | kicks_cw = { 599 | I = { 600 | -- flat -> standing 601 | {{ 0, 0}, { 0,-1}, { 1, 0}, { 1,-1}, { 0, 1}, { 1, 1}, { 0,-2}, { 1,-2}, {-1, 0}, {-1,-1}, {-2, 0}, {-2,-1}, {-1, 1}, {-2, 1}, {-1,-2}, {-2,-2}}, 602 | -- standing -> flat 603 | {{ 1, 0}, { 1,-1}, { 2, 0}, { 2,-1}, { 1, 1}, { 2, 1}, { 1, 2}, { 2, 2}, { 0, 0}, { 0,-1}, {-1, 0}, {-1,-1}, { 0, 1}, {-1, 1}, { 0, 2}, {-1, 2}}, 604 | }, 605 | 606 | TLJ = { 607 | -- U -> R (left) 608 | {{ 0, 0}, { 0,-1}, {-1, 0}, {-1,-1}, { 1, 0}, { 1,-1}, { 0, 1}, {-1, 1}, { 1, 1}, { 0,-2}, { 0, 2}}, 609 | -- R -> D (right) 610 | {{ 0, 0}, { 0,-1}, { 1, 0}, { 1,-1}, {-1, 0}, {-1,-1}, { 0, 1}, { 1, 1}, {-1, 1}, { 0,-2}, { 0, 2}}, 611 | -- D -> L (right) 612 | {{ 0, 0}, { 0,-1}, { 1, 0}, { 1,-1}, {-1, 0}, {-1,-1}, { 0, 1}, { 1, 1}, {-1, 1}, { 0,-2}, { 0, 2}}, 613 | -- L -> U (left) 614 | {{ 0, 0}, { 0,-1}, {-1, 0}, {-1,-1}, { 1, 0}, { 1,-1}, { 0, 1}, {-1, 1}, { 1, 1}, { 0,-2}, { 0, 2}}, 615 | }, 616 | 617 | SZ = { 618 | -- H -> V (right no offset) 619 | {{ 0, 0}, { 0,-1}, { 1, 0}, { 1,-1}, { 0, 1}, { 1, 1}, {-1, 0}, {-1,-1}, {-1, 1}, { 0,-2}, { 0, 2}}, 620 | -- V -> H (right with offset) 621 | {{ 1, 0}, { 1,-1}, { 2, 0}, { 2,-1}, { 1, 1}, { 2, 1}, { 0, 0}, { 0,-1}, { 0, 1}, { 1,-2}, { 1, 2}}, 622 | }, 623 | 624 | O = { 625 | -- lol 626 | {{ 0, 0}, { 0, 1}, { 0, 2}} 627 | }, 628 | 629 | default = { 630 | -- all 631 | {{ 0, 0}, { 0,-1}, { 1, 0}, { 1,-1}, {-1, 0}, {-1,-1}, { 0, 1}, { 1, 1}, {-1, 1}, { 0,-2}, { 0, 2}} 632 | }, 633 | 634 | -- special cases 635 | i45 = { 636 | -- _ -> \ 637 | {{ 0, 0}, {-1,-1}, {-1, 0}, {-2,-1}, { 1, 1}, { 0, 1}, {-2,-2}, {-3,-2}, { 1, 0}, { 0,-1}, { 2, 0}, { 1,-1}, { 2, 1}, { 3, 1}, {-1,-2}, { 0,-2}}, 638 | -- \ -> | 639 | {{ 0, 0}, { 1,-1}, {-1, 0}, { 0,-1}, {-1, 1}, {-2, 1}, {-2, 2}, {-3, 2}, { 1, 0}, { 2,-1}, { 2, 0}, { 3,-1}, { 0, 1}, { 1, 1}, {-1, 2}, { 0, 2}}, 640 | -- | -> / 641 | {{ 0, 0}, { 0,-1}, {-1, 1}, {-1, 0}, { 0, 1}, {-1, 2}, { 0, 2}, {-1, 3}, { 1,-1}, { 1,-2}, { 2,-2}, { 2,-3}, { 1, 0}, { 2,-1}, { 1, 1}, { 2, 0}}, 642 | -- / -> _ 643 | {{-1, 0}, {-1,-1}, {-2,-1}, {-2,-2}, {-1,-2}, {-2,-3}, {-1, 1}, {-2, 0}, { 0, 1}, { 0, 0}, { 1, 2}, { 1, 1}, { 0,-1}, { 1, 0}, { 0, 2}, { 1, 3}}, 644 | }, 645 | h = { 646 | -- all 647 | {{ 0, 0}, {-1, 0}} 648 | }, 649 | 650 | --[[ 651 | flat_I = {{ 0, 0}, { 0,-1}, { 1, 0}, { 1,-1}, { 0, 1}, { 1, 1}, { 0,-2}, { 1,-2}, {-1, 0}, {-1,-1}, {-2, 0}, {-2,-1}, {-1, 1}, {-2, 1}, {-1,-2}, {-2,-2}}, 652 | standing_I = {{ 1, 0}, { 1,-1}, { 2, 0}, { 2,-1}, { 1, 1}, { 2, 1}, { 1, 2}, { 2, 2}, { 0, 0}, { 0,-1}, {-1, 0}, {-1,-1}, { 0, 1}, {-1, 1}, { 0, 2}, {-1, 2}}, 653 | 654 | -- not_I = {{ 0, 0}, { 0,-1}, { 1, 0}, {-1, 0}, { 1,-1}, {-1,-1}, { 0, 1}, { 1, 1}, {-1, 1}, { 0,-2}, { 0, 2}} 655 | default = {{ 0, 0}, { 0,-1}, {-1, 0}, { 1, 0}, {-1,-1}, { 1,-1}, { 0, 1}, {-1, 1}, { 1, 1}, { 0,-2}, { 0, 2}}, 656 | 657 | o45_diamond = {{ 0, 0}, { 0,-1}, { 1, 0}, {-1, 0}, { 1,-1}, {-1,-1}, { 0, 1}, { 1, 1}, {-1, 1}, { 0,-2}, { 0, 2}}, 658 | o45_square = {{ 1, 0}, { 1,-1}, { 2, 0}, { 0, 0}, { 2,-1}, { 0,-1}, { 1, 1}, { 2, 1}, { 0, 1}, { 1,-2}, { 1, 2}}, 659 | 660 | i45_1 = {{ 0, 0}, { 1,-1}, { 1, 0}, { 2,-1}, {-1, 1}, { 0, 1}, { 2,-2}, { 3,-2}, {-1, 0}, { 0,-1}, {-2, 0}, {-1,-1}, {-2, 1}, {-3, 1}, { 1,-2}, { 0,-2}}, 661 | i45_2 = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1,-2}, { 0,-2}, { 1,-3}, { 0, 1}, { 1, 0}, {-1, 1}, {-1, 0}, {-2, 2}, {-2, 1}, {-1,-1}, {-2, 0}, {-1, 2}, {-2, 3}}, 662 | i45_3 = {{ 1, 0}, { 1,-1}, { 2, 1}, { 2, 0}, { 1, 1}, { 2, 2}, { 1, 2}, { 2, 3}, { 0,-1}, { 0,-2}, {-1,-2}, {-1,-3}, { 0, 0}, {-1,-1}, { 0, 1}, {-1, 0}}, 663 | i45_4 = {{ 0, 0}, {-1,-1}, { 1, 0}, { 0,-1}, { 1, 1}, { 2, 1}, { 2, 2}, { 3, 2}, {-1, 0}, {-2,-1}, {-2, 0}, {-3,-1}, { 0, 1}, {-1, 1}, { 1, 2}, { 0, 2}}, 664 | h = {{ 0, 0}, {-1, 0}}, 665 | --]] 666 | }, 667 | 668 | pentomino_pool = { 669 | I = {"L5l", "L5r", "Y5l", "Y5r"}, 670 | J = {"F5l", "N5l", "P5l", "T5", "U5", "V5", "Y5l", "Z5l"}, 671 | L = {"F5r", "N5r", "P5r", "T5", "U5", "V5", "Y5r", "Z5r"}, 672 | T = {"F5l", "F5r", "P5l", "P5r", "T5", "X5", "Y5l", "Y5r"}, 673 | S = {"F5l", "N5r", "P5r", "W5"}, 674 | Z = {"F5r", "N5l", "P5l", "W5"}, 675 | O = {"P5l", "P5r"}, 676 | }, 677 | 678 | rotateCW = function(piece, board) 679 | local x, y, id, rot = piece.x, piece.y, piece.id, piece.orientation 680 | local newrot = (rot) % piece.max_orientations + 1 681 | --[[ 682 | local kickid 683 | if id == "I" then kickid = rot % 2 == 1 and "flat_I" or "standing_I" 684 | elseif id == "o" then kickid = rot % 2 == 1 and "o45_diamond" or "o45_square" 685 | elseif id == "i" then kickid = "i45_"..rot 686 | elseif id == "h" then kickid = "h" 687 | else kickid = "default" 688 | end 689 | local kicktable = Piece["kicks_cw"][kickid] 690 | --]] 691 | local kickid = Piece.kicktables[id] 692 | local kicktable = Piece.kicks_cw[kickid] 693 | if #kicktable > 1 then kicktable = kicktable[rot] else kicktable = kicktable[1] end 694 | piece.orientation = newrot 695 | for n, kick in ipairs(kicktable) do 696 | piece.x = x + kick[1] 697 | piece.y = y + kick[2] 698 | if not board:check_collision_with(piece) then 699 | -- print(id, kickid, "CW", kick[1], kick[2], n) 700 | return kick 701 | end 702 | end 703 | 704 | piece.x = x 705 | piece.y = y 706 | piece.orientation = rot 707 | return nil 708 | end, 709 | 710 | rotateCCW = function(piece, board) 711 | local x, y, id, rot = piece.x, piece.y, piece.id, piece.orientation 712 | if id == "h" then return piece:rotateCW(board) end 713 | local newrot = (rot-2) % piece.max_orientations + 1 714 | --[[ 715 | local kickid 716 | if id == "I" then kickid = rot % 2 == 1 and "flat_I" or "standing_I" 717 | elseif id == "o" then kickid = rot % 2 == 1 and "o45_diamond" or "o45_square" 718 | elseif id == "i" then kickid = "i45_"..rot 719 | else kickid = "default" 720 | end 721 | local kicktable = Piece["kicks_ccw"][kickid] 722 | --]] 723 | local kickid = Piece.kicktables[id] 724 | local kicktable = Piece.kicks_ccw[kickid] 725 | if #kicktable > 1 then kicktable = kicktable[rot] else kicktable = kicktable[1] end 726 | piece.orientation = newrot 727 | for n, kick in ipairs(kicktable) do 728 | piece.x = x + kick[1] 729 | piece.y = y + kick[2] 730 | if not board:check_collision_with(piece) then 731 | -- print(id, kickid, "CCW", kick[1], kick[2], n) 732 | return kick 733 | end 734 | end 735 | 736 | piece.x = x 737 | piece.y = y 738 | piece.orientation = rot 739 | return nil 740 | end, 741 | 742 | get_rotate_point = function(piece, dir, offset) 743 | local pts = Piece.rotation_points[dir][piece.id] 744 | if not pts then return nil end 745 | local x, y = unpack(pts[piece.orientation]) 746 | local i, j = unpack(offset) 747 | local u, v 748 | if dir == "cw" then 749 | u = -i + j 750 | v = -i - j 751 | else 752 | u = -i - j 753 | v = i - j 754 | end 755 | return {0.5*(x+u), 0.5*(y+v)} 756 | end, 757 | 758 | new = function(id) 759 | if id then 760 | return { 761 | blocks = Deepcopy(Piece.definitions[id]), 762 | id = id, 763 | parent = Piece.parents[id], 764 | colour = Piece.colours[id], 765 | rotateCW = Piece.rotateCW, 766 | rotateCCW = Piece.rotateCCW, 767 | get_rotate_point = Piece.get_rotate_point, 768 | x = 5, 769 | y = 21, 770 | orientation = 1, -- 1 = neutral, 2 = CW, 3 = upside down, 4 = CCW 771 | max_orientations = Piece.orientations[id] 772 | } 773 | end 774 | -- no id: dummy piece to be filled later by code 775 | return { 776 | blocks = {{}}, 777 | id = nil, 778 | parent = nil, 779 | colour = nil, 780 | rotateCW = Piece.rotateCW, 781 | rotateCCW = Piece.rotateCCW, 782 | x = 5, 783 | y = 21, 784 | orientation = 1, -- 1 = neutral, 2 = CW, 3 = upside down, 4 = CCW 785 | max_orientations = nil 786 | } 787 | end, 788 | } 789 | 790 | 791 | --[==[ kick table image 792 | local width, height = 4000, 3000 793 | local outcanvas = love.graphics.newCanvas(width,height) 794 | love.graphics.setCanvas(outcanvas) 795 | love.graphics.clear(1,1,1,1) 796 | love.graphics.push() 797 | do 798 | love.graphics.translate(width/2,0) 799 | love.graphics.scale(16, -16) 800 | local block_mesh = love.graphics.newMesh({ 801 | {-1/2, -1/2, 0, 0, 1.0,1.0,1.0, 1}, 802 | { 1/2, -1/2, 1, 0, 0.9,0.9,0.9, 1}, 803 | { 1/2, 1/2, 1, 1, 0.7,0.7,0.7, 1}, 804 | {-1/2, 1/2, 0, 1, 0.8,0.8,0.8, 1}, 805 | }, "fan", "static") 806 | local y = 0 807 | local pieces = {"T", "L", "J", "S", "Z", "I"} 808 | for k, id in ipairs(pieces) do 809 | y = y + 7 810 | local def = Piece.definitions[id] 811 | local R, G, B, A = unpack(Piece.colours[id]) 812 | local rotmax = Piece.orientations[id] 813 | for rot = 1, rotmax do 814 | y = y + 7 815 | local kickl = Piece.kicks_ccw[Piece.kicktables[id]][rot] 816 | local kickr = Piece.kicks_cw [Piece.kicktables[id]][rot] 817 | for _, bl in ipairs(def[rot]) do 818 | love.graphics.setColor(R, G, B, A) 819 | local i, j = unpack(bl) 820 | love.graphics.draw(block_mesh, i, j-y) 821 | love.graphics.setColor(0.5, 0.5, 0.5) 822 | for n = 1, #kickr do 823 | love.graphics.draw(block_mesh, i+7*n, j-y) 824 | end 825 | for n = 1, #kickl do 826 | love.graphics.draw(block_mesh, i-7*n, j-y) 827 | end 828 | end 829 | love.graphics.setColor(R*0.7, G*0.7, B*0.7, A) 830 | love.graphics.setLineWidth(1/8) 831 | for _, bl in ipairs(def[rot%rotmax + 1]) do 832 | local i, j = unpack(bl) 833 | for n, k in ipairs(kickr) do 834 | local i2, j2 = i+k[1]+n*7, j+k[2]-y 835 | love.graphics.rectangle("line", i2-7/16, j2-7/16, 7/8, 7/8) 836 | love.graphics.line(i2-5/16, j2+5/16, i2+5/16, j2-5/16) 837 | end 838 | end 839 | for _, bl in ipairs(def[(rot-2)%rotmax + 1]) do 840 | local i, j = unpack(bl) 841 | for n, k in ipairs(kickl) do 842 | local i2, j2 = i+k[1]-n*7, j+k[2]-y 843 | love.graphics.rectangle("line", i2-7/16, j2-7/16, 7/8, 7/8) 844 | love.graphics.line(i2-5/16, j2+5/16, i2+5/16, j2-5/16) 845 | end 846 | end 847 | 848 | end 849 | end 850 | end 851 | love.graphics.pop() 852 | love.graphics.setCanvas() 853 | outcanvas:newImageData():encode("png", "kicktable.png") 854 | outcanvas:release() 855 | -- ]==] -------------------------------------------------------------------------------- /shaders/bg1.glsl: -------------------------------------------------------------------------------- 1 | uniform float dt; 2 | uniform float time; 3 | uniform int level; 4 | #define RANGE 2 5 | #define SCALE 2 6 | 7 | const vec4 BG0X = vec4(.2, .0, .5, 1.); 8 | const vec4 BG1X = vec4(.5, .0, .2, 1.); 9 | const vec4 BGXX = vec4(.5, .0, .0, 1.); 10 | const vec4 BGPb = vec4(.3, .3, .4, 1.); 11 | const vec4 BGSn = vec4(.2, .2, .2, 1.); 12 | const vec4 BGFe = vec4(.3, .3, .3, 1.); 13 | const vec4 BGCu = vec4(.5, .2, .0, 1.); 14 | const vec4 BGAg = vec4(.5, .5, .5, 1.); 15 | const vec4 BGAu = vec4(.4, .3, .1, 1.); 16 | 17 | const vec4 cloudsXX = vec4(2., 0., 2., 1.); 18 | const vec4 cloudsPb = vec4(2., 2., 0., 1.); 19 | const vec4 cloudsSn = vec4(0., 0., 0., 1.); 20 | const vec4 cloudsFe = vec4(2., .4, 0., 1.); 21 | const vec4 cloudsCu = vec4(-1.,2., 2., 1.); 22 | const vec4 cloudsAg = vec4(1.5,1.5,2., 1.); 23 | const vec4 cloudsAu = vec4(2., 2., 0., 1.); 24 | const vec4 cloudsIn = vec4(2., 2., 2., 1.); 25 | 26 | // from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl // 27 | const vec4 K = vec4(1., 2. / 3., 1. / 3., 3.); 28 | vec3 hsv2rgb(vec3 c) { 29 | vec3 p = abs(fract(c.xxx + K.xyz) * 6. - K.www); 30 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0., 1.), c.y); 31 | } 32 | 33 | //from https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 // 34 | float rand(vec2 n) { 35 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 36 | } 37 | 38 | float noise(vec2 p){ 39 | vec2 ip = floor(p); 40 | vec2 u = fract(p); 41 | u = u*u*(3.0-2.0*u); 42 | 43 | float res = mix( 44 | mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), 45 | mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); 46 | return res*res; 47 | } 48 | /////////////////////////////////////////////////////////////////////////// 49 | 50 | vec4 getBGColor() { 51 | if (level <= 10) return BG0X; 52 | if (level <= 20) return BG1X; 53 | if (level <= 30) return BGXX; 54 | if (level == 31) return BGPb; 55 | if (level == 32) return BGSn; 56 | if (level == 33) return BGFe; 57 | if (level == 34) return BGCu; 58 | if (level == 35) return BGAg; 59 | if (level == 36) return BGAu; 60 | return vec4(hsv2rgb(vec3(time*.0666, .7, .4)), 1.); 61 | } 62 | 63 | vec4 getCloudColor() { 64 | if (level <= 30) return cloudsXX; 65 | if (level == 31) return cloudsPb; 66 | if (level == 32) return cloudsSn; 67 | if (level == 33) return cloudsFe; 68 | if (level == 34) return cloudsCu; 69 | if (level == 35) return cloudsAg; 70 | if (level == 36) return cloudsAu; 71 | return cloudsIn; 72 | } 73 | 74 | #ifdef PIXEL 75 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 76 | // shitty box blur previous frame 77 | vec4 oldpixel = vec4(0.); 78 | for (int i = -RANGE; i <= RANGE; i++) { 79 | for (int j = -RANGE; j <= RANGE; j++) { 80 | oldpixel += Texel(image, uvs+SCALE*vec2(i, j)/love_ScreenSize.xy); 81 | } 82 | } 83 | oldpixel /= 4*RANGE*(RANGE+1)+1; 84 | 85 | // background colour 86 | vec4 newpixel = getBGColor(); 87 | vec2 uv2 = uvs - .5; 88 | uv2.x *= love_ScreenSize.x/love_ScreenSize.y; 89 | if (uvs.y > .5) { 90 | // ground: grid thing 91 | newpixel += uv2.y*max(smoothstep(.1, .0, abs(fract(uv2.x/uv2.y)-.5)), pow(fract(time-1/uv2.y), 4))*0.6; 92 | } else { 93 | // sky: noise clouds 94 | uv2.y *= -1; 95 | newpixel += getCloudColor()*uv2.y*noise(25.*vec2(uv2.x/uv2.y, time-1./uv2.y)); 96 | } 97 | 98 | return color * mix(oldpixel, newpixel, 1-pow(64, -dt)); 99 | // return newpixel; 100 | } 101 | #endif 102 | 103 | #ifdef VERTEX 104 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 105 | return transform_projection * vertex_position; 106 | } 107 | #endif 108 | -------------------------------------------------------------------------------- /shaders/bg10.glsl: -------------------------------------------------------------------------------- 1 | uniform float dt; 2 | uniform float time; 3 | uniform int level; 4 | const vec4 BG0 = vec4(.1, .1, .3, 1.); 5 | const vec4 BG1 = vec4(.2, .1, .3, 1.); 6 | const vec4 BG2 = vec4(.3, .0, .0, 1.); 7 | const vec4 BG3 = vec4(.0, .0, .0, 1.); 8 | const vec4 CLOUD0 = vec4(0.5, 1.5, 1.5, 0.0); 9 | const vec4 CLOUD1 = vec4(1.5, 0.5, 1.5, 0.0); 10 | const vec4 CLOUD2 = vec4(2.0, 0.5, 0.5, 0.0); 11 | const vec4 CLOUD3 = vec4(0.5, 0.5, 0.5, 0.0); 12 | 13 | // from https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 // 14 | float rand(vec2 n) { 15 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 16 | } 17 | 18 | float noise(vec2 p){ 19 | vec2 ip = floor(p); 20 | vec2 u = fract(p); 21 | u = u*u*(3.0-2.0*u); 22 | 23 | float res = mix( 24 | mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), 25 | mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); 26 | return res*res; 27 | } 28 | //////////////////////////////////////////////////////////////////////////// 29 | 30 | // adapted from https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment // 31 | float line_distance(vec2 v, vec2 w, vec2 p) { 32 | // Return minimum distance between line segment vw and point p 33 | vec2 vw = w - v; 34 | float l2 = dot(vw, vw); // i.e. |w-v|^2 - avoid a sqrt 35 | if (l2 == 0.0) return distance(p, v); // v == w case 36 | // Consider the line extending the segment, parameterized as v + t (w - v). 37 | // We find projection of point p onto the line. 38 | // It falls where t = [(p-v) . (w-v)] / |w-v|^2 39 | // We clamp t from [0,1] to handle points outside the segment vw. 40 | float t = max(0, min(1, dot(p - v, vw) / l2)); 41 | vec2 projection = v + t * vw; // Projection falls on the segment 42 | return distance(p, projection); 43 | } 44 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 45 | 46 | vec4 getBGColor() { 47 | if (level < 10) return mix(BG0, BG1, smoothstep(01., 10., float(level))); 48 | if (level < 20) return mix(BG1, BG2, smoothstep(11., 20., float(level))); 49 | if (level < 30) return mix(BG2, BG3, smoothstep(21., 30., float(level))); 50 | return BG3; 51 | } 52 | 53 | vec4 getCloudColor() { 54 | if (level < 10) return mix(CLOUD0, CLOUD1, smoothstep(01., 10., float(level))); 55 | if (level < 20) return mix(CLOUD1, CLOUD2, smoothstep(11., 20., float(level))); 56 | if (level < 30) return mix(CLOUD2, CLOUD3, smoothstep(21., 30., float(level))); 57 | return CLOUD3; 58 | } 59 | 60 | float rand1(float s){ 61 | return fract(sin(s)*100000.); 62 | } 63 | 64 | #ifdef PIXEL 65 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 66 | // previous frame 67 | vec4 oldpixel = Texel(image, (uvs-.5)*pow(1.2, dt)+.5); 68 | 69 | // background colour 70 | vec4 newpixel = getBGColor(); 71 | // noise clouds 72 | float angle = .5*cos(time*.1); 73 | float c = cos(angle); 74 | float s = sin(angle); 75 | vec2 uv2 = mat2(c, -s, s, c)*(uvs - .5); 76 | uv2.y = abs(uv2.y); 77 | uv2.x *= love_ScreenSize.x/love_ScreenSize.y; 78 | vec2 skypoint = vec2(uv2.x/uv2.y, 10.*time-1./uv2.y); 79 | // lightning 80 | float lightningintensity = 1-fract(time*.4); 81 | float lightningcount = time*.4 + lightningintensity; 82 | vec2 lightningabs = vec2(rand1(lightningcount)-.5, rand1(lightningcount+rand1(lightningcount))*.2+.1); 83 | lightningabs.x = sign(lightningabs.x)*sqrt(abs(lightningabs.x)); 84 | vec2 lightningsky = vec2(lightningabs.x/lightningabs.y,10.*time-1./lightningabs.y); 85 | float d = line_distance(lightningabs, vec2(lightningabs.x, 0.), uv2)*20.; 86 | 87 | newpixel += getCloudColor()*uv2.y*noise(10.*skypoint)*(1+lightningintensity*3./distance(skypoint, lightningsky)); 88 | newpixel += vec4(1.,1.,1.,0.)*lightningintensity/(1+d*d); 89 | 90 | 91 | return color * mix(oldpixel, newpixel, 1-pow(64, -dt)); 92 | // return newpixel; 93 | } 94 | #endif 95 | 96 | #ifdef VERTEX 97 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 98 | return transform_projection * vertex_position; 99 | } 100 | #endif 101 | -------------------------------------------------------------------------------- /shaders/bgbeginner.glsl: -------------------------------------------------------------------------------- 1 | uniform float dt; 2 | uniform float time; 3 | uniform int level; 4 | uniform int levelprev; 5 | uniform float leveltime; 6 | #define PI 3.141592653589793238463 7 | #define TAU 6.283185307179586476925 8 | #define SCREENHEIGHT .60 9 | #define SEALEVEL 20. 10 | #define SEAREFRACTION 1.4 11 | #define SHAPETHICNESS 15. 12 | #define SHAPEGLOWNESS 20. 13 | #define SHAPESIZENESS 250. 14 | #define TRANSITIONLEN 3. 15 | #define STARSDENSITY 35. 16 | const vec3 SHAPEPOS = vec3(0., -200., 1000); 17 | const vec3 LIGHTDIR = vec3(0.,0.,1.); 18 | const vec2 DVEC = vec2(.00, .01); 19 | // colours 20 | const vec4 sky0 = vec4(.4, .6, .7, 1.); 21 | const vec4 sky1 = vec4(.1, .2, .3, 1.); 22 | const vec4 light0 = vec4(.9, .9, .9, 1.); 23 | const vec4 light1 = vec4(.9, .4, .0, 1.); 24 | const vec4 water1 = vec4(.0, .0, .0, 1.); 25 | const vec4 water2 = vec4(.7, .9, .7, 1.); 26 | const vec4 shape = vec4(.9, .6, .7, 1.); 27 | 28 | vec4 getSky(float lv) { 29 | return mix(sky0, sky1, min(1., lv)); 30 | } 31 | 32 | vec4 getLight(float lv) { 33 | return mix(light0, light1, min(1., lv)); 34 | } 35 | 36 | vec3 getLightdir(float lv) { 37 | float a = min(1., lv)*PI*.5; 38 | return vec3(0., sin(a), cos(a)); 39 | } 40 | 41 | float distanceLineToSegment(vec3 lineOrigin, vec3 lineDir, vec3 segA, vec3 segB) { 42 | float distanceA = distance(dot(segA-lineOrigin, lineDir)*lineDir + lineOrigin, segA); 43 | float distanceB = distance(dot(segB-lineOrigin, lineDir)*lineDir + lineOrigin, segB); 44 | 45 | // get the line perpendicular to both the line and the segment since that's where the shortest distance is 46 | vec3 segDir = segB - segA; 47 | vec3 shortDir = normalize(cross(lineDir, segDir)); 48 | 49 | // parallel lines case 50 | if (dot(shortDir, shortDir) == 0.) 51 | return distanceA; 52 | 53 | // get distance from line to line 54 | float shortest = abs(dot(shortDir, segA - lineOrigin)); 55 | // project on the line perpendicular to the original line and the shortest line 56 | // this allows us to see if A and B are on opposite sides compared to the closest segment point 57 | vec3 p = cross(shortDir, lineDir); 58 | if (dot(lineOrigin - segA, p) * dot(lineOrigin - segB, p) < 0) 59 | // A and B are either side of the closest point meaning the closest point is part of the segment 60 | return shortest; 61 | // A and B are on the same side so whichever is the closest is the distance we're looking for 62 | return min(distanceA, distanceB); 63 | } 64 | 65 | //from https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 // 66 | float rand(vec2 n) { 67 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 68 | } 69 | 70 | float noise(vec2 p){ 71 | vec2 ip = floor(p); 72 | vec2 u = fract(p); 73 | u = u*u*(3.0-2.0*u); 74 | 75 | float res = mix( 76 | mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), 77 | mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); 78 | return res*res; 79 | } 80 | 81 | float mod289(float x){return x - floor(x * (1.0 / 289.0)) * 289.0;} 82 | vec4 mod289(vec4 x){return x - floor(x * (1.0 / 289.0)) * 289.0;} 83 | vec4 perm(vec4 x){return mod289(((x * 34.0) + 1.0) * x);} 84 | 85 | float noise(vec3 p){ 86 | vec3 a = floor(p); 87 | vec3 d = p - a; 88 | d = d * d * (3.0 - 2.0 * d); 89 | 90 | vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0); 91 | vec4 k1 = perm(b.xyxy); 92 | vec4 k2 = perm(k1.xyxy + b.zzww); 93 | 94 | vec4 c = k2 + a.zzzz; 95 | vec4 k3 = perm(c); 96 | vec4 k4 = perm(c + 1.0); 97 | 98 | vec4 o1 = fract(k3 * (1.0 / 41.0)); 99 | vec4 o2 = fract(k4 * (1.0 / 41.0)); 100 | 101 | vec4 o3 = o2 * d.z + o1 * (1.0 - d.z); 102 | vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); 103 | 104 | return o4.y * d.y + o4.x * (1.0 - d.y); 105 | } 106 | 107 | #define NUM_OCTAVES 4 108 | float fbm(vec3 x) { 109 | float v = 0.0; 110 | float a = 0.5; 111 | vec3 shift = vec3(100); 112 | for (int i = 0; i < NUM_OCTAVES; ++i) { 113 | v += a * noise(x); 114 | x = x * 2.0 + shift; 115 | a *= 0.5; 116 | } 117 | return v; 118 | } 119 | /////////////////////////////////////////////////////////////////////////// 120 | 121 | #ifdef PIXEL 122 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 123 | float lv = mix(levelprev, level, smoothstep(0., TRANSITIONLEN, leveltime))*.1 - .1; 124 | vec4 light = getLight (lv); 125 | vec4 sky = getSky (lv); 126 | vec3 lightdir = getLightdir(lv); 127 | 128 | //scaling uvs 129 | vec2 uv2 = uvs - .5; 130 | float aspectratio = love_ScreenSize.x/love_ScreenSize.y; 131 | uv2.x *= aspectratio; 132 | uv2 *= 2*SCREENHEIGHT; 133 | 134 | // direction of camera pixel 135 | vec3 uvdir = normalize(vec3(uv2, 1)); 136 | 137 | vec3 startpoint = vec3(0.); 138 | vec4 reflectioncolour = mix(sky, light, max(pow(dot(uvdir, lightdir), 5), 0.)); 139 | vec4 refractioncolour = vec4(0.); 140 | float reflectionindex = 1.; 141 | if (uvdir.y > 0.) { 142 | startpoint = uvdir / uvdir.y * (SEALEVEL); 143 | if (startpoint.z < 400.) { 144 | // point we hit on the sea plane 145 | vec2 planecoord = startpoint.xz*.5; 146 | // generate random height map from fbm 147 | vec3 tide = vec3(planecoord, time*.5+noise(planecoord)); 148 | float h = fbm(tide); 149 | // derivate to get normals 150 | vec3 dx = normalize(vec3(.02, fbm(tide+DVEC.yxx) - h, .00)); 151 | vec3 dz = normalize(vec3(.00, fbm(tide+DVEC.xyx) - h, .02)); 152 | vec3 normal = cross(dz, dx); 153 | // reflected and refracted rays 154 | vec3 reflected = normalize(reflect(uvdir, normal)); 155 | vec3 refracted = normalize(refract(uvdir, normal, 1./SEAREFRACTION)); 156 | // Schlick's approximation for how much refraction 157 | float r = (1-SEAREFRACTION)/(1+SEAREFRACTION); 158 | r *= r; 159 | r += (1-r)*pow(1-dot(uvdir, normal), 5); 160 | // update values 161 | refractioncolour = mix(water2, water1, (.5+.5*dot(refracted, lightdir))); 162 | reflectioncolour = mix(sky , light, max(pow(dot(reflected, lightdir), 5),0.)); 163 | reflectionindex = clamp(0, 1, r + startpoint.z*.0025); 164 | uvdir = reflected; 165 | } 166 | } 167 | 168 | vec2 uvb = uvdir.xy/uvdir.z; 169 | // random stars 170 | // lots of random but essentially, the screen is divided into square cells 171 | // in which i place one star at a random spot towards the middle 172 | vec2 cell = floor(uvb*STARSDENSITY); 173 | // the randomness makes the stars off the grid 174 | // and with enough of them it looks somewhat convincing i guess 175 | float r = rand(cell); 176 | vec2 starpos = .25+.5*vec2(r, rand(cell+r)); 177 | // shine of the stars changes with time so it's a bit sparkly 178 | // not how stars behave but i think it looks good 179 | float intensity = noise((starpos+cell)*5.+time)*max(0., lv); 180 | reflectioncolour.xyz += vec3(1., 1., rand(cell*intensity)) * (1.-smoothstep(.0, .004*intensity, distance(uv2, (cell+starpos)/STARSDENSITY)))*intensity; 181 | 182 | // rainbow stripes 183 | vec4 blend = vec4( 184 | 1.-smoothstep(0,0.15,abs(uvb.y-2*abs(uvb.x) + aspectratio*.5+.40)), // position of red ray 185 | 1.-smoothstep(0,0.15,abs(uvb.y-2*abs(uvb.x) + aspectratio*.5+.45)), // position of green ray 186 | 1.-smoothstep(0,0.15,abs(uvb.y-2*abs(uvb.x) + aspectratio*.5+.50)), // position of blue ray 187 | 0. // don't touch alpha 188 | ); 189 | // each ray gets mixed with its own component 190 | reflectioncolour = mix(reflectioncolour, vec4(1.), blend); 191 | 192 | // octahedron 193 | // calculate rotation with 3 linearly changing angles for yaw/pitch/roll 194 | float a = time*0.8, b = time*0.4, c = time*.2; 195 | float ca = cos(a), sa = sin(a), cb = cos(b), sb = sin(b), cc = cos(c), sc = sin(c); 196 | mat3 spin = mat3(sc, cc, 0, 197 | cc,-sc, 0, 198 | 0, 0, 1) 199 | * mat3( 0, sb, cb, 200 | 0, cb,-sb, 201 | 1, 0, 0) 202 | * mat3(ca, 0, sa, 203 | 0, 1, 0, 204 | -sa, 0, ca); 205 | // the resulting matrix contains the results of the transformation of +x, +y and +z 206 | vec3 spinX = spin[0], spinY = spin[1], spinZ = spin[2]; 207 | // the points of a octahedron are +x, -x, +y, -y, +z, -z 208 | vec3 Xpos = SHAPEPOS + SHAPESIZENESS*spinX, Xneg = SHAPEPOS - SHAPESIZENESS*spinX; 209 | vec3 Ypos = SHAPEPOS + SHAPESIZENESS*spinY, Yneg = SHAPEPOS - SHAPESIZENESS*spinY; 210 | vec3 Zpos = SHAPEPOS + SHAPESIZENESS*spinZ, Zneg = SHAPEPOS - SHAPESIZENESS*spinZ; 211 | // see what edge we're closest to 212 | float d = distanceLineToSegment(startpoint, uvdir, Xpos, Ypos); 213 | d = min(d, distanceLineToSegment(startpoint, uvdir, Xpos, Yneg)); 214 | d = min(d, distanceLineToSegment(startpoint, uvdir, Xpos, Zpos)); 215 | d = min(d, distanceLineToSegment(startpoint, uvdir, Xpos, Zneg)); 216 | d = min(d, distanceLineToSegment(startpoint, uvdir, Xneg, Ypos)); 217 | d = min(d, distanceLineToSegment(startpoint, uvdir, Xneg, Yneg)); 218 | d = min(d, distanceLineToSegment(startpoint, uvdir, Xneg, Zpos)); 219 | d = min(d, distanceLineToSegment(startpoint, uvdir, Xneg, Zneg)); 220 | d = min(d, distanceLineToSegment(startpoint, uvdir, Ypos, Zpos)); 221 | d = min(d, distanceLineToSegment(startpoint, uvdir, Ypos, Zneg)); 222 | d = min(d, distanceLineToSegment(startpoint, uvdir, Yneg, Zpos)); 223 | d = min(d, distanceLineToSegment(startpoint, uvdir, Yneg, Zneg)); 224 | if (d < SHAPETHICNESS) { 225 | // we're hitting the shape: make the shape colour 226 | reflectioncolour = shape; 227 | } else { 228 | // we're not hitting the shape: make the shape glow 229 | reflectioncolour = mix(reflectioncolour, shape, 1/(1+d/SHAPEGLOWNESS)); 230 | } 231 | 232 | return mix(refractioncolour, reflectioncolour, reflectionindex); 233 | } 234 | #endif 235 | 236 | #ifdef VERTEX 237 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 238 | return transform_projection * vertex_position; 239 | } 240 | #endif 241 | -------------------------------------------------------------------------------- /shaders/bgclassic.glsl: -------------------------------------------------------------------------------- 1 | uniform float dt; 2 | uniform float time; 3 | uniform int level; 4 | #define SCREENHEIGHT 240 5 | #define LINEWIDTH .15 6 | #define NOISEAMOUNT .03 7 | const vec3 ORIGIN = vec3(0.); 8 | // const vec4 bgcolour = vec4(.3, .4, .3, 1.); 9 | // const vec4 linecolour = vec4(.3, .9, .3, 1.); 10 | const vec3 forward = vec3( 0., 0., 1.); 11 | const vec3 plane1normal = vec3( 0., 1., 0.); 12 | const vec3 plane1side = vec3(-1., 0., 0.); 13 | const vec3 plane1point = vec3( 0., 2., 0.); 14 | const vec3 plane2normal = vec3( 0.,-1., 0.); 15 | const vec3 plane2point = vec3( 0.,-5., 0.); 16 | const vec3 plane2side = vec3( 1., 0., 0.); 17 | const mat2 planeangle = mat2(cos(.5), sin(.5), 18 | -sin(.5), cos(.5)); 19 | const vec4[10] BG_COLOUR_ARRAY = vec4[]( 20 | vec4( 0/255., 88/255., 248/255., 1.), 21 | vec4( 0/255., 168/255., 0/255., 1.), 22 | vec4(216/255., 0/255., 204/255., 1.), 23 | vec4( 0/255., 88/255., 248/255., 1.), 24 | vec4(228/255., 0/255., 88/255., 1.), 25 | vec4(104/255., 136/255., 252/255., 1.), 26 | vec4(124/255., 124/255., 124/255., 1.), 27 | vec4(168/255., 0/255., 32/255., 1.), 28 | vec4( 0/255., 88/255., 248/255., 1.), 29 | vec4(248/255., 56/255., 0/255., 1.) 30 | ); 31 | const vec4[10] FG_COLOUR_ARRAY = vec4[]( 32 | vec4( 60/255., 188/255., 252/255., 1.), 33 | vec4(184/255., 248/255., 24/255., 1.), 34 | vec4(248/255., 120/255., 248/255., 1.), 35 | vec4( 88/255., 216/255., 84/255., 1.), 36 | vec4( 88/255., 248/255., 152/255., 1.), 37 | vec4( 88/255., 248/255., 152/255., 1.), 38 | vec4(248/255., 56/255., 0/255., 1.), 39 | vec4(104/255., 68/255., 252/255., 1.), 40 | vec4(248/255., 56/255., 0/255., 1.), 41 | vec4(252/255., 160/255., 68/255., 1.) 42 | ); 43 | 44 | // from https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection#Algebraic_form // 45 | vec3 linePlaneIntersect(vec3 planenorm, vec3 planepoint, vec3 linedir, vec3 linepoint) { 46 | return linepoint + linedir * (dot(planepoint-linepoint, planenorm)/dot(linedir, planenorm)); 47 | } 48 | 49 | //from https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 // 50 | float rand(vec2 n) { 51 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 52 | } 53 | 54 | #ifdef PIXEL 55 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 56 | // determine closest "big pixel" position - we're using this instead of the actual position from there on 57 | float aspectratio = love_ScreenSize.x/love_ScreenSize.y; 58 | float pxscale = ceil(love_ScreenSize.y / SCREENHEIGHT); //size of the "big pixel" in real-screen pixels 59 | vec2 pxsize = pxscale/love_ScreenSize.xy; // uv range of a "big pixel" 60 | vec2 uvpx = uvs/pxsize; //uvs stretched to "big pixel" units 61 | vec2 pxcoord = (floor(uvpx)+.5) * pxsize; // uv position of the "big pixel" we're on 62 | vec2 subpxuv = (fract(uvpx)-.5); // position inside the "big pixel" (used later for glowing pixel effect) 63 | 64 | // sample previous frame, make it transparent a bit to make it like the pixels take time to fade out 65 | vec4 oldpixel = Texel(image, pxcoord); 66 | oldpixel.a = pow(0.01, dt); 67 | 68 | // background colour, prep for 3d funnies 69 | int bgindex = int(mod(level-1, 10)); 70 | if (level > 30) bgindex += 10; 71 | vec4 newpixel = BG_COLOUR_ARRAY[bgindex]; 72 | vec4 linecolour = FG_COLOUR_ARRAY[bgindex]; 73 | vec2 uv2 = pxcoord - .5; 74 | uv2.x *= aspectratio; 75 | uv2 = planeangle * uv2; // rotated uvs because that's easier than doing a rotated plane lol 76 | vec3 uvdir = normalize(vec3(uv2, 1)); // direction of our (rotated) camera beam 77 | 78 | // two planes where we draw either a / or a \, similar to that one C64 program 79 | vec3 intersect1 = linePlaneIntersect(plane1normal, plane1point, uvdir, ORIGIN) - plane1point; // vector from the point of origin to the intersection 80 | if (intersect1.z > 0. && intersect1.z < 20.) { 81 | vec2 plane1pos = vec2(dot(intersect1, plane1side), dot(intersect1, forward)+time*.22); //same vector, projected into to the plane's coord system 82 | vec2 plane1cell = floor(plane1pos); // index of the tile we're on 83 | vec2 plane1frac = fract(plane1pos); // subtile position 84 | float distfactor1 = 1. - pow(intersect1.z/20., 2); // distance to camera (more or less) 85 | if (rand(plane1cell) < .5) { 86 | // doing a / line 87 | if (abs(plane1frac.y-plane1frac.x) < LINEWIDTH) newpixel = mix(newpixel, linecolour, distfactor1); 88 | } else { 89 | // doing a \ line 90 | if (abs(plane1frac.y+plane1frac.x - 1.) < LINEWIDTH) newpixel = mix(newpixel, linecolour, distfactor1); 91 | } 92 | // add a bit of random noise cause idk it looks more interesting or something 93 | newpixel.rgb += (clamp(vec3(rand(plane1pos), rand(plane1pos+1.), rand(plane1pos-1.)),1-NOISEAMOUNT, 1.)-1.+NOISEAMOUNT)/NOISEAMOUNT*distfactor1; 94 | } 95 | 96 | //same exact stuff with different values 97 | vec3 intersect2 = linePlaneIntersect(plane2normal, plane2point, uvdir, ORIGIN) - plane2point; 98 | if (intersect2.z > 0. && intersect2.z < 30.) { 99 | vec2 plane2pos = vec2(dot(intersect2, plane2side), dot(intersect2, forward)+time*.44); 100 | vec2 plane2cell = floor(plane2pos); 101 | vec2 plane2frac = fract(plane2pos); 102 | float distfactor2 = 1. - pow(intersect2.z/30., 2); 103 | if (rand(plane2cell) < .5) { 104 | if (abs(plane2frac.y-plane2frac.x) < LINEWIDTH) newpixel = mix(newpixel, linecolour, distfactor2); 105 | } else { 106 | if (abs(plane2frac.y+plane2frac.x - 1.) < LINEWIDTH) newpixel = mix(newpixel, linecolour, distfactor2); 107 | } 108 | newpixel.rgb += (clamp(vec3(rand(plane2pos), rand(plane2pos+1.), rand(plane2pos-1.)),1-NOISEAMOUNT, 1.)-1.+NOISEAMOUNT)/NOISEAMOUNT*distfactor2; 109 | } 110 | 111 | // merging old transparent frame and new frame, and adding a bit of brightness decay with distance 112 | vec4 finalpixel = mix(newpixel, oldpixel, pow(2., -15*dt)); 113 | if (ceil(love_ScreenSize.y / SCREENHEIGHT) >= 3) 114 | // "big pixels" big enough to do the decay (1px doesn't leave room, 2px the bright spot lands between all 4 pixels and it looks uniform and dark) 115 | finalpixel.rgb *= 1. - .5*dot(subpxuv, subpxuv); 116 | return finalpixel; 117 | } 118 | #endif 119 | 120 | #ifdef VERTEX 121 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 122 | return transform_projection * vertex_position; 123 | } 124 | #endif 125 | -------------------------------------------------------------------------------- /shaders/bgdeath.glsl: -------------------------------------------------------------------------------- 1 | uniform float time; 2 | 3 | #define HEIGHTFLOOR 2. 4 | #define HEIGHTSKY -2. 5 | #define SPIKEHEIGHT 0.2 6 | #define RAYSTEP 0.2 7 | #define RAYREACH 10. 8 | 9 | //from https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 // 10 | float rand(vec2 n) { 11 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 12 | } 13 | 14 | float noise(vec2 p){ 15 | vec2 ip = floor(p); 16 | vec2 u = fract(p); 17 | u = u*u*(3.0-2.0*u); 18 | 19 | float res = mix( 20 | mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), 21 | mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); 22 | return res*res; 23 | } 24 | 25 | #ifdef PIXEL 26 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 27 | // 3d-ized uvs 28 | vec2 uv2 = uvs*2.-1.; 29 | uv2.x *= love_ScreenSize.x/love_ScreenSize.y; 30 | vec3 uvdir = normalize(vec3(uv2, 1)); 31 | 32 | // Cloud background 33 | vec2 planecoord = HEIGHTSKY*uvdir.xz/uv2.y; 34 | vec4 newpixel = vec4(noise(planecoord+vec2(0., time))/planecoord.y, 0., 0., 1.); 35 | 36 | // Iterate up to a maximum length to simulate the ray's trajectory 37 | for (vec3 ray = vec3(0.); length(ray) < RAYREACH; ray += uvdir*RAYSTEP) { 38 | float maxheight = SPIKEHEIGHT*ray.x*ray.x; 39 | float curheight = -ray.y + HEIGHTFLOOR; 40 | // Use noise to generate random spikes 41 | // If the ray is under the spike then we just hit it 42 | if (curheight < maxheight*noise(ray.xz+vec2(0., time))) { 43 | // Red component is just how far the ray is from the camera 44 | float bright = curheight*(1.-length(ray)/RAYREACH); 45 | // Yellow component scales with the maximum possible height of the spike 46 | float yellow = bright*curheight*curheight/maxheight/maxheight; 47 | newpixel = vec4(bright, yellow, 0., 1.); 48 | break; 49 | } 50 | } 51 | return newpixel; 52 | } 53 | #endif 54 | 55 | #ifdef VERTEX 56 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 57 | return transform_projection * vertex_position; 58 | } 59 | #endif 60 | -------------------------------------------------------------------------------- /shaders/bgmenu.glsl: -------------------------------------------------------------------------------- 1 | uniform float dt; 2 | uniform float time; 3 | 4 | // from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl // 5 | const vec4 K = vec4(1., 2. / 3., 1. / 3., 3.); 6 | vec3 hsv2rgb(vec3 c) { 7 | vec3 p = abs(fract(c.xxx + K.xyz) * 6. - K.www); 8 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0., 1.), c.y); 9 | } 10 | ////////////////////////////////////////////////////////////////// 11 | 12 | #ifdef PIXEL 13 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 14 | // pick a point that moves around the screen 15 | vec2 focus = .4*vec2(cos(time*.45), sin(time*.44)) + .5; 16 | vec2 uv2 = uvs - focus; 17 | // rescale previous frame around said moving point 18 | vec3 oldpixel = pow(.9, dt)*Texel(image, uv2*pow(5., dt) + focus).xyz; 19 | // ooo rainbow funny 20 | vec3 overlay = hsv2rgb(vec3(time*.1, 1., .5*sin(2*time)+.5)); 21 | // apply rainbow to edge of screen (which will be rescaled on the next frame hence tunnel effect) 22 | vec2 uvabs = abs(uvs-.5); 23 | return vec4(mix(oldpixel, overlay, smoothstep(0.49, 0.5, max(uvabs.x, uvabs.y))), 1.); 24 | } 25 | #endif 26 | 27 | #ifdef VERTEX 28 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 29 | return transform_projection * vertex_position; 30 | } 31 | #endif 32 | -------------------------------------------------------------------------------- /shaders/bgpractice.glsl: -------------------------------------------------------------------------------- 1 | uniform float dt; 2 | uniform float time; 3 | 4 | #define PI 3.141592653589793238463 5 | #define TAU 6.283185307179586476925 6 | 7 | #define SCREENHEIGHT .35 8 | uniform Image moontex; 9 | uniform Image moonnor; 10 | 11 | const vec3 LIGHTDIR = normalize(vec3( -40., -20., -50.)); 12 | const vec3 moonpos = vec3(0., 52., 10.); 13 | const vec3 moonaxis = vec3(1., 0., 0.); 14 | const vec3 moonback = vec3(0., 0., 1.); 15 | const vec3 moonleft = vec3(0., 1., 0.); 16 | #define AMIGAROWS 6. 17 | #define AMIGACOLS 12. 18 | 19 | #define STARSDENSITY 35. 20 | 21 | struct ballpoint { 22 | bool valid; 23 | float longitude; 24 | float latitude; 25 | vec3 normal; 26 | mat3 normaltransform; 27 | }; 28 | 29 | ballpoint pointOnBall(vec3 uvdir, vec3 ballpos, float ballsize, vec3 ballaxis, vec3 ballback, vec3 ballleft) { 30 | // assuming all vec3s except ballpos are normalized 31 | 32 | // get point closest to ball 33 | float d = dot(uvdir, ballpos); 34 | vec3 closest = d * uvdir; 35 | float mindist = distance(closest, ballpos); 36 | if (mindist < ballsize && d >= 0) { 37 | // actually hitting the ball, draw it 38 | 39 | // the contact point, the closest point and the centre of the ball make a right triangle 40 | // so we can get the length between the first two from the other distances 41 | // we then move back along the beam that distance to get the contact point 42 | vec3 surfacepoint = closest - uvdir * sqrt(ballsize*ballsize - mindist*mindist); 43 | vec3 surfacevec = normalize(surfacepoint - ballpos); 44 | 45 | // longitude and latitude calculations for texture position 46 | // shoutouts to https://stackoverflow.com/questions/5674149/3d-coordinates-on-a-sphere-to-latitude-and-longitude 47 | float longitude = .5*PI-acos(dot(surfacevec, ballaxis)); 48 | float latitude = atan(dot(ballback, surfacevec), dot(ballleft, surfacevec)); 49 | float clo = cos(longitude), slo = sin(longitude); 50 | float cla = cos(latitude) , sla = sin(latitude) ; 51 | mat3 transform = mat3(ballback, ballaxis, ballleft) 52 | * mat3(cla, 0 ,-sla, 53 | 0 , 1 , 0 , 54 | sla, 0 , cla) 55 | * mat3(1 , 0 , 0 , 56 | 0 , clo,-slo, 57 | 0 , slo, clo) 58 | ; 59 | return ballpoint(true, longitude, latitude, surfacevec, transform); 60 | } 61 | return ballpoint(false, 0., 0., vec3(0.), mat3(0.)); 62 | } 63 | 64 | //from https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 // 65 | float rand(vec2 n) { 66 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 67 | } 68 | 69 | float noise(vec2 p){ 70 | vec2 ip = floor(p); 71 | vec2 u = fract(p); 72 | u = u*u*(3.0-2.0*u); 73 | 74 | float res = mix( 75 | mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), 76 | mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); 77 | return res*res; 78 | } 79 | 80 | #define NUM_OCTAVES 5 81 | float fbm(vec2 x) { 82 | float v = 0.0; 83 | float a = 0.5; 84 | vec2 shift = vec2(100); 85 | // Rotate to reduce axial bias 86 | mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.50)); 87 | for (int i = 0; i < NUM_OCTAVES; ++i) { 88 | v += a * noise(x); 89 | x = rot * x * 2.0 + shift; 90 | a *= 0.5; 91 | } 92 | return v; 93 | } 94 | 95 | /////////////////////////////////////////////////////////////////////////// 96 | 97 | #ifdef PIXEL 98 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 99 | //scaling uvs 100 | vec2 uv2 = uvs - .5; 101 | uv2.x *= love_ScreenSize.x/love_ScreenSize.y; 102 | uv2 *= 2*SCREENHEIGHT; 103 | 104 | // background colour 105 | vec4 newpixel = vec4(.1, .0, .3, 1.); 106 | // direction of camera pixel 107 | vec3 uvdir = normalize(vec3(uv2, 1)); 108 | 109 | ballpoint moonpoint = pointOnBall(uvdir, moonpos, 50., moonaxis, moonback, moonleft); 110 | if (moonpoint.valid) { 111 | // test 112 | // newpixel.xyz = vec3(longitude, latitude, 0.); 113 | // moon texture 114 | moonpoint.latitude = mod(moonpoint.latitude - .01*time, TAU); 115 | newpixel = Texel(moontex, vec2(moonpoint.latitude/TAU, moonpoint.longitude/PI +.5))*2; 116 | vec3 mapnormal = Texel(moonnor, vec2(moonpoint.latitude/TAU, moonpoint.longitude/PI +.5)).xyz * 2. - 1.; 117 | newpixel.xyz *= dot(moonpoint.normaltransform * mapnormal, LIGHTDIR); 118 | 119 | } else { 120 | // floating amigaball 121 | vec3 amigapos = vec3(150., -70.+10.*cos(time*.2), 400.); 122 | vec3 amigaaxis = normalize(vec3(.3, cos(time*.04), sin(time*.04))); 123 | vec3 amigaback = normalize(cross(amigaaxis, vec3(0.,0.,1.))); 124 | vec3 amigaleft = cross(amigaaxis, amigaback); 125 | 126 | ballpoint amigapoint = pointOnBall(uvdir, amigapos, 100., amigaaxis, amigaback, amigaleft); 127 | if (amigapoint.valid) { 128 | amigapoint.latitude = mod(amigapoint.latitude + time*.1, TAU); 129 | // amiga ball 130 | // checkerboard on longitude and latitude 131 | float red = mod(floor(amigapoint.longitude/PI*AMIGAROWS) + floor(amigapoint.latitude/TAU*AMIGACOLS), 2); 132 | newpixel.xyz = vec3(1.,red,red) * dot(amigapoint.normal, LIGHTDIR); 133 | } else { 134 | // space clouds background 135 | newpixel.xyz += vec3(.7, .1, .3) * sqrt(fbm(15*uv2+time*vec2(.2, .1)) * fbm(10*uv2.yx+time*vec2(.1,-.2)))*2; 136 | // random stars 137 | 138 | // lots of random but essentially, the screen is divided into square cells 139 | // in which i place one star at a random spot towards the middle 140 | vec2 cell = floor(uv2*STARSDENSITY); 141 | // the randomness makes the stars off the grid 142 | // and with enough of them it looks somewhat convincing i guess 143 | float r = rand(cell); 144 | vec2 starpos = .25+.5*vec2(r, rand(cell+r)); 145 | // shine of the stars changes with time so it's a bit sparkly 146 | // not how stars behave but i think it looks good 147 | float intensity = noise((starpos+cell)*5.+time); 148 | newpixel.xyz += vec3(1., 1., rand(cell*intensity)) * (1.-smoothstep(.0, .002*intensity, distance(uv2, (cell+starpos)/STARSDENSITY)))*intensity; 149 | } 150 | } 151 | return newpixel; 152 | } 153 | #endif 154 | 155 | #ifdef VERTEX 156 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 157 | return transform_projection * vertex_position; 158 | } 159 | #endif 160 | -------------------------------------------------------------------------------- /shaders/bgpractice2.glsl: -------------------------------------------------------------------------------- 1 | uniform float dt; 2 | uniform float time; 3 | 4 | #define PI 3.141592653589793238463 5 | #define TAU 6.283185307179586476925 6 | 7 | #define SCREENHEIGHT .35 8 | uniform Image moontex; 9 | uniform Image moonnor; 10 | 11 | const vec4 BG_COLOUR = vec4(.01, .02, .02, 1.); 12 | const vec3 LIGHT_DIR = normalize(vec3( 0., -1., 10.)); 13 | const vec3 amigapos = vec3(0., 52., 10.); 14 | const vec3 amigaaxis = vec3(1., 0., 0.); 15 | const vec3 amigaback = vec3(0., 0., 1.); 16 | const vec3 amigaleft = vec3(0., 1., 0.); 17 | #define AMIGAROWS 50. 18 | #define AMIGACOLS 100. 19 | 20 | #define STARSDENSITY 35. 21 | 22 | const vec3 FLARE_COLOUR = vec3(1., 2., 4.); 23 | const vec2 FLARE_POS = vec2(.0, -.2); 24 | #define FLAREPOW 0.7 25 | #define FLARESPREAD 2.8 26 | #define FLAREBRIGHT 0.04 27 | 28 | const vec3 GRID_COLOUR = vec3(1., 0., 1.); 29 | 30 | 31 | struct ballpoint { 32 | bool valid; 33 | float longitude; 34 | float latitude; 35 | vec3 normal; 36 | mat3 normaltransform; 37 | }; 38 | 39 | ballpoint pointOnBall(vec3 uvdir, vec3 ballpos, float ballsize, vec3 ballaxis, vec3 ballback, vec3 ballleft) { 40 | // assuming all vec3s except ballpos are normalized 41 | 42 | // get point closest to ball 43 | float d = dot(uvdir, ballpos); 44 | vec3 closest = d * uvdir; 45 | float mindist = distance(closest, ballpos); 46 | if (mindist < ballsize && d >= 0) { 47 | // actually hitting the ball, draw it 48 | 49 | // the contact point, the closest point and the centre of the ball make a right triangle 50 | // so we can get the length between the first two from the other distances 51 | // we then move back along the beam that distance to get the contact point 52 | vec3 surfacepoint = closest - uvdir * sqrt(ballsize*ballsize - mindist*mindist); 53 | vec3 surfacevec = normalize(surfacepoint - ballpos); 54 | 55 | // longitude and latitude calculations for texture position 56 | // shoutouts to https://stackoverflow.com/questions/5674149/3d-coordinates-on-a-sphere-to-latitude-and-longitude 57 | float longitude = .5*PI-acos(dot(surfacevec, ballaxis)); 58 | float latitude = atan(dot(ballback, surfacevec), dot(ballleft, surfacevec)); 59 | float clo = cos(longitude), slo = sin(longitude); 60 | float cla = cos(latitude) , sla = sin(latitude) ; 61 | mat3 transform = mat3(ballback, ballaxis, ballleft) 62 | * mat3(cla, 0 ,-sla, 63 | 0 , 1 , 0 , 64 | sla, 0 , cla) 65 | * mat3(1 , 0 , 0 , 66 | 0 , clo,-slo, 67 | 0 , slo, clo) 68 | ; 69 | return ballpoint(true, longitude, latitude, surfacevec, transform); 70 | } 71 | return ballpoint(false, 0., 0., vec3(0.), mat3(0.)); 72 | } 73 | 74 | // from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl // 75 | const vec4 K = vec4(1., 2. / 3., 1. / 3., 3.); 76 | vec3 hsv2rgb(vec3 c) { 77 | vec3 p = abs(fract(c.xxx + K.xyz) * 6. - K.www); 78 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0., 1.), c.y); 79 | } 80 | 81 | //from https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 // 82 | float rand(float n) { 83 | return fract(sin(n) * 43758.5453123); 84 | } 85 | 86 | float noise(float p){ 87 | float fl = floor(p); 88 | float fc = fract(p); 89 | return mix(rand(fl), rand(fl + 1.0), fc); 90 | } 91 | 92 | float rand(vec2 n) { 93 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 94 | } 95 | 96 | float noise(vec2 p){ 97 | vec2 ip = floor(p); 98 | vec2 u = fract(p); 99 | u = u*u*(3.0-2.0*u); 100 | 101 | float res = mix( 102 | mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), 103 | mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); 104 | return res*res; 105 | } 106 | 107 | #define NUM_OCTAVES 5 108 | float fbm(vec2 x) { 109 | float v = 0.0; 110 | float a = 0.5; 111 | vec2 shift = vec2(100); 112 | // Rotate to reduce axial bias 113 | mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.50)); 114 | for (int i = 0; i < NUM_OCTAVES; ++i) { 115 | v += a * noise(x); 116 | x = rot * x * 2.0 + shift; 117 | a *= 0.5; 118 | } 119 | return v; 120 | } 121 | 122 | /////////////////////////////////////////////////////////////////////////// 123 | 124 | #ifdef PIXEL 125 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 126 | //scaling uvs 127 | vec2 uv2 = uvs - .5; 128 | uv2.x *= love_ScreenSize.x/love_ScreenSize.y; 129 | uv2 *= 2*SCREENHEIGHT; 130 | 131 | // background colour 132 | vec4 newpixel = BG_COLOUR; 133 | // direction of camera pixel 134 | vec3 uvdir = normalize(vec3(uv2, 1)); 135 | 136 | ballpoint amigapoint = pointOnBall(uvdir, amigapos, 50., amigaaxis, amigaback, amigaleft); 137 | if (amigapoint.valid) { 138 | // test 139 | // newpixel.xyz = vec3(longitude, latitude, 0.); 140 | amigapoint.latitude = mod(amigapoint.latitude - .01*time, TAU); 141 | // amiga ball 142 | // checkerboard on longitude and latitude 143 | float red = mod(floor(amigapoint.longitude/PI*AMIGAROWS) + floor(amigapoint.latitude/TAU*AMIGACOLS), 2); 144 | newpixel.rgb = vec3(1.,red,red) * (4.*dot(amigapoint.normal, LIGHT_DIR)); 145 | 146 | } else { 147 | // floating moon 148 | vec3 moonpos = vec3(150., -70.+10.*cos(time*.2), 400.); 149 | vec3 moonaxis = normalize(vec3(.3, cos(time*.04), sin(time*.04))); 150 | vec3 moonback = normalize(cross(moonaxis, vec3(0.,0.,1.))); 151 | vec3 moonleft = cross(moonaxis, moonback); 152 | // vec3 moonpos = vec3(150., -70, 400.); 153 | // vec3 moonaxis = vec3(0.,1.,0.); 154 | // vec3 moonback = vec3(0.,0.,1.); 155 | // vec3 moonleft = cross(moonaxis, moonback); 156 | 157 | 158 | ballpoint moonpoint = pointOnBall(uvdir, moonpos, 100., moonaxis, moonback, moonleft); 159 | if (moonpoint.valid) { 160 | moonpoint.latitude = mod(moonpoint.latitude, TAU); 161 | // moon texture 162 | newpixel = Texel(moontex, vec2(moonpoint.latitude/TAU, moonpoint.longitude/PI +.5)); 163 | // adjusting normal with normalmap 164 | vec3 mapnormal = Texel(moonnor, vec2(moonpoint.latitude/TAU, moonpoint.longitude/PI +.5)).xyz * 2. - 1.; 165 | newpixel.rgb *= .5+.5*dot(moonpoint.normaltransform * mapnormal, LIGHT_DIR); 166 | } else { 167 | // lens flare 168 | vec2 flareuv = uv2 - FLARE_POS; 169 | newpixel.rgb += FLARE_COLOUR * (FLAREBRIGHT * (1.+.05*noise(time*4))) / (pow(abs(flareuv.x/FLARESPREAD), FLAREPOW) + pow(abs(flareuv.y), FLAREPOW)); 170 | 171 | // random stars 172 | // lots of random but essentially, the screen is divided into square cells 173 | // in which i place one star at a random spot towards the middle 174 | vec2 cell = floor(uv2*STARSDENSITY); 175 | // the randomness makes the stars off the grid 176 | // and with enough of them it looks somewhat convincing i guess 177 | float r = rand(cell); 178 | vec2 starpos = .25+.5*vec2(r, rand(cell+r)); 179 | // shine of the stars changes with time so it's a bit sparkly 180 | // not how stars behave but i think it looks good 181 | float intensity = noise((starpos+cell)*5.+time); 182 | newpixel.rgb += vec3(1., 1., rand(cell*intensity)) * (1.-smoothstep(.0, .002*intensity, distance(uv2, (cell+starpos)/STARSDENSITY)))*intensity; 183 | } 184 | } 185 | return newpixel; 186 | } 187 | #endif 188 | 189 | #ifdef VERTEX 190 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 191 | return transform_projection * vertex_position; 192 | } 193 | #endif 194 | -------------------------------------------------------------------------------- /shaders/bgstandard.glsl: -------------------------------------------------------------------------------- 1 | uniform float dt; 2 | uniform float time; 3 | uniform float leveltime; 4 | uniform int levelprev; 5 | uniform int level; 6 | uniform Image moontex; 7 | uniform Image moonnor; 8 | 9 | #define PI 3.141592653589793238463 10 | #define TAU 6.283185307179586476925 11 | #define SCREENHEIGHT .35 12 | #define TRANSITION_TIME 3. 13 | const vec3 ORIGIN = vec3(0., 0., 0.); 14 | const vec3 BG_COLOUR = vec3(.2, .2, .2); 15 | 16 | const vec3 moonpos = vec3(-200., 150., 500.); 17 | const vec3 moonaxis = normalize(vec3(2., -5., -1.)); 18 | const vec3 moonback = normalize(cross(moonaxis, vec3(1., 0., 0.))); 19 | const vec3 moonleft = normalize(cross(moonaxis, moonback)); 20 | #define moonsize 200. 21 | #define PIECE_SIZE 40. 22 | 23 | // const vec3[5][21] PIECE_BLOCKS = vec3[5][20]( 24 | const vec3[5] PIECE_O1 = vec3[5](vec3( 0.0, 0.0, 0.0), vec3( 0.0, 0.0, 0.0), vec3( 0.0, 0.0, 0.0), vec3( 0.0, 0.0, 0.0), vec3( 0.0, 0.0, 0.0)); 25 | 26 | const vec3[5] PIECE_I2 = vec3[5](vec3( 0.5, 0.0, 0.0), vec3(-0.5, 0.0, 0.0), vec3(-0.5, 0.0, 0.0), vec3( 0.5, 0.0, 0.0), vec3( 0.5, 0.0, 0.0)); 27 | 28 | const vec3[5] PIECE_L3 = vec3[5](vec3( 0.5,-0.5, 0.0), vec3(-0.5, 0.5, 0.0), vec3(-0.5,-0.5, 0.0), vec3( 0.5,-0.5, 0.0), vec3( 0.5,-0.5, 0.0)); 29 | const vec3[5] PIECE_I3 = vec3[5](vec3( 1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3( 0.0, 0.0, 0.0), vec3( 1.0, 0.0, 0.0), vec3( 1.0, 0.0, 0.0)); 30 | 31 | const vec3[5] PIECE_I4 = vec3[5](vec3( 0.5, 0.0, 0.0), vec3(-1.5, 0.0, 0.0), vec3(-0.5, 0.0, 0.0), vec3( 1.5, 0.0, 0.0), vec3( 1.5, 0.0, 0.0)); 32 | const vec3[5] PIECE_O4 = vec3[5](vec3( 0.5,-0.5, 0.0), vec3(-0.5, 0.5, 0.0), vec3(-0.5,-0.5, 0.0), vec3( 0.5, 0.5, 0.0), vec3( 0.5, 0.5, 0.0)); 33 | const vec3[5] PIECE_S4 = vec3[5](vec3( 1.0,-1.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3( 0.0,-1.0, 0.0), vec3( 0.0, 0.0, 0.0), vec3( 0.0, 0.0, 0.0)); 34 | const vec3[5] PIECE_T4 = vec3[5](vec3( 1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3( 0.0,-1.0, 0.0), vec3( 0.0, 0.0, 0.0), vec3( 0.0, 0.0, 0.0)); 35 | const vec3[5] PIECE_L4 = vec3[5](vec3( 1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(-1.0,-1.0, 0.0), vec3( 0.0, 0.0, 0.0), vec3( 0.0, 0.0, 0.0)); 36 | 37 | const vec3[5] PIECE_P5 = vec3[5](vec3( 1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(-1.0,-1.0, 0.0), vec3( 0.0, 0.0, 0.0), vec3( 0.0,-1.0, 0.0)); 38 | const vec3[5] PIECE_U5 = vec3[5](vec3( 1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(-1.0,-1.0, 0.0), vec3( 0.0, 0.0, 0.0), vec3( 1.0,-1.0, 0.0)); 39 | const vec3[5] PIECE_I5 = vec3[5](vec3( 1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(-2.0, 0.0, 0.0), vec3( 0.0, 0.0, 0.0), vec3( 2.0, 0.0, 0.0)); 40 | const vec3[5] PIECE_N5 = vec3[5](vec3( 0.5, 0.5, 0.0), vec3(-0.5,-0.5, 0.0), vec3(-1.5,-0.5, 0.0), vec3(-0.5, 0.5, 0.0), vec3( 1.5, 0.5, 0.0)); 41 | const vec3[5] PIECE_Y5 = vec3[5](vec3( 0.5, 0.5, 0.0), vec3(-0.5,-0.5, 0.0), vec3(-1.5, 0.5, 0.0), vec3(-0.5, 0.5, 0.0), vec3( 1.5, 0.5, 0.0)); 42 | const vec3[5] PIECE_L5 = vec3[5](vec3( 0.5, 0.5, 0.0), vec3( 1.5,-0.5, 0.0), vec3(-1.5, 0.5, 0.0), vec3(-0.5, 0.5, 0.0), vec3( 1.5, 0.5, 0.0)); 43 | const vec3[5] PIECE_V5 = vec3[5](vec3( 1.0, 1.0, 0.0), vec3( 1.0,-1.0, 0.0), vec3(-1.0, 1.0, 0.0), vec3( 0.0, 1.0, 0.0), vec3( 1.0, 0.0, 0.0)); 44 | const vec3[5] PIECE_W5 = vec3[5](vec3( 0.0, 0.0, 0.0), vec3( 1.0,-1.0, 0.0), vec3(-1.0, 1.0, 0.0), vec3( 0.0, 1.0, 0.0), vec3( 1.0, 0.0, 0.0)); 45 | const vec3[5] PIECE_Z5 = vec3[5](vec3( 0.0, 0.0, 0.0), vec3( 1.0,-1.0, 0.0), vec3(-1.0, 1.0, 0.0), vec3( 0.0, 1.0, 0.0), vec3( 0.0,-1.0, 0.0)); 46 | const vec3[5] PIECE_T5 = vec3[5](vec3( 0.0, 0.0, 0.0), vec3( 1.0, 1.0, 0.0), vec3(-1.0, 1.0, 0.0), vec3( 0.0, 1.0, 0.0), vec3( 0.0,-1.0, 0.0)); 47 | const vec3[5] PIECE_F5 = vec3[5](vec3( 0.0, 0.0, 0.0), vec3( 1.0, 0.0, 0.0), vec3(-1.0, 1.0, 0.0), vec3( 0.0, 1.0, 0.0), vec3( 0.0,-1.0, 0.0)); 48 | const vec3[5] PIECE_X5 = vec3[5](vec3( 0.0, 0.0, 0.0), vec3( 1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3( 0.0, 1.0, 0.0), vec3( 0.0,-1.0, 0.0)); 49 | // ) 50 | 51 | vec3[5] getPieceForLevel(int lv) { 52 | // mom can we have array of array 53 | // we have array of array at home 54 | // array of array at home: 55 | 56 | if (lv == 0) return PIECE_O1; 57 | 58 | if (lv == 1) return PIECE_I2; 59 | 60 | if (lv == 2) return PIECE_L3; 61 | if (lv == 3) return PIECE_I3; 62 | 63 | if (lv == 4) return PIECE_I4; 64 | if (lv == 5) return PIECE_O4; 65 | if (lv == 6) return PIECE_S4; 66 | if (lv == 7) return PIECE_T4; 67 | if (lv == 8) return PIECE_L4; 68 | 69 | if (lv == 9) return PIECE_P5; 70 | if (lv == 10) return PIECE_U5; 71 | if (lv == 11) return PIECE_I5; 72 | if (lv == 12) return PIECE_N5; 73 | if (lv == 13) return PIECE_Y5; 74 | if (lv == 14) return PIECE_L5; 75 | if (lv == 15) return PIECE_V5; 76 | if (lv == 16) return PIECE_W5; 77 | if (lv == 17) return PIECE_Z5; 78 | if (lv == 18) return PIECE_T5; 79 | if (lv == 19) return PIECE_F5; 80 | return PIECE_X5; 81 | } 82 | 83 | 84 | // from https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB_alternative // 85 | const vec3 N = vec3(0., 8., 4.); 86 | vec3 hsl2rgb(vec3 c) { 87 | vec3 k = mod(N + c.x * 12., 12.); 88 | return c.z - c.y*min(c.z, 1.-c.z)*clamp(min(k-3., 9.-k), -1., 1.); 89 | } 90 | 91 | // from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl // 92 | const vec4 K = vec4(1., 2. / 3., 1. / 3., 3.); 93 | vec3 hsv2rgb(vec3 c) { 94 | vec3 p = abs(fract(c.xxx + K.xyz) * 6. - K.www); 95 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0., 1.), c.y); 96 | } 97 | 98 | // from https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection#Algebraic_form // 99 | vec3 linePlaneIntersect(vec3 planenorm, vec3 planepoint, vec3 linedir, vec3 linepoint) { 100 | return linepoint + linedir * (dot(planepoint-linepoint, planenorm)/dot(linedir, planenorm)); 101 | } 102 | 103 | 104 | //from https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 // 105 | float rand(vec2 n) { 106 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 107 | } 108 | 109 | float noise(vec2 p){ 110 | vec2 ip = floor(p); 111 | vec2 u = fract(p); 112 | u = u*u*(3.0-2.0*u); 113 | 114 | float res = mix( 115 | mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), 116 | mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); 117 | return res*res; 118 | } 119 | 120 | vec4 mod289(vec4 x){return x - floor(x * (1.0 / 289.0)) * 289.0;} 121 | vec4 perm(vec4 x){return mod289(((x * 34.0) + 1.0) * x);} 122 | float noise(vec3 p){ 123 | vec3 a = floor(p); 124 | vec3 d = p - a; 125 | d = d * d * (3.0 - 2.0 * d); 126 | 127 | vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0); 128 | vec4 k1 = perm(b.xyxy); 129 | vec4 k2 = perm(k1.xyxy + b.zzww); 130 | 131 | vec4 c = k2 + a.zzzz; 132 | vec4 k3 = perm(c); 133 | vec4 k4 = perm(c + 1.0); 134 | 135 | vec4 o1 = fract(k3 * (1.0 / 41.0)); 136 | vec4 o2 = fract(k4 * (1.0 / 41.0)); 137 | 138 | vec4 o3 = o2 * d.z + o1 * (1.0 - d.z); 139 | vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); 140 | 141 | return o4.y * d.y + o4.x * (1.0 - d.y); 142 | } 143 | 144 | #define NUM_OCTAVES 5 145 | float fbm(vec3 x) { 146 | float v = 0.0; 147 | float a = 0.5; 148 | vec3 shift = vec3(100); 149 | for (int i = 0; i < NUM_OCTAVES; ++i) { 150 | v += a * noise(x); 151 | x = x * 2.0 + shift; 152 | a *= 0.5; 153 | } 154 | return v; 155 | } 156 | /////////////////////////////////////////////////////////////////////////// 157 | 158 | mat3 roll(float angle) { 159 | float c = cos(angle), s = sin(angle); 160 | return mat3(1, 0, 0, 161 | 0, c, s, 162 | 0,-s, c); 163 | } 164 | 165 | mat3 pitch(float angle) { 166 | float c = cos(angle), s = sin(angle); 167 | return mat3(c, 0,-s, 168 | 0, 1, 0, 169 | s, 0, c); 170 | } 171 | 172 | mat3 yaw(float angle) { 173 | float c = cos(angle), s = sin(angle); 174 | return mat3(c, s, 0, 175 | -s, c, 0, 176 | 0, 0, 1); 177 | } 178 | 179 | void drawSquare(inout vec3 pxcolour, vec3 norm, vec3 pos, vec3 sideA, vec3 sideB, vec3 uvdir, float size) { 180 | // get the point on the face plane where uvdir is pointing 181 | vec3 intersect = linePlaneIntersect(norm, pos, uvdir, ORIGIN) - pos; 182 | vec2 sqpos = vec2(dot(intersect, sideA), dot(intersect, sideB)); 183 | 184 | // only draw if we're actually in the face 185 | if (sqpos == clamp(sqpos, -size*.5, size*.5)) { 186 | 187 | // projection of incident vector on the face's coordinates 188 | vec2 faceproj = vec2(dot(uvdir, sideA), dot(uvdir, sideB)); 189 | 190 | pxcolour = hsv2rgb(vec3( 191 | // hue is based on the angle of said coordinates (gives rainbow effect as the face rotates) 192 | .5 + atan(faceproj.y, faceproj.x) / TAU, 193 | // when the projection is small (ie when looking at the face straight on), a colour wheel 194 | // singularity would show up, to fix this we lower the saturation to turn it white 195 | max(0., 10.*dot(faceproj, faceproj)), 196 | // value is based on how the reflected ray is coming back towards us 197 | // ie how dead on we are looking at the face 198 | .5 * (1.+dot(reflect(uvdir, norm), -uvdir)) 199 | )); 200 | 201 | } 202 | } 203 | 204 | void drawPiece(inout vec3 px_colour, vec3 position, mat3 orientation, vec3[5] blocks, float size, vec3 uvdir) { 205 | // apply rotation matrix, size, and global piece position to the block positions 206 | vec3[5] bl_rotated; 207 | for (int i = 0; i < blocks.length(); i++) { 208 | bl_rotated[i] = orientation*(blocks[i] * size) + position; 209 | } 210 | 211 | // in-place selection sort by closest to the camera for draw order 212 | // gets buggy when the blocks overlap but outside of the piece morph animation it never happens 213 | vec3 temp; 214 | for (int i = 0; i < bl_rotated.length(); i++) { 215 | int s = i; 216 | for (int j = i+1; j < bl_rotated.length(); j++) { 217 | if (length(bl_rotated[s] + position) < length(bl_rotated[j] + position)) { 218 | s = j; 219 | } 220 | } 221 | if (s != i) { 222 | temp = bl_rotated[s]; 223 | bl_rotated[s] = bl_rotated[i]; 224 | bl_rotated[i] = temp; 225 | } 226 | } 227 | 228 | // draw faces 229 | for (int bl = 0; bl < bl_rotated.length(); bl++) { 230 | for (int i = 0; i < 3; i++) { 231 | // the rotation matrix is column per column the result of the transformation of (1;0;0), (0;1;0) and (0;0;1) 232 | // the 6 faces of the blocks have each of their normals being one of either those or the opposites 233 | // for each column of the matrix, either that vector or its opposite is pointing towards the camera 234 | // (ie v and -v can't both be pointing towards the camera) so we can pair them off and draw just one 235 | // so we can iterate over the matrix columns and if the vector is not facing us we draw the opposite face instead 236 | vec3 facenorm = orientation[i]; 237 | if (dot(facenorm, uvdir) > 0) { 238 | facenorm = -facenorm; 239 | } 240 | drawSquare( 241 | px_colour, 242 | facenorm, 243 | // this is the center of the face, it is away from the middle of the block towards the face's normal 244 | bl_rotated[bl] + .45 * facenorm * size, 245 | // we use the other two columns in the rotation matrix for the up and right vectors of the face 246 | orientation[i>=2 ? i-2 : i+1], 247 | orientation[i>=1 ? i-1 : i+2], 248 | uvdir, 249 | size * .9 250 | ); 251 | } 252 | } 253 | } 254 | 255 | 256 | struct ballpoint { 257 | bool valid; 258 | float longitude; 259 | float latitude; 260 | vec3 normal; 261 | mat3 normaltransform; 262 | }; 263 | 264 | ballpoint pointOnBall(vec3 uvdir, vec3 ballpos, float ballsize, vec3 ballaxis, vec3 ballback, vec3 ballleft) { 265 | // assuming all vec3s except ballpos are normalized 266 | 267 | // get point closest to ball 268 | float d = dot(uvdir, ballpos); 269 | vec3 closest = d * uvdir; 270 | float mindist = distance(closest, ballpos); 271 | if (mindist < ballsize && d >= 0) { 272 | // actually hitting the ball, draw it 273 | 274 | // the contact point, the closest point and the centre of the ball make a right triangle 275 | // so we can get the length between the first two from the other distances 276 | // we then move back along the beam that distance to get the contact point 277 | vec3 surfacepoint = closest - uvdir * sqrt(ballsize*ballsize - mindist*mindist); 278 | vec3 surfacevec = normalize(surfacepoint - ballpos); 279 | 280 | // longitude and latitude calculations for texture position 281 | // shoutouts to https://stackoverflow.com/questions/5674149/3d-coordinates-on-a-sphere-to-latitude-and-longitude 282 | float longitude = .5*PI-acos(dot(surfacevec, ballaxis)); 283 | float latitude = atan(dot(ballback, surfacevec), dot(ballleft, surfacevec)); 284 | // compute a transformation matrix too to apply to the normalmap 285 | float clo = cos(longitude), slo = sin(longitude); 286 | float cla = cos(latitude) , sla = sin(latitude) ; 287 | mat3 transform = mat3(ballback, ballaxis, ballleft) 288 | * mat3(cla, 0 ,-sla, 289 | 0 , 1 , 0 , 290 | sla, 0 , cla) 291 | * mat3(1 , 0 , 0 , 292 | 0 , clo,-slo, 293 | 0 , slo, clo) 294 | ; 295 | return ballpoint(true, longitude, latitude, surfacevec, transform); 296 | } 297 | return ballpoint(false, 0., 0., vec3(0.), mat3(0.)); 298 | } 299 | 300 | 301 | #ifdef PIXEL 302 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 303 | //scaling uvs 304 | vec2 uv2 = uvs - .5; 305 | uv2.x *= love_ScreenSize.x/love_ScreenSize.y; 306 | uv2 *= 2*SCREENHEIGHT; 307 | 308 | // direction of camera pixel 309 | vec3 uvdir = normalize(vec3(uv2, 1)); 310 | 311 | // background colour 312 | vec3 colour = BG_COLOUR; 313 | 314 | // space cloudssssssssssssssssss 315 | colour += hsv2rgb(vec3( 316 | // hue varying smoothly on uvs and time 317 | noise(vec3(uv2*4., time*.1)), 318 | // not too bright but that doesn't need to vary 319 | .5, 320 | // value as fbm because fbm is good at making clouds 321 | .7*fbm(vec3(uv2*10., time*.1)) 322 | )); 323 | 324 | // piece position / angle 325 | vec3 piece_pos = vec3(170, -70+10*sin(.5*time), 500); 326 | mat3 rotation = yaw(cos(.5*time))*pitch(.33*time)*roll(.11*time); 327 | 328 | // get 2 pieces and interpolate block positions for 329 | // an animation of the piece morphing from one to the other 330 | vec3[5] current_piece = getPieceForLevel(level); 331 | vec3[5] previous_piece = getPieceForLevel(levelprev); 332 | for (int i = 0; i < 5; i++) 333 | current_piece[i] = mix(previous_piece[i], current_piece[i], smoothstep(0., TRANSITION_TIME, leveltime)); 334 | drawPiece(colour, piece_pos, rotation, current_piece, PIECE_SIZE, uvdir); 335 | 336 | 337 | ballpoint moonpoint = pointOnBall(uvdir, moonpos, moonsize, moonaxis, moonback, moonleft); 338 | if (moonpoint.valid) { 339 | // put the coordinates from latitude-longitude into x-y in a [0;1] range 340 | // the `- .1*time` bit on latitude is to make the moon appear to spin 341 | vec2 mooncoords = vec2(mod(moonpoint.latitude - .1*time, TAU)/TAU, moonpoint.longitude/PI +.5); 342 | 343 | // from those coordinates, poll the texture and the normal map 344 | colour = Texel(moontex, mooncoords).xyz; 345 | vec3 mapnormal = Texel(moonnor, mooncoords).xyz * 2. - 1.; 346 | // scaling the normal's z and renormalising it to affect the "bumpiness" 347 | mapnormal.z *= .5; 348 | mapnormal = normalize(mapnormal); 349 | 350 | // apply a filter that literally maps the coordinates to hsl because rainbow 351 | colour *= hsl2rgb(vec3(mooncoords.x, 1., mooncoords.y)); 352 | // also apply the normalmap for shadows 353 | colour *= (.5+.5*dot(moonpoint.normaltransform * mapnormal, -uvdir)); 354 | } 355 | return vec4(colour, 1.); 356 | } 357 | #endif 358 | 359 | #ifdef VERTEX 360 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 361 | return transform_projection * vertex_position; 362 | } 363 | #endif 364 | -------------------------------------------------------------------------------- /shaders/blur.glsl: -------------------------------------------------------------------------------- 1 | uniform vec2 Direction; 2 | uniform int Spread; 3 | 4 | float gaussian(float x) { 5 | return exp(-.5*pow(x, 2)); 6 | } 7 | 8 | #ifdef PIXEL 9 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 10 | vec3 result = vec3(0.); 11 | float weightsum = 0.; 12 | for (int i = 0; i <= 10; i++) { 13 | vec2 vtex = i*Direction*Spread/4000; 14 | float weight = gaussian(length(vtex)*Spread); 15 | weightsum += weight; 16 | vec3 left = Texel(image, uvs - vtex).xyz; 17 | vec3 right = Texel(image, uvs + vtex).xyz; 18 | result += .5*weight*(left + right); 19 | } 20 | 21 | return vec4(result/weightsum, 1.); 22 | } 23 | #endif 24 | 25 | #ifdef VERTEX 26 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 27 | return transform_projection * vertex_position; 28 | } 29 | #endif 30 | -------------------------------------------------------------------------------- /shaders/chroma-misalign.glsl: -------------------------------------------------------------------------------- 1 | #define UV_CENTRE vec2(0.5, 0.5) 2 | 3 | #ifdef PIXEL 4 | extern mat3x3 shift; 5 | extern vec3 angle; 6 | extern vec2 distort; 7 | extern float time; 8 | 9 | float random(in float s){ 10 | return fract(sin(s)*100000.); 11 | } 12 | 13 | vec2 random2(in float s) { 14 | return vec2(random(s), random(s+1.)); 15 | } 16 | 17 | mat2 rotateOrigin(in float ang){ 18 | float c = cos(ang); 19 | float s = sin(ang); 20 | return mat2(c,-s, 21 | s, c); 22 | } 23 | 24 | vec2 rotateAround(in vec2 uv, in vec2 centre, in float angle) { 25 | return ( (uv-centre) * rotateOrigin(angle) ) + centre; 26 | } 27 | 28 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 29 | // vec4 pixel = Texel(image, uvs); 30 | vec4 pixel = vec4(0.); 31 | vec4 sampleR = Texel(image, random2(time+0.+uvs.x+uvs.y)*distort + rotateAround(uvs, UV_CENTRE, angle.r)+shift[0].xy); 32 | vec4 sampleG = Texel(image, random2(time+2.+uvs.x+uvs.y)*distort + rotateAround(uvs, UV_CENTRE, angle.g)+shift[1].xy); 33 | vec4 sampleB = Texel(image, random2(time+4.+uvs.x+uvs.y)*distort + rotateAround(uvs, UV_CENTRE, angle.b)+shift[2].xy); 34 | pixel.ra += sampleR.r / sampleR.a; 35 | pixel.ga += sampleG.g / sampleG.a; 36 | pixel.ba += sampleB.b / sampleB.a; 37 | pixel.a /= 3; 38 | 39 | // return vec4(pixelr, pixelg, pixelb, color.a); 40 | return pixel; 41 | } 42 | #endif 43 | 44 | #ifdef VERTEX 45 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 46 | return transform_projection * vertex_position; 47 | } 48 | #endif -------------------------------------------------------------------------------- /shaders/creditball.glsl: -------------------------------------------------------------------------------- 1 | uniform float dt; 2 | uniform float time; 3 | 4 | #define PI 3.141592653589793238463 5 | #define TAU 6.283185307179586476925 6 | 7 | #define SCREENHEIGHT .35 8 | uniform Image moontex; 9 | uniform Image moonnor; 10 | 11 | const vec3 LIGHTDIR = vec3(0., 0., -1.); 12 | #define AMIGAROWS 6. 13 | #define AMIGACOLS 12. 14 | 15 | struct ballpoint { 16 | bool valid; 17 | float longitude; 18 | float latitude; 19 | vec3 normal; 20 | mat3 normaltransform; 21 | }; 22 | 23 | ballpoint pointOnBall(vec3 uvdir, vec3 ballpos, float ballsize, vec3 ballaxis, vec3 ballback, vec3 ballleft) { 24 | // assuming all vec3s except ballpos are normalized 25 | 26 | // get point closest to ball 27 | float d = dot(uvdir, ballpos); 28 | vec3 closest = d * uvdir; 29 | float mindist = distance(closest, ballpos); 30 | if (mindist < ballsize && d >= 0) { 31 | // actually hitting the ball, draw it 32 | 33 | // the contact point, the closest point and the centre of the ball make a right triangle 34 | // so we can get the length between the first two from the other distances 35 | // we then move back along the beam that distance to get the contact point 36 | vec3 surfacepoint = closest - uvdir * sqrt(ballsize*ballsize - mindist*mindist); 37 | vec3 surfacevec = normalize(surfacepoint - ballpos); 38 | 39 | // longitude and latitude calculations for texture position 40 | // shoutouts to https://stackoverflow.com/questions/5674149/3d-coordinates-on-a-sphere-to-latitude-and-longitude 41 | float longitude = .5*PI-acos(dot(surfacevec, ballaxis)); 42 | float latitude = atan(dot(ballback, surfacevec), dot(ballleft, surfacevec)); 43 | float clo = cos(longitude), slo = sin(longitude); 44 | float cla = cos(latitude) , sla = sin(latitude) ; 45 | mat3 transform = mat3(ballback, ballaxis, ballleft) 46 | * mat3(cla, 0 ,-sla, 47 | 0 , 1 , 0 , 48 | sla, 0 , cla) 49 | * mat3(1 , 0 , 0 , 50 | 0 , clo,-slo, 51 | 0 , slo, clo) 52 | ; 53 | return ballpoint(true, longitude, latitude, surfacevec, transform); 54 | } 55 | return ballpoint(false, 0., 0., vec3(0.), mat3(0.)); 56 | } 57 | 58 | //from https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 // 59 | float rand(vec2 n) { 60 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 61 | } 62 | 63 | float noise(vec2 p){ 64 | vec2 ip = floor(p); 65 | vec2 u = fract(p); 66 | u = u*u*(3.0-2.0*u); 67 | 68 | float res = mix( 69 | mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x), 70 | mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y); 71 | return res*res; 72 | } 73 | 74 | #define NUM_OCTAVES 5 75 | float fbm(vec2 x) { 76 | float v = 0.0; 77 | float a = 0.5; 78 | vec2 shift = vec2(100); 79 | // Rotate to reduce axial bias 80 | mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.50)); 81 | for (int i = 0; i < NUM_OCTAVES; ++i) { 82 | v += a * noise(x); 83 | x = rot * x * 2.0 + shift; 84 | a *= 0.5; 85 | } 86 | return v; 87 | } 88 | 89 | /////////////////////////////////////////////////////////////////////////// 90 | 91 | #ifdef PIXEL 92 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 93 | //scaling uvs 94 | vec2 uv2 = uvs - .5; 95 | uv2.x *= love_ScreenSize.x/love_ScreenSize.y; 96 | uv2 *= 2*SCREENHEIGHT; 97 | 98 | // background colour 99 | vec4 newpixel = vec4(0.); 100 | // direction of camera pixel 101 | vec3 uvdir = normalize(vec3(uv2, 1)); 102 | 103 | vec3 moonpos = vec3(-300., -250.+10.*cos(time*.2), 1000.); 104 | vec3 moonaxis = normalize(vec3(.3, cos(time*.04), sin(time*.04))); 105 | vec3 moonback = normalize(cross(moonaxis, vec3(0.,0.,1.))); 106 | vec3 moonleft = cross(moonaxis, moonback); 107 | 108 | ballpoint moonpoint = pointOnBall(uvdir, moonpos, 100., moonaxis, moonback, moonleft); 109 | if (moonpoint.valid) { 110 | // test 111 | // newpixel.xyz = vec3(longitude, latitude, 0.); 112 | // moon texture 113 | moonpoint.latitude = mod(moonpoint.latitude - .01*time, TAU); 114 | newpixel = Texel(moontex, vec2(moonpoint.latitude/TAU, moonpoint.longitude/PI +.5)); 115 | vec3 mapnormal = Texel(moonnor, vec2(moonpoint.latitude/TAU, moonpoint.longitude/PI +.5)).xyz * 2. - 1.; 116 | newpixel.rgb *= dot(moonpoint.normaltransform * mapnormal, LIGHTDIR); 117 | newpixel.a = 1; 118 | 119 | } else { 120 | // floating amigaball 121 | vec3 amigapos = vec3(100., 65.+10.*cos(time*.2), 400.); 122 | vec3 amigaaxis = normalize(vec3(5.+cos(time*.04), -10.+sin(time*.04), -5.)); 123 | vec3 amigaback = normalize(cross(amigaaxis, vec3(0.,0.,1.))); 124 | vec3 amigaleft = cross(amigaaxis, amigaback); 125 | 126 | ballpoint amigapoint = pointOnBall(uvdir, amigapos, 65., amigaaxis, amigaback, amigaleft); 127 | if (amigapoint.valid) { 128 | amigapoint.latitude = mod(amigapoint.latitude - time*.25, TAU); 129 | // amiga ball 130 | // checkerboard on longitude and latitude 131 | float red = mod(floor(amigapoint.longitude/PI*AMIGAROWS) + floor(amigapoint.latitude/TAU*AMIGACOLS), 2); 132 | newpixel.rgb = vec3(1.,red,red) * (dot(amigapoint.normal, -uvdir)*.7+.3); 133 | newpixel.a = 1; 134 | } 135 | } 136 | return newpixel; 137 | } 138 | #endif 139 | 140 | #ifdef VERTEX 141 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 142 | return transform_projection * vertex_position; 143 | } 144 | #endif 145 | -------------------------------------------------------------------------------- /shaders/displacementnormals.glsl: -------------------------------------------------------------------------------- 1 | // adapted from https://stackoverflow.com/questions/5281261/generating-a-normal-map-from-a-height-map // 2 | uniform vec3 off; // will be specified when generating the normal map 3 | const vec2 size = vec2(.3,0.0); 4 | #ifdef PIXEL 5 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 6 | float s01 = Texel(image, uvs - off.xz).x; 7 | float s21 = Texel(image, uvs + off.xz).x; 8 | float s10 = Texel(image, uvs - off.zy).x; 9 | float s12 = Texel(image, uvs + off.zy).x; 10 | vec3 va = normalize(vec3(size.xy,s21-s01)); 11 | vec3 vb = normalize(vec3(size.yx,s12-s10)); 12 | vec3 bump = normalize(cross(va,vb)); 13 | 14 | return vec4(bump*.5+.5, 1); 15 | } 16 | #endif 17 | 18 | #ifdef VERTEX 19 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 20 | return transform_projection * vertex_position; 21 | } 22 | #endif -------------------------------------------------------------------------------- /shaders/infinity.glsl: -------------------------------------------------------------------------------- 1 | uniform float time; 2 | 3 | 4 | /* 5 | 0102 0607 6 | 09 12 14 17 7 | 18 22 26 8 | 27 30 32 35 9 | 3738 4243 10 | */ 11 | // 20 pixels are in the loop (double counting the crossing point) 12 | #define delta 0.05 13 | 14 | // from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl // 15 | const vec4 K = vec4(1., 2. / 3., 1. / 3., 3.); 16 | vec3 hsv2rgb(vec3 c) { 17 | vec3 p = abs(fract(c.xxx + K.xyz) * 6. - K.www); 18 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0., 1.), c.y); 19 | } 20 | ////////////////////////////////////////////////////////////////// 21 | 22 | #ifdef PIXEL 23 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 24 | int index = int(uvs.y*5)*9+int(uvs.x*9); 25 | float offset; 26 | if (index == 22) offset = min(fract(time), fract(time+.5)); // this is either 0*delta or 10*delta 27 | 28 | else if (index == 12) offset = fract(time + 1*delta); 29 | else if (index == 2) offset = fract(time + 2*delta); 30 | else if (index == 1) offset = fract(time + 3*delta); 31 | else if (index == 9) offset = fract(time + 4*delta); 32 | else if (index == 18) offset = fract(time + 5*delta); 33 | else if (index == 27) offset = fract(time + 6*delta); 34 | else if (index == 37) offset = fract(time + 7*delta); 35 | else if (index == 38) offset = fract(time + 8*delta); 36 | else if (index == 30) offset = fract(time + 9*delta); 37 | 38 | else if (index == 14) offset = fract(time + 11*delta); 39 | else if (index == 6) offset = fract(time + 12*delta); 40 | else if (index == 7) offset = fract(time + 13*delta); 41 | else if (index == 17) offset = fract(time + 14*delta); 42 | else if (index == 26) offset = fract(time + 15*delta); 43 | else if (index == 35) offset = fract(time + 16*delta); 44 | else if (index == 43) offset = fract(time + 17*delta); 45 | else if (index == 42) offset = fract(time + 18*delta); 46 | else if (index == 32) offset = fract(time + 19*delta); 47 | 48 | else return vec4(0.); // not part of the infinity sign 49 | 50 | 51 | return vec4(hsv2rgb(vec3(offset+time*.5, .5, 1.-.5*offset)), 1.) * color; 52 | } 53 | #endif 54 | 55 | #ifdef VERTEX 56 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 57 | return transform_projection * vertex_position; 58 | } 59 | #endif -------------------------------------------------------------------------------- /shaders/nop.glsl: -------------------------------------------------------------------------------- 1 | #ifdef PIXEL 2 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 3 | vec4 pixel = Texel(image, uvs); 4 | 5 | return pixel * color; 6 | } 7 | #endif 8 | 9 | #ifdef VERTEX 10 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 11 | return transform_projection * vertex_position; 12 | } 13 | #endif -------------------------------------------------------------------------------- /shaders/rainbow.glsl: -------------------------------------------------------------------------------- 1 | uniform float time; 2 | 3 | // from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl // 4 | const vec4 K = vec4(1., 2. / 3., 1. / 3., 3.); 5 | vec3 hsv2rgb(vec3 c) { 6 | vec3 p = abs(fract(c.xxx + K.xyz) * 6. - K.www); 7 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0., 1.), c.y); 8 | } 9 | ////////////////////////////////////////////////////////////////// 10 | 11 | #ifdef PIXEL 12 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 13 | vec4 pixel = Texel(image, uvs); 14 | return pixel * color * vec4(hsv2rgb(vec3(time + uvs.x, 0.5, 1)), 1); 15 | } 16 | #endif 17 | 18 | #ifdef VERTEX 19 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 20 | return transform_projection * vertex_position; 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /shaders/wave.glsl: -------------------------------------------------------------------------------- 1 | #define MAXDIST .005 2 | #define SAMPLES 50 3 | #define XSTEP 1./SAMPLES 4 | 5 | uniform float time; 6 | uniform float dt; 7 | uniform float[SAMPLES+1] soundL; 8 | uniform float[SAMPLES+1] soundR; 9 | uniform float avgL; 10 | uniform float avgR; 11 | #ifdef PIXEL 12 | 13 | vec2 proj(vec2 a, vec2 b, vec2 m) { 14 | vec2 v = b-a; 15 | vec2 w = m-a; 16 | return a+(b-a)*clamp(dot(v,w)/dot(v,v),0,1); 17 | } 18 | 19 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 20 | /* 21 | vec4 oldpixel = Texel(image, uvs); 22 | return oldpixel * color; 23 | */ 24 | 25 | float dL = MAXDIST*2; 26 | float dR = MAXDIST*2; 27 | // float avg = 0.; 28 | 29 | for (int i = 0; i < SAMPLES; i++) { 30 | dL = min(dL, distance(uvs, proj(vec2(i*XSTEP, .5 + .5*soundL[i]), vec2((i+1)*XSTEP, .5 + .5*soundL[i+1]), uvs))); 31 | dR = min(dR, distance(uvs, proj(vec2(i*XSTEP, .5 + .5*soundR[i]), vec2((i+1)*XSTEP, .5 + .5*soundR[i+1]), uvs))); 32 | // avg += abs(soundL[i]*soundR[i])/SAMPLES; 33 | } 34 | float resL = smoothstep(-MAXDIST, 0, -dL); 35 | float resR = smoothstep(-MAXDIST, 0, -dR); 36 | return vec4(resL+avgL, max(resL, resR), resR+avgR, 1); 37 | } 38 | #endif 39 | 40 | #ifdef VERTEX 41 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 42 | return transform_projection * vertex_position; 43 | } 44 | #endif 45 | -------------------------------------------------------------------------------- /shaders/wave2.glsl: -------------------------------------------------------------------------------- 1 | #define SAMPLES 200 2 | #define TAU 6.28318530717958647692528676655900576839 3 | #define MAXFREQ .5*samplerate 4 | #define MINFREQ SAMPLES/samplerate 5 | 6 | uniform float dt; 7 | uniform int samplerate; 8 | uniform float[SAMPLES+1] soundL; 9 | uniform float[SAMPLES+1] soundR; 10 | 11 | 12 | const vec4 K = vec4(1., 2. / 3., 1. / 3., 3.); 13 | vec3 hsv2rgb(vec3 c) { 14 | // from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl 15 | vec3 p = abs(fract(c.xxx + K.xyz) * 6. - K.www); 16 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0., 1.), c.y); 17 | } 18 | 19 | vec2 ftL(float freq) { 20 | vec2 sum = vec2(0.,0.); 21 | float arg = 0.; 22 | for (int i = 0; i < soundL.length(); i++) { 23 | arg = -freq * TAU * i / samplerate; 24 | sum += vec2(cos(arg), sin(arg)) * soundL[i]; 25 | } 26 | return sum; 27 | } 28 | 29 | vec2 ftR(float freq) { 30 | vec2 sum = vec2(0.,0.); 31 | float arg = 0.; 32 | for (int i = 0; i < soundR.length(); i++) { 33 | arg = -freq * TAU * i / samplerate; 34 | sum += vec2(cos(arg), sin(arg)) * soundR[i]; 35 | } 36 | return sum; 37 | } 38 | 39 | #ifdef PIXEL 40 | vec4 effect(vec4 color, Image image, vec2 uvs, vec2 screen_coords){ 41 | 42 | vec4 oldpixel = Texel(image, uvs); 43 | // return oldpixel * color; 44 | 45 | // float avg = 0.; 46 | 47 | 48 | // uvs.x -= .5; 49 | float freq = mix(MINFREQ, MAXFREQ, distance(uvs, vec2(.5))); 50 | float fR = length(ftR(freq)); 51 | float fL = length(ftL(freq)); 52 | vec4 newpixel = vec4(fR*fR, fL*fL, fR*fL, 1); 53 | return mix(oldpixel, newpixel, pow(.15, 60*dt)); 54 | } 55 | #endif 56 | 57 | #ifdef VERTEX 58 | vec4 position(mat4 transform_projection, vec4 vertex_position){ 59 | return transform_projection * vertex_position; 60 | } 61 | #endif 62 | -------------------------------------------------------------------------------- /speedcurve.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | List of gamemode speedcurves for example block game 3 | Copyright (C) 2021 Lilla Oshisaure 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | ]==] 18 | 19 | -- classic mode speedcurve is in the form a*b^lv+c 20 | -- with speed(lv=0) = 1; speed(lv=20) = 30; speed(lv=30) = 60 21 | -- a*b^0+c = 1 => c = 1-a 22 | -- a*b^20+(1-a) = a*(b^20-1) + 1 = 30 => a = 29/(b^20-1) 23 | -- then idk solve 1+29*(b^30-1)/(b^20-1) = 60 in wolfram alpha or something 24 | local b = ((1095^0.5+15)/29)^0.1 25 | local a = 29/(b^20 - 1) 26 | local function classic_gravity (level) return 1 + a * (b^(level-1) - 1) end 27 | local function classic_lockdelay(level) return 1 / classic_gravity(level) end 28 | 29 | local tensdigits = [=[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#]=] 30 | local possibledigits = #tensdigits 31 | local function classic_levelname(level) 32 | local lv = level - 1 33 | local tens = math.floor(lv/10)+1 34 | if tens > possibledigits then tens = possibledigits end 35 | return tensdigits:sub(tens, tens)..(lv%10) 36 | end 37 | local parameters = { 38 | "level_name", 39 | "gravity", 40 | "lock_delay", 41 | "spawn_delay", 42 | "spawn_delay_after_line", 43 | "line_delay", 44 | "BGM", 45 | } 46 | 47 | local procedural_mt = { 48 | __index = function(self, i) 49 | -- print(self, i) 50 | if type(i) ~= "number" then return nil end 51 | local entry = {} 52 | for _, param in ipairs(parameters) do 53 | -- print(param, self[param]) 54 | entry[param] = self[param](i) 55 | end 56 | return entry 57 | end, 58 | } 59 | 60 | Levels = { 61 | {name = "Beginner", description = "An easier 10-level mode for people who are new to block games", 62 | LV = "10L", prev = 7, hold = true, colour = {0.75,0.95,1.00,1}, BG = "beginner", maxstart = 10, 63 | {level_name = "01", gravity = 0.15, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.5, line_delay = 0.5, AS_delay = 0.3, BGM = 1}, 64 | {level_name = "02", gravity = 0.25, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.5, line_delay = 0.5, AS_delay = 0.3, BGM = 1}, 65 | {level_name = "03", gravity = 0.40, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.5, line_delay = 0.5, AS_delay = 0.3, BGM = 1}, 66 | {level_name = "04", gravity = 0.65, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.5, line_delay = 0.5, AS_delay = 0.3, BGM = 1}, 67 | {level_name = "05", gravity = 0.80, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.5, line_delay = 0.5, AS_delay = 0.3, BGM = 1}, 68 | 69 | {level_name = "06", gravity = 0.90, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.5, line_delay = 0.5, AS_delay = 0.3, BGM = 2}, 70 | {level_name = "07", gravity = 1.10, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.5, line_delay = 0.5, AS_delay = 0.3, BGM = 2}, 71 | {level_name = "08", gravity = 1.45, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.5, line_delay = 0.5, AS_delay = 0.3, BGM = 2}, 72 | {level_name = "09", gravity = 1.75, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.5, line_delay = 0.5, AS_delay = 0.3, BGM = 2}, 73 | {level_name = "10", gravity = 2.00, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.5, line_delay = 0.5, AS_delay = 0.3, BGM = 2}, 74 | 75 | {level_name = math.huge, gravity = 2.00, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.5, line_delay = 0.5, AS_delay = 0.3, BGM = 2}, 76 | }, 77 | 78 | {name = "Standard", description = "A fairly balanced 20-level mode going from slow to fast gravity", 79 | LV = "10L", prev = 6, hold = true, colour = {0.85,0.85,0.85,1}, BG = "standard", maxstart = 20, 80 | {level_name = "01", gravity = 1.0, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 2}, 81 | {level_name = "02", gravity = 1.1, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 2}, 82 | {level_name = "03", gravity = 1.2, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 2}, 83 | {level_name = "04", gravity = 1.4, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 2}, 84 | {level_name = "05", gravity = 1.7, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 2}, 85 | 86 | {level_name = "06", gravity = 2, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 2}, 87 | {level_name = "07", gravity = 3, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 2}, 88 | {level_name = "08", gravity = 4, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 2}, 89 | {level_name = "09", gravity = 6, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 2}, 90 | {level_name = "10", gravity = 8, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 2}, 91 | 92 | {level_name = "11", gravity = 10, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 3}, 93 | {level_name = "12", gravity = 12, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 3}, 94 | {level_name = "13", gravity = 15, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 3}, 95 | {level_name = "14", gravity = 20, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 3}, 96 | {level_name = "15", gravity = 30, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 3}, 97 | 98 | {level_name = "16", gravity = 060, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 3}, 99 | {level_name = "17", gravity = 090, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 3}, 100 | {level_name = "18", gravity = 120, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 3}, 101 | {level_name = "19", gravity = 180, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 3}, 102 | {level_name = "20", gravity = 300, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 3}, 103 | 104 | {level_name = math.huge, gravity = 300, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 3}, 105 | }, 106 | 107 | -- Original build speed curve 108 | {name = "Original", description = "A 36-level mode covering a wide range of speeds", 109 | LV = "10L", prev = 5, hold = true, colour = {0.90,0.75,1.00,1}, BG = "original", maxstart = 36, 110 | {level_name = "01", gravity = 1.0, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 2}, 111 | {level_name = "02", gravity = 1.1, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 2}, 112 | {level_name = "03", gravity = 1.2, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 2}, 113 | {level_name = "04", gravity = 1.4, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 2}, 114 | {level_name = "05", gravity = 1.7, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 2}, 115 | 116 | {level_name = "06", gravity = 2, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 2}, 117 | {level_name = "07", gravity = 3, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 2}, 118 | {level_name = "08", gravity = 4, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 2}, 119 | {level_name = "09", gravity = 6, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 2}, 120 | {level_name = "10", gravity = 8, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 2}, 121 | 122 | {level_name = "11", gravity = 10, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 3}, 123 | {level_name = "12", gravity = 12, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 3}, 124 | {level_name = "13", gravity = 15, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 3}, 125 | {level_name = "14", gravity = 20, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 3}, 126 | {level_name = "15", gravity = 30, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 3}, 127 | 128 | {level_name = "16", gravity = 060, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 3}, 129 | {level_name = "17", gravity = 090, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 3}, 130 | {level_name = "18", gravity = 120, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 3}, 131 | {level_name = "19", gravity = 180, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 3}, 132 | {level_name = "20", gravity = 300, lock_delay = 2, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.5, AS_delay = 0.25, BGM = 3}, 133 | 134 | {level_name = "X1", gravity = math.huge, lock_delay = 2.00, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.4, AS_delay = 0.15, BGM = 4}, --0.5PPS 135 | {level_name = "X2", gravity = math.huge, lock_delay = 1.67, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.4, AS_delay = 0.15, BGM = 4}, --0.6PPS 136 | {level_name = "X3", gravity = math.huge, lock_delay = 1.43, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.4, AS_delay = 0.15, BGM = 4}, --0.7PPS 137 | {level_name = "X4", gravity = math.huge, lock_delay = 1.25, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.4, AS_delay = 0.15, BGM = 4}, --0.5PPS 138 | {level_name = "X5", gravity = math.huge, lock_delay = 1.11, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.4, AS_delay = 0.15, BGM = 4}, --0.9PPS 139 | 140 | {level_name = "X6", gravity = math.huge, lock_delay = 1.00, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.3, AS_delay = 0.15, BGM = 4}, --1.0PPS 141 | {level_name = "X7", gravity = math.huge, lock_delay = 0.80, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.3, AS_delay = 0.15, BGM = 4}, --1.2PPS 142 | {level_name = "X8", gravity = math.huge, lock_delay = 0.75, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.3, AS_delay = 0.15, BGM = 4}, --1.3PPS 143 | {level_name = "X9", gravity = math.huge, lock_delay = 0.67, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.3, AS_delay = 0.15, BGM = 4}, --1.5PPS 144 | {level_name = "XX", gravity = math.huge, lock_delay = 0.50, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.3, AS_delay = 0.15, BGM = 4}, --2.0PPS 145 | 146 | {level_name = "PB", gravity = math.huge, lock_delay = 0.45, spawn_delay = 0.25, spawn_delay_after_line = 0.20, line_delay = 0.2, AS_delay = 0.1, BGM = 5}, --2.2PPS 147 | {level_name = "SN", gravity = math.huge, lock_delay = 0.40, spawn_delay = 0.25, spawn_delay_after_line = 0.20, line_delay = 0.2, AS_delay = 0.1, BGM = 5}, --2.5PPS 148 | {level_name = "FE", gravity = math.huge, lock_delay = 0.35, spawn_delay = 0.25, spawn_delay_after_line = 0.20, line_delay = 0.2, AS_delay = 0.1, BGM = 5}, --2.9PPS 149 | {level_name = "CU", gravity = math.huge, lock_delay = 0.30, spawn_delay = 0.25, spawn_delay_after_line = 0.20, line_delay = 0.2, AS_delay = 0.1, BGM = 5}, --3.3PPS 150 | {level_name = "AG", gravity = math.huge, lock_delay = 0.25, spawn_delay = 0.25, spawn_delay_after_line = 0.20, line_delay = 0.2, AS_delay = 0.1, BGM = 5}, --4.0PPS 151 | 152 | {level_name = "AU", gravity = math.huge, lock_delay = 0.20, spawn_delay = 0.25, spawn_delay_after_line = 0.20, line_delay = 0.1, AS_delay = 0.1, BGM = 5}, --5.0PPS 153 | 154 | {level_name = math.huge, gravity = math.huge, lock_delay = 0.20, spawn_delay = 0.25, spawn_delay_after_line = 0.20, line_delay = 0.1, AS_delay = 0.1, BGM = 5}, 155 | }, 156 | 157 | {name = "Master", description = "A 30-level mode that starts on infinite gravity, yet keeps going faster", 158 | LV = "10L", prev = 5, hold = true, colour = {0.35,0.10,0.15,1}, BG = "master", maxstart = 30, 159 | {level_name = "M01", gravity = math.huge, lock_delay = 1.000, spawn_delay = 0.500, spawn_delay_after_line = 0.500, line_delay = 0.375, AS_delay = 0.150, BGM = 4}, 160 | {level_name = "M02", gravity = math.huge, lock_delay = 0.946, spawn_delay = 0.473, spawn_delay_after_line = 0.473, line_delay = 0.355, AS_delay = 0.150, BGM = 4}, 161 | {level_name = "M03", gravity = math.huge, lock_delay = 0.895, spawn_delay = 0.447, spawn_delay_after_line = 0.447, line_delay = 0.336, AS_delay = 0.150, BGM = 4}, 162 | {level_name = "M04", gravity = math.huge, lock_delay = 0.847, spawn_delay = 0.423, spawn_delay_after_line = 0.423, line_delay = 0.317, AS_delay = 0.150, BGM = 4}, 163 | {level_name = "M05", gravity = math.huge, lock_delay = 0.801, spawn_delay = 0.400, spawn_delay_after_line = 0.400, line_delay = 0.300, AS_delay = 0.150, BGM = 4}, 164 | 165 | {level_name = "M06", gravity = math.huge, lock_delay = 0.758, spawn_delay = 0.379, spawn_delay_after_line = 0.379, line_delay = 0.284, AS_delay = 0.150, BGM = 4}, 166 | {level_name = "M07", gravity = math.huge, lock_delay = 0.717, spawn_delay = 0.358, spawn_delay_after_line = 0.358, line_delay = 0.268, AS_delay = 0.150, BGM = 4}, 167 | {level_name = "M08", gravity = math.huge, lock_delay = 0.678, spawn_delay = 0.339, spawn_delay_after_line = 0.339, line_delay = 0.254, AS_delay = 0.150, BGM = 4}, 168 | {level_name = "M09", gravity = math.huge, lock_delay = 0.641, spawn_delay = 0.321, spawn_delay_after_line = 0.321, line_delay = 0.241, AS_delay = 0.150, BGM = 4}, 169 | {level_name = "M10", gravity = math.huge, lock_delay = 0.607, spawn_delay = 0.303, spawn_delay_after_line = 0.303, line_delay = 0.228, AS_delay = 0.150, BGM = 4}, 170 | 171 | {level_name = "M11", gravity = math.huge, lock_delay = 0.574, spawn_delay = 0.287, spawn_delay_after_line = 0.287, line_delay = 0.215, AS_delay = 0.150, BGM = 5}, 172 | {level_name = "M12", gravity = math.huge, lock_delay = 0.543, spawn_delay = 0.272, spawn_delay_after_line = 0.272, line_delay = 0.204, AS_delay = 0.150, BGM = 5}, 173 | {level_name = "M13", gravity = math.huge, lock_delay = 0.514, spawn_delay = 0.257, spawn_delay_after_line = 0.257, line_delay = 0.193, AS_delay = 0.150, BGM = 5}, 174 | {level_name = "M14", gravity = math.huge, lock_delay = 0.486, spawn_delay = 0.243, spawn_delay_after_line = 0.243, line_delay = 0.182, AS_delay = 0.150, BGM = 5}, 175 | {level_name = "M15", gravity = math.huge, lock_delay = 0.460, spawn_delay = 0.230, spawn_delay_after_line = 0.230, line_delay = 0.172, AS_delay = 0.150, BGM = 5}, 176 | 177 | {level_name = "M16", gravity = math.huge, lock_delay = 0.435, spawn_delay = 0.217, spawn_delay_after_line = 0.217, line_delay = 0.163, AS_delay = 0.150, BGM = 5}, 178 | {level_name = "M17", gravity = math.huge, lock_delay = 0.411, spawn_delay = 0.206, spawn_delay_after_line = 0.206, line_delay = 0.154, AS_delay = 0.150, BGM = 5}, 179 | {level_name = "M18", gravity = math.huge, lock_delay = 0.389, spawn_delay = 0.195, spawn_delay_after_line = 0.195, line_delay = 0.146, AS_delay = 0.146, BGM = 5}, 180 | {level_name = "M19", gravity = math.huge, lock_delay = 0.368, spawn_delay = 0.184, spawn_delay_after_line = 0.184, line_delay = 0.138, AS_delay = 0.138, BGM = 5}, 181 | {level_name = "M20", gravity = math.huge, lock_delay = 0.348, spawn_delay = 0.174, spawn_delay_after_line = 0.174, line_delay = 0.131, AS_delay = 0.131, BGM = 5}, 182 | 183 | {level_name = "M21", gravity = math.huge, lock_delay = 0.330, spawn_delay = 0.165, spawn_delay_after_line = 0.165, line_delay = 0.124, AS_delay = 0.124, BGM = 6}, 184 | {level_name = "M22", gravity = math.huge, lock_delay = 0.312, spawn_delay = 0.156, spawn_delay_after_line = 0.156, line_delay = 0.117, AS_delay = 0.117, BGM = 6}, 185 | {level_name = "M23", gravity = math.huge, lock_delay = 0.295, spawn_delay = 0.147, spawn_delay_after_line = 0.147, line_delay = 0.111, AS_delay = 0.111, BGM = 6}, 186 | {level_name = "M24", gravity = math.huge, lock_delay = 0.279, spawn_delay = 0.140, spawn_delay_after_line = 0.140, line_delay = 0.105, AS_delay = 0.105, BGM = 6}, 187 | {level_name = "M25", gravity = math.huge, lock_delay = 0.264, spawn_delay = 0.132, spawn_delay_after_line = 0.132, line_delay = 0.099, AS_delay = 0.099, BGM = 6}, 188 | 189 | {level_name = "M26", gravity = math.huge, lock_delay = 0.250, spawn_delay = 0.125, spawn_delay_after_line = 0.125, line_delay = 0.094, AS_delay = 0.094, BGM = 6}, 190 | {level_name = "M27", gravity = math.huge, lock_delay = 0.236, spawn_delay = 0.118, spawn_delay_after_line = 0.118, line_delay = 0.089, AS_delay = 0.089, BGM = 6}, 191 | {level_name = "M28", gravity = math.huge, lock_delay = 0.223, spawn_delay = 0.112, spawn_delay_after_line = 0.112, line_delay = 0.084, AS_delay = 0.084, BGM = 6}, 192 | {level_name = "M29", gravity = math.huge, lock_delay = 0.211, spawn_delay = 0.106, spawn_delay_after_line = 0.106, line_delay = 0.079, AS_delay = 0.079, BGM = 6}, 193 | {level_name = "M30", gravity = math.huge, lock_delay = 0.200, spawn_delay = 0.100, spawn_delay_after_line = 0.100, line_delay = 0.075, AS_delay = 0.075, BGM = 6}, 194 | 195 | {level_name = -math.huge, gravity = math.huge, lock_delay = 0.200, spawn_delay = 0.100, spawn_delay_after_line = 0.100, line_delay = 0.075, AS_delay = 0.075, BGM = 6}, 196 | }, 197 | 198 | {name = "Classic", description = "\"Block game is easy with hold and lock delay\" -- clueless block game non-player", 199 | LV = "10L", prev = 1, hold = false, colour = {0.60,0.95,0.85,1}, BG = "classic", maxstart = 30, procedural = true, no_harddrop = true, 200 | level_name = classic_levelname, 201 | gravity = classic_gravity, 202 | lock_delay = classic_lockdelay, 203 | spawn_delay = function() return 16/60 end, 204 | spawn_delay_after_line = function() return 16/60 end, 205 | line_delay = function() return 20/60 end, 206 | BGM = function() return 4 end 207 | }, 208 | 209 | -- Practice level (low-g) 210 | {name = "Zero-G Practice", description = "A practice mode with no gravity and infinite time to place your piece", 211 | LV = "10L", prev = 5, hold = true, colour = {0.65,0.75,0.80,1}, BG = "practice", 212 | {level_name = math.huge, gravity = 0, lock_delay = math.huge, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 1}, 213 | }, 214 | 215 | -- Practice level (inf-g) 216 | {name = "Max-G Practice", description = "A practice mode with infinite gravity but infinite time to place your piece", 217 | LV = "10L", prev = 5, hold = true, colour = {0.85,0.55,0.55,1}, BG = "practice2", 218 | {level_name = math.huge, gravity = math.huge, lock_delay = math.huge, spawn_delay = 0.5, spawn_delay_after_line = 0.25, line_delay = 0.25, AS_delay = 0.25, BGM = 4}, 219 | }, 220 | 221 | --TGM2 Death 222 | {name = "Death", description = "On the final week of March, the Grand Masters gathered around this unforgiving mode for a formidable celebration\n\nThey called it the Carnival of Death", 223 | LV = "SEC", prev = 1, hold = false, colour = {0.55,0.25,0.25,1}, BG = "death", maxstart = 10, 224 | {level_name = 0, gravity = math.huge, lock_delay = 30/60, spawn_delay = 18/60, spawn_delay_after_line = 14/60, line_delay = 12/60, AS_delay = 12/60, BGM = 4}, 225 | {level_name = 100, gravity = math.huge, lock_delay = 26/60, spawn_delay = 14/60, spawn_delay_after_line = 08/60, line_delay = 06/60, AS_delay = 12/60, BGM = 4}, 226 | {level_name = 200, gravity = math.huge, lock_delay = 22/60, spawn_delay = 14/60, spawn_delay_after_line = 08/60, line_delay = 06/60, AS_delay = 11/60, BGM = 4}, 227 | {level_name = 300, gravity = math.huge, lock_delay = 18/60, spawn_delay = 08/60, spawn_delay_after_line = 08/60, line_delay = 06/60, AS_delay = 10/60, BGM = 5}, 228 | {level_name = 400, gravity = math.huge, lock_delay = 15/60, spawn_delay = 07/60, spawn_delay_after_line = 07/60, line_delay = 05/60, AS_delay = 08/60, BGM = 5}, 229 | {level_name = 500, gravity = math.huge, lock_delay = 15/60, spawn_delay = 06/60, spawn_delay_after_line = 06/60, line_delay = 04/60, AS_delay = 08/60, BGM = 6}, 230 | {level_name = 600, gravity = math.huge, lock_delay = 15/60, spawn_delay = 06/60, spawn_delay_after_line = 06/60, line_delay = 04/60, AS_delay = 08/60, BGM = 6}, 231 | {level_name = 700, gravity = math.huge, lock_delay = 15/60, spawn_delay = 06/60, spawn_delay_after_line = 06/60, line_delay = 04/60, AS_delay = 08/60, BGM = 6}, 232 | {level_name = 800, gravity = math.huge, lock_delay = 15/60, spawn_delay = 06/60, spawn_delay_after_line = 06/60, line_delay = 04/60, AS_delay = 08/60, BGM = 6}, 233 | {level_name = 900, gravity = math.huge, lock_delay = 15/60, spawn_delay = 06/60, spawn_delay_after_line = 06/60, line_delay = 04/60, AS_delay = 08/60, BGM = 6}, 234 | {level_name = math.huge, gravity = math.huge, lock_delay = 15/60, spawn_delay = 06/60, spawn_delay_after_line = 06/60, line_delay = 04/60, AS_delay = 08/60, BGM = 6}, 235 | }, 236 | --[[ 237 | -- Server spec, won't show in the list 238 | ["serv"] = {name = "server spec", LV = "10L", prev = 0, hold = false, 239 | {level_name = "Practice", gravity = 0, lock_delay = math.huge, spawn_delay = 0.5, AS_delay = 0.25}, 240 | }, 241 | 242 | ["vs"] = {name = "versus", LV = "10L", prev = 5, hold = true, colour = {1,1,1,1}, 243 | {level_name = "06", gravity = 2, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 244 | {level_name = "07", gravity = 3, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 245 | {level_name = "08", gravity = 4, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 246 | {level_name = "09", gravity = 6, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 247 | {level_name = "10", gravity = 8, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 248 | 249 | {level_name = "11", gravity = 10, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 250 | {level_name = "12", gravity = 12, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 251 | {level_name = "13", gravity = 15, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 252 | {level_name = "14", gravity = 20, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 253 | {level_name = "15", gravity = 30, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 254 | 255 | {level_name = "16", gravity = 060, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 256 | {level_name = "17", gravity = 090, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 257 | {level_name = "18", gravity = 120, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 258 | {level_name = "19", gravity = 180, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 259 | {level_name = "20", gravity = 300, lock_delay = 2, spawn_delay = 0.5, AS_delay = 0.25}, 260 | 261 | {level_name = "X0", gravity = math.huge, lock_delay = 2.00, spawn_delay = 0.5, AS_delay = 0.15}, --0.5PPS 262 | }, 263 | ]] 264 | } 265 | 266 | for _, curve in ipairs(Levels) do 267 | if curve.procedural then setmetatable(curve, procedural_mt) end 268 | if not curve.maxlevel then curve.maxlevel = curve.procedural and math.huge or #curve end 269 | end -------------------------------------------------------------------------------- /splash.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | Splash logo screen for example block game 3 | Copyright (C) 2021 Lilla Oshisaure 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | ]==] 18 | 19 | local splashlogo = love.graphics.newImage("assets/splashscreen/splashlogo.png") 20 | local splashbg = love.graphics.newImage("assets/splashscreen/splashbg.png") 21 | local splashtext = love.graphics.newImage("assets/splashscreen/splashtext.png") 22 | local splashtime = 0 23 | local splashcanvas = love.graphics.newCanvas(1600, 900) 24 | 25 | 26 | function ResetSplashScreen(t) splashtime = t or 0 end -- idk maybe itll be useful 27 | 28 | function UpdateSplashScreen(dt) 29 | splashtime = math.min(splashtime + dt, 4) 30 | if STATE == "splash" and (love.keyboard.isDown("return") or splashtime == 4) then 31 | love.graphics.setCanvas(CanvasBG) 32 | DrawSplashScreen() 33 | love.graphics.setCanvas() 34 | STATE = "menu" 35 | SetBGM("menu") 36 | end 37 | end 38 | 39 | function DrawSplashScreen() 40 | local _c = love.graphics.getCanvas() 41 | love.graphics.push() 42 | love.graphics.translate(splashcanvas:getWidth()/2, splashcanvas:getHeight()/2) 43 | local t = math.max(0,splashtime) 44 | love.graphics.setCanvas(splashcanvas) 45 | love.graphics.clear() 46 | love.graphics.setColor(1,1,1,1) 47 | local ease2 = (math.sin(math.pi * (math.max(0, math.min((t-0.7)/0.5, 1)) - 0.5)) + 1) * 0.5 48 | love.graphics.draw(splashbg, -(-splashlogo:getWidth()/2+splashbg:getWidth())/2, 0, (ease2-1)*math.pi, ease2, 1, 0, splashbg:getHeight()/2) 49 | local ease1 = ((math.min(t/1.2, 1) - 1)^3 + 1) 50 | love.graphics.draw(splashlogo, -(-splashlogo:getWidth()/2+splashbg:getWidth())/2, 0, (ease1-1)*math.pi*10, ease1, ease1, splashlogo:getWidth()/2, splashlogo:getHeight()/2) 51 | love.graphics.setColor(1,1,1,(t-1.2)*2) 52 | love.graphics.draw(splashtext, -(-splashlogo:getWidth()/2+splashbg:getWidth())/2, 0, 0, 1, 1, 0, splashtext:getHeight()/2) 53 | 54 | love.graphics.setCanvas(_c) 55 | love.graphics.pop() 56 | love.graphics.setColor(1,1,1,1) 57 | love.graphics.draw(splashcanvas, Width/2, Height/2, 0, Height/900, Height/900, splashcanvas:getWidth()/2, splashcanvas:getHeight()/2) 58 | end -------------------------------------------------------------------------------- /title.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | Title screen for example block game 3 | Copyright (C) 2021 Lilla Oshisaure 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | ]==] 18 | 19 | Title = {current = "main"} 20 | 21 | local change = function(menu) return function(button) button.parent.highlight = 1; SaveConfig(); Title.current = menu end end 22 | local open = function(menu) return function(button) SaveConfig(); Title.current = menu end end 23 | 24 | Title.main = Menu.new("Menu", { 25 | {x = 0, y = -0.2, label = "START GAME", action_e = open("play")}, 26 | {x = 0, y = 0.0, label = "SETTINGS", action_e = open("settings")}, 27 | {x = 0, y = 0.2, label = "HIGH SCORES", action_e = open("highscores")}, 28 | {x = 0, y = 0.4, label = "CREDITS", 29 | action_e = function(button) 30 | STATE = "credits" 31 | ResetSplashScreen() 32 | end, 33 | }, 34 | --[[ 35 | {x = 0, y = 0.2, label = "Networking test", 36 | action_e = function(button) 37 | Game = Games.ss 38 | Game:reset(os.time()) 39 | Game.time = 0 40 | -- ServerThreadUDP:start() 41 | ServerThreadTCP:start() 42 | STATE = "serverspec" 43 | end, 44 | }, 45 | --]] 46 | }) 47 | 48 | --[[ 49 | Title.play = Menu.new(MenuFont, { 50 | {x = 0, y = -0.2, label = "SINGLEPLAYER\n< DIFFICULTY : REGULAR >", param = 1, 51 | action_e = function(button) 52 | Game = Games[button.param] 53 | SetBGM(1) 54 | Game:reset(os.time()) 55 | -- ConnectServer() 56 | STATE = "ingame" 57 | -- BGM:play() 58 | end, 59 | action_r = function(button) 60 | button.param = (button.param) % #Levels + 1 61 | button.label = ("SINGLEPLAYER\n< DIFFICULTY : %s >"):format(Levels[button.param].name) 62 | end, 63 | action_l = function(button) 64 | button.param = (button.param-2) % #Levels + 1 65 | button.label = ("SINGLEPLAYER\n< DIFFICULTY : %s >"):format(Levels[button.param].name) 66 | end, 67 | }, 68 | {x = 0, y = 0.1, label = "Local 1 vs 1", 69 | action_e = function() 70 | STATE = "battle2P" 71 | local seed = os.time() 72 | Games.P1:reset(seed, "Lilla") 73 | Games.P2:reset(seed, "Lilla") 74 | end 75 | }, 76 | {x = 0, y = 0.3, label = "BACK", action_e = change("main")}, 77 | }) 78 | --]] 79 | local today = os.date("*t") 80 | local carnivalhours = (today.month == 2 and today.day >= 28) 81 | or (today.month == 3) 82 | or (today.month == 4 and today.day == 1) 83 | local gamemodes = {} 84 | for i, mode in ipairs(Levels) do 85 | if mode.name ~= "Death" or #HighScores["Death"] > 0 or carnivalhours then 86 | table.insert(gamemodes, 87 | {x = -0.5, y = -0.5+0.1*i, label = mode.name, id = i, 88 | action_e = function(button) 89 | Game = Games[i] 90 | Game:reset(os.time(), button.parent.startlv) 91 | SetBGM(Game.BGM) 92 | Game.display_score = 999999999 -- 999,999,999 93 | if Config.dynamic_bg == "X" then 94 | SendShaderUniform("level", Game.level) 95 | SendShaderUniform("levelprev", Game.level) 96 | PrerenderBG(Game.speedcurve.BG) 97 | end 98 | -- ApplyZoneMod(Game) 99 | STATE = "ingame" 100 | end, 101 | action_l = function(button) 102 | button.parent.startlv = math.max(button.parent.startlv - 1, 1) 103 | end, 104 | action_r = function(button) 105 | button.parent.startlv = math.min(button.parent.startlv + 1, mode.maxstart or mode.maxlevel) 106 | end, 107 | } 108 | ) 109 | end 110 | end 111 | table.insert(gamemodes, {x = 0, y = 0.7, label = "BACK", action_e = change("main")}) 112 | Title.play = Menu.new("Menu", gamemodes) 113 | Title.play.startlv = 1 114 | Title.play.updateSelected = function(self, key) 115 | Menu.updateSelected(self, key) 116 | if key == "up" or key == "down" then self.startlv = 1 end 117 | local modeid = Title.play.items[Title.play.highlight].id 118 | 119 | if modeid then 120 | self.text:addf({{1,1,1}, Levels[modeid].description}, Width*0.4, "right", math.floor(Width*0.5), math.floor(Height*0.35)) 121 | local leveldisp = Levels[modeid][Title.play.startlv].level_name 122 | if leveldisp == math.huge then leveldisp = " " end 123 | self.text:addf({{1, 0.7, 0.5}, string.format("Start level: < %s >", leveldisp)}, Width, "center", 0, math.floor(Height*0.7)) 124 | end 125 | end 126 | Title.play:updateSelected() 127 | 128 | Title.settings = Menu.new("Menu", { 129 | {x = 0, y = -0.3, label = ("BGM VOLUME : < %d%% >"):format(Config.bgm_volume), 130 | action_e = function(button) 131 | Config.bgm_volume = tostring((tonumber(Config.bgm_volume) + 5) % 105) 132 | end, 133 | action_r = function(button) 134 | Config.bgm_volume = tostring(math.min(tonumber(Config.bgm_volume) + 5, 100)) 135 | end, 136 | action_l = function(button) 137 | Config.bgm_volume = tostring(math.max(tonumber(Config.bgm_volume) - 5, 0)) 138 | end, 139 | update = function(button) 140 | button.label = ("BGM VOLUME : < %d%% >"):format(Config.bgm_volume) 141 | SetBGMVolume(tonumber(Config.bgm_volume)/100) 142 | end 143 | }, 144 | {x = 0, y = -0.2, label = ("SFX VOLUME : < %d%% >"):format(Config.sfx_volume), 145 | action_e = function(button) 146 | Config.sfx_volume = tostring((tonumber(Config.sfx_volume) + 5) % 105) 147 | end, 148 | action_r = function(button) 149 | Config.sfx_volume = tostring(math.min(tonumber(Config.sfx_volume) + 5, 100)) 150 | end, 151 | action_l = function(button) 152 | Config.sfx_volume = tostring(math.max(tonumber(Config.sfx_volume) - 5, 0)) 153 | end, 154 | update = function(button) 155 | button.label = ("SFX VOLUME : < %d%% >"):format(Config.sfx_volume) 156 | SetSFXVolume(tonumber(Config.sfx_volume)/100) 157 | end 158 | }, 159 | 160 | {x = 0, y = 0.0, label = "KEYBOARD CONFIG", action_e = open("keyconf")}, 161 | {x = 0, y = 0.1, label = "GAMEPAD CONFIG", action_e = open("padconf")}, 162 | {x = 0, y = 0.3, label = "GRAPHICS OPTIONS", action_e = open("graphics")}, 163 | {x = 0, y = 0.4, label = "WINDOW OPTIONS", action_e = open("window")}, 164 | {x = 0, y = 0.7, label = "BACK", action_e = change("main")}, 165 | }) 166 | 167 | local keySettingsItems = {} 168 | local padSettingsItems = { 169 | {x = 0, y = -0.4, label = ("AXIS DEADZONE : < %d%% >"):format(Config.pad_deadzone), 170 | action_e = function(button) 171 | Config.pad_deadzone = tostring((tonumber(Config.pad_deadzone) + 5) % 105) 172 | end, 173 | action_r = function(button) 174 | Config.pad_deadzone = tostring(math.min(tonumber(Config.pad_deadzone) + 5, 100)) 175 | end, 176 | action_l = function(button) 177 | Config.pad_deadzone = tostring(math.max(tonumber(Config.pad_deadzone) - 5, 0)) 178 | end, 179 | update = function(button) 180 | button.label = ("AXIS DEADZONE : < %d%% >"):format(Config.pad_deadzone) 181 | end 182 | }, 183 | } 184 | for i = 1, #Bindlist do 185 | local kbt = {x = 0, y = -0.3+0.07*i, label = ("%s : %s"):format(Bindlist[i][2], (KeyBindings[Bindlist[i][1]] or ""):upper()), id = i, action_e = function(button) 186 | button.label = ("%s : [AWAITING INPUT]"):format(Bindlist[i][2]) 187 | STATE = "keyinput" 188 | end} 189 | table.insert(keySettingsItems, kbt) 190 | 191 | local cbt = {x = 0, y = -0.3+0.07*i, label = ("%s : %s"):format(Bindlist[i][2], (PadBindings[Bindlist[i][1]] or ""):upper()), id = i, action_e = function(button) 192 | button.label = ("%s : [AWAITING INPUT]"):format(Bindlist[i][2]) 193 | STATE = "padinput" 194 | end} 195 | table.insert(padSettingsItems, cbt) 196 | end 197 | 198 | table.insert(keySettingsItems, {x = 0, y = -0.07+0.07*#Bindlist, label = ("DONE"), id = i, action_e = change("settings")}) 199 | table.insert(padSettingsItems, {x = 0, y = -0.07+0.07*#Bindlist, label = ("DONE"), id = i, action_e = change("settings")}) 200 | 201 | Title.keyconf = Menu.new("Menu", keySettingsItems) 202 | Title.padconf = Menu.new("Menu", padSettingsItems) 203 | 204 | Title.graphics = Menu.new("Menu", { 205 | {x = 0, y = -0.4, param = 0, 206 | label = ("MATRIX SWAY AMPLITUDE: <%s>"):format(DrawBar(Config.sway_amplitude, 10)), 207 | action_e = function(button) 208 | Config.sway_amplitude = tostring((tonumber(Config.sway_amplitude) + 1) % 11) 209 | end, 210 | action_r = function(button) 211 | button.param = 1 212 | end, 213 | action_l = function(button) 214 | button.param = -1 215 | end, 216 | update = function(button) 217 | Config.sway_amplitude = tostring(Clamp(tonumber(Config.sway_amplitude) + button.param, 0, 10)) 218 | button.param = 0 219 | button.label = ("MATRIX SWAY AMPLITUDE: <%s>"):format(DrawBar(Config.sway_amplitude, 10)) 220 | end 221 | }, 222 | 223 | {x = 0, y = -0.3, param = 0, 224 | label = ("MATRIX SWAY SPEED: <%s>"):format(DrawBar(Config.sway_speed, 10)), 225 | action_e = function(button) 226 | Config.sway_speed = tostring(Mod1(tonumber(Config.sway_speed) + 1, 10)) 227 | end, 228 | action_r = function(button) 229 | button.param = 1 230 | end, 231 | action_l = function(button) 232 | button.param = -1 233 | end, 234 | update = function(button) 235 | Config.sway_speed = tostring(Clamp(tonumber(Config.sway_speed) + button.param, 1, 10)) 236 | button.param = 0 237 | button.label = ("MATRIX SWAY SPEED: <%s>"):format(DrawBar(Config.sway_speed, 10)) 238 | end 239 | }, 240 | 241 | {x = 0, y = -0.2, param = 0, label = ("MATRIX SWAY BOUNCINESS: <%s>"):format(DrawBar(Config.sway_bounciness, 10)), 242 | action_e = function(button) 243 | Config.sway_bounciness = tostring(Mod1(tonumber(Config.sway_bounciness) + 1, 10)) 244 | end, 245 | action_r = function(button) 246 | button.param = 1 247 | end, 248 | action_l = function(button) 249 | button.param = -1 250 | end, 251 | update = function(button) 252 | Config.sway_bounciness = tostring(Clamp(tonumber(Config.sway_bounciness) + button.param, 1, 10)) 253 | button.param = 0 254 | button.label = ("MATRIX SWAY BOUNCINESS: <%s>"):format(DrawBar(Config.sway_bounciness, 10)) 255 | end 256 | }, 257 | 258 | {x = 0, y = 0.0, label = ("DYNAMIC BACKGROUNDS : < %s >"):format(Config.dynamic_bg), 259 | update = function(button) 260 | Config.dynamic_bg = (Config.dynamic_bg == "O" and "X" or "O") 261 | button.label = ("DYNAMIC BACKGROUNDS : < %s >"):format(Config.dynamic_bg) 262 | if Config.dynamic_bg == "X" then PrerenderBG() end 263 | end 264 | }, 265 | 266 | {x = 0, y = 0.1, param = 0, label = ("BG REFRESH RATE CAP : < %sHz >"):format(Config.bg_framerate), 267 | action_e = function(button) 268 | Config.bg_framerate = tostring(Mod1(tonumber(Config.bg_framerate) + 5, 500)) 269 | end, 270 | action_r = function(button) 271 | button.param = 5 272 | end, 273 | action_l = function(button) 274 | button.param = -5 275 | end, 276 | update = function(button) 277 | Config.bg_framerate = tostring(Clamp(tonumber(Config.bg_framerate) + button.param, 5, 500)) 278 | button.param = 0 279 | button.label = ("BG REFRESH RATE CAP : < %sHz >"):format(Config.bg_framerate) 280 | end 281 | }, 282 | 283 | {x = 0, y = 0.2, param = 0, label = ("BACKGROUND BRIGHTNESS : < %d%% >"):format(Config.bg_brightness), 284 | action_e = function(button) 285 | Config.bg_brightness = tostring((tonumber(Config.bg_brightness) + 5) % 105) 286 | end, 287 | action_r = function(button) 288 | button.param = 1 289 | end, 290 | action_l = function(button) 291 | button.param = -1 292 | end, 293 | update = function(button) 294 | Config.bg_brightness = tostring(Clamp(tonumber(Config.bg_brightness) + 5 * button.param, 0, 100)) 295 | button.param = 0 296 | button.label = ("BACKGROUND BRIGHTNESS : < %d%% >"):format(Config.bg_brightness) 297 | end 298 | }, 299 | 300 | {x = 0, y = 0.3, param = 0, label = ("BLUR SPREAD: <%s>"):format(DrawBar(Config.blur_spread, 10)), 301 | action_e = function(button) 302 | Config.blur_spread = tostring((tonumber(Config.blur_spread) + 1) % 11) 303 | end, 304 | action_r = function(button) 305 | button.param = 1 306 | end, 307 | action_l = function(button) 308 | button.param = -1 309 | end, 310 | update = function(button) 311 | Config.blur_spread = tostring(Clamp(tonumber(Config.blur_spread) + button.param, 0, 10)) 312 | button.param = 0 313 | button.label = ("BLUR SPREAD: <%s>"):format(DrawBar(Config.blur_spread, 10)) 314 | end 315 | }, 316 | 317 | {x = 0, y = 0.4, param = 0, label = ("TRAIL FADEOUT DURATION: <%s>"):format(DrawBar(Config.trail_duration, 10)), 318 | action_e = function(button) 319 | Config.trail_duration = tostring((tonumber(Config.trail_duration) + 1) % 11) 320 | end, 321 | action_r = function(button) 322 | button.param = 1 323 | end, 324 | action_l = function(button) 325 | button.param = -1 326 | end, 327 | update = function(button) 328 | Config.trail_duration = tostring(Clamp(tonumber(Config.trail_duration) + button.param, 0, 10)) 329 | button.param = 0 330 | button.label = ("TRAIL FADEOUT DURATION: <%s>"):format(DrawBar(Config.trail_duration, 10)) 331 | end 332 | }, 333 | 334 | {x = 0, y = 0.7, label = "BACK", action_e = change("settings")}, 335 | }) 336 | 337 | Title.window = Menu.new("Menu", { 338 | {x = 0, y = -0.3, param = 1, prev_screen = 1, label = ("SCREEN : < %s >"):format(1), 339 | action_e = function(button) 340 | button.prev_screen = button.param 341 | button.param = Mod1(button.param + 1, #FullScreenModes) 342 | end, 343 | action_r = function(button) 344 | button.prev_screen = button.param 345 | button.param = Mod1(button.param + 1, #FullScreenModes) 346 | end, 347 | action_l = function(button) 348 | button.prev_screen = button.param 349 | button.param = Mod1(button.param - 1, #FullScreenModes) 350 | end, 351 | update = function(button) 352 | button.label = ("SCREEN : < %s >"):format(button.param) 353 | 354 | local prev_resolution_id = button.parent.items[2].param 355 | local prev_resolutions = FullScreenModes[button.prev_screen] 356 | local cur_resolutions = FullScreenModes[button.param] 357 | local prev_mode = prev_resolutions[prev_resolution_id] 358 | 359 | local res_id_found 360 | for i, mode in pairs(cur_resolutions) do 361 | if mode.width == prev_mode.width and mode.height == prev_mode.height then 362 | res_id_found = i 363 | break 364 | elseif (mode.width > prev_mode.width and mode.height == prev_mode.height) or 365 | mode.height > prev_mode.height then 366 | res_id_found = i-1 367 | break 368 | end 369 | end 370 | if not res_id_found then res_id_found = #cur_resolutions end 371 | 372 | -- Updates `RESOLUTION` item to match a selected screen's resolutions. 373 | button.parent.items[2].param = res_id_found 374 | button.parent.items[2]:update(2) 375 | end 376 | }, 377 | 378 | {x = 0, y = -0.2, param = 1, label = ("RESOLUTION : < %4dx%4d >"):format("640", "480"), 379 | action_e = function(button) 380 | local cur_screen = button.parent.items[1].param 381 | button.param = Mod1(button.param + 1, #FullScreenModes[cur_screen]) 382 | end, 383 | action_r = function(button) 384 | local cur_screen = button.parent.items[1].param 385 | button.param = Mod1(button.param + 1, #FullScreenModes[cur_screen]) 386 | end, 387 | action_l = function(button) 388 | local cur_screen = button.parent.items[1].param 389 | button.param = Mod1(button.param - 1, #FullScreenModes[cur_screen]) 390 | end, 391 | update = function(button) 392 | local cur_screen = button.parent.items[1].param 393 | local mode = FullScreenModes[cur_screen][button.param] 394 | button.label = ("RESOLUTION : < %4dx%4d >"):format(mode.width, mode.height) 395 | end 396 | }, 397 | 398 | {x = 0, y = 0.1, param = Config.fullscreen, label = ("FULLSCREEN : < %s >"):format(Config.fullscreen), 399 | update = function(button) 400 | button.param = (button.param == "O" and "X" or "O") 401 | button.label = ("FULLSCREEN : < %s >"):format(button.param) 402 | end 403 | }, 404 | 405 | {x = 0, y = 0.2, param = Config.vsync, label = ("VSYNC : < %s >"):format(Config.vsync), 406 | update = function(button) 407 | button.param = (button.param == "O" and "X" or "O") 408 | button.label = ("VSYNC : < %s >"):format(button.param) 409 | end 410 | }, 411 | 412 | {x = 0, y = 0.5, label = "APPLY", 413 | action_e = function(button) 414 | local buttons = button.parent.items 415 | local display = buttons[1].param 416 | local mode = FullScreenModes[display][buttons[2].param] 417 | 418 | local width, height = mode.width, mode.height 419 | local fs, vsync = buttons[3].param, buttons[4].param 420 | 421 | SetDisplayMode(width, height, display, fs == "O", vsync == "O") 422 | Config.window_width = tostring(width ) 423 | Config.window_height = tostring(height ) 424 | Config.window_display = tostring(display) 425 | Config.fullscreen = fs 426 | Config.vsync = vsync 427 | end 428 | }, 429 | 430 | {x = 0, y = 0.7, label = "BACK", action_e = change("settings")}, 431 | }) 432 | 433 | 434 | -- [[ === === === [ HIGHSCORES ] === === === ]] -- 435 | -- sets the default high score mode view to the first mode alphabetically 436 | --TODO: Proper Sorting 437 | --TODO: Unlocking Secret Modes 438 | local mode_list = {} 439 | local mode_indices = {} -- to prevent unnecessary lookups 440 | for k, _ in pairs(HighScores) do 441 | if k ~= "Death" or #HighScores["Death"] > 0 or carnivalhours then -- death is technically a secret mode, only show it if you have a score in it 442 | table.insert(mode_list, k) 443 | end 444 | for i = 1, #Levels do 445 | if Levels[i].name == k then mode_indices[k] = i end 446 | end 447 | end 448 | 449 | Title.highscores = Menu.new("Menu", { 450 | {x = 0, y = -0.8, param = 0, label = ("MODE: < %s >"):format(mode_list[1]), 451 | action_r = function(button) 452 | button.param = 1 453 | end, 454 | action_l = function(button) 455 | button.param = -1 456 | end, 457 | update = function(button) 458 | Title.highscores.selection = Mod1(Title.highscores.selection + button.param, #button.parent.mode_list) 459 | button.param = 0 460 | button.label = ("MODE: < %s >"):format(Title.highscores.mode_list[Title.highscores.selection]) 461 | end 462 | }, 463 | 464 | {x = 0, y = -0.7, label = "SHOW CLEAR STATISTICS: < X >", 465 | update = function(button) 466 | button.parent.show_clear = not button.parent.show_clear 467 | button.label = ("SHOW CLEAR STATISTICS: < %s >"):format(button.parent.show_clear and "O" or "X") 468 | end 469 | }, 470 | 471 | {x = 0, y = 0.7, label = "BACK", 472 | action_e = function(button) 473 | button.parent.reset_counter = 1 474 | button.parent.items[4]:update() 475 | change("main")(button) 476 | end 477 | }, 478 | 479 | {x = 0, y = 0.8, label = "RESET HIGH SCORES", 480 | labels = {"RESET HIGH SCORES", 481 | "ARE YOU SURE YOU WANT TO CLEAR RECORDS?", 482 | "ARE YOU REALLY SURE?", 483 | "LAST CHANCE! ARE YOU SURE YOU WANT TO?"}, 484 | action_e = function(button) 485 | button.parent.reset_counter = button.parent.reset_counter + 1 486 | end, 487 | update = function(button) 488 | if button.parent.reset_counter > #button.labels then 489 | button.parent.reset_counter = 1 490 | for _, mode in pairs(Levels) do HighScores[mode.name] = {} end 491 | SaveHighScores() 492 | end 493 | button.label = button.labels[button.parent.reset_counter] 494 | end 495 | } 496 | }) 497 | --TODO: Apply general styling to this 498 | Title.highscores.selection = 1 499 | Title.highscores.mode_list = mode_list 500 | Title.highscores.mode_indices = mode_indices 501 | Title.highscores.show_clear = false 502 | Title.highscores.reset_counter = 1 -------------------------------------------------------------------------------- /util.lua: -------------------------------------------------------------------------------- 1 | --[==[ 2 | Utility functions for example block game 3 | Copyright (C) 2021 Lilla Oshisaure 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | ]==] 18 | 19 | function NADA() end 20 | 21 | function Clamp(val, min, max) 22 | if min > max then min, max = max, min end 23 | return math.max(math.min(val, max), min) 24 | end 25 | 26 | function Mod1(x, base) return ((x - 1) % base) + 1 end 27 | 28 | function DrawBar(rep, size) 29 | local _rep = tonumber(rep) 30 | return ("%s%s"):format(("|"):rep(_rep), ("."):rep(size - _rep)) 31 | end 32 | 33 | function Deepcopy(orig) 34 | local orig_type = type(orig) 35 | local copy 36 | if orig_type == 'table' then 37 | copy = {} 38 | for orig_key, orig_value in next, orig, nil do 39 | copy[Deepcopy(orig_key)] = Deepcopy(orig_value) 40 | end 41 | setmetatable(copy, Deepcopy(getmetatable(orig))) 42 | else -- number, string, boolean, etc 43 | copy = orig 44 | end 45 | return copy 46 | end 47 | 48 | function HSA(h, s, a) 49 | -- find a colour from hue and saturation 50 | h = (h%360)/60 51 | local i, f, g, t 52 | i, f = math.modf(h) 53 | g = 1-f -- for descending gradients 54 | t = 1-s -- min colour intensity based on saturation 55 | f, g = s*f+t, s*g+t -- apply saturation to the gradient values 56 | if i == 0 then return {1, f, t, a} 57 | elseif i == 1 then return {g, 1, t, a} 58 | elseif i == 2 then return {t, 1, f, a} 59 | elseif i == 3 then return {t, g, 1, a} 60 | elseif i == 4 then return {f, t, 1, a} 61 | elseif i == 5 then return {1, t, g, a} 62 | else return {1, 1, 1, a} 63 | end 64 | end 65 | 66 | function HSVA(h, s, v, a) 67 | -- apply value to the hue/saturation colour 68 | local c = HSA(h, s, a or 1) 69 | for i = 1, 3 do c[i] = c[i]*v end 70 | return c 71 | end 72 | 73 | function CheckKeyInput(input) 74 | return input and love.keyboard.isDown(input) 75 | end 76 | 77 | function CheckPadInput(pad, input) 78 | if input == "" or not pad or not pad:isGamepad() then return false end 79 | local dz = tonumber(Config.pad_deadzone)/100 80 | local lastchr = input:sub(-1,-1) 81 | if lastchr == "+" then 82 | -- bound to axis up 83 | return pad:getGamepadAxis(input:sub(1, -2)) >= dz 84 | elseif lastchr == "-" then 85 | -- bound to axis down 86 | return -pad:getGamepadAxis(input:sub(1, -2)) >= dz 87 | else 88 | -- bound to button 89 | return pad:isGamepadDown(input) 90 | end 91 | end 92 | 93 | function FormatTime(s) 94 | if not s then return [[--'--"--]] end 95 | if s < 0 then s = 0 end 96 | local sec, cen, min = math.modf(math.max(0,s)) 97 | sec, min = sec % 60, math.floor(sec/60) 98 | return string.format("%02d\'%02d\"%02d", min, sec, math.floor(cen*100)) 99 | end 100 | 101 | function CommaValue(n) -- credit http://richard.warburton.it 102 | if not n then return "--" end 103 | local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$') 104 | return left..(num:reverse():gsub('(%d%d%d)','%1,'):reverse())..right 105 | end 106 | 107 | function LineClearName(count) 108 | -- normal range 109 | if count <= 0 then return "" end 110 | if count == 1 then return "SINGLE" end 111 | if count == 2 then return "DOUBLE" end 112 | if count == 3 then return "TRIPLE" end 113 | if count == 4 then return "QUAD" end 114 | -- pento? 115 | if count == 5 then return "QUINT" end 116 | -- zone?? 117 | if count == 20 then return "FULL-TUPLE" end 118 | if count == 21 then return "KIRB-TUPLE" end 119 | if count == 22 then return "EXTRA-TUPLE" end 120 | if count == 23 then return "ULTIMA-TUPLE" end 121 | if count == 24 then return "INFINI-TUPLE" end 122 | -- zone pento??? (really this is just in case) 123 | if count >= 25 then return "ALEPH"..(count-25).."-TUPLE" end 124 | -- general case 125 | return count.."-TUPLE" 126 | end 127 | 128 | function BoolNumber(b) return b and 1 or 0 end 129 | 130 | function AvgArrays(base, ...) 131 | if not base then return nil end 132 | local arg = {...} 133 | local a = Deepcopy(base) 134 | for _, arr in ipairs(arg) do 135 | for k, v in pairs(arr) do 136 | a[k] = a[k] + v 137 | end 138 | end 139 | for k, v in pairs(a) do 140 | a[k] = v/(#arg + 1) 141 | end 142 | return a 143 | end 144 | 145 | function Interpolate(fun, t, min, max) 146 | if not (min and max) then min, max = 0, 1 end 147 | t = (t-min)/(max-min) 148 | return fun(t) 149 | end 150 | 151 | function SwayFormula(t) 152 | local amplitude = tonumber(Config.sway_amplitude)/18 153 | local resttime = 1/tonumber(Config.sway_speed) 154 | local bounciness = tonumber(Config.sway_bounciness) 155 | return amplitude * math.exp(-0.5/resttime*t) * math.cos(0.5/resttime*t*(bounciness*bounciness-1)^0.5) 156 | end 157 | 158 | local e = math.log(2, 20) 159 | local c = 2^(-1-math.log(3, 20)) 160 | -- These values were worked out so that the formula below is in the form F(x) = c*x^e with F(60) = 1 and F(1200) = 2 161 | -- 60 rows/second being softdrop speed and 1200 rows/second being the speed cap on the input (20G with 1G = 1 row/frame@60FPS) 162 | -- Note to future me: don't question them. I worked them out so they work. 163 | function ImpactFormula(speed) return c*speed^e end 164 | 165 | function SetBGM(id) 166 | for k, t in pairs(BGM) do 167 | if k == id then 168 | t:play() 169 | else 170 | t:stop() 171 | end 172 | end 173 | end 174 | 175 | function PlaySFX(id) 176 | SFX[id]:stop() 177 | SFX[id]:play() 178 | end 179 | 180 | function SetBGMVolume(v) 181 | for k, t in pairs(BGM) do 182 | if k == 4 then 183 | t:setVolume(v*0.6) 184 | else 185 | t:setVolume(v) 186 | end 187 | end 188 | end 189 | 190 | function SetSFXVolume(v) 191 | for _, t in pairs(SFX) do 192 | t:setVolume(v) 193 | end 194 | end 195 | 196 | local bgframetimeacc = 0 197 | local newframe = false 198 | function UpdateShadersUniforms(dt) 199 | local time = os.clock() 200 | bgframetimeacc = bgframetimeacc + dt 201 | pcall(function() ShaderRainbow:send("time", time) end) 202 | for _, v in pairs(ShaderBG) do 203 | pcall(function() v:send("time", time ) end) 204 | pcall(function() v:send("dt" , bgframetimeacc) end) 205 | end 206 | end 207 | 208 | function SendShaderUniform(name, value) 209 | for _, v in pairs(ShaderBG) do 210 | pcall(function() v:send(name, value) end) 211 | end 212 | end 213 | 214 | local buffercanvas 215 | function DrawBlurred(drawable, blurfactor, ...) 216 | local _c = love.graphics.getCanvas() 217 | local r, g, b, a = love.graphics.getColor() 218 | love.graphics.setCanvas(buffercanvas) 219 | love.graphics.clear(0,0,0,1) 220 | 221 | if tonumber(Config.blur_spread) > 0 then 222 | love.graphics.setShader(ShaderBlur) 223 | ShaderBlur:send("Spread", blurfactor) 224 | ShaderBlur:send("Direction", {0,1}) 225 | end 226 | love.graphics.setBlendMode("add") 227 | love.graphics.draw(drawable, ...) 228 | 229 | love.graphics.setCanvas(_c) 230 | love.graphics.setColor(1,1,1,1) 231 | if tonumber(Config.blur_spread) > 0 then 232 | ShaderBlur:send("Direction", {1,0}) 233 | end 234 | love.graphics.push() 235 | love.graphics.origin() 236 | love.graphics.draw(buffercanvas) 237 | 238 | love.graphics.pop() 239 | love.graphics.setShader() 240 | love.graphics.setBlendMode("alpha") 241 | love.graphics.setColor(r, g, b, a) 242 | end 243 | 244 | function DrawRainbow(drawable, ...) 245 | love.graphics.setShader(ShaderRainbow) 246 | love.graphics.draw(drawable, ...) 247 | love.graphics.setShader() 248 | end 249 | 250 | PrerenderedFrame = nil 251 | local currentbg = "menu" 252 | function PrerenderBG(id) 253 | id = id or "menu" 254 | currentbg = id 255 | -- for k, v in pairs(ShaderBG) do 256 | if PrerenderedFrame then PrerenderedFrame:release() end 257 | PrerenderedFrame = love.graphics.newCanvas(Width, Height) 258 | love.graphics.setCanvas(CanvasBG) 259 | love.graphics.clear(0,0,0,1) 260 | local tmin, tmax, dt = 998.5, 1001.5, 0.02 261 | for t = tmin, tmax, dt do 262 | pcall(function() ShaderBG[id]:send("time", t) end) 263 | pcall(function() ShaderBG[id]:send("dt", dt) end) 264 | RenderBGShader(id) 265 | end 266 | love.graphics.setCanvas(PrerenderedFrame) 267 | love.graphics.draw(CanvasBG) 268 | -- end 269 | 270 | love.graphics.setCanvas() 271 | end 272 | 273 | function RenderBGShader(id) 274 | id = id or "menu" 275 | 276 | local _c = love.graphics.getCanvas() 277 | local r, g, b, a = love.graphics.getColor() 278 | love.graphics.push() 279 | love.graphics.origin() 280 | 281 | love.graphics.setCanvas(CanvasBGprev) 282 | love.graphics.setShader(ShaderBG[id]) 283 | love.graphics.draw(CanvasBG) 284 | love.graphics.setShader() 285 | love.graphics.setCanvas(CanvasBG) 286 | love.graphics.draw(CanvasBGprev) 287 | 288 | love.graphics.pop() 289 | love.graphics.setCanvas(_c) 290 | love.graphics.setColor(r, g, b, a) 291 | end 292 | 293 | function RenderBG(id) 294 | if Config.dynamic_bg == "O" then 295 | newframe = (bgframetimeacc >= 1/tonumber(Config.bg_framerate)) 296 | if newframe then 297 | bgframetimeacc = 0 298 | RenderBGShader(id) 299 | end 300 | return 301 | end 302 | id = id or "menu" 303 | 304 | local _c = love.graphics.getCanvas() 305 | local r, g, b, a = love.graphics.getColor() 306 | love.graphics.push() 307 | love.graphics.origin() 308 | love.graphics.setCanvas(CanvasBG) 309 | 310 | love.graphics.draw(PrerenderedFrame) 311 | 312 | love.graphics.pop() 313 | love.graphics.setCanvas(_c) 314 | love.graphics.setColor(r, g, b, a) 315 | end 316 | 317 | function DrawOnBG(...) 318 | if Config.dynamic_bg == "O" and newframe then 319 | local _c = love.graphics.getCanvas() 320 | 321 | love.graphics.setCanvas(CanvasBG) 322 | love.graphics.draw(...) 323 | 324 | love.graphics.setCanvas(_c) 325 | end 326 | end 327 | 328 | function GetAdjustedFontSize(size) 329 | local s = math.ceil(size) 330 | local r = s % 6 331 | if s < 9 then return 6 332 | elseif r == 4 then return s-1 333 | elseif r == 5 then return s-2 334 | end 335 | return s 336 | end 337 | 338 | function LoadShaders() 339 | ShaderBG = { 340 | practice = love.graphics.newShader("shaders/bgpractice.glsl"), 341 | practice2 = love.graphics.newShader("shaders/bgpractice2.glsl"), 342 | beginner = love.graphics.newShader("shaders/bgbeginner.glsl"), 343 | standard = love.graphics.newShader("shaders/bgstandard.glsl"), 344 | original = love.graphics.newShader("shaders/bg1.glsl"), 345 | master = love.graphics.newShader("shaders/bg10.glsl"), 346 | classic = love.graphics.newShader("shaders/bgclassic.glsl"), 347 | death = love.graphics.newShader("shaders/bgdeath.glsl"), 348 | menu = love.graphics.newShader("shaders/bgmenu.glsl"), 349 | credits = love.graphics.newShader("shaders/creditball.glsl") 350 | } 351 | ShaderBlur = love.graphics.newShader("shaders/blur.glsl") 352 | ShaderRainbow = love.graphics.newShader("shaders/rainbow.glsl") 353 | ShaderShaking = love.graphics.newShader("shaders/chroma-misalign.glsl") 354 | ShaderInfinity = love.graphics.newShader("shaders/infinity.glsl") 355 | 356 | local moontex = love.graphics.newImage("assets/texture/lroc_color_poles_2k.png") 357 | local moondis = love.graphics.newImage("assets/texture/ldem_4_uint.png") 358 | local moondw, moondh = moondis:getDimensions() 359 | local moonnor = love.graphics.newCanvas(moondw, moondh) 360 | local normalshader = love.graphics.newShader("shaders/displacementnormals.glsl") 361 | normalshader:send("off", {1/moondw, 1/moondh, 0}) 362 | love.graphics.setCanvas(moonnor) 363 | love.graphics.setShader(normalshader) 364 | love.graphics.draw(moondis) 365 | love.graphics.setCanvas() 366 | love.graphics.setShader() 367 | SendShaderUniform("moontex", moontex) 368 | SendShaderUniform("moonnor", moonnor) 369 | moontex:release() 370 | moondis:release() 371 | moonnor:release() 372 | -- normalshader:release() 373 | end 374 | 375 | local first_resize = true 376 | function ProcessResize(w, h) 377 | Width, Height = w, h 378 | if not first_resize then 379 | for _, font in pairs(Font) do font:release() end 380 | CanvasBG :release() 381 | CanvasBGprev :release() 382 | CanvasRainbow:release() 383 | buffercanvas:release() 384 | for _, game in pairs(Games) do 385 | game.size = h/40 386 | game.canvas :release() 387 | game.field_canvas :release() 388 | game.glow_canvas :release() 389 | game.overlay_canvas:release() 390 | game.canvas = love.graphics.newCanvas(w, h) 391 | game.field_canvas = love.graphics.newCanvas(w, h) 392 | game.glow_canvas = love.graphics.newCanvas(w, h) 393 | game.overlay_canvas = love.graphics.newCanvas(w, h) 394 | end 395 | end 396 | 397 | 398 | 399 | -- font = love.graphics.newImageFont( filename, glyphs, extraspacing ) 400 | 401 | Font = { 402 | HUD = love.graphics.newFont("assets/font/exampleblockgame.ttf", GetAdjustedFontSize(h/50)), 403 | Menu = love.graphics.newFont("assets/font/exampleblockgame.ttf", GetAdjustedFontSize(h/32)), 404 | Title = love.graphics.newFont("assets/font/exampleblockgame.ttf", GetAdjustedFontSize(h/16)), 405 | } 406 | Font.HUD :setFilter("nearest", "nearest") 407 | Font.Menu :setFilter("nearest", "nearest") 408 | Font.Title:setFilter("nearest", "nearest") 409 | CanvasBG = love.graphics.newCanvas() 410 | CanvasBGprev = love.graphics.newCanvas() 411 | CanvasRainbow = love.graphics.newCanvas() 412 | buffercanvas = love.graphics.newCanvas() 413 | 414 | if not first_resize then 415 | TitleText :setFont(Font.Title) 416 | ScoreText :setFont(Font.Title) 417 | ScorePopup:setFont(Font.HUD) 418 | for name, menu in pairs(Title) do 419 | if name ~= "current" then menu:updateSelected() end 420 | end 421 | Credits:resize() 422 | UpdatePauseMenuFonts() 423 | if Config.dynamic_bg == "X" then PrerenderBG(currentbg) end 424 | end 425 | 426 | first_resize = false 427 | LoadShaders() 428 | end 429 | 430 | local arrow_buttons = { 431 | up = 0, 432 | down = 0, 433 | left = 0, 434 | right = 0, 435 | } 436 | local controller_buttons = {} 437 | local autorepeat_delay = 0.5 438 | local tick_count, last_tick_time, last_key = 1, 0, nil 439 | function ProcessMenuAutorepeat(dt) 440 | -- update the state of the button presses on gamepad to check with them 441 | for button, key in pairs(MenuPadControls) do 442 | controller_buttons[key] = CheckPadInput(CurrentController, button) 443 | end 444 | -- check for the key that has been held for the least amount of time 445 | local recent_key, recent_time = nil, math.huge 446 | for key, time_held in pairs(arrow_buttons) do 447 | if love.keyboard.isDown(key) or controller_buttons[key] then 448 | local newtime = time_held + dt 449 | arrow_buttons[key] = newtime 450 | if newtime < recent_time then 451 | recent_key, recent_time = key, newtime 452 | end 453 | else 454 | arrow_buttons[key] = 0 455 | end 456 | end 457 | 458 | -- if there is a change in keypresses then reset the autorepeat 459 | if last_key ~= recent_key then 460 | tick_count, last_tick_time, last_key = 1, 0, recent_key 461 | end 462 | 463 | -- if there is a key being pressed and we need to tick forward 464 | -- then update the counters and return the key press 465 | -- we're using 1/tick_count as the delay to make it faster the longer you press 466 | if last_key and recent_time > last_tick_time + autorepeat_delay / tick_count then 467 | last_tick_time = recent_time 468 | tick_count = tick_count + 1 469 | return recent_key 470 | end 471 | 472 | --either there is no key being pressed or we don't need to send a keypress yet 473 | return nil 474 | end 475 | 476 | InfinitySymbol = love.graphics.newCanvas(9, 5) 477 | function DrawInfinitySymbol(...) 478 | local _s = love.graphics.getShader() 479 | local _c = {love.graphics.getColor()} 480 | love.graphics.setShader(ShaderInfinity) 481 | ShaderInfinity:send("time", os.clock()) 482 | 483 | love.graphics.draw(InfinitySymbol, ...) 484 | 485 | love.graphics.setShader(_s) 486 | love.graphics.setColor(_c) 487 | end 488 | 489 | 490 | -- Get all available screens and their supported resolutions. 491 | FullScreenModes = {} 492 | for i = 1, love.window.getDisplayCount() do 493 | local modes = love.window.getFullscreenModes(i) 494 | local screen = {} 495 | for _, mode in ipairs(modes) do 496 | table.insert(screen, mode) 497 | end 498 | table.sort(screen, function(a, b) -- Sort the resolutions 499 | if a.height ~= b.height then return a.height < b.height 500 | elseif a.width ~= b.width then return a.width < b.width 501 | end 502 | end) 503 | table.insert(FullScreenModes, screen) 504 | end 505 | 506 | --for _, screen in pairs(FullScreenModes) do 507 | -- for i, mode in pairs(screen) do 508 | -- print(i, mode.width, mode.height) 509 | -- end 510 | -- --break 511 | --end 512 | 513 | function SetDisplayMode(width, height, display, fs, vsync) 514 | love.window.updateMode(width, height, {display = display, fullscreen = fs, vsync = vsync, fullscreentype = "exclusive"}) 515 | ProcessResize(width, height) 516 | end --------------------------------------------------------------------------------