Compare commits

...

210 commits

Author SHA1 Message Date
51e8de7451 make it run, not work 2022-12-25 06:17:30 +01:00
75c67a0c5a finished flakes 2022-12-24 18:25:39 +01:00
5cf9a906db update affection 2022-12-24 17:36:05 +01:00
abaf864c91 flakify 2022-12-24 17:27:03 +01:00
5bef189c30 get working flake 2022-12-01 19:56:25 +01:00
efbb9b4c5e remove ballast 2022-12-01 16:34:40 +01:00
76225dff4b more flakyness 2022-12-01 16:23:43 +01:00
eda79eb626 fix flake 2022-12-01 15:45:21 +01:00
04d6905de1 remove ballast 2022-12-01 15:45:07 +01:00
b56ed86e45 revert 2022-09-14 19:30:22 +02:00
af0dcc16da fix typo 2022-09-14 19:29:46 +02:00
81a76d4eb3 flakify 2022-09-14 18:53:44 +02:00
1b8d754cd6 introduce resourcet 2022-07-10 04:18:22 +02:00
60d38217c8 hunting warnings and other nasties 2022-07-09 23:26:37 +02:00
17978b0bd7 deconfuse the haskell language server 2022-07-09 22:16:34 +02:00
9aef49da94 continue rewrite 2021-09-30 06:09:39 +02:00
9f47b192a5 reworked physics 2021-09-05 10:46:33 +02:00
c574671244 laying out data 2021-08-31 02:15:33 +02:00
01c152c1a5 starting rewrite of collision detection to force based model 2021-08-30 06:04:06 +02:00
f5782fc935 start rewriting physics to force based model 2021-08-29 23:50:44 +02:00
337fcf9cd0 interaction in y direction works 2021-07-25 16:14:05 +02:00
0a2e3f05d4 halfway interactable objects 2021-07-25 15:51:42 +02:00
819559607c some cleanup 2021-07-25 13:29:02 +02:00
5c634c3865 reworked player movement 2021-07-25 10:11:28 +02:00
8dd37e8e95 reworking walking 2021-07-24 09:35:41 +02:00
87b70cd93a better description 2021-07-24 03:33:12 +02:00
d71437774f linted 2021-07-24 03:33:00 +02:00
95f5b2c3a8 linting 2021-07-14 02:42:24 +02:00
3f45ae580c linted 2021-07-09 19:49:09 +02:00
5567b515e8 linted 2021-07-09 19:39:50 +02:00
26e6825d0e clean 2021-07-08 19:16:56 +02:00
d7373e0c28 increase player mass 2021-07-08 18:45:02 +02:00
b3a2e58af4 tinker more on collision. objects move, but glitches ensue. 2021-07-08 18:39:31 +02:00
40e82e26f6 try making objects move 2021-05-14 09:22:01 +02:00
25b6eb659d fix view update issue 2021-05-01 22:27:18 +02:00
2e25c4cfcc introduce and propagate view direction 2021-05-01 22:16:06 +02:00
58ae21724b cat flips in move direction 2021-04-29 21:02:41 +02:00
865e931a43 collision corner case 2021-04-29 20:53:03 +02:00
6633fffb30 decrease collision boundaries of player 2021-04-29 20:52:42 +02:00
1ab8befd8f restructure: moved Graphics and Physics into own subfolders 2021-04-29 20:28:52 +02:00
61451a4a10 first powerup including effects works 2021-04-29 06:09:32 +02:00
03a577fd18 reduce debug output 2021-04-29 02:18:30 +02:00
6f816eedef remove obsolete debug output 2021-04-28 16:40:23 +02:00
3079ee74e1 some tinkering with cast and collision 2021-04-28 16:35:51 +02:00
21df43b773 starting work on powerups. Drawable for now 2021-04-22 05:37:57 +02:00
370d6def45 Merge branch 'collectable' of ssh://gitea.nek0.eu:2200/nek0/pituicat into collectable 2021-04-21 14:40:55 +02:00
9e04a4fddb start collectables 2021-04-21 14:39:26 +02:00
d095569911 add missing imports 2021-04-21 14:39:26 +02:00
5a15be6eef update cabal file 2021-04-21 14:39:26 +02:00
b51248e7e7 create base classes for collectable powerups 2021-04-21 14:39:26 +02:00
310105ed48 start tangibles 2021-04-21 14:39:26 +02:00
927d7af132 fix error in movement caused by numlock modifier 2021-04-21 14:37:47 +02:00
2ec22ea6b0 start collectables 2021-04-20 04:19:54 +02:00
039d567be8 add missing imports 2021-04-18 15:07:56 +02:00
083ef004cd update cabal file 2021-04-18 15:05:20 +02:00
f3a70c100f create base classes for collectable powerups 2021-04-18 14:56:32 +02:00
efb5046ec7 make proper wall slide 2021-04-17 11:33:28 +02:00
84923b2622 wrong key given 2021-04-17 10:16:07 +02:00
b5064d8c7c update readme: game controls 2021-04-17 05:46:36 +02:00
7456840238 start tangibles 2021-04-17 05:12:19 +02:00
9a9408edf3 deduplication 2021-04-17 05:10:36 +02:00
7657a35415 first cleanup 2021-04-17 04:53:21 +02:00
56bf486fbf collision detection finally works! cleanup is next... 2021-04-17 04:20:34 +02:00
134445ed20 further attempt at physics 2021-04-16 21:47:02 +02:00
9da16fc6a7 fix collision halfway 2021-04-16 18:46:01 +02:00
d9a0128958 collision detection still not working 2021-03-22 07:01:02 +01:00
7654888f6f more work on collision 2021-03-22 06:36:28 +01:00
7799387cf2 broadphase saves a bit of the day 2021-03-04 01:56:42 +01:00
c7f4f00a2f squish 2021-02-27 16:08:45 +01:00
9d09365e7d add test column 2021-02-18 07:25:46 +01:00
50329f7790 update 2021-02-16 06:18:52 +01:00
9118d6fcde typos 2021-02-12 21:51:16 +01:00
67e4c9ca80 add reverse-checking of collision 2021-01-20 07:13:04 +01:00
55db04c27f Merge branch 'master' of ssh://gitea.nek0.eu:2200/nek0/pituicat 2021-01-20 05:22:07 +01:00
de22c2fffe zero allmovement in collision direction 2021-01-20 05:21:50 +01:00
87b2b2ad05 make camera follow the cat 2021-01-20 05:21:50 +01:00
f9ff9d1db2 zero allmovement in collision direction 2021-01-20 05:20:56 +01:00
a1af74be74 make camera follow the cat 2021-01-20 05:12:16 +01:00
bf04946a1b add collision direction to overlaps and make unground possible in movement 2021-01-20 04:40:57 +01:00
ae38902d9c restructuring 2021-01-19 21:46:17 +01:00
3d9af3eaf8 fix collision detection yet again 2021-01-19 21:46:07 +01:00
fe947c8ba5 stop lerping on collision with vertical wall 2021-01-19 12:50:15 +01:00
0b03c306c3 correct reaction 2021-01-17 20:57:45 +01:00
bb61df8b6d more granular reaction 2021-01-17 20:29:27 +01:00
a99a70eb9d make better collision reaction 2021-01-17 02:30:45 +01:00
03c406fd3e Merge branch 'master' of ssh://gitea.nek0.eu:2200/nek0/pituicat 2021-01-14 23:09:14 +01:00
3b3b9822f5 make window size fix 2021-01-14 23:08:57 +01:00
430e2fff14 reworked collision again 2021-01-14 23:08:24 +01:00
8627f80be2 tinkering with collision and movement 2021-01-12 03:12:02 +01:00
6a4c0e5b42 adjust gravitation and jump 2021-01-12 00:53:00 +01:00
56779daa22 hunt warnings 2021-01-12 00:52:29 +01:00
e820437bdf first jump implementation 2021-01-12 00:51:58 +01:00
1e71b63f67 change subscriber function and close window on request 2021-01-11 17:06:29 +01:00
43f5649ce5 add missing module 2021-01-11 16:54:17 +01:00
2e359d9b0f remove double export 2021-01-11 12:01:24 +01:00
24a04ecb36 prepare for keyboard input events 2021-01-11 12:01:07 +01:00
5ec8901dd7 split Scene 'Test' up in submodules per method and hunt warnings 2021-01-11 10:33:20 +01:00
eecbe73893 make window size fix 2021-01-11 08:22:07 +01:00
ecffdaf467 ignore profiling data 2021-01-03 18:33:51 +01:00
bbf2cc9911 make affection strict 2021-01-03 18:21:12 +01:00
e3c3caec0b more 'fixing' 2021-01-03 10:19:05 +01:00
a6977e81aa 'fix' shader error for AMD cards 2021-01-03 10:14:36 +01:00
3797869d14 mitigate 'swamping' and hunt warnings 2021-01-03 01:43:37 +01:00
20954c0484 fix haddock markup 2021-01-02 23:21:01 +01:00
cd303affc8 fix remnants 2021-01-02 23:17:53 +01:00
b043ff7707 switch to deltatime-based collision detection to prevent tunneling 2021-01-02 22:46:46 +01:00
8e870e3525 move player 2021-01-02 15:02:33 +01:00
02d11e7e2e remove unused module reference 2021-01-02 15:02:25 +01:00
2f8b9054f2 implement physics including collision detection and handling 2021-01-02 13:32:20 +01:00
4941ec1904 fix missing imports and wrong type declaration 2020-12-27 05:30:57 +01:00
0977680004 add new modules to package definition 2020-12-27 05:28:03 +01:00
d55cb1f38e add or modify container modules 2020-12-27 05:27:07 +01:00
7b174248ec move velocity into Mass typeclass 2020-12-27 05:25:54 +01:00
454405b564 add typeclass for collidible objects 2020-12-27 05:19:51 +01:00
253e698cc4 commence physics implementation with mass 2020-12-27 02:31:10 +01:00
4137101dbc make Pituicat an Actor and update it properly 2020-12-24 12:02:09 +01:00
3bfa8671b0 do not limit texture color 2020-12-23 17:16:30 +01:00
675b0ff155 we have a player! 2020-12-23 16:39:45 +01:00
99f37fc6b2 correct wrong output 2020-12-23 16:17:25 +01:00
2f03adc8f4 add player and tinker around 2020-12-23 07:47:20 +01:00
a859814fda fix infinite loop when expanding buffers 2020-12-23 07:20:23 +01:00
5c90eee84a preparations for populating the stage with actors and props 2020-12-16 18:57:37 +01:00
3873c62f17 update test level map 2020-12-14 08:00:42 +01:00
c557b9cc3c make drawables and fix tiles to make it work 2020-12-14 08:00:21 +01:00
9c2f93a60c move more stuff around 2020-12-14 03:27:21 +01:00
efbbc49b54 get a texture to show up correctly 2020-12-14 03:12:33 +01:00
6523f13cac get more output... 2020-12-14 03:12:04 +01:00
b176155b8a move stuff around 2020-12-14 03:11:46 +01:00
e4bd5198e0 fix haddock markup 2020-12-07 17:09:05 +01:00
1eae6ae381 fix haddock markup 2020-12-07 16:03:16 +01:00
7142ce9b55 bind buffers before reading from them 2020-12-07 16:02:57 +01:00
d0a9d342c7 add packages in extern to project packages 2020-12-07 12:59:52 +01:00
dbc3188317 clarification 2020-12-07 01:50:32 +01:00
a3f92c9960 expand build instructions for other Penguins 2020-12-07 01:49:27 +01:00
29c531ef73 fix headings 2020-12-07 01:47:20 +01:00
84e1c4ded2 add readme file at last 2020-12-07 01:45:49 +01:00
c58cc24910 notes turned obsolete. modular design preferred. 2020-12-07 00:42:33 +01:00
6073ba6cc9 removing references 2020-12-07 00:38:54 +01:00
e7e157c8ab deleting obsolete files 2020-12-07 00:38:17 +01:00
ec087113f8 preapare for textures 2020-12-07 00:26:17 +01:00
e3fd34731b ignore OpenGL trace files 2020-12-06 08:15:52 +01:00
5675cf7f6d reorganize modules and add new dependencies 2020-12-06 08:15:31 +01:00
b3e1d204c5 get something on screen. finally. 2020-12-06 08:14:57 +01:00
7e3fbd45c7 add shaders 2020-12-06 08:14:05 +01:00
5aaa2dd41b more verbose messages from engine on startup 2020-12-05 14:28:57 +01:00
a9165b852a change byte offset to offset in number of buffer elements 2020-12-05 10:40:07 +01:00
0fb10b0981 lots of plumbing to automate stuff 2020-12-05 10:10:37 +01:00
de5e692305 try out some freshly discovered magic tricks 2020-12-04 05:22:56 +01:00
171f71180f start restructuring 2020-10-30 07:38:18 +01:00
dc78b0fe7b meow 2020-10-28 11:48:58 +01:00
3b5d0a90ad introduce scenes and thus get rid of overly big enums 2020-10-25 19:33:18 +01:00
31f56d62a3 add Scene class 2020-10-25 19:32:44 +01:00
00ac2ef5b2 preparing for graphics 2020-10-17 16:19:18 +02:00
2e280d6e19 Draft more state implementation and make engine verbose 2020-10-17 10:35:25 +02:00
7c18915e9b build foundations for state machine 2020-10-17 01:53:25 +02:00
cf520852d0 extract size alongside data when reding in textures 2020-10-17 01:52:22 +02:00
ada9c1368f finish constructing maps 2020-10-17 01:51:19 +02:00
4eb31b1bf5 move gl call after context creation 2020-10-16 06:06:39 +02:00
29f8551b84 typo 2020-10-16 06:00:30 +02:00
864aa694ee whopsie 2020-10-15 19:45:30 +02:00
a49afbdee8 add package 2020-10-15 19:39:32 +02:00
a66e67ee69 add sync and GL infos 2020-10-15 18:49:04 +02:00
02ed49a17b prepare map construction 2020-10-12 06:29:09 +02:00
ff77587042 working on textures 2020-10-12 06:08:33 +02:00
31a58280ae successful read of map layer 2020-10-10 01:00:33 +02:00
4ea4de78f6 added test tile in map layer 2020-10-10 00:59:52 +02:00
77698627eb remove wrong export 2020-10-10 00:58:46 +02:00
bdc020e511 add tile map export 2020-10-10 00:58:18 +02:00
a1144db6d9 expand on types 2020-10-09 05:51:00 +02:00
bb364d6b1a remove doubles 2020-10-09 05:28:04 +02:00
12b788af5e more types 2020-10-06 04:19:07 +02:00
0c53c11942 meow 2020-09-24 06:14:44 +02:00
9b30b11e18 meow 2020-09-24 01:26:47 +02:00
b945cc1fb6 bumping types around 2020-09-22 14:07:13 +02:00
f5353c5de2 start laying out types 2020-09-21 04:26:45 +02:00
2bff7b942f add test level and tileset 2020-09-21 04:26:21 +02:00
3ada04c3c1 add new dependencies 2020-09-21 04:25:49 +02:00
e5e4c7a4d1 ignore krita temp files 2020-09-21 00:17:19 +02:00
8ce4edba25 resized pituicat 2020-09-21 00:16:32 +02:00
35810c7b4a add first character: Pituicat 2020-09-20 23:54:25 +02:00
556ebd814d expand beat chart 2020-09-20 19:05:26 +02:00
86c1791822 expand beat chart 2020-09-20 18:24:25 +02:00
e1039005c8 extended beat chart 2020-09-20 01:52:15 +02:00
900e8bd777 extended beat chart 2020-09-18 13:03:05 +02:00
53e164fe6a expanded beat chart 2020-09-11 20:44:39 +02:00
0fb1d12702 Merge branch 'master' of ssh://gitea.nek0.eu:2200/nek0/pituicat into master 2020-09-11 13:58:53 +02:00
2dae38d4b4 expanded and predrafted beat chart 2020-09-11 13:54:50 +02:00
ac24c3605d typo 2020-09-11 13:54:15 +02:00
dcad00d93f baby steps 2020-09-10 21:28:55 +02:00
52c6d2ab3a extended beat chart 2020-09-10 09:10:06 +02:00
80ae98220d expanded beat chart 2020-09-09 07:13:18 +02:00
8fafe65b84 commence beat chart 2020-09-08 18:48:13 +02:00
6d8c93b7a0 add new powerup 2020-09-04 07:17:46 +02:00
7925bbb600 defined Powerups 2020-09-03 14:06:28 +02:00
eb3fc7052a changes after feedback. TODO: define Powerups 2020-09-03 13:19:45 +02:00
9a7eeace0a finished 10pager 2020-09-03 12:17:06 +02:00
e7089411f5 extended 10pager 2020-09-03 10:58:21 +02:00
eabbb22fb1 extended 10pager 2020-09-03 09:38:57 +02:00
864c135dae extended 10pager 2020-09-03 09:26:15 +02:00
43264e7856 extended 10pager 2020-09-03 08:53:56 +02:00
3ed715c7aa meow 2020-09-02 15:17:16 +02:00
129912174e expand game design document 2020-09-02 13:26:51 +02:00
96670cdc8c expand game design documents 2020-09-02 12:23:15 +02:00
ca7b14d6a2 finish 1 pager game design document 2020-09-02 10:00:02 +02:00
fd06185f6a meow 2020-09-02 08:56:54 +02:00
8aecda952f cleaned files 2020-08-31 01:02:54 +02:00
ee5ed9d376 add old resources 2020-08-30 18:35:33 +02:00
9787364ae6 make affection dependency and nixify 2020-08-30 17:53:03 +02:00
58fd609095 Add 'extern/affection/' from commit 'fc6bfa1ca5307e915d44eeac507ccc56ba01b487'
git-subtree-dir: extern/affection
git-subtree-mainline: 9e89962af0
git-subtree-split: fc6bfa1ca5
2020-08-30 17:40:22 +02:00
9e89962af0 init 2020-08-30 17:36:32 +02:00
113 changed files with 5412 additions and 261 deletions

28
.gitignore vendored
View file

@ -1,20 +1,16 @@
*.hi
*.o
.hsenv*
.stack-work
.git
.cabal-sandbox/
cabal.sandbox.config
dist-newstyle/
dist/
*.prof
*.out
*.toc
*.aux
*.log
*.synctex.gz
.*.swp
*.*~
*.trace
*.hp
*.ps
*.swp
.ghc*
cabal.project.local*
dist*/
report.html
*.bak
.direnv/
.envrc
/*.pdf
*.prof
*.ps
*.html

5
CHANGELOG.md Normal file
View file

@ -0,0 +1,5 @@
# Revision history for pituicat
## 0.0.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

755
LICENSE
View file

@ -1,165 +1,674 @@
GNU LESSER GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
0. Additional Definitions.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
1. Exception to Section 3 of the GNU GPL.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
The precise terms and conditions for copying, distribution and
modification follow.
2. Conveying Modified Versions.
TERMS AND CONDITIONS
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
0. Definitions.
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
"This License" refers to version 3 of the GNU General Public License.
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
3. Object Code Incorporating Material from Library Header Files.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
A "covered work" means either the unmodified Program or a work based
on the Program.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
4. Combined Works.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
1. Source Code.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
d) Do one of the following:
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
The Corresponding Source for a work in source code form is that
same work.
5. Combined Libraries.
2. Basic Permissions.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
6. Revised Versions of the GNU Lesser General Public License.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View file

@ -1,8 +1,63 @@
# ![Affection logo](./affection.svg)
# pituicat
A simple Game Engine in Haskell using SDL
This repository contains the source and game design files for the game
`pituicat`.
## Disclaimer
This game is still an experimental work in progress, so things are subject to
change and no version or revision numbers are being assigned (yet).
This Engine is not stable yet. Things may change. horribly.
Even minor version bumps may break the API.
## Build and run instructions
For any system you want to buil on, you will need a current version of `ghc`
as well as the build tool `cabal`. Additional libraries may be needed,
depending on your operating system.
### Linux
I develop this game in [NixOS](https://nixos.org), so the build instructions
for this system are the ones I use on a daily basis.
#### NixOS
To set up the build environment, simply run this in the command line of your
choice in the project's root directory:
```bash
nix-shell shell.nix
```
After the setup and build process is finished and you are given the prompt of
the environmet you just built, you can run
```bash
cabal new-run
```
to compile and run the game.
#### Other Linux flavours
You will need at least the development libraries for `sdl2` and a development
setup for `OpenGL`.
TODO: provide list of development libraries this game directly depends on.
After setting up the system with all development dependencies, simply run
```bash
cabal new-run
```
for compiling and running the game.
### Other operating systems
I wish you good luck with that. If you manage to run the game on other systms,
ping me with build and run instructions on <nek0@nek0.eu> and I will gladly
include them here.
## Gameplay
To move around, use the `D` and `A` Keys respectively.
Press `Space` to jump.

View file

@ -1,4 +1,6 @@
constraints: affection +verbose
profiling: true
packages:
./.
./
./extern/*
constraints:
affection +verbose

20
extern/affection/.gitignore vendored Normal file
View file

@ -0,0 +1,20 @@
*.hi
*.o
.hsenv*
.stack-work
.git
.cabal-sandbox/
cabal.sandbox.config
dist/
*.prof
*.aux
*.hp
*.ps
*.swp
.ghc*
cabal.project.local*
dist*/
report.html
*.bak
.direnv/
.envrc

165
extern/affection/LICENSE vendored Normal file
View file

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

8
extern/affection/README.md vendored Normal file
View file

@ -0,0 +1,8 @@
# ![Affection logo](./affection.svg)
A simple Game Engine in Haskell using SDL
## Disclaimer
This Engine is not stable yet. Things may change. horribly.
Even minor version bumps may break the API.

2
extern/affection/Setup.hs vendored Normal file
View file

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

View file

@ -102,7 +102,7 @@ library
ghc-options: -Wall
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && < 5
, sdl2 ^>= 2.5
, sdl2 >= 2.5
, linear
, text
, mtl
@ -117,6 +117,7 @@ library
, stm
, uuid
, vector
, resourcet
-- This example shows the message system. only makes sense when compiling with
-- verbose flag.
@ -126,12 +127,11 @@ executable example00
ghc-options: -threaded -Wall
default-language: Haskell2010
default-extensions: OverloadedStrings
if flag(examples)
build-depends: base >=4.9 && < 5
, affection
, sdl2 ^>= 2.5
, sdl2 >= 2.5
, stm
else
if !flag(examples)
buildable: False
-- A small game of life implementation
@ -142,10 +142,9 @@ executable example01
ghc-options: -threaded -Wall
default-language: Haskell2010
default-extensions: OverloadedStrings
if flag(examples)
build-depends: base >=4.9 && < 5
, affection
, sdl2 ^>= 2.5
, sdl2 >= 2.5
, stm
, OpenGL
, random
@ -154,7 +153,7 @@ executable example01
, matrix
, nanovg >= 0.6.0.0
, deepseq
else
if !flag(examples)
buildable: False
-- Another small game of life implementation
@ -165,10 +164,9 @@ executable example02
ghc-options: -threaded -Wall
default-language: Haskell2010
default-extensions: OverloadedStrings
if flag(examples)
build-depends: base >=4.9 && < 5
, affection
, sdl2 ^>= 2.5
, sdl2 >= 2.5
, stm
, OpenGL
, random
@ -177,5 +175,5 @@ executable example02
, matrix
, nanovg >= 0.6.0.0
, deepseq
else
if !flag(examples)
buildable: False

View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

4
extern/affection/cabal.project vendored Normal file
View file

@ -0,0 +1,4 @@
constraints: affection +verbose
profiling: true
packages:
./.

18
extern/affection/default.nix vendored Normal file
View file

@ -0,0 +1,18 @@
{ pkgs ? import <nixpkgs> (import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz))
, haskellCompiler ? "ghc865"
}:
pkgs.haskell-nix.cabalProject {
src = pkgs.haskell-nix.haskellLib.cleanGit { src = pkgs.nix-gitignore.gitignoreSource [] ./.; };
ghc = pkgs.buildPackages.pkgs.haskell-nix.compiler.${haskellCompiler};
# pkg-def-extras = [
# # Additional packages ontop of all those listed in `cabal.project`
# ];
# modules = [
# # Specific package overrides would go here for example:
# packages.cbors.package.ghcOptions = "-Werror";
# packages.cbors.patches = [ ./one.patch ];
# packages.cbors.flags.optimize-gmp = false;
# # It may be better to set flags in `cabal.project` instead
# # (`plan-to-nix` will include them as defaults).
# ];
}

7
extern/affection/shell.nix vendored Normal file
View file

@ -0,0 +1,7 @@
{ pkgs ? import <nixpkgs> {}}:
let
affection = pkgs.haskellPackages.callCabal2nix "affection" (gitignore ./.) {};
gitignore = dir: pkgs.nix-gitignore.gitignoreSource [] dir;
in
affection.env

View file

@ -1,6 +1,5 @@
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE Strict #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}
@ -23,12 +22,16 @@ import qualified Graphics.GL as GLRaw
import Foreign.Marshal.Array
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
import Data.String (fromString)
import System.Clock
import Control.Monad.Loops
import Control.Monad.State.Strict
import Control.Monad.Trans.Resource
-- internal imports
-- internal imports
@ -46,46 +49,83 @@ withAffection
:: forall us. (Affectionate us)
=> AffectionConfig us -- ^ Configuration of the Game and its engine.
-> IO ()
withAffection AffectionConfig{..} = do
withAffection AffectionConfig{..} = runResourceT $ do
liftIO $ logIO Debug "Affection starting"
liftIO $ logIO Debug "Initializing SDL"
-- intialiaze SDL
case initComponents of
All ->
SDL.initializeAll
Only is ->
SDL.initialize is
-- give SDL render quality
SDL.HintRenderScaleQuality SDL.$= SDL.ScaleLinear
-- just checking…
do
renderQuality <- SDL.get SDL.HintRenderScaleQuality
when (renderQuality /= SDL.ScaleLinear) $
logIO Warn "Linear texture filtering not enabled!"
liftIO $ logIO Warn "Linear texture filtering not enabled!"
void $ liftIO (logIO Debug . fromString . show <$> (SDL.version :: IO (Integer, Integer, Integer)))
-- construct window
liftIO $ logIO Debug "Creating Window(s)"
windows <- zip3 (map (\(x,_,_) -> x) windowConfigs) <$>
windows <-
mapM
(\wc -> SDL.createWindow windowTitle ((\(_,y,_) -> y) wc))
windowConfigs <*>
pure (map (\(_,_,z) -> z) windowConfigs)
(\(_, sdlWindowConfig, mode) -> do
(windowKey, window) <-
allocate
(SDL.createWindow windowTitle sdlWindowConfig)
(\window -> do
logIO Debug "Destroying Window"
SDL.destroyWindow window
)
return $ AffectionWindow window windowKey mode
)
windowConfigs
-- Show windows
mapM_ (SDL.showWindow . awWindow) windows
-- set modes of windows
mapM_ (\(AffectionWindow window _ mode) -> SDL.setWindowMode window mode) windows
-- Make GL context shareable
void $ SDL.glSetAttribute SDL.SDL_GL_SHARE_WITH_CURRENT_CONTEXT 1
-- Create OpenGL contexts
contexts <-
mapM
(\(AffectionWindow window _ _) -> do
(contextKey, context) <-
allocate
(SDL.glCreateContext window)
(\context -> do
logIO Debug "Destroying context"
SDL.glDeleteContext context
)
return $ AffectionContext context contextKey
)
windows
mapM_ (SDL.showWindow . (\(_,y,_) -> y)) windows
_ <- SDL.glSetAttribute SDL.SDL_GL_SHARE_WITH_CURRENT_CONTEXT 1
contexts <- zip (map (\(x,_,_) -> x) windows) <$>
mapM (SDL.glCreateContext . (\(_,y,_) -> y)) windows
mapM_ (\w -> flip SDL.setWindowMode ((\(_,_,z) -> z) w) ((\(_,y,_) -> y) w)) windows
-- sync updates with monitor
-- SDL.swapInterval $= SDL.SynchronizedUpdates -- <- causes Problems with windows
liftIO $ logIO Debug "Getting Time"
-- print current used GL Version
version <- peekArray0 (0 :: Word8) =<< GLRaw.glGetString GLRaw.GL_VERSION
print (B.pack version)
version <- liftIO $ peekArray0 (0 :: Word8) =<< GLRaw.glGetString GLRaw.GL_VERSION
liftIO $ print (B.pack version)
-- get current time
execTime <- getTime Monotonic
liftIO $ logIO Debug "Getting Time"
execTime <- liftIO $ getTime Monotonic
liftIO $ logIO Debug "Loading initial data container"
-- construct game data object from provided Affectionate instance
gameData <- loadState @us
(gameDataKey, gameData) <-
allocate
(liftIO $ loadState @us)
(liftIO . cleanUp)
-- build state container
let initContainer = AffectionData
{ drawWindows = windows
@ -95,9 +135,9 @@ withAffection AffectionConfig{..} = do
, sysTime = execTime
, pausedTime = False
}
-- initialize and run state
(_, _) <- runStateT
(A.runState
void $ liftIO $ runAffection initContainer
(do
liftIO $ logIO Debug "Running Pre-Loop stage"
-- run preLoop function from Affectionate
@ -107,9 +147,11 @@ withAffection AffectionConfig{..} = do
(do
-- get state
ad <- get
-- Measure time difference form last run
now <- liftIO $ getTime Monotonic
let lastTime = sysTime ad
-- compute dt and update elapsedTime
let dt = fromIntegral
(toNanoSecs $ diffTimeSpec lastTime now) / (10 ^ (9 :: Int))
@ -119,19 +161,25 @@ withAffection AffectionConfig{..} = do
{ elapsedTime = ne
, deltaTime = dt
}
-- poll events
liftIO SDL.pumpEvents
evs <- preHandleEvents =<< liftIO SDL.pollEvents
-- handle events
handleEvents gameData evs
-- execute user defined update loop
unless (pausedTime ad) (update gameData dt)
-- clear GL buffer >> execute user defined draw loop >> flush GL buffer
liftIO $ GL.clear [GL.ColorBuffer, GL.DepthBuffer, GL.StencilBuffer]
draw gameData
liftIO GL.flush
-- actual displaying of newly drawn frame
mapM_ (SDL.glSwapWindow . (\(_,y,_) -> y)) windows
mapM_ (SDL.glSwapWindow . awWindow) windows
-- save new time
ad3 <- get
when (sysTime ad == sysTime ad3) (
@ -141,11 +189,16 @@ withAffection AffectionConfig{..} = do
)
)
)
) initContainer
-- Cleanup works
liftIO $ logIO Debug "Loop ended. Cleaning"
cleanUp gameData
liftIO $ logIO Debug "Destroying Window"
mapM_ (SDL.glDeleteContext . snd) contexts
mapM_ (SDL.destroyWindow . (\(_,y,_) -> y)) windows
release gameDataKey
-- mapM_ (SDL.glDeleteContext . snd) contexts
-- mapM_ (SDL.destroyWindow . (\(_,y,_) -> y)) windows
-- SDL.quit -- <- This causes segfaults depending on hardware
liftIO $ logIO Debug "This is the end"
runAffection
:: AffectionData
-> AffectionState AffectionData ResIO a
-> IO (a, AffectionData)
runAffection initialState a = runResourceT $ runStateT (A.runState a) initialState

View file

@ -5,6 +5,8 @@ module Affection.Types
, AffectionData(..)
, AffectionStateInner
, AffectionState(..)
, AffectionWindow(..)
, AffectionContext(..)
, InitComponents(..)
, Angle
-- | SDL reexports
@ -23,6 +25,7 @@ import qualified Data.Text as T
import Control.Monad.IO.Class
import Control.Monad.State.Strict
import Control.Monad.Trans.Resource
import qualified Control.Monad.Parallel as MP
import System.Clock (TimeSpec)
@ -50,19 +53,8 @@ data InitComponents
-- | Main type for defining the look, feel and action of the whole application.
data AffectionData = AffectionData
{ drawWindows ::
[
( Word -- --^ Window identifier
, SDL.Window -- --^ Window linked with identifier
, SDL.WindowMode -- -- ^ current screen mode
)
] -- ^ SDL windows
, glContext ::
[
( Word -- --^ Window identifier
, SDL.GLContext -- --^ Associated OpenGL context
)
] -- ^ OpenGL rendering contexts
{ drawWindows :: [ AffectionWindow ] -- ^ SDL windows
, glContext :: [ AffectionContext ] -- ^ OpenGL rendering contexts
, elapsedTime :: Double -- ^ Elapsed time in seconds
, deltaTime :: Double -- ^ Elapsed time in seconds since last tick
, sysTime :: TimeSpec -- ^ System time (NOT the time on the clock)
@ -70,15 +62,26 @@ data AffectionData = AffectionData
}
-- | Inner 'StateT' monad for the update state
type AffectionStateInner sd a = StateT sd a
type AffectionStateInner sd m = StateT sd m
-- | Affection's state monad
newtype AffectionState sd m a = AffectionState
{ runState :: AffectionStateInner sd m a }
deriving (Functor, Applicative, Monad, MonadIO, MonadState sd)
deriving (Functor, Applicative, Monad, MonadIO, MonadState sd, MonadResource)
instance MP.MonadParallel m => MP.MonadParallel (AffectionState sd m)
type Affection a = AffectionState AffectionData IO a
type Affection a = AffectionState AffectionData ResIO a
type Angle = Double
data AffectionWindow = AffectionWindow
{ awWindow :: SDL.Window
, awReleaseKey :: ReleaseKey
, awMode :: SDL.WindowMode
}
data AffectionContext = AffectionContext
{ acContext :: SDL.GLContext
, acReleaseKey :: ReleaseKey
}

View file

@ -39,31 +39,34 @@ getDelta = gets deltaTime
toggleScreen :: Word -> Affection ()
toggleScreen windowIdent = do
ad <- get
let mwindow = find (\(ident, _, _) -> ident == windowIdent) (drawWindows ad)
case mwindow of
Just (ident, window, mode) -> do
(stop, alteredWindowList) <- foldM
(\(stop, resWindows) (num, aw@(AffectionWindow window _ mode)) -> do
if stop || num == windowIdent
then do
newMode <- case mode of
SDL.FullscreenDesktop -> do
SDL.setWindowMode window SDL.Windowed
liftIO $ SDL.setWindowMode window SDL.Windowed
return SDL.Windowed
SDL.Windowed -> do
SDL.setWindowMode window SDL.FullscreenDesktop
liftIO $ SDL.setWindowMode window SDL.FullscreenDesktop
return SDL.FullscreenDesktop
x -> do
liftIO $ logIO Warn ("Unexpected window mode: " <> fromString (show x))
return x
return (True, resWindows ++ [aw { awMode = newMode }])
else
return (stop, resWindows ++ [aw])
)
(False, [])
(zip [0..] (drawWindows ad))
if stop
then do
now <- liftIO $ getTime Monotonic
put ad
{ sysTime = now
, drawWindows = map
(\e@(lid, win, _) ->
if lid == ident
then (lid, win, newMode)
else e
)
(drawWindows ad)
, drawWindows = alteredWindowList
}
Nothing -> do
else
liftIO $ logIO Warn ("No window found with ident " <> fromString (show windowIdent))
-- | Fit the GL Viewport to Window size

42
extern/flake.lock vendored Normal file
View file

@ -0,0 +1,42 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1669901374,
"narHash": "sha256-lObvfluyiZ9cgCMgKSrUOjzbx9Oap9949jlnNRPPYqg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1c5443f6cdce419174f198141bd76a3fd9502950",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

61
extern/flake.nix vendored Normal file
View file

@ -0,0 +1,61 @@
{
description = "A game stub written in Haskell";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
haskellPackages = pkgs.haskellPackages;
jailbreakUnbreak = pkg:
pkgs.haskell.lib.doJailbreak (pkg.overrideAttrs (_: { meta = { }; }));
packageName = "affection";
in rec {
packages.${packageName} = # (ref:haskell-package-def)
haskellPackages.callCabal2nixWithOptions packageName self "--flag examples" {
# Dependency overrides go here
};
defaultPackage = self.packages.${system}.${packageName};
apps = {
example00 = {
type = "app";
program = "${self.packages.${system}.${packageName}}/bin/example00";
};
example01 = {
type = "app";
program = "${self.packages.${system}.${packageName}}/bin/example01";
};
example02 = {
type = "app";
program = "${self.packages.${system}.${packageName}}/bin/example02";
};
};
#devShell = packages.${packageName}.env;
devShell = pkgs.mkShell {
buildInputs = with haskellPackages; [
haskell-language-server
ghcid
cabal-install
];
nativeBuildInputs = with pkgs; [
pkg-config
glib
SDL2
freetype
glew
];
inputsFrom = builtins.attrValues self.packages.${system};
};
});
}

89
flake.lock Normal file
View file

@ -0,0 +1,89 @@
{
"nodes": {
"affection-local": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1,
"narHash": "sha256-jml+z0oNeFXjYreNdEMVmYPuFlDmxuYyXBZQ573Tido=",
"path": "./extern/affection",
"type": "path"
},
"original": {
"path": "./extern/affection",
"type": "path"
}
},
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1669901374,
"narHash": "sha256-lObvfluyiZ9cgCMgKSrUOjzbx9Oap9949jlnNRPPYqg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1c5443f6cdce419174f198141bd76a3fd9502950",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1671901901,
"narHash": "sha256-dTeqgnX1HfTeQ3+VtCXB3/t/ilE5iWmjYucwgSvpwnQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b1a9ae8dfd85d144ff1b268d6c7b47934358c9a1",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"affection-local": "affection-local",
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_2"
}
}
},
"root": "root",
"version": 7
}

58
flake.nix Normal file
View file

@ -0,0 +1,58 @@
{
description = "A game stub written in Haskell";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
flake-utils.url = "github:numtide/flake-utils";
affection-local.url = "path:./extern/affection";
};
outputs = { self, nixpkgs, flake-utils, affection-local }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
haskellPackages = pkgs.haskellPackages;
jailbreakUnbreak = pkg:
pkgs.haskell.lib.doJailbreak (pkg.overrideAttrs (_: { meta = { }; }));
packageName = "pituicat";
in rec {
packages.${packageName} = # (ref:haskell-package-def)
haskellPackages.callCabal2nix packageName self rec {
# Dependency overrides go here
affection = affection-local.defaultPackage.${system};
};
defaultPackage = self.packages.${system}.${packageName};
devShell = haskellPackages.shellFor {
packages = p: [ defaultPackage ];
withHoogle = true;
buildInputs = (with haskellPackages; [
haskell-language-server
ghcid
cabal-install
]) ++ (with pkgs; [
glib.dev
]);
};
#devShell = pkgs.mkShell {
# buildInputs = with haskellPackages; [
# haskell-language-server
# ghcid
# cabal-install
# ];
# nativeBuildInputs = with pkgs; [
# pkg-config
# SDL2
# vulkan-validation-layers
# vulkan-headers
# vulkan-loader
# ];
# inputsFrom = builtins.attrValues self.packages.${system};
#};
});
}

BIN
gdd/10pager.pdf Normal file

Binary file not shown.

445
gdd/10pager.tex Normal file
View file

@ -0,0 +1,445 @@
\documentclass[12pt,a4paper]{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[english]{babel}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage{graphicx}
\usepackage{csquotes}
\usepackage{menukeys}
\usepackage[hidelinks]{hyperref}
\usepackage[left=3.00cm, right=3.00cm, top=3.00cm, bottom=3.00cm]{geometry}
\setlength{\parindent}{0pt}
\newcommand{\nbreak}{
\hspace*{0pt}
\newpage
\hspace*{0pt}
}
\author{Amedeo Molnár}
\title{\vfill Pituicat}
\date{\today}
\begin{document}
\pagenumbering{gobble}
\maketitle
\begin{center}
Target Platform: PC(Linux/Win) \quad Target audience: Ages 15 - 35
\end{center}
\vfill
\newpage
\tableofcontents
\newpage
\pagenumbering{arabic}
\setcounter{page}{1}
\vfill
\section{Game story}
You are a cat. or at least you used to be one, until you accidentally fell into
a vat with the latest failed biochem experiment of your owner as the military,
your owners employers, sack him and clean the lab in a hurry. You get poured down
the drain by an assistant while your owner gets escorted away in handcuffs.
When you wake up again in the sewer you have solidified again to a small meowing
gelatinous blob. You are hungry and you yearn for your owner's affection. But
where has he been taken to?
You set out to find him, but have to fend off enemies and withstand dangerous
environments while you make your journey through multiple locations finding clues
about your owner's whereabouts.
Powerups and DNA-Fragments for unlocking new
abilities help you on your way through increasingly dangerous stages full of
different levels.
\vfill
\section{Game play}
In \enquote{Pituicat} You play as slime cat in search of its owner.
The Levels of the game as well as its story are divided into six stages,
each ending with a boss fight.
\vfill
\nbreak
\vfill
\section{Player character}
In the beginning the player character is about the size of a pea, has low health and
can barely lift off the ground when jumping. But as the player advances he will be
awarded DNA-Fragments for defeating bosses and will be able to find powerups and
hidden DNA-Fragments.
Powerups grant some temporary boni whereas DNA-Fragments are used to activate
permanent abilities or increase your health.
With every stage also the size of the character increases. Starting at pea size, you
grow to grape size to golf ball size to tennis ball size to rugby size until you
reach the size of a large cat.
\vfill
\subsection{Abilities}
The activatable abilities, all having three levels, include:
\begin{itemize}
\item Health up (Adds 50\% of your start health to your max health per level)
\item Congeal (reduces damage by 25\% of remaining damage per level)
\item Vacuole (store one collected powerup per level)
\item Spit (shoot acidic projectiles with ranges from 2 over 4 to 8 tiles)
\item Harden (become invulnerable but almost immobile for 1 second. Cooldown starts at 4
seconds and can be reduced to two and finally to 1 second)
\end{itemize}
\vfill
\subsection{Powerups}
Powerups appear on the map as floating droplets and can also be occasionally dropped by
enemies or during boss fights. Droplets spawning on the map do respawn, others not.
These are the available powerups, which will be gradually introduced into the game with
every new stage:
\begin{itemize}
\item Peptide (replenishes 5\% of start health)
\item Oily Emulsion (Increase speed by 25\% for 5 seconds)
\item Latex (Increase jump height by 100\% for 5 seconds)
\item Organic Lead (decrease damage by 50\% but disable jumping for 5 seconds)
\item Antidote (Heal poisoning immediately)
\item Purified Caffeine (Stop surrounding time for 2 seconds)
\item Agent X (Become almost invisible for 5 seconds)
\end{itemize}
\vfill
\section{Player Controls}
The controls described here take a QWERTZ-keyboard as reference.
\begin{tabular}{ll}
\keys{A} & Slither left\\
\keys{D} & Slither right\\
\keys{W} & Jump\\
\keys{S} & Jump down through Platform\\
\keys{F} & Interact\\
\keys{1}, \keys{2}, \keys{3} & Release powerup from specified vacuole\\
\keys{E} & Harden\\
\keys{\space} & Spit\\
\keys{Esc} & Enter/Exit Menu\\
\end{tabular}
\vfill
\nbreak
\vfill
\section{Game world}
The game world is a mostly urbanized landscape with some elements
from cyberpunk.
The levels are themed after the stages they are in and vary slightly
in size, but aim to be rather big and not monolinear. Players can collect
powerups and rarely even DNA-Fragments, which at the end of the Level can
be traded in for new or improved abilities.
Hand crafted carefully composed assets paired with thoughtful level
design aim for an immersable game with a strong retro feel.
\vfill
\section{Game experience}
The game is divided into the following six stages:
\begin{itemize}
\item The Sewer
\item Back Alley
\item Abandoned Lab
\item The Highway
\item Military-Industrial complex
\item Secret base
\end{itemize}
Every stage has its distinct design and set of enemies, puzzles and obstacles as
well as a final Boss to be overcome to succeed.
\vfill
\nbreak
\vfill
\subsection{The Sewer}
The Sewer is a dark and wet place full of dangerous waste and different vermin.
Your main enemies are giant woodlice, mice, rats and goldfish. Contact with
water or other fluids pooling, flowing or driping down there is harmful to the
character.
Puzzles include opening valve systems or restoring lighting to pitch
dark areas.
The Boss at the end of this stage is \enquote{King Rat}.
\vfill
\subsection{Back Alley}
The back alley is a dim place heaping with garbage of various kinds and access
point to many different kinds of infrastructure mostly fallen into disrepair.
Fluids pooling in dumpsters or the street as well as ill maintained electrical
wiring are the environmental dangers accompanied by rats, spiders, robotic
cleaners and various birds as enemies.
You will have to shut down electricity to progress or carefully time runs
between active garbage chutes and trick birds.
The boss at the end of this stage is \enquote{The Raven}.
\vfill
\subsection{Abandoned Lab}
The abandoned lab is the place you used to live with your owner while he was
tinkering on various projects from varying fields of science and engineering.
Now this place is deserted, stripped of all usable equipment and left to rot.
Unfortunately for you this place is far from empty. Semi conscious AI robots
cybernetically or genetically enhanced hamsters which all managed to escape
the sudden purge of the lab as well es still present malfunctioning equipment
and custom or even selfmade security measures are blocking your way.
As you might expect, the puzzles in an former lab of an almost mad scientist
are hard, include teleporting or even manipulation of space and time and force
you to be present at multiple locations simultaneously.
The boss at the end of this stage is \enquote{CyberPsy}
\vfill
\nbreak
\vfill
\subsection{The Highway}
The hot and dusty highway is not a good place for a small lonely little slime
cat.
These surroundings are only scarcely populated, but some snakelings and
lizards thrive here. In addition the scorching sun heating up the asphalt and
lizard traps pose dangers in this environment.
The main Puzzle here will be how to stay outside the sun most of the time or
how to change the environment to create shade.
The boss at the end of this stage is \enquote{Mother Snake}
\vfill
\subsection{Military-Industrial Complex}
This compound of barracks, factories, workshops and traininig grounds is off
limits to the general public and is surrounded by a heavily guarded death strip.
You will have to deal with automated turrets, electric fences, ground spikes in
addition to guard dogs, mobile defense bots and flying surveillance or attack
drones.
You will have to figure out how to enter the seemingly impenetrable fortress
of the secret base without arising too much suspicion on your way in.
The boss at the end of this stage is \enquote{M30w}.
\vfill
\subsection{Secret Base}
Under the Complex it lies, the secret base and only few know of its existence.
Here lies the heart of most secret military research and surveillance.
Enemies include military personel and special operatives and even experimental
cyborgs or armed killer robots. There is a lot of lethal security measures
installed to prevent unauthorized entering.
Trick your way in through mazes of deadly laser arrays, confuse enemies so they
turn on each other and deactivate complex defense mechanisms to path your way
to the heart of the base.
The Boss at the end of this stage is \enquote{Node Kroenen}.
\vfill
\subsection{How to win}
clear all levels and stages and defeat all bosses.
\vfill
\subsection{How to lose}
Your health drops to zero.
\vfill
\nbreak
\vfill
\section{Game mechanics}
\enquote{Pituicat} takes place in a two-dimensional world viewed from the side, the typical
platformer view. The camera is usually centered on the player and follows his character thus
scrolling horizontally and vertically, depending on level size in the direction of movement.
The player moves around the map in real time as do the enemies. A HUD on screen schows
remaining health in from of a bar the active powerup if any and the stored up to three
powerups if any.
\hspace{.5cm}
A player in game can move around, jump and interact with certain objects in the game like
valves or switches. The number of active control buttons on the keyboard rises as the player
progresses and activates new abilities, but are mapped out to be easily reachable with one
hand. Powerups and DNA-Fragments laying around on the map or dropped by enemies are collected
actively by the player through direct contact. After clearing each level the player can spend
newly gained DNA-Fragments on abilities on a static screen also showing some statistics on
the player's progress before advancing to the next level.
\hspace{.5cm}
In \enquote{Pituicat} there is a number of active and passive abilities a player can invest
DNA-Fragments in, enabling new actions or simply aiding the player's survival.
Every ability has three levels of power, which can be activated coonsuming a DNA-Fragment,
but only when the previous power level has been already activated. Unlocking an ability,
also raising the power level to one, does not require any previous level of any other
ability, but also consumes a DNA-Fragment.
\hspace{.5cm}
An example for a passive ability would be \enquote{Congeal}, which reduces received damage
by 25\% per power level.
\hspace{.5cm}
Another example, this time for an active ability, is \enquote{Spit}, which enables the
player to shoot a projectile with increasing range for each power level starting with
two tile lengths over four to eight before dissolving into thin air.
\vfill
\nbreak
\vfill
\section{Enemies}
Every stage of the game has it's own set of enemies with varying sets of abilities. Below
is a sample of enemies ordered by stage and a short description of the stage's boss.
\vfill
\subsection{The Sewer}
\begin{itemize}
\item Woodlice eat everything looking edible and can roll up to a ball if threatened.
\item Goldfish jump out of water to catch prey.
\item \enquote{King Rat} is accompanied by his large and ever hungry court of rat minions
\end{itemize}
\vfill
\subsection{Back alley}
\begin{itemize}
\item Rats mourn their lost king and are furious
\item Spiders can shoot webs slowing you down
\item \enquote{The Raven} drops heavy pieces of garbage onto its prey and deflects
projectiles with mighty gusts of wind.
\end{itemize}
\vfill
\subsection{Abandoned Lab}
\begin{itemize}
\item Cyberhamsters ccan dash forward very quickly with their cybernetic limbs
\item Axolotls spit acidic drops and can return from the dead.
\item \enquote{CyberPsy} is a genetically altered cybernetically enhanced capuchin monkey
with psionic abilities.
\end{itemize}
\vfill
\nbreak
\vfill
\subsection{The Highway}
\begin{itemize}
\item Lizards can be particularly tough and fast, it is very hot there.
\item Bigger snakescan spit venom at the player.
\item \enquote{Mother Snake} has a lot of hungry babies and has powerful venom dealing
damage over a prolonged period of time.
\end{itemize}
\vfill
\subsection{Military-Industrial Complex}
\begin{itemize}
\item Flying drones use the blades of their rotors as weapons
\item Guard dogs can alert other guard dogs
\item \enquote{M30w}, a cybernetically enhanced bobcat, has a powerful jaw and can eject
its claws attached with dangerous microfilament
\end{itemize}
\vfill
\subsection{Secret Base}
\begin{itemize}
\item Military personel is armed and shoots on sight
\item Operatives can turn invisible for a period of time
\item \enquote{Node Kroenen} is your owner, stripped of his personality and attached as
central command and control unit to a giant Mech
\end{itemize}
\vfill
\section{Cutscenes}
Narrative cutscenes will take place at the beginning of the game and et the end of each stage.
\section{Bonus Materials}
Bonus levels might be possible.
\hspace*{0pt}
\end{document}

BIN
gdd/1pager.pdf Normal file

Binary file not shown.

67
gdd/1pager.tex Normal file
View file

@ -0,0 +1,67 @@
\documentclass[10pt,a4paper]{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[english]{babel}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage{graphicx}
\usepackage[left=2.00cm, right=2.00cm, top=2.00cm, bottom=2.00cm]{geometry}
\author{Amedeo Molnár}
\title{Pituicat}
\date{\today}
\setlength{\parindent}{0pt}
\begin{document}
\maketitle
\section{Game Platform}
PC (Linux/Win)
\section{Target audience}
Ages 15 - 35
\section{Game summary}
You are a cat. At least you used to be one, until you fell into one of your
owner's experiments at the biochem lab, dissolved to goo and got poured down
the drain by one of the assistants.
When you wake up in the sewer you turned into a gelatinous meowing blob. As
you set out to look for your owner you pass multiple locations and find useful
stuff.
\section{Game outline}
The player controls a slime cat in a through a jump'n'run styled game avoiding
enemies and harsh environment.
The Game and its story are divided into several stages, which in turn are
divided into multiple levels. Between levels you can invest collected
DNA-Fragments on already unlocked abilities or increase your health and
every few levels you hit a milestone where you can unlock new abilities
through DNA-Fragments.
\section{Unique selling points}
\begin{itemize}
\item Old school Jump'n'Run meets RPG elements
\item Retro-styled graphics
\item Solve puzzles as you progress
\item 3.1415 different difficulty modes
\item 6.022 $\cdot$ 10\textsuperscript{23} unique enemies
\item meow
\end{itemize}
\section{Similar competitive products}
\begin{itemize}
\item Super Mario game series
\item Celeste
\end{itemize}
\end{document}

BIN
gdd/beat_chart.pdf Normal file

Binary file not shown.

482
gdd/beat_chart.tex Normal file
View file

@ -0,0 +1,482 @@
\documentclass[12pt,a4paper,landscape]{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[english]{babel}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage{graphicx}
\usepackage{csquotes}
\usepackage[left=2.00cm,right=2.00cm,
top=2.00cm,bottom=2.00cm]{geometry}
\author{Amedeo Molnár}
\title{Pituicat -- Beat Chart}
\date{\today}
\begin{document}
\pagenumbering{gobble}
\maketitle
\newpage
\tableofcontents
\newpage
\pagenumbering{arabic}
\setcounter{page}{1}
\section{The Sewer}
\begin{tabular}{
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
}
\textbf{Level: The Sewer 1} &
\textbf{Level: The Sewer 2} &
\textbf{Level: The Sewer 3} &
\textbf{Level: The Sewer 4} \\
\textbf{Name: The Sewer -- This is (not) the end} &
\textbf{Name: The Sewer -- Fish and Chips} &
\textbf{Name: The Sewer -- Ratty business} &
\textbf{Name: The Sewer -- Facing the King} \\
\textbf{Story:} Pituicat awakens in a dark corner
of the sewers. &
\textbf{Story:} Pituicat crosses the sewer, encounters
first rat scouts. &
\textbf{Story:} Pituicat has to fights his way through
the sewer. &
\textbf{Story:} King Rat is furious about Pituicat
entering his domain and fights. \\
\textbf{Progression:} Player taught basic movement,
combat and collecting powerups &
\textbf{Progression:} Player crosses first level and
collects first powerups and solves first puzzles &
\textbf{Progression:} Player solves more complex
puzzles
and encounters lots of rats &
\textbf{Progression:} Player fights
\enquote{King Rat} \\
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 5 min. \\
\textbf{Color map:} brownish tones (eww!), red
(bricks, pipes), yellow (lights), blue (pipes) &
\textbf{Color map:} brownish tones (eww!), red
(bricks, pipes), yellow (lights), blue (pipes),
yellowish blue (water?) &
\textbf{Color map:} brownish tones (eww!), red
(bricks, pipes), yellow (lights), blue (pipes),
yellowish blue (water?) &
\textbf{Color map:} brownish tones (eww!, Wood), red
(bricks, pipes), yellow (lights), blue (pipes),
yellowish blue (water?) \\
\textbf{Enemies}: Woodlice, Goldfish &
\textbf{Enemies}: Woodlice, Goldfish, Rat scout &
\textbf{Enemies}: Woodlice, Goldfish, Rat scout, Rat,
Big Rat &
\textbf{Enemies}: \enquote{King Rat} \\
\textbf{Mechanics:} Wall jump &
\textbf{Mechanics:} Wall jump &
\textbf{Mechanics:} Wall jump &
\textbf{Mechanics:} Wall jump \\
\textbf{Hazards:} Cracky platform, Sewage &
\textbf{Hazards:} Cracky platform, Sewage, Sewage Drops &
\textbf{Hazards:} Cracky platform, Sewage, Sewage Drops,
Slippery ground &
\textbf{Hazards:} None \\
\textbf{Power-ups:} Peptide, Oily Emulsion &
\textbf{Power-ups:} Peptide, Oily Emulsion &
\textbf{Power-ups:} Peptide, Oily Emulsion &
\textbf{Power-ups:} Peptide, Oily Emulsion \\
\textbf{Economy:} None &
\textbf{Economy:} DNA-Fragment &
\textbf{Economy:} None &
\textbf{Economy:} DNA-Fragment \\
\textbf{Puzzles:} Light switch &
\textbf{Puzzles:} Timed pump activation run &
\textbf{Puzzles:} Slime ground maze &
\textbf{Puzzles:} None \\
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD \\
\end{tabular}
\newpage
\section{Back Alley}
\begin{tabular}{
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
}
\textbf{Level: Back Alley 1} &
\textbf{Level: Back Alley 2} &
\textbf{Level: Back Alley 3} &
\textbf{Level: Back Alley 4} \\
\textbf{Name: Back Alley -- Under pressure} &
\textbf{Name: Back Alley -- Light of day} &
\textbf{Name: Back Alley -- Up the Pipe} &
\textbf{Name: Back Alley -- Shadow in the sky} \\
\textbf{Story:} Pituicat first reaches the world above
ground since transformation and is greeted by a
garbage compressor. &
\textbf{Story:} After getting out of the garbage
Compressor the back alley is open for exploration. &
\textbf{Story:} To get out of the alley to the lab
Pituicat has to get higher up. &
\textbf{Story:} The roof is reached, but guarded by a
raven. \\
\textbf{Progression:} Player reaches new stage and is
introduced to new Powerup &
\textbf{Progression:} Player has to traverse a mostly
vertical level to get above the garbage containers &
\textbf{Progression:} Player climbs through garbage
chutes and over fire excape ladders to get to the roof
&
\textbf{Progression:} Player fights
\enquote{The Raven} \\
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 5 min. \\
\textbf{Color map:} red (bricks), blueish grey
(steel), grey (asphalt, concrete) &
\textbf{Color map:} red (bricks), blueish grey
(steel), grey (asphalt, concrete) &
\textbf{Color map:} red (bricks), blueish grey
(steel), grey (asphalt, concrete),
blue/black (glass panes) &
\textbf{Color map:} grey (roof), white (air chutes),
red (brick), blue (glass panes) \\
\textbf{Enemies}: Rat, Spider, Big Spider &
\textbf{Enemies}: Rat, Spider, Big Spider, Cleaner Bot
&
\textbf{Enemies}: Spider, Big Spider, Sparrow &
\textbf{Enemies}: \enquote{The Raven} \\
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump \\
\textbf{Hazards:} Glass shard pit, Battery acid,
conveyor belt, cardboard platform (breaks) &
\textbf{Hazards:} Glass shard pit, Battery acid,
conveyor belt, cardboard platform (breaks),
dripping bleh &
\textbf{Hazards:} conveyor belt,
cardboard platform (breaks),
dripping bleh &
\textbf{Hazards:} None \\
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex \\
\textbf{Economy:} None &
\textbf{Economy:} DNA-Fragment &
\textbf{Economy:} None &
\textbf{Economy:} DNA-Fragment \\
\textbf{Puzzles:} Disable garbage compressor &
\textbf{Puzzles:} Find and shutt off breakers for bad
wiring &
\textbf{Puzzles:} trick a flock of birds into going
somewhere else &
\textbf{Puzzles:} None \\
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD \\
\end{tabular}
\newpage
\section{Abandoned Lab}
\begin{tabular}{
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
}
\textbf{Level: Abandoned Lab 1} &
\textbf{Level: Abandoned Lab 2} &
\textbf{Level: Abandoned Lab 3} &
\textbf{Level: Abandoned Lab 4} \\
\textbf{Name: Abandoned Lab -- Knock Knock} &
\textbf{Name: Abandoned Lab -- Downwards} &
\textbf{Name: Abandoned Lab -- Back home?} &
\textbf{Name: Abandoned Lab -- The Experiment} \\
\textbf{Story:} Pituicat enters its former home through a ventilation chute on the
roof. &
\textbf{Story:} Pituicat has to find a way through the ventilation system. &
\textbf{Story:} Pituicat finds its old now mostly empty home and looks for his
owner, but someone seems to have claimed the lab in the meantime. &
\textbf{Story:} Pituicat fights the claimer of the lab. \\
\textbf{Progression:} Player is introduced to new stage and new powerup &
\textbf{Progression:} Player has to traverse a labyrinthine level &
\textbf{Progression:} Player crosses tha lab and fights through hordes of
enemies &
\textbf{Progression:} Player faces and fights \enquote{CyberPsy} \\
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 5 min. \\
\textbf{Color map:} greyish blue (steel), red (rust), brown (wood) &
\textbf{Color map:} greyish blue (steel), red (rust), brown (wood) &
\textbf{Color map:} grey-white (walls), brown (wood, furniture) &
\textbf{Color map:} grey-white (walls), brown (wood, furniture) \\
\textbf{Enemies}: Crazy Hamster, CyberSpider &
\textbf{Enemies}: Crazy Hamster, CyberSpider, CyberHamster &
\textbf{Enemies}: Crazy Hamster, CyberSpider, CyberHamster, PsyHamster &
\textbf{Enemies}: \enquote{CyberPsy} \\
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump \\
\textbf{Hazards:} Ventilator, UV-Sterilizer &
\textbf{Hazards:} Ventilator, UV-Sterilizer, Laser Trap &
\textbf{Hazards:} Robotic Limbs, Acid Puddle, Laser Trap, Explosive Bottles &
\textbf{Hazards:} None \\
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead \\
\textbf{Economy:} None &
\textbf{Economy:} DNA-Fragment &
\textbf{Economy:} None &
\textbf{Economy:} DNA-Fragment \\
\textbf{Puzzles:} Cross UV-Sterilizer &
\textbf{Puzzles:} Get to the bottom of the main shaft &
\textbf{Puzzles:} Deactivate shields guarding the new master of the lab &
\textbf{Puzzles:} None \\
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD \\
\end{tabular}
\newpage
\section{The Highway}
\begin{tabular}{
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
}
\textbf{Level: The Highway 1} &
\textbf{Level: The Highway 2} &
\textbf{Level: The Highway 3} &
\textbf{Level: The Highway 4} \\
\textbf{Name: The Highway -- On the Road} &
\textbf{Name: The Highway -- Searing Sun} &
\textbf{Name: The Highway -- Snake Country} &
\textbf{Name: The Highway -- Serpent Dream} \\
\textbf{Story:} After defeating \enquote{CyberPsy} Pituicat makes his way to the
Highway. &
\textbf{Story:} Pituicat is on the Road to the Military-Industrial Complex. &
\textbf{Story:} Pituicat follows the Highway and enters Rattlesnake infested
areas. &
\textbf{Story:} Pituicat faces \enquote{Mother Snake}. \\
\textbf{Progression:} Player is introduced to new stage and powerup &
\textbf{Progression:} Player crosses a rather long level with scarce resources &
\textbf{Progression:} Player enters a rattlesnake infested area of the Highway &
\textbf{Progression:} Player faces \enquote{Mother Snake}, the Boss of this
Area \\
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 5 min. \\
\textbf{Color map:} grey (concrete), black (asphalt), yellowish brown (sand),
light blue (sky) &
\textbf{Color map:} grey (concrete), black (asphalt), yellowish brown (sand),
light blue (sky), brown (dirt) &
\textbf{Color map:} grey (concrete), black (asphalt), yellowish brown (sand),
light blue (sky), brown (dirt) &
\textbf{Color map:} yellowish brown (sand), brown (dirt) \\
\textbf{Enemies}: Small Yellow Lizard, Small green Lizard &
\textbf{Enemies}: Small Lizards (green and yellow),
Big Lizard (yellow and green), Tarantulas &
\textbf{Enemies}: Big Lizard (yellow and green), Snakeling, Rattlesnake &
\textbf{Enemies}: \enquote{Mother Snake}, Snakeling, Rattlesnake \\
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump \\
\textbf{Hazards:} Hot Asphalt &
\textbf{Hazards:} Hot Asphalt, Poison bait &
\textbf{Hazards:} Hot Asphalt, Poison bait, Snake nest &
\textbf{Hazards:} None \\
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote \\
\textbf{Economy:} None &
\textbf{Economy:} DNA-Fragment &
\textbf{Economy:} None &
\textbf{Economy:} DNA-Fragment \\
\textbf{Puzzles:} Avoid Lizard Traps &
\textbf{Puzzles:} Avoid long stretches of hot asphalt &
\textbf{Puzzles:} Avoid long stretches of hot asphalt and snake nests &
\textbf{Puzzles:} None \\
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD \\
\end{tabular}
\newpage
\section{Military-Industrial Complex}
\begin{tabular}{
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
}
\textbf{Level: Military-Industrial Complex 1} &
\textbf{Level: Military-Industrial Complex 2} &
\textbf{Level: Military-Industrial Complex 3} &
\textbf{Level: Military-Industrial Complex 4} \\
\textbf{Name: Military-Industrial Complex -- Outer Ring} &
\textbf{Name: Military-Industrial Complex -- Death Zone} &
\textbf{Name: Military-Industrial Complex -- At the Gates} &
\textbf{Name: Military-Industrial Complex -- Nice Kitty?} \\
\textbf{Story:} Pituicat reaches the Military-Industrial Complex and needs to
find a way to get past the first security ring. &
\textbf{Story:} Pituicat has to cross the death zone between the outer ring and
the entrance to the Base. &
\textbf{Story:} Pituicat enters the Military-Industrial Complex and works his way
to the base. &
\textbf{Story:} Pituicat faces \enquote{M30w} guarding the base. \\
\textbf{Progression:} Introduction to new stage and powerup &
\textbf{Progression:} Crossing of a vast, open and dangerous terrain &
\textbf{Progression:} Crossing a branched Level with lots of obstacles &
\textbf{Progression:} Fight the stage boss \\
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 5 min. \\
\textbf{Color map:} black (asphalt), dark green (grasy groud), purple (sky),
dark gray (concrete, steel) &
\textbf{Color map:} black (asphalt), dark green (grasy groud), purple (sky),
dark gray (concrete, steel) &
\textbf{Color map:} black (asphalt), purple (sky), dark gray (concrete, steel) &
\textbf{Color map:} black (asphalt), purple (sky), dark gray (concrete, steel) \\
\textbf{Enemies}: Guard Dog, TaserTurret, CyberEagle, Doubleheaded Cobra &
\textbf{Enemies}: Guard Dog, CyberDog, LaserTurret, CyberEagle,
Doubleheaded Cobra, ManHack &
\textbf{Enemies}: Guard Dog, CyberDog, LaserTurret, CyberEagle,
Doubleheaded Cobra, ManHack, Armed Skimmer &
\textbf{Enemies}: \enquote{M30w} \\
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump \\
\textbf{Hazards:} Electric Fence, Ground Spikes &
\textbf{Hazards:} Electric Fence, Electrified Ground Spikes, Mines &
\textbf{Hazards:} Electric Fence, Electrified Ground Spikes, Mines,
Conveyor Belt, Industrial Press &
\textbf{Hazards:} None \\
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote,
Purified Caffeine &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote,
Purified Caffeine &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote,
Purified Caffeine &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote,
Purified Caffeine \\
\textbf{Economy:} None &
\textbf{Economy:} DNA-Fragment &
\textbf{Economy:} None &
\textbf{Economy:} DNA-Fragment \\
\textbf{Puzzles:} Get over electric Fence, Turret Area, Deactivate Ground Spikes &
\textbf{Puzzles:} Deactivate electrified Ground spikes &
\textbf{Puzzles:} Deactivate the industrial machines to reach the base entrance &
\textbf{Puzzles:} None \\
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD \\
\end{tabular}
\newpage
\section{Secret Base}
\begin{tabular}{
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
p{.23\linewidth}|
}
\textbf{Level: Secret Base 1} &
\textbf{Level: Secret Base 2} &
\textbf{Level: Secret Base 3} &
\textbf{Level: Secret Base 4} \\
\textbf{Name: Secret Base -- Into Darkness} &
\textbf{Name: Secret Base -- Elevator music} &
\textbf{Name: Secret Base -- Where the Strange Things are} &
\textbf{Name: Secret Base -- Friend or Foe?} \\
\textbf{Story:} Pituicat enters the secret base, where his owner is kept. &
\textbf{Story:} Pituicat is inside the base and has to reach the bottom most
level. &
\textbf{Story:} Pituicat reaches the bottom most level of the base and proceeds
to The Core. &
\textbf{Story:} Pituicat faces \enquote{Node Kroenen}, his former owner. \\
\textbf{Progression:} Introduction to new stage and powerup &
\textbf{Progression:} traverse an inveresely vertical level &
\textbf{Progression:} Traverse a branched, non-linear level full of enemies &
\textbf{Progression:} The Final boss of the game \\
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 15 min. &
\textbf{Est. play time:} 5 min. \\
\textbf{Color map:} black (ground, ceiling), blue-grey (steel),
dark grey (concrete), blue-gray (wall paint) &
\textbf{Color map:} black (ground, ceiling), blue-grey (steel),
dark grey (concrete), green-gray (wall paint) &
\textbf{Color map:} black (ground, ceiling), blue-grey (steel),
dark grey (concrete), yellow-gray (wall paint) &
\textbf{Color map:} black (ground, ceiling), blue-grey (steel),
dark grey (concrete), red-gray (wall paint) \\
\textbf{Enemies}: Armed Guard, RoboGuard, Operative &
\textbf{Enemies}: Armed Guard, RoboGuard, RoboSamurai, Operative &
\textbf{Enemies}: RoboSamurai, Operative, BrainSpider, ExploFly &
\textbf{Enemies}: \enquote{Node Kroenen} \\
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump &
\textbf{Mechanics:} Wall Jump \\
\textbf{Hazards:} Turret, Plasma Burners, Laser Barrier,
Charged MicroFilament Fence &
\textbf{Hazards:} Elevator, Electric Trap, Laser Barrier, Charged Microfilament Fence &
\textbf{Hazards:} PulsarTurret, LaserBarrier, PlasmaFilament Cutter, Enzyme Pit &
\textbf{Hazards:} None \\
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote,
Purified Caffeine, Agent X &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote,
Purified Caffeine, Agent X &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote,
Purified Caffeine, Agent X &
\textbf{Power-ups:} Peptide, Oily Emulsion, Latex, Organic lead, Antidote,
Purified Caffeine, Agent X \\
\textbf{Economy:} None &
\textbf{Economy:} DNA-Fragment &
\textbf{Economy:} None &
\textbf{Economy:} None \\
\textbf{Puzzles:} Work your way into the base &
\textbf{Puzzles:} Use elevators to pass barriers inside elevator shafts &
\textbf{Puzzles:} Deactivate various defence mechanisms &
\textbf{Puzzles:} None \\
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD &
\textbf{Music track:} TBD \\
\end{tabular}
\end{document}

81
pituicat.cabal Normal file
View file

@ -0,0 +1,81 @@
cabal-version: 2.4
-- Initial package description 'pituicat.cabal' generated by 'cabal init'.
-- For further documentation, see http://haskell.org/cabal/users-guide/
name: pituicat
version: 0.0.0.0
-- synopsis:
-- description:
-- bug-reports:
license: GPL-3.0-only
license-file: LICENSE
author: nek0
maintainer: nek0@nek0.eu
-- copyright:
category: Game
extra-source-files: CHANGELOG.md
executable pituicat
main-is: Main.hs
other-modules: Types
, Types.Application
, Types.Subsystems
, Types.Map
, Types.Cast
, Types.StageSet
, Types.Tangible
, Types.Util
, Types.Player
, Types.PowerUp
, Classes
, Classes.Scene
, Classes.Actor
, Classes.Prop
, Classes.Collectable
, Graphics
, Graphics.Types
, Graphics.Types.VertexArray
, Graphics.Types.VertexBuffer
, Graphics.Types.IndexBuffer
, Graphics.Types.Shader
, Graphics.Types.Texture
, Graphics.Classes
, Graphics.Classes.Bindable
, Graphics.Classes.Buffer
, Graphics.Classes.VertexLayout
, Graphics.Classes.Drawable
, Physics
, Physics.Classes
, Physics.Classes.Mass
, Physics.Classes.Collidible
, Scenes.Test
, Scenes.Test.Types
, Scenes.Test.Util
, Scenes.Test.Load
, Scenes.Test.Update
, Scenes.Test.EventHandler
, Map
, StateMachine
, Renderer
, Util
-- other-extensions:
build-depends: base >=4.13.0.0
, affection
, stm
, OpenGL
, OpenGLRaw
, sdl2
, containers
, linear
, text
, aeson
, vector
, JuicyPixels
, JuicyPixels-extra
, bytestring
, derive-storable
, monad-loops
, StateVar
hs-source-dirs: src
default-language: Haskell2010
ghc-options: -Wall -threaded

BIN
res-old/audio/meow-1.wav Normal file

Binary file not shown.

BIN
res-old/audio/meow-3.wav Normal file

Binary file not shown.

BIN
res-old/audio/meow-4.wav Normal file

Binary file not shown.

BIN
res-old/audio/meow-8.wav Normal file

Binary file not shown.

BIN
res-old/audio/meow.wav Normal file

Binary file not shown.

BIN
res-old/audio/purr.wav Normal file

Binary file not shown.

BIN
res-old/img/pituicat.kra Normal file

Binary file not shown.

BIN
res-old/img/pituicat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

BIN
res/pituicat/pituicat.kra Normal file

Binary file not shown.

BIN
res/pituicat/pituicat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

18
res/shaders/frag.shader Normal file
View file

@ -0,0 +1,18 @@
#version 400 core
layout(location = 0) out vec4 color;
in vec4 v_color;
in vec2 v_texCoord;
in float v_texIndex;
uniform sampler2D[2] u_textures;
void main()
{
//vec4 texColor = texture(u_texture, v_texCoord);
int index = int(v_texIndex);
//color = texture(u_textures[index], v_texCoord);
color = texture(u_textures[index], v_texCoord) * v_color;
//color = vec4(0.0, 1.0, 0.0, 1.0);
}

20
res/shaders/vert.shader Normal file
View file

@ -0,0 +1,20 @@
#version 400 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_texCoord;
layout(location = 3) in float a_texIndex;
out vec4 v_color;
out vec2 v_texCoord;
out float v_texIndex;
uniform mat4 u_mvp;
void main()
{
gl_Position = u_mvp * vec4(a_position, 1);
v_texCoord = a_texCoord;
v_color = a_color;
v_texIndex = a_texIndex;
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -1,7 +1,53 @@
{ pkgs ? import <nixpkgs> {}}:
{ nixpkgs ? import <nixpkgs> {}, compiler ? "default", doBenchmark ? false }:
let
affection = pkgs.haskellPackages.callCabal2nix "affection" (gitignore ./.) {};
gitignore = dir: pkgs.nix-gitignore.gitignoreSource [] dir;
inherit (nixpkgs) pkgs;
affection = with haskellPackages; callPackage(
{ mkDerivation, base, bytestring, clock, containers, glib, linear
, monad-loops, monad-parallel, mtl, OpenGL, sdl2, stdenv, stm, text
, uuid, vector
}:
mkDerivation {
pname = "affection";
version = "0.0.0.10";
src = ./extern/affection;
configureFlags = [ "-fverbose" ];
isLibrary = true;
isExecutable = true;
libraryHaskellDepends = [
base bytestring clock containers glib linear monad-loops
monad-parallel mtl OpenGL sdl2 stm text uuid vector
];
homepage = "https://github.com/nek0/affection#readme";
description = "A simple Game Engine using SDL";
license = pkgs.lib.licenses.lgpl3;
}) {};
f = { mkDerivation, aeson, base, bytestring, containers, derive-storable
, JuicyPixels, JuicyPixels-extra, linear, monad-loops, OpenGL, OpenGLRaw
, stdenv, sdl2, StateVar, stm, text, vector}:
mkDerivation {
pname = "pituicat";
version = "0.0.0.0";
src = ./.;
isLibrary = false;
isExecutable = true;
executableHaskellDepends = [ aeson affection base bytestring containers
derive-storable JuicyPixels JuicyPixels-extra linear monad-loops OpenGL
OpenGLRaw sdl2 StateVar stm text vector];
license = pkgs.lib.licenses.gpl3;
};
haskellPackages = if compiler == "default"
then pkgs.haskellPackages
else pkgs.haskell.packages.${compiler};
variant = if doBenchmark then pkgs.haskell.lib.doBenchmark else pkgs.lib.id;
drv = variant (haskellPackages.callPackage f {});
in
affection.env
if pkgs.lib.inNixShell then drv.env else drv

8
src/Classes.hs Normal file
View file

@ -0,0 +1,8 @@
module Classes
( module C
) where
import Classes.Scene as C
import Classes.Prop as C
import Classes.Actor as C
import Classes.Collectable as C

10
src/Classes/Actor.hs Normal file
View file

@ -0,0 +1,10 @@
module Classes.Actor where
-- internal imports
import Classes.Prop
import Physics.Classes.Collidible
class (Prop a, Collidible a) => Actor a where
perform :: Double -> a -> a

View file

@ -0,0 +1,27 @@
module Classes.Collectable where
import Physics.Classes.Collidible (Collidible)
-- | All kinds of Effects present in the game
data Effect
= HealthUp
| SpeedUp
| JumpUp
| ArmorUp
| Antidote
| StopTime
| Invisibility
deriving (Eq, Show)
-- | The Effect holder type definition
data EffectHolder = EffectHolder
{ effectDuration :: Double -- ^ Duration of stored effect
, effectReleased :: Effect -- ^ The actual effect released
}
deriving (Eq, Show)
class Collidible c => Collectable c where
effect
:: c -- ^ Collectable object
-> EffectHolder -- ^ The descriptor of the effect contained

11
src/Classes/Prop.hs Normal file
View file

@ -0,0 +1,11 @@
module Classes.Prop where
-- internal imports
import Graphics.Classes.Drawable
class (Show a, Drawable a) => Prop a where
residentLayer :: a -> Word
bindPropTexture :: a -> IO ()

30
src/Classes/Scene.hs Normal file
View file

@ -0,0 +1,30 @@
module Classes.Scene where
import Affection
import Control.Concurrent.STM
-- internal imports
import Types.Util
import Types.Subsystems
class Scene a where
-- | Perform initialization and output a (possibly empty) data container.
initScene :: IO a
-- | Load actual data into the initialized container.
loadScene :: a -> TMVar Progress -> IO ()
-- | Query whether loading data is finished.
isSceneLoaded :: a -> Affection Bool
-- | Run updates on the data given the time elapsed since last frame
update :: a -> Double -> Affection ()
-- | Handle input events
onEvents :: a -> TranslatorMessage -> Affection ()
-- | perform the drawing
render :: a -> Affection ()

6
src/Graphics.hs Normal file
View file

@ -0,0 +1,6 @@
module Graphics
( module G
) where
import Graphics.Classes as G
import Graphics.Types as G

8
src/Graphics/Classes.hs Normal file
View file

@ -0,0 +1,8 @@
module Graphics.Classes
( module GC
) where
import Graphics.Classes.Bindable as GC
import Graphics.Classes.Buffer as GC
import Graphics.Classes.VertexLayout as GC
import Graphics.Classes.Drawable as GC

View file

@ -0,0 +1,10 @@
module Graphics.Classes.Bindable where
-- | typeclass for bindable objects like buffers, vertex arrays or shaders
class Bindable a where
-- bind the object in context
bind :: a -> IO ()
-- release object from context
unbind :: a -> IO ()

View file

@ -0,0 +1,47 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
module Graphics.Classes.Buffer where
import qualified Graphics.Rendering.OpenGL as GL
import Foreign.Storable
-- internal imports
import Graphics.Classes.Bindable
-- | this aims to be a typeclass for all used buffer objects throughout the
-- game. all buffer objects have to instanciate the Bindable typeclass.
-- think of it as some kind of interface in the object oriented context
class (Bindable a, Storable (StoreType a)) =>
Buffer a where
-- | datatype inside the buffer
type StoreType a :: *
-- | container datatype for 'StoreType's
type StoreContainer a :: * -> *
-- | what buffer target does the buffer bind to
target :: a -> GL.BufferTarget
-- | retrieve ID from buffer object
glId :: a -> IO (GL.BufferObject)
-- | bind the buffer object and fill it with null data
initialize :: a -> IO ()
-- | fill the buffer with actual data
write
:: a
-> Word
-- ^ write offset from beginning of buffer in number of data elements
-> StoreContainer a (StoreType a)
-- ^ Data to write
-> IO ()
-- | expand the buffer to double of it's previous size.
expand :: a -> IO ()
-- | free resources and delete the object
delete :: a -> IO ()

View file

@ -0,0 +1,18 @@
{-# LANGUAGE TypeFamilies #-}
module Graphics.Classes.Drawable where
import qualified Data.Vector as V
-- iternal imports
import Graphics.Types.VertexBuffer (Vertex)
-- | A typeclass for all drawable objects
class Drawable a where
-- -- | List type for resulting vertices and indices
-- type VertexList a :: * -> *
-- | Function returning a tuple of Vectors of draw order indices and the
-- appropriate vertices.
toVertices :: a -> (V.Vector Word, V.Vector Vertex)

View file

@ -0,0 +1,18 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
module Graphics.Classes.VertexLayout where
import qualified Graphics.Rendering.OpenGL as GL
-- internal imports
import Graphics.Classes.Bindable
class (Bindable (VertBuffer v)) => VertexLayout v where
type VertBuffer v :: *
layoutElements :: v -> [(GL.GLuint, GL.VertexArrayDescriptor GL.GLfloat)]
addBuffer :: v -> VertBuffer v -> IO ()

9
src/Graphics/Types.hs Normal file
View file

@ -0,0 +1,9 @@
module Graphics.Types
( module GT
) where
import Graphics.Types.VertexArray as GT
import Graphics.Types.VertexBuffer as GT
import Graphics.Types.IndexBuffer as GT
import Graphics.Types.Shader as GT
import Graphics.Types.Texture as GT

View file

@ -0,0 +1,148 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
module Graphics.Types.IndexBuffer where
import Affection
import qualified Graphics.Rendering.OpenGL as GL
import SDL (($=))
import Data.String (fromString)
import qualified Data.Vector.Storable as VS
import Control.Monad (when)
import Control.Monad.Loops (whileM_)
import Control.Concurrent.STM
import Foreign
import Foreign.C.Types
-- internal imports
import Graphics.Classes.Bindable
import Graphics.Classes.Buffer
-- layout of the IndexBuffer data object
data IndexBuffer = IndexBuffer
{ iBufId :: TVar GL.BufferObject -- buffer id
, iBufSize :: TVar GL.GLsizeiptr -- size of data
-- , iBufData :: Ptr a -- pointer to data
, iBufCount :: TVar GL.GLint -- number of data elements
}
-- instanciate typeclass from Buffer and fill in missing implementations
instance Buffer IndexBuffer where
type StoreType IndexBuffer = GL.GLuint
type StoreContainer IndexBuffer = VS.Vector
target _ = GL.ElementArrayBuffer
glId = readTVarIO . iBufId
initialize buf = do
-- bind the buffer using the default iplementation of the typeclass
bind buf
size <- readTVarIO $ iBufSize buf
-- fill in the data
GL.bufferData (target buf) $=
( size
, nullPtr
, GL.DynamicDraw
)
-- release the buffer using the default implementation of the typeclass
unbind buf
write buf offset dat = do
whileM_
(do
currentSize <- readTVarIO (iBufSize buf)
pure $ (fromIntegral offset + VS.length dat) *
sizeOf (undefined :: StoreType IndexBuffer)
> fromIntegral currentSize)
$ do
currentBufSize <- readTVarIO (iBufSize buf)
allocaArray
(fromIntegral currentBufSize `div`
sizeOf (undefined :: StoreType IndexBuffer))
$ \ (ptr :: Ptr (StoreType IndexBuffer)) -> do
bind buf
GL.bufferSubData
(target buf)
GL.ReadFromBuffer
0
currentBufSize
ptr
expand buf
bind buf
GL.bufferSubData
(target buf)
GL.WriteToBuffer
0
currentBufSize
ptr
-- bind buffer, just to be safe
bind buf
let elemCount = fromIntegral offset + VS.length dat
logIO Verbose ("elemCount: " <> (fromString $ show elemCount))
currentCount <- atomically $ readTVar (iBufCount buf)
when (fromIntegral elemCount > currentCount) $
atomically $ writeTVar (iBufCount buf) (fromIntegral elemCount)
VS.unsafeWith dat $ \ ptr ->
GL.bufferSubData
(target buf)
GL.WriteToBuffer
(CPtrdiff $ fromIntegral offset *
fromIntegral (sizeOf (undefined :: StoreType IndexBuffer)))
(CPtrdiff $ fromIntegral $
VS.length dat * sizeOf (undefined :: StoreType IndexBuffer))
ptr
expand buf = do
unbind buf
delete buf
newId <- GL.genObjectName
newSize <- (2 *) <$> readTVarIO (iBufSize buf)
atomically $ do
writeTVar (iBufId buf) newId
writeTVar (iBufSize buf) newSize
initialize buf
delete buf = do
unbind buf
GL.deleteObjectName =<< (readTVarIO $ iBufId buf)
instance Bindable IndexBuffer where
-- bind the buffer
bind buf =
(\ a -> GL.bindBuffer (target buf) $= Just a) =<< glId buf
-- unbind the buffer
unbind buf = GL.bindBuffer (target buf) $= Nothing
newIndexBuffer
:: Word
-> IO IndexBuffer -- newly built IndexBuffer data object
newIndexBuffer initLength = do
-- create the buffer object in applicative style
buf <- IndexBuffer
-- generate the ID
<$> (newTVarIO =<< GL.genObjectName)
-- compute buffer size
<*> newTVarIO (CPtrdiff (fromIntegral $
(fromIntegral initLength * sizeOf (undefined :: StoreType IndexBuffer))))
-- -- make pointer out of list
-- <*> newArray list
-- -- get count
<*> newTVarIO 0
-- fill the data in to the buffer
initialize buf
-- return the data object
return buf

View file

@ -0,0 +1,201 @@
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
module Graphics.Types.Shader where
import qualified Graphics.Rendering.OpenGL as GL
import qualified Graphics.GL as GLRaw
import Data.StateVar
import qualified Data.ByteString as B
import Control.Concurrent.MVar
import Linear
import Foreign.Marshal.Array (withArray)
import Foreign.Storable
-- internal imports
import Graphics.Classes.Bindable
data Shader = Shader
{ shaderId :: GL.Program
, shaderSources :: [ShaderSource]
, shaderUniforms :: MVar [ShaderUniform]
}
-- make Shader Bindable
instance Bindable Shader where
bind s = GL.currentProgram $= Just (shaderId s)
unbind _ = GL.currentProgram $= Nothing
data ShaderSource = ShaderSource
{ shaderSourceType :: GL.ShaderType
, shaderPath :: FilePath
}
data ShaderUniform = ShaderUniform
{ shaderUniformName :: String
, shaderUniformLocation :: GL.UniformLocation
}
-- orphan instance to make linear's M44 uniforms
instance GL.Uniform (M44 GL.GLfloat) where
uniform (GL.UniformLocation ul) = makeStateVar getter setter
where
getter = error "cannot implement: get uniform M44 GLfloat"
-- GL.withNewMatrix GL.RowMajor $ getUniformWith GLRaw.glGetUniformfv loc
setter (V4
(V4 a b c d)
(V4 e f g h)
(V4 i j k l)
(V4 m n o p)) = do
mat <- GL.newMatrix GL.RowMajor [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p] :: IO (GL.GLmatrix GL.GLfloat)
GL.withMatrix mat $ GLRaw.glUniformMatrix4fv ul 1 . isRowMajor
uniformv (GL.UniformLocation _) _ _ = error "can not implement uniformv for M44 GLfloat"
-- GLRaw.glUniformMatrix4fv ul count 0 (castPtr buf `asTypeOf` elemType buf)
-- where
-- elemType = undefined :: GL.MatrixComponent c => Ptr (GL.GLmatrix c) -> Ptr c
-- getUniformWith :: (GL.GLuint -> GL.GLint -> Ptr a -> IO ()) -> GL.UniformLocation -> Ptr a -> IO ()
-- getUniformWith getter (GL.UniformLocation ul) buf = do
-- program <- fmap (GL.programID . fromJust) $ get GL.currentProgram
-- getter program ul buf
isRowMajor :: GL.MatrixOrder -> GL.GLboolean
isRowMajor p = if (GL.RowMajor == p) then 1 else 0
-- create new data object of type Shader
newShader :: [ShaderSource] -> IO Shader
newShader shaderSrc = do
-- create program Object
program <- GL.createProgram
-- create shaders from the source codes given
compilates <- mapM
(\(ShaderSource type_ path) -> do
-- read in shader source from File
source<- B.readFile path
-- compile the shader
compiledShader <- compileShaderSource type_ source
return (type_, compiledShader)
)
shaderSrc
-- attach, link and validate the shader program
GL.attachedShaders program $= map snd compilates
GL.linkProgram program
GL.validateProgram program
ok <- get (GL.validateStatus program)
if ok
then
putStrLn "Shaderprogram linked successfully!"
else do
info <- get (GL.programInfoLog program)
putStrLn "Shaderprogram linking failed!\nInfo log says:"
putStrLn info
GL.deleteObjectName program
-- throw away the shaders, since they are linked into the shader program
mapM_ (\s -> GL.deleteObjectName s) (map snd compilates)
-- return data object
Shader program shaderSrc <$> newMVar []
-- pass uniform values into Shader program
setUniformList :: (Storable a, GL.Uniform a) => Shader -> String -> [a] -> IO ()
setUniformList (Shader shaderProgram _ sUniforms) uniname data_ = do
-- check if uniform location is already cached
locs <- readMVar sUniforms
-- retrieve uniform location
let unilocs = filter
(\(ShaderUniform name _) -> name == uniname)
locs
case unilocs of
[] -> do
print ("Unknown uniform: " <> uniname)
print "Retrieving uniform location from shader program"
loc@(GL.UniformLocation locNum) <- get $ GL.uniformLocation shaderProgram uniname
if locNum < 0
then
print ("Uniform does not exist in shader program: " <> uniname)
else do
-- set the data
withArray data_ $ \ptr ->
GL.uniformv loc (fromIntegral $ length data_) ptr
--GL.uniform loc $= data_
-- add uniform to cache
modifyMVar_ sUniforms
(\list -> return $ ShaderUniform uniname loc : list)
[ShaderUniform _ loc] ->
-- set the data
withArray data_ $ \ptr ->
GL.uniformv loc (fromIntegral $ length data_) ptr
setUniform :: (GL.Uniform a) => Shader -> String -> a -> IO ()
setUniform (Shader shaderProgram _ sUniforms) uniname data_ = do
-- check if uniform location is already cached
locs <- readMVar sUniforms
-- retrieve uniform location
let unilocs = filter
(\(ShaderUniform name _) -> name == uniname)
locs
case unilocs of
[] -> do
print ("Unknown uniform: " <> uniname)
print "Retrieving uniform location from shader program"
loc@(GL.UniformLocation locNum) <- get $ GL.uniformLocation shaderProgram uniname
if locNum < 0
then
print ("Uniform does not exist in shader program: " <> uniname)
else do
-- set the data
--withArray data_ $ \ptr ->
-- GL.uniformv loc (length data_) ptr
GL.uniform loc $= data_
-- add uniform to cache
modifyMVar_ sUniforms
(\list -> return $ ShaderUniform uniname loc : list)
[ShaderUniform _ loc] ->
-- set the data
GL.uniform loc $= data_
-- | compile a shader from source
compileShaderSource
:: GL.ShaderType -- ^ what type of shader we are compiling
-> B.ByteString -- ^ its source code
-> IO GL.Shader
compileShaderSource type_ source = do
-- create shader object of specified type
shaderObject <- GL.createShader type_
-- assign source code to shader object
GL.shaderSourceBS shaderObject $= source
-- actually compile the shader
GL.compileShader shaderObject
-- error handling
ok <- GL.compileStatus shaderObject
if ok
then
putStrLn (show type_ ++ ": compilation successful!")
else do
info <- get (GL.shaderInfoLog shaderObject)
putStrLn (show type_ ++ ": compilation failed!\nInfo log says:")
putStrLn info
GL.deleteObjectName shaderObject
return shaderObject

View file

@ -0,0 +1,83 @@
module Graphics.Types.Texture where
import SDL (($=))
import qualified Graphics.Rendering.OpenGL as GL
import Codec.Picture
import Codec.Picture.Extra
import Data.Vector.Storable (unsafeWith)
import Linear
import Foreign
-- internal imports
import Graphics.Classes.Bindable
data Texture = Texture
{ textureId :: GL.TextureObject
, textureSlot :: GL.TextureUnit
}
deriving (Eq, Show)
instance Bindable Texture where
bind t = do
GL.activeTexture $= textureSlot t
GL.textureBinding GL.Texture2D $= Just (textureId t)
unbind _ = GL.textureBinding GL.Texture2D $= Nothing
newTexture :: FilePath -> GL.GLuint -> IO (V2 Word, Texture)
newTexture fp slot = do
-- read in image from filesystem
img <- flipVertically <$> convertRGBA8 <$>
either
(\err -> error $ "reading image file " <> fp <> " failed: " <> err)
id
<$>
readImage fp
-- extract the raw pointer from vector
unsafeWith (imageData img) $ \ptr -> do
-- create texture object
tex <- Texture
<$> GL.genObjectName
<*> (pure $ GL.TextureUnit slot)
-- <*> (pure fp)
let dimensions = fromIntegral <$> V2 (imageWidth img) (imageHeight img)
-- <*> (pure $ componentCount (VS.head $ imageData img))
data_ = castPtr ptr
-- bind texture
bind tex
-- set texture parameters
GL.textureFilter GL.Texture2D $= ((GL.Linear', Nothing), GL.Linear')
GL.textureWrapMode GL.Texture2D GL.S $= (GL.Repeated, GL.Repeat)
GL.textureWrapMode GL.Texture2D GL.T $= (GL.Repeated, GL.Repeat)
-- put data into GPU memory
loadTexture tex dimensions data_
-- unbind texture
unbind tex
-- pass texture object out
return (fmap fromIntegral dimensions, tex)
loadTexture :: Texture -> V2 GL.GLsizei -> Ptr () -> IO ()
loadTexture _ dimensions data_ =
let (V2 w h) = dimensions
in GL.texImage2D
GL.Texture2D
GL.NoProxy
0
GL.RGBA'
(GL.TextureSize2D w h)
0
(GL.PixelData GL.RGBA GL.UnsignedByte data_)

View file

@ -0,0 +1,25 @@
{-# LANGUAGE TypeFamilies #-}
module Graphics.Types.VertexArray where
import qualified Graphics.Rendering.OpenGL as GL
import SDL (($=))
-- internal imports
import Graphics.Classes.Bindable
newtype VertexArray = VertexArray
{ vArrId :: GL.VertexArrayObject
}
deriving (Eq, Show)
instance Bindable VertexArray where
bind va = GL.bindVertexArrayObject $= Just (vArrId va)
unbind _ = GL.bindVertexArrayObject $= Nothing
newVertexArray :: IO VertexArray
newVertexArray = VertexArray
<$> GL.genObjectName

View file

@ -0,0 +1,222 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Graphics.Types.VertexBuffer where
import qualified Graphics.Rendering.OpenGL as GL
import SDL (($=))
import Control.Monad.Loops (whileM_)
import Control.Concurrent.STM
import qualified Data.Vector.Storable as VS
import Linear
import Foreign
import Foreign.C.Types
import Foreign.Storable.Generic
import GHC.Generics
-- internal imports
import Graphics.Classes.Bindable
import Graphics.Classes.Buffer
import Graphics.Classes.VertexLayout
-- | layout of the VertexBuffer data object
data VertexBuffer = VertexBuffer
{ vBufId :: TVar GL.BufferObject -- ^ buffer id
, vBufSize :: TVar GL.GLsizeiptr -- ^ size of data
-- , vBufData :: Ptr a -- pointer to data
}
data Vertex = Vertex
{ vertPosition :: V3 GL.GLfloat
, vertColor :: V4 GL.GLfloat
, vertTexCoord :: V2 GL.GLfloat
, vertTexID :: GL.GLfloat
}
deriving (Generic, Show)
-- | make vertices instances of 'Storable'. it's a kind of magic ^.^
instance GStorable Vertex
instance GStorable (V2 GL.GLfloat)
instance GStorable (V3 GL.GLfloat)
instance GStorable (V4 GL.GLfloat)
-- | Smart constructor for a new Vertex
newVertex
:: V3 GL.GLfloat
-> V4 GL.GLfloat
-> V2 GL.GLfloat
-> GL.GLfloat
-> Vertex
newVertex pos color texcoord index =
Vertex
pos
color
texcoord
index
-- | Instanciate typeclass Buffer and fill in missing implementations
instance Buffer VertexBuffer where
type StoreType VertexBuffer = Vertex
type StoreContainer VertexBuffer = VS.Vector
target _ = GL.ArrayBuffer
glId = readTVarIO . vBufId
initialize buf = do
-- bind the buffer using the default iplementation of the typeclass
bind buf
size <- readTVarIO (vBufSize buf)
-- fill in the data
GL.bufferData (target buf) $=
( size
, nullPtr
, GL.DynamicDraw
)
-- release the buffer using the default implementation of the typeclass
unbind buf
write buf offset dat = do
whileM_
(do
currentSize <- readTVarIO (vBufSize buf)
pure $ (fromIntegral offset + VS.length dat) *
sizeOf (undefined :: StoreType VertexBuffer)
> fromIntegral currentSize)
$ do
currentBufSize <- readTVarIO (vBufSize buf)
allocaArray
(fromIntegral currentBufSize `div`
sizeOf (undefined :: StoreType VertexBuffer))
$ \ (ptr :: Ptr (StoreType VertexBuffer)) -> do
bind buf
GL.bufferSubData
(target buf)
GL.ReadFromBuffer
0
currentBufSize
ptr
expand buf
bind buf
GL.bufferSubData
(target buf)
GL.WriteToBuffer
0
currentBufSize
ptr
-- bind buffer, just to be safe
bind buf
VS.unsafeWith dat $ \ ptr ->
GL.bufferSubData
(target buf)
-- target buffer type
GL.WriteToBuffer
-- data access direction
(CPtrdiff $ fromIntegral offset *
fromIntegral (sizeOf (undefined :: StoreType VertexBuffer)))
-- offset from buffer start
(CPtrdiff $ fromIntegral $
VS.length dat * sizeOf (undefined :: StoreType VertexBuffer))
-- length of data to write
ptr
-- pointer to first data object
--le | expand the buffer to double of it's previous size.
-- This discards all previously written data and layouts, as internally the
-- old buffer is discarded and a new buffer allocated.
expand buf = do
-- unbind the buffer before meddling with its innards
unbind buf
-- free the old buffer
delete buf
-- generate new buffer
newId <- GL.genObjectName
-- calculate new size
newSize <- (2 *) <$> readTVarIO (vBufSize buf)
-- update data object
atomically $ do
writeTVar (vBufId buf) newId
writeTVar (vBufSize buf) newSize
-- allocate and fill buffer with null data
initialize buf
delete buf = do
unbind buf
GL.deleteObjectName =<< (readTVarIO $ vBufId buf)
-- | Instanciate Bindable typeclass required by Buffer typeclass implement it
instance Bindable VertexBuffer where
-- bind the buffer
bind buf =
(\a -> GL.bindBuffer (target buf) $= Just a) =<< glId buf
-- unbind the buffer
unbind buf =
GL.bindBuffer (target buf) $= Nothing
-- | smart constructor for a VertexBuffer data object
newVertexBuffer
:: Word -- ^ initial Buffer size
-> IO VertexBuffer -- ^ newly built VertexBuffer data object
newVertexBuffer initSize = do
-- let dummyVertex = newVertex (V3 0 0 0) (V4 0 0 0 0) (V2 0 0) 0
-- create the buffer object in applicative style
buf <- VertexBuffer
-- generate the ID
<$> (newTVarIO =<< GL.genObjectName)
-- compute buffer size
<*> (newTVarIO (CPtrdiff (fromIntegral $
(fromIntegral initSize * sizeOf (undefined :: StoreType VertexBuffer)))))
-- make pointer out of list
-- <*> newArray list
-- fill the data in to the buffer
initialize buf
-- return the data object
return buf
instance VertexLayout Vertex where
type VertBuffer Vertex = VertexBuffer
layoutElements _ =
foldl
(\acc (index, count) ->
( index
, GL.VertexArrayDescriptor
count
GL.Float
(fromIntegral $ sizeOf (undefined :: Vertex))
(nullPtr `plusPtr` (sizeOf (undefined :: GL.GLfloat) * fromIntegral
(sum (map ((\(GL.VertexArrayDescriptor c _ _ _) -> c) . snd) acc))))
) : acc
)
[]
(zip [0 ..] [3, 4, 2, 1])
addBuffer vert vb = do
-- bind vertex buffer to associate them
bind vb
-- enable and fill vertex attrib pointer(s)
let list = layoutElements vert
mapM_
(\(index, descriptor) -> do
GL.vertexAttribArray (GL.AttribLocation index) $= GL.Enabled
GL.vertexAttribPointer (GL.AttribLocation index) $=
( GL.ToNormalizedFloat
, descriptor
)
)
list
unbind vb

156
src/Main.hs Normal file
View file

@ -0,0 +1,156 @@
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Affection
import SDL (($=), get)
import qualified SDL
import qualified SDL.Raw.Video as SDL (glSetAttribute)
import qualified SDL.Raw.Enum as SDL
import qualified Graphics.Rendering.OpenGL as GL
import Control.Monad (when, void, unless)
import Control.Concurrent.STM
import qualified Data.Map.Strict as M
import Data.String (fromString)
import Linear
-- internal imports
import StateMachine()
import Types
import Classes
import Util
import Scenes.Test
instance Affectionate GameData where
preLoop = preLoad
handleEvents = handle
update = Main.update
draw = Main.draw
loadState = Main.init
cleanUp _ = return ()
hasNextStep = liftIO . readTVarIO . gameRunning
main :: IO ()
main = do
let config = AffectionConfig
{ initComponents = All
, windowTitle = "Pituicat"
, windowConfigs =
[ ( 0
, SDL.defaultWindow
{ SDL.windowInitialSize = V2 800 600
, SDL.windowResizable = False
, SDL.windowGraphicsContext = SDL.OpenGLContext SDL.defaultOpenGL
{ SDL.glProfile = SDL.Core SDL.Normal 4 0
}
}
, SDL.Windowed
)
]
} :: AffectionConfig GameData
withAffection config
preLoad :: GameData -> Affection ()
preLoad gd = do
translatorUUID <- partSubscribe
(subKeyboard $ gameSubsystems gd)
(\mesg ->
case mesg of
(MsgKeyboardEvent time _ motion False keysym) -> do
translator <- liftIO $ readTVarIO (gameActionTranslation gd)
case
( SDL.keysymScancode keysym
, translateSDLModifiers (SDL.keysymModifier keysym)
) `M.lookup`
translator
of
Just action ->
partEmit
(subTranslator $ gameSubsystems gd)
(TranslatorMessage action time motion)
Nothing ->
globalKeyHandle gd mesg
_ ->
return ()
)
windowCloseUUID <- partSubscribe
(subWindow $ gameSubsystems gd)
(\mesg -> case mesg of
MsgWindowClose _ _ ->
liftIO $ atomically $ writeTVar (gameRunning gd) False
MsgWindowResize {} ->
fitViewport (800 / 600) mesg
_ ->
return ()
)
liftIO $ atomically $ writeTVar
(gameGeneralClean gd)
[translatorUUID, windowCloseUUID]
handle :: GameData -> [SDL.EventPayload] -> Affection ()
handle gd evs = do
state <- liftIO (readTVarIO $ gameState gd)
smEvent state gd evs
update :: GameData -> Double -> Affection ()
update gd dt = do
loadProgress <- liftIO $ atomically $ readTMVar $ gameStateLoadProgress gd
when (fst loadProgress < 1) $
liftIO (logIO Verbose ("Progress: " <> snd loadProgress))
state <- liftIO $ readTVarIO $ gameState gd
isStagePresent <- liftIO $ atomically $ fmap not $ isEmptyTMVar $ gameScene gd
if isStagePresent
then do
liftIO $ logIO Verbose "Stage is present"
(Stage sceneContainer) <- liftIO $ atomically $ readTMVar $ gameScene gd
sceneLoaded <- isSceneLoaded sceneContainer
if not sceneLoaded
then do
liftIO $ logIO Verbose "Loading scene"
smLoad state gd
evHandler <- partSubscribe
(subTranslator $ gameSubsystems gd)
(onEvents sceneContainer)
liftIO $ atomically $ writeTVar (gameSceneClean gd) (Just evHandler)
else
smUpdate state gd dt
else
liftIO $ logIO Error "No Stage to play on"
draw :: GameData -> Affection ()
draw gd = do
state <- liftIO (readTVarIO $ gameState gd)
liftIO $ logIO Verbose (fromString $ "now drawing: " <> show state)
GL.clearColor $= GL.Color4 0 0 0 1
smDraw state gd
err <- SDL.get GL.errors
unless (null err) (liftIO $ logIO Error ("loop errors: " <> fromString (show err)))
init :: IO GameData
init = do
GL.blend $= GL.Enabled
GL.blendFunc $= (GL.SrcAlpha, GL.OneMinusSrcAlpha)
void $ SDL.glSetAttribute SDL.SDL_GL_SHARE_WITH_CURRENT_CONTEXT 1
GameData
<$> (newTMVarIO . Stage =<< (initScene :: IO Test))
<*> newTVarIO Loading
<*> newTMVarIO (0, "Ohai!")
<*> (Subsystems
<$> (SubWindow <$> newTVarIO [])
<*> (SubMouse <$> newTVarIO [])
<*> (SubKeyboard <$> newTVarIO [])
<*> (SubTranslator <$> newTVarIO [])
)
<*> newTVarIO defaultTranslation
<*> newTVarIO True
<*> newTVarIO Nothing
<*> newTVarIO Nothing
<*> newTVarIO []
<*> newTVarIO Nothing

72
src/Map.hs Normal file
View file

@ -0,0 +1,72 @@
{-# LANGUAGE OverloadedStrings #-}
module Map where
import qualified Graphics.Rendering.OpenGL as GL
import Linear
import Codec.Picture
import Codec.Picture.Extra
import qualified Data.Vector as V
import Control.Monad (foldM)
-- internal imports
import Types
import Graphics
constructMap :: LevelDescriptor -> GL.GLuint -> IO LevelMap
constructMap desc tilemapSlot = do
tilemap <- uncurry TileMap <$> newTexture (levelTileMap desc) tilemapSlot
(layers, dimensions) <- foldM
(\(layers, dims) descTup -> do
(nlayer, ndims) <-
readLayer descTup (fmap fromIntegral $ tileMapDimensions tilemap)
if not (null layers) && ndims /= dims
then error ("Map Layer dimensions mismatch in: " <> snd descTup)
else return (layers ++ [nlayer], ndims)
)
([], V2 0 0)
(V.fromList $ levelLayerPath desc)
LevelMap
<$> pure (V.fromList layers)
<*> pure (levelWalkLayer desc)
<*> pure (fmap fromIntegral dimensions)
<*> pure tilemap
<*> pure (V2 (fst $ levelStartPos desc) (snd $ levelStartPos desc))
readLayer
:: (Word, FilePath) -- ^ index and path of the layer descriptor image
-> V2 Float -- ^ size of Tilemap in pixels
-> IO ((V.Vector Tile), V2 Int)
readLayer (_, path) (V2 tx ty) = do
img <- flipVertically <$> convertRGBA8 <$> either error id <$> readImage path
let width = imageWidth img
height = imageHeight img
layer = V.foldl
(\acc (py, px) -> case pixelAt img px py of
(PixelRGBA8 r g b _) -> if r >= 253
then
acc `V.snoc` Tile
(V2 (fromIntegral px) (fromIntegral py))
( V2
((255 - fromIntegral g) * (32 / tx))
(1 - (255 - fromIntegral b + 1) * (32 / ty))
, V2
((255 - fromIntegral g + 1) * (32 / tx))
(1 - (255 - fromIntegral b) * (32 / ty))
)
(case r of
255 -> Solid
254 -> Platform
_ -> Decoration
)
False
else
acc
)
V.empty
(V.fromList ((,) <$> [0 .. height - 1] <*> [0 .. width - 1]))
return (layer, V2 width height)

5
src/Physics.hs Normal file
View file

@ -0,0 +1,5 @@
module Physics
( module P
) where
import Physics.Classes as P

6
src/Physics/Classes.hs Normal file
View file

@ -0,0 +1,6 @@
module Physics.Classes
( module PC
) where
import Physics.Classes.Mass as PC
import Physics.Classes.Collidible as PC

View file

@ -0,0 +1,136 @@
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE OverloadedStrings #-}
module Physics.Classes.Collidible where
import Affection as A
import Linear
-- internal imports
import Physics.Classes.Mass
data CollisionResult direction
= NoCollision
-- | CollisionImminent
-- { collisionTime :: time
-- , collisionDirection :: direction
-- }
| OverlapCollision
{ collisionDepth :: direction
}
deriving (Show, Eq)
-- | Typeclass for implementing collision results on objects.
class (Show c, Mass c) => Collidible c where
-- | Final position of the object in the previous timestep
prevPosition :: c -> V2 Double
-- | Aggregated impact forces in a simulation step
impactForces :: c -> V2 Double
-- | Overwrite the impact forces of the mass object
impactForcesUpdater :: c -> (V2 Double -> c)
-- | reset impact forces vector at the beginning of a simulation step
resetImpactForces :: c -> c
resetImpactForces c = impactForcesUpdater c (V2 0 0)
-- | Add a impact force to the impact forces acting on the mass object
addImpactForce
:: c
-> V2 Double
-> c
addImpactForce c force =
impactForcesUpdater c (impactForces c + force)
-- | Flag indicating a collision during the current time step
collisionOccured :: c -> Bool
-- | Update the collision occurence flag
updateCollisionOccurence :: c -> Bool -> c
-- | returns the bottom left and top right corners relative to the objects
-- positional vector of the axis aligned bounding box (AABB) serving here
-- as collision boundaries.
boundary
:: c -- ^ Object
-> ( V2 Double
, V2 Double
) -- ^ Bottom left and top right corner of AABB relative to position
collisionCheck
:: (Collidible other)
=> c -- ^ First object
-> other -- ^ second object
-> CollisionResult (V2 Double) -- ^ Do the objects collide?
collisionCheck m1 m2 =
let (V2 p1x p1y) = position m1
(V2 p2x p2y) = position m2
(V2 b1minx b1miny, V2 b1maxx b1maxy) = boundary m1
(V2 b2minx b2miny, V2 b2maxx b2maxy) = boundary m2
getCoordinates
| p1x <= p2x && p1y <= p2y =
let x1 = p1x + b1maxx
y1 = p1y + b1maxy
x2 = p2x + b2minx
y2 = p2y + b2minx
in (x1, y1, x2, y2)
| p1x > p2x && p1y <= p2y =
let x1 = p1x + b1minx
y1 = p1y + b1maxy
x2 = p2x + b2maxx
y2 = p2y + b2miny
in (x1, y1, x2, y2)
| p1x <= p2x && p1y > p2y =
let x1 = p1x + b1maxx
y1 = p1y + b1miny
x2 = p2x + b2minx
y2 = p2y + b2maxy
in (x1, y1, x2, y2)
| otherwise =
let x1 = p1x + b1minx
y1 = p1y + b1miny
x2 = p2x + b2maxx
y2 = p2y + b2maxy
in (x1, y1, x2, y2)
(ox1, oy1, ox2, oy2) = getCoordinates
in
if ox2 - ox1 < 0 || oy2 - oy1 < 0
then OverlapCollision
(V2
(min 0 (ox2 - ox1) * (- 1))
(min 0 (oy2 - oy1) * (- 1))
)
else
NoCollision
-- | This Function is called for every collision on both colliding objects.
collide
:: (Collidible other)
=> c -- ^ Original object
-> [(other, CollisionResult (V2 Double))] -- ^ Collision partners and results
-> Double -- ^ Timestep length
-> c -- ^ Updated original object
collide coll1 collrs dt =
foldl (\acc a -> elasticCollision 0.9 acc a dt) coll1 collrs
-- | Implementation of a dampened elastic collision used as default collision
-- implementation of the collision reaction
elasticCollision
:: (Collidible c1, Collidible c2)
=> Double -- ^ Restitution coefficient
-> c1 -- ^ First collision partner
-> (c2, CollisionResult (V2 Double)) -- ^ Second collision partner with collision result
-> Double -- ^ Timestep length
-> c1 -- ^ Updated first collision partner
elasticCollision _ mo1 (_, NoCollision) _ = mo1
elasticCollision restitution mo1 (mo2, OverlapCollision depth) dt =
let dvel = (velocity mo1 - velocity mo2) * normalize depth
j = (restitution + 1) * (- (dvel `dot` dvel)) /
(1 / mass mo1 + 1 / mass mo2)
fi = (* (j / dt)) <$> normalize depth
in
positionUpdater (addImpactForce (updateCollisionOccurence mo1 True) fi)
(position mo1 - depth)

View file

@ -0,0 +1,62 @@
module Physics.Classes.Mass where
import Linear
-- | This typeclass is a centerpiece of the physics implementation and is used
-- to implement the basic mass properties of a (very simplified) physical body.
class Mass m where
-- | The mass of the mass object.
mass :: m -> Double
-- | Retrieve the position of the mass object.
position :: m -> V2 Double
-- | Overwrite the position of the mass object.
positionUpdater :: m -> (V2 Double -> m)
-- | retrieve the velocity of the mass object.
velocity :: m -> V2 Double
-- | Overwrite the velocity of the mass object.
velocityUpdater :: m -> (V2 Double -> m)
-- | Total force acting on a particle in one simulation step
forces :: m -> V2 Double
-- | Overwrite the forces acting on a particle
forcesUpdater :: m -> (V2 Double -> m)
-- | Reset forces vector for the begining of a new simulation step
resetForces :: m -> m
resetForces m = forcesUpdater m (V2 0 0)
-- | Calculate the loads or forces acting on the mass object except for
-- collision forces.
addLoads
:: m -- ^ The mass object
-> V2 Double -- ^ force to be aggregated acting on the particle (e.g.: gravity)
-> m -- ^ Resulting mass object
addLoads m vec =
forcesUpdater m (forces m + vec)
-- | Euler integration updates
updateByEuler
:: m -- ^ The mass object
-> Double -- ^ Time step in fraction of a second
-> m -- ^ Resulting mass object
updateByEuler m dt =
let acc = (/ mass m) <$> forces m
dvel = (* dt) <$> acc
nvel = velocity m + dvel
dpos = (* dt) <$> nvel
npos = position m + dpos
in
resetForces $ positionUpdater (velocityUpdater m nvel) npos
calculateLoads
:: m -- ^ Original mass object
-> V2 Double -- ^ Gravitational force vector
-> m -- ^ Updated mass object
calculateLoads =
addLoads

23
src/Renderer.hs Normal file
View file

@ -0,0 +1,23 @@
{-# LANGUAGE OverloadedStrings #-}
module Renderer where
import qualified Graphics.Rendering.OpenGL as GL
import Control.Concurrent.STM
import Foreign.Ptr
-- internal imports
import Graphics
draw :: VertexArray -> IndexBuffer -> Shader -> IO ()
draw va ib sp = do
bind sp
bind va
bind ib
count <- atomically $ readTVar $ iBufCount ib
GL.drawElements GL.Triangles count GL.UnsignedInt nullPtr
-- clear :: IO ()
-- clear = GL.clear [GL.ColorBuffer]

94
src/Scenes/Test.hs Normal file
View file

@ -0,0 +1,94 @@
{-# LANGUAGE OverloadedStrings #-}
module Scenes.Test
( module Test
) where
import Affection as A
import Linear
import qualified Data.Vector.Storable as VS
import qualified Data.Vector as V
import Data.String (fromString)
import Data.Maybe (fromJust)
import Control.Concurrent.STM
-- internal imports
import Types
import Classes
import Graphics
import Renderer as R
import Scenes.Test.Types as Test
import Scenes.Test.Util as Test
import Scenes.Test.Load as Test
import Scenes.Test.Update as Test
import Scenes.Test.EventHandler as Test
instance Scene Test where
initScene =
Test
<$> newEmptyTMVarIO
<*> newEmptyTMVarIO
<*> newTVarIO False
<*> newTVarIO Nothing
<*> newTVarIO V.empty
<*> newTVarIO V.empty
<*> newTVarIO V.empty
<*> newTVarIO (PVM identity identity identity)
<*> newTVarIO []
loadScene = load
isSceneLoaded = liftIO . readTVarIO . testLoaded
update = Test.update
onEvents = singleHandler
render level = liftIO $ do
(GLAssets va vb ib sh _) <- atomically (readTMVar $ testGraphics level)
(LevelMap layers _ _ tileMap _) <-
atomically (readTMVar $ testMap level)
stageSet <- readTVarIO (testStageSet level)
nonPlayerCast <- readTVarIO (testCast level)
powerups <- V.map Cast <$> readTVarIO (testPowerups level)
-- A.logIO A.Debug (fromString $ V.foldl (\acc (Cast p) -> acc ++ show p ++ " ") "" powerups)
pituicat <- readTVarIO (testPlayer level)
let cast = Cast (fromJust pituicat) `V.cons` powerups V.++
nonPlayerCast
(V2 px py) = realToFrac <$> pcPos (fromJust pituicat)
atomically $ modifyTVar (testMatrices level) $ \pvm ->
pvm
{ pvmV = mkTransformationMat
identity
(V3 (400 - px) (300 - py) 0)
}
(PVM p v m) <- readTVarIO (testMatrices level)
let (indices, vertices) = populate
layers
stageSet
cast
bind va
bind vb
bind ib
bind sh
setUniform sh "u_mvp" (p !*! v !*! m)
write vb 0 vertices
write ib 0 (VS.map fromIntegral indices)
bind (tileMapTexture tileMap)
V.mapM_(\(StageSet prop) -> bindPropTexture prop) stageSet
V.mapM_(\(Cast c) -> bindPropTexture c) cast
R.draw va ib sh

View file

@ -0,0 +1,44 @@
{-# LANGUAGE OverloadedStrings #-}
module Scenes.Test.EventHandler where
import Affection as A
import qualified SDL
import Linear
import Control.Concurrent.STM
import Data.String (fromString)
-- internal imports
import Types
import Scenes.Test.Types
singleHandler
:: Test
-> TranslatorMessage
-> Affection ()
singleHandler level event = do
let multiplier = if tmKeyMotion event == SDL.Pressed then 1 else 0
changeDirection cat state =
Just cat
{ pcMoveState = if multiplier > 0 then Just state else Nothing
}
liftIO $ atomically $ modifyTVar (testPlayer level) (\(Just pituicat) ->
A.log Debug ("Grounded: " <> fromString (show $ pcGrounded pituicat)) $
case tmAction event of
Jump -> if multiplier > 0 && pcGrounded pituicat
then A.log Debug "jumping"
(Just pituicat
{ pcVel = pcVel pituicat + V2 0 300
, pcGrounded = False
}
)
else
Just pituicat
MoveRight -> changeDirection pituicat MoveRight
MoveLeft -> changeDirection pituicat MoveLeft
_ -> Just pituicat
)

135
src/Scenes/Test/Load.hs Normal file
View file

@ -0,0 +1,135 @@
{-# LANGUAGE OverloadedStrings #-}
module Scenes.Test.Load where
import qualified Graphics.Rendering.OpenGL as GL
import qualified Data.Vector as V
import qualified Data.Vector.Storable as VS
import Control.Concurrent.STM
import Control.Monad (void)
import Linear
-- internal imports
import Types
import Classes
import Graphics
import Map
import Scenes.Test.Types
import Scenes.Test.Util
load
:: Test
-> TMVar Progress
-> IO ()
load level progress = do
atomically $ do
void $ takeTMVar progress
putTMVar progress (0, "Loading test level...")
loadedMap <- constructMap testLevelDesc 0
atomically $ putTMVar (testMap level) loadedMap
void $ atomically $ do
void $ takeTMVar progress
putTMVar progress (0.5, "Loaded test level map!")
void $ atomically $ swapTVar (testLoaded level) True
vertexArray <- newVertexArray
bind vertexArray
vertexBuffer <- newVertexBuffer 1024
bind vertexBuffer
--let vertices = VS.fromList (createQuad (V2 (400 - 16) (300 - 16)) 0)
--write vertexBuffer 0 vertices
indexBuffer <- newIndexBuffer 1024
bind indexBuffer
-- let indices = VS.fromList [0, 1, 2, 2, 3, 0]
-- write indexBuffer 0 indices
addBuffer (undefined :: Vertex) vertexBuffer
(_, tex) <- newTexture "res/pituicat/pituicat.png" 1
bind tex
shader <- newShader
[ ShaderSource GL.VertexShader "./res/shaders/vert.shader"
, ShaderSource GL.FragmentShader "./res/shaders/frag.shader"
]
let projection = ortho 0 800 0 600 (-1) 1 :: M44 GL.GLfloat
view = mkTransformationMat
(identity :: M33 GL.GLfloat)
(V3 (400 - sx) (300 - sy) 0)
model = mkTransformationMat (identity :: M33 GL.GLfloat) (V3 0 0 0)
startpos@(V2 sx sy) = V2 100 1948
pituicat = Pituicat
(realToFrac <$> startpos)
(V2 0 0)
(V2 0 0)
(V2 0 0)
(V2 0 0)
(V2 0 0)
(V2 0 0)
(V2 0 0)
100
tex
False
Nothing
ViewRight
[]
False
False
oil = PowerUp
(realToFrac <$> (startpos + V2 200 0))
(V2 0 0)
(V2 0 0)
(V2 0 0)
5000
tex
(EffectHolder 5 SpeedUp)
(V2 0 0)
(V2 0 0)
False
bind shader
setUniform shader "u_mvp" (projection !*! view !*! model)
setUniformList
shader
"u_textures"
(map
textureSlot
[ (tileMapTexture $ mapTileMap loadedMap)
, tex
]
)
unbind vertexArray
unbind vertexBuffer
unbind indexBuffer
unbind tex
unbind shader
atomically $ do
putTMVar (testGraphics level)
(GLAssets vertexArray vertexBuffer indexBuffer shader [tex])
writeTVar (testPlayer level) (Just pituicat)
writeTVar (testCast level)
(V.fromList [Cast (oil {puPos = puPos oil + V2 200 0})])
writeTVar (testPowerups level) (V.fromList [oil])
writeTVar (testLoaded level) True
writeTVar (testMatrices level) (PVM projection view model)
void $ atomically $ do
void $ takeTMVar progress
putTMVar progress (1, "Loaded graphics!")

40
src/Scenes/Test/Types.hs Normal file
View file

@ -0,0 +1,40 @@
module Scenes.Test.Types where
import Affection
import Linear
import Control.Concurrent.STM
import qualified Data.Vector as V
-- internal imports
import Types
import Graphics
data Test = Test
{ testMap :: TMVar LevelMap
, testGraphics :: TMVar GLAssets
, testLoaded :: TVar Bool
, testPlayer :: TVar (Maybe Pituicat)
, testStageSet :: TVar (V.Vector StageSet)
, testCast :: TVar (V.Vector Cast)
, testPowerups :: TVar (V.Vector PowerUp)
, testMatrices :: TVar PVM
, testClean :: TVar [UUID]
}
data GLAssets = GLAssets
{ glVA :: VertexArray
, glVB :: VertexBuffer
, glIB :: IndexBuffer
, glSP :: Shader
, glTx :: [Texture]
}
data PVM = PVM
{ pvmP :: M44 Float
, pvmV :: M44 Float
, pvmM :: M44 Float
}

135
src/Scenes/Test/Update.hs Normal file
View file

@ -0,0 +1,135 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
module Scenes.Test.Update where
import Affection as A
import Linear
import Control.Concurrent.STM
import Data.List (sortOn)
import qualified Data.Vector as V
import Data.Maybe
import Data.String (fromString)
-- internal imports
import Scenes.Test.Types
import Classes
import Types
import Physics
import Util
update
:: Test
-> Double
-> Affection ()
update level dt = liftIO $ do
logIO Debug ("FPS: " <> fromString (show $ 1 / dt))
atomically $ do
lmap <- readTMVar (testMap level)
cast <-
V.map (\(Cast c) -> Cast $ perform dt c) <$> readTVar (testCast level)
powerups <- V.filter ((0 <) . puTTL) .
V.map (perform dt) <$>
readTVar (testPowerups level)
cat <- perform dt . fromJust <$> readTVar (testPlayer level)
let layer = mapLayers lmap V.! fromIntegral (mapWalkLayer lmap)
tangibles =
V.map TTile layer V.++
V.map TCast cast V.++
V.map TPowerUp powerups `V.snoc`
TPlayer cat
indexedTangibles = V.zip
(V.fromList [0 .. (V.length tangibles - 1)])
tangibles
collidedTangibles =
let collisionPartners =
V.foldl
(\iacc (index, tangible1) ->
let partners =
V.foldl
(\jacc (qInd, tangible2) ->
let res = collisionCheck tangible1 tangible2
in
case res of
c@(OverlapCollision _) ->
if qInd /= index
then jacc `V.snoc` (qInd, c)
else jacc
_ -> jacc
)
V.empty
indexedTangibles
in
if V.null partners
then iacc
else iacc `V.snoc`
( index
, sortOn
((\v -> collisionDepth v `dot` collisionDepth v) . snd)
(V.toList partners)
)
)
V.empty
indexedTangibles
updater (aindex, partners) =
let aobject = (snd $ indexedTangibles V.! aindex)
digest = map
(\(pindex, presults) ->
( snd $ indexedTangibles V.! pindex
, presults
)
)
partners
res =
(\d ->
let inter =
collide
aobject
d
dt
in
resetImpactForces $ flip updateCollisionOccurence False $
if collisionOccured inter
then addLoads inter (impactForces inter)
else addLoads inter constG
)
digest
in
(aindex, res)
in
tangibles `V.update`
V.map
updater
(A.log
A.Debug
("Collision partners: " <>
fromString (show collisionPartners))
collisionPartners
)
(newCast, newPowerups, mnewCat@(Just _)) = V.foldl
(\acc@(castAcc, pAcc, _) input ->
case input of
TCast c ->
(castAcc `V.snoc` updateByEuler c dt, pAcc, mnewCat)
TPowerUp p ->
(castAcc, pAcc `V.snoc` updateByEuler p dt, mnewCat)
TPlayer ncat ->
(castAcc, pAcc, Just (updateByEuler ncat dt))
TTile _ ->
acc
)
(V.empty, V.empty, Nothing)
collidedTangibles
writeTVar (testCast level) newCast
writeTVar (testPowerups level) newPowerups
writeTVar (testPlayer level) mnewCat

Some files were not shown because too many files have changed in this diff Show more