├── 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 | [](https://github.com/Oshisaure/example-block-game/blob/master/LICENSE)
2 | [](https://github.com/love2d/love/releases/tag/11.3)
3 | [](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
--------------------------------------------------------------------------------