├── README.md
├── fonts
├── russoone
│ ├── OFL.txt
│ └── RussoOne-Regular.ttf
└── vt323
│ ├── SIL Open Font License.txt
│ └── VT323-Regular.ttf
├── main.lua
├── maps
├── LICENSE.txt
├── _template1.tmx
├── _template2.tmx
├── _template3.tmx
├── _template4.tmx
├── blank.lua
├── blank.tmx
├── blank2.lua
├── blank2.tmx
├── rm1.lua
├── rm1.tmx
├── rm10.lua
├── rm10.tmx
├── rm11.lua
├── rm11.tmx
├── rm12.lua
├── rm12.tmx
├── rm13.lua
├── rm13.tmx
├── rm14.lua
├── rm14.tmx
├── rm15.lua
├── rm15.tmx
├── rm16.lua
├── rm16.tmx
├── rm17.lua
├── rm17.tmx
├── rm18.lua
├── rm18.tmx
├── rm19.lua
├── rm19.tmx
├── rm2.lua
├── rm2.tmx
├── rm20.lua
├── rm20.tmx
├── rm21.lua
├── rm21.tmx
├── rm22.lua
├── rm22.tmx
├── rm23.lua
├── rm23.tmx
├── rm24.lua
├── rm24.tmx
├── rm25.lua
├── rm25.tmx
├── rm26.lua
├── rm26.tmx
├── rm27.lua
├── rm27.tmx
├── rm28.lua
├── rm28.tmx
├── rm3.lua
├── rm3.tmx
├── rm4.lua
├── rm4.tmx
├── rm5.lua
├── rm5.tmx
├── rm6.lua
├── rm6.tmx
├── rm7.lua
├── rm7.tmx
├── rm8.lua
├── rm8.tmx
├── rm9.lua
├── rm9.tmx
├── rmBoss.lua
├── rmBoss.tmx
├── rmBossAfter.lua
├── rmBossAfter.tmx
├── rmCredits.lua
├── rmCredits.tmx
├── rmIntro.lua
├── rmIntro.tmx
├── rmMainMenu.lua
├── rmMainMenu.tmx
└── tilesheets
│ ├── 3by3_1.png
│ ├── darkCorners_sheet.png
│ ├── sheet1.png
│ └── sheet2.png
├── music
├── LICENSE.txt
├── boss.ogg
├── cavern.ogg
├── danger.ogg
├── ending.ogg
├── intro.ogg
└── menu.ogg
├── sounds
├── LICENSE.txt
├── blip.wav
├── enemies
│ ├── bossDie.wav
│ ├── bossDie_old.wav
│ ├── bossExplode.wav
│ ├── bossLaser.wav
│ ├── enemyHurt.wav
│ ├── enemyHurt_2.wav
│ ├── helloBoss.wav
│ ├── spikes.wav
│ ├── starfish-old1.wav
│ └── starfish.wav
├── itemGet.wav
├── laser.wav
├── player
│ ├── bombShot.wav
│ ├── explosion.wav
│ ├── explosion2.wav
│ ├── laser1.wav
│ ├── laser2.wav
│ ├── laser3.wav
│ ├── playerHurt.wav
│ ├── spear.wav
│ └── splash.wav
└── ui
│ ├── click.wav
│ └── text.wav
├── source
├── LICENSE.txt
├── draw.lua
├── enemies
│ ├── bat.lua
│ ├── boss.lua
│ ├── egg.lua
│ ├── enemy.lua
│ ├── enemyProj.lua
│ ├── eye.lua
│ ├── fish.lua
│ ├── spike.lua
│ └── spikeProj.lua
├── environment
│ ├── blast.lua
│ ├── breakable.lua
│ ├── explosion.lua
│ ├── fire.lua
│ ├── particle.lua
│ ├── trail.lua
│ ├── vine.lua
│ └── water.lua
├── global
│ ├── collision_classes.lua
│ ├── gameState.lua
│ ├── saveGame.lua
│ ├── saveUtil.lua
│ ├── shake.lua
│ ├── soundManager.lua
│ └── utilities.lua
├── levels
│ ├── background.lua
│ ├── destroyAll.lua
│ ├── drawWalls.lua
│ ├── intro.lua
│ └── map_loader.lua
├── libraries
│ ├── Tserial.lua
│ ├── anim8.lua
│ ├── flux.lua
│ ├── hump
│ │ ├── camera.lua
│ │ └── vector.lua
│ ├── slam.lua
│ ├── sti
│ │ ├── graphics.lua
│ │ ├── init.lua
│ │ ├── plugins
│ │ │ ├── box2d.lua
│ │ │ └── bump.lua
│ │ └── utils.lua
│ └── windfield
│ │ ├── init.lua
│ │ └── mlib
│ │ ├── Changes.txt
│ │ ├── LICENSE.md
│ │ ├── README.md
│ │ └── mlib.lua
├── pickup.lua
├── player.lua
├── startup
│ ├── loadFonts.lua
│ ├── loadSounds.lua
│ ├── loadSprites.lua
│ ├── main_require.lua
│ └── startup.lua
├── text
│ ├── credits.lua
│ ├── damage.lua
│ ├── messages.lua
│ ├── scrollText.lua
│ ├── textBox.lua
│ └── tutorial.lua
├── ui
│ ├── blackScreen.lua
│ ├── cam.lua
│ ├── flash.lua
│ └── mainMenu.lua
├── update.lua
└── weapon.lua
└── sprites
├── LICENSE.txt
├── enemies
├── bigBossEye.png
├── bossBody.png
├── egg.png
├── evilBubble.png
├── flyerBody.png
├── flyerEye.png
├── flyerWing1.png
├── flyerWing2.png
├── spikeBody.png
├── spikeProj.png
└── starfish.png
├── environment
├── bg.png
├── breakParticle.png
├── crack.png
├── cracks
│ ├── crack1.png
│ ├── crack2.png
│ └── crack3.png
├── rockySurface.png
├── rockySurface2.png
├── rockySurface_white.png
├── sinkingWall.png
├── vine.png
├── wall.png
├── wall2.png
├── wall_old.png
└── waterSheet.png
├── fire
├── fire_1.png
├── fire_2.png
├── fire_3.png
├── fire_4.png
└── fire_5.png
├── items
├── aquaPack.png
├── blaster.png
├── healthPickup.png
├── itemPickup.png
├── pickup_back.png
├── rocketLauncher.png
└── spearGun.png
├── newPlayer
├── aquapack.png
├── arm.png
├── armBlaster.png
├── armRocket.png
├── armSpear.png
├── armSpearLoaded.png
├── backArm.png
├── backArm_old.png
├── body.png
├── helmet.png
├── jetpack.png
├── newPlayer.png
└── spear.png
├── newPlayer2
├── arm.png
├── armBlaster.png
├── armRocket.png
├── armSpear.png
├── armSpearLoaded.png
├── backArm.png
├── body.png
└── helmet.png
├── player
├── aquapack.png
├── armSpear.png
├── arm_blaster.png
├── arm_blaster2.png
├── arm_empty.png
├── body.png
├── bomb.png
├── helmet.png
├── jetpack.png
├── rocketLauncher.png
├── rocketLauncherAlt.png
└── spear.png
└── ui
├── github.png
└── sound.png
/README.md:
--------------------------------------------------------------------------------
1 | # Cavern
2 |
3 | A 2D adventure game made with Lua and LÖVE
4 |
5 | ## About this project
6 |
7 | Cavern is an open-source game written in Lua utilizing the game framework [LÖVE](https://love2d.org/). This game was created to provide LÖVE developers with a fully completed project to refer to when creating their own games.
8 |
9 | ## Running the game
10 |
11 | Download or clone this repo, and then run the project with LÖVE, which can be downloaded [here](https://love2d.org/). The game will only work properly with versions 11.0 or later. The easiest way to run a project with LÖVE is to drag the project folder onto a LÖVE application shortcut.
12 |
13 | ## License
14 |
15 | Each folder for this project (maps, music, sounds, source, and sprites) contains its own LICENSE.txt file. All code (found in
16 | the "source" directory) is under the MIT license, meaning you can use the code for any purpose without restriction. The assets
17 | (maps, music, sounds, and sprites) are under the BY-NC-ND 4.0 license, which requires attribution, denies use for commercial
18 | projects, and prevents modification of those assets.
19 |
20 | Simply put, you can use the code however you want, but the assets (art, music, etc.) have restrictions in place. The
21 | project is set up like this to prevent people from downloading the full game and selling it as their own. At the same time,
22 | developers can still use the code in their own projects.
23 |
--------------------------------------------------------------------------------
/fonts/russoone/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011-2012, Jovanny Lemonad (jovanny.ru), with Reserved Font Name "Russo"
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/fonts/russoone/RussoOne-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/fonts/russoone/RussoOne-Regular.ttf
--------------------------------------------------------------------------------
/fonts/vt323/SIL Open Font License.txt:
--------------------------------------------------------------------------------
1 | Copyright 2011, The VT323 Project Authors (peter.hull@oikoi.com)
2 |
3 |
4 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
6 |
7 | -----------------------------------------------------------
8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
9 | -----------------------------------------------------------
10 |
11 | PREAMBLE
12 | The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
13 |
14 | The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
15 |
16 | DEFINITIONS
17 | "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
18 |
19 | "Reserved Font Name" refers to any names specified as such after the copyright statement(s).
20 |
21 | "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
22 |
23 | "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
24 |
25 | "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
26 |
27 | PERMISSION & CONDITIONS
28 | Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
29 |
30 | 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
31 |
32 | 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
33 |
34 | 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
35 |
36 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
37 |
38 | 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
39 |
40 | TERMINATION
41 | This license becomes null and void if any of the above conditions are not met.
42 |
43 | DISCLAIMER
44 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
--------------------------------------------------------------------------------
/fonts/vt323/VT323-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/fonts/vt323/VT323-Regular.ttf
--------------------------------------------------------------------------------
/main.lua:
--------------------------------------------------------------------------------
1 | function love.load()
2 |
3 | -- This function configures game window and performs additional setup
4 | require("source/startup/startup")
5 | startup()
6 |
7 | -- Debug value, comment out for final game
8 | debug = 0
9 | debug2 = 0
10 |
11 | -- Temporary, used for drawing Explosion radius before particles
12 | expX = 0
13 | expY = 0
14 |
15 | -- Temporary, used to determine if physics will be drawn
16 | drawPhysics = false
17 |
18 | end
19 |
20 | function love.update(dt)
21 |
22 | if gameState.state == 1 then
23 | -- Handles most updating for the game
24 | local updateGameplay = require("source/update")
25 | updateGameplay(dt)
26 | end
27 |
28 | scroll:update(dt)
29 | textBox:update(dt)
30 |
31 | end
32 |
33 | function love.draw()
34 |
35 | cam:attach()
36 |
37 | -- Handles most drawing for the game
38 | local drawGameplay = require("source/draw")
39 | drawGameplay()
40 |
41 | -- Draw the colliders for all physics objects
42 | -- Commented out for final game, used for debugging
43 | if drawPhysics then
44 | love.graphics.setLineWidth(2)
45 | world:draw(150)
46 | gravWorld:draw(150)
47 | end
48 |
49 | cam:detach()
50 |
51 | menuDraw()
52 |
53 | textBox:draw()
54 | intro:drawInterrupt()
55 |
56 | saveUtil:drawMessage()
57 |
58 | blackScreen:draw()
59 | flash:draw()
60 |
61 | --[[
62 | love.graphics.setColor(1, 1, 1, 1)
63 | love.graphics.print(debug, 0, 100)
64 | love.graphics.print(debug2, 0, 120)
65 | ]]
66 |
67 | end
68 |
69 | function love.mousepressed( x, y, button, istouch )
70 |
71 | -- Interrupts the intro text
72 | intro:interrupt()
73 |
74 | -- Checks for button clicks on the main menu
75 | if button == 1 and gameState.room == "rmMainMenu" then
76 | buttons:click()
77 | end
78 |
79 | end
80 |
81 | function love.keypressed(key, scancode, isrepeat)
82 |
83 | -- Interrupts the intro text
84 | intro:interrupt()
85 |
86 | if key == "space" then
87 | player:swapWeapon()
88 | end
89 |
90 | if key == "backspace" then
91 | if drawPhysics then
92 | drawPhysics = false
93 | else
94 | drawPhysics = true
95 | end
96 | end
97 |
98 | end
99 |
100 | function love.wheelmoved(x, y)
101 |
102 | -- Change weapons
103 | if y < 0 then
104 | player:swapWeapon()
105 | elseif y > 0 then
106 | player:swapWeapon(true)
107 | end
108 |
109 | end
110 |
--------------------------------------------------------------------------------
/maps/LICENSE.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/maps/LICENSE.txt
--------------------------------------------------------------------------------
/maps/_template1.tmx:
--------------------------------------------------------------------------------
1 |
2 |
38 |
--------------------------------------------------------------------------------
/maps/_template3.tmx:
--------------------------------------------------------------------------------
1 |
2 |
38 |
--------------------------------------------------------------------------------
/maps/_template4.tmx:
--------------------------------------------------------------------------------
1 |
2 |
38 |
--------------------------------------------------------------------------------
/maps/rm12.tmx:
--------------------------------------------------------------------------------
1 |
2 |
87 |
--------------------------------------------------------------------------------
/maps/rm13.tmx:
--------------------------------------------------------------------------------
1 |
2 |
82 |
--------------------------------------------------------------------------------
/maps/rm17.tmx:
--------------------------------------------------------------------------------
1 |
2 |
73 |
--------------------------------------------------------------------------------
/maps/rm20.tmx:
--------------------------------------------------------------------------------
1 |
2 |
76 |
--------------------------------------------------------------------------------
/maps/rm22.tmx:
--------------------------------------------------------------------------------
1 |
2 |
59 |
--------------------------------------------------------------------------------
/maps/rm23.tmx:
--------------------------------------------------------------------------------
1 |
2 |
75 |
--------------------------------------------------------------------------------
/maps/rm24.tmx:
--------------------------------------------------------------------------------
1 |
2 |
63 |
--------------------------------------------------------------------------------
/maps/rm4.tmx:
--------------------------------------------------------------------------------
1 |
2 |
76 |
--------------------------------------------------------------------------------
/maps/rm5.tmx:
--------------------------------------------------------------------------------
1 |
2 |
68 |
--------------------------------------------------------------------------------
/maps/rm7.tmx:
--------------------------------------------------------------------------------
1 |
2 |
63 |
--------------------------------------------------------------------------------
/maps/rm9.tmx:
--------------------------------------------------------------------------------
1 |
2 |
82 |
--------------------------------------------------------------------------------
/maps/rmBoss.tmx:
--------------------------------------------------------------------------------
1 |
2 |
48 |
--------------------------------------------------------------------------------
/maps/rmBossAfter.tmx:
--------------------------------------------------------------------------------
1 |
2 |
55 |
--------------------------------------------------------------------------------
/maps/rmIntro.tmx:
--------------------------------------------------------------------------------
1 |
2 |
29 |
--------------------------------------------------------------------------------
/maps/rmMainMenu.tmx:
--------------------------------------------------------------------------------
1 |
2 |
29 |
--------------------------------------------------------------------------------
/maps/tilesheets/3by3_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/maps/tilesheets/3by3_1.png
--------------------------------------------------------------------------------
/maps/tilesheets/darkCorners_sheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/maps/tilesheets/darkCorners_sheet.png
--------------------------------------------------------------------------------
/maps/tilesheets/sheet1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/maps/tilesheets/sheet1.png
--------------------------------------------------------------------------------
/maps/tilesheets/sheet2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/maps/tilesheets/sheet2.png
--------------------------------------------------------------------------------
/music/LICENSE.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/music/LICENSE.txt
--------------------------------------------------------------------------------
/music/boss.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/music/boss.ogg
--------------------------------------------------------------------------------
/music/cavern.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/music/cavern.ogg
--------------------------------------------------------------------------------
/music/danger.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/music/danger.ogg
--------------------------------------------------------------------------------
/music/ending.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/music/ending.ogg
--------------------------------------------------------------------------------
/music/intro.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/music/intro.ogg
--------------------------------------------------------------------------------
/music/menu.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/music/menu.ogg
--------------------------------------------------------------------------------
/sounds/LICENSE.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/LICENSE.txt
--------------------------------------------------------------------------------
/sounds/blip.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/blip.wav
--------------------------------------------------------------------------------
/sounds/enemies/bossDie.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/enemies/bossDie.wav
--------------------------------------------------------------------------------
/sounds/enemies/bossDie_old.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/enemies/bossDie_old.wav
--------------------------------------------------------------------------------
/sounds/enemies/bossExplode.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/enemies/bossExplode.wav
--------------------------------------------------------------------------------
/sounds/enemies/bossLaser.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/enemies/bossLaser.wav
--------------------------------------------------------------------------------
/sounds/enemies/enemyHurt.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/enemies/enemyHurt.wav
--------------------------------------------------------------------------------
/sounds/enemies/enemyHurt_2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/enemies/enemyHurt_2.wav
--------------------------------------------------------------------------------
/sounds/enemies/helloBoss.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/enemies/helloBoss.wav
--------------------------------------------------------------------------------
/sounds/enemies/spikes.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/enemies/spikes.wav
--------------------------------------------------------------------------------
/sounds/enemies/starfish-old1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/enemies/starfish-old1.wav
--------------------------------------------------------------------------------
/sounds/enemies/starfish.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/enemies/starfish.wav
--------------------------------------------------------------------------------
/sounds/itemGet.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/itemGet.wav
--------------------------------------------------------------------------------
/sounds/laser.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/laser.wav
--------------------------------------------------------------------------------
/sounds/player/bombShot.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/player/bombShot.wav
--------------------------------------------------------------------------------
/sounds/player/explosion.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/player/explosion.wav
--------------------------------------------------------------------------------
/sounds/player/explosion2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/player/explosion2.wav
--------------------------------------------------------------------------------
/sounds/player/laser1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/player/laser1.wav
--------------------------------------------------------------------------------
/sounds/player/laser2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/player/laser2.wav
--------------------------------------------------------------------------------
/sounds/player/laser3.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/player/laser3.wav
--------------------------------------------------------------------------------
/sounds/player/playerHurt.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/player/playerHurt.wav
--------------------------------------------------------------------------------
/sounds/player/spear.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/player/spear.wav
--------------------------------------------------------------------------------
/sounds/player/splash.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/player/splash.wav
--------------------------------------------------------------------------------
/sounds/ui/click.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/ui/click.wav
--------------------------------------------------------------------------------
/sounds/ui/text.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sounds/ui/text.wav
--------------------------------------------------------------------------------
/source/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2018 Kyle Schaub
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/source/draw.lua:
--------------------------------------------------------------------------------
1 | local function drawGameplay()
2 | love.graphics.setLineWidth(2)
3 |
4 | -- Draw the background (drawn before anything else)
5 | background:draw()
6 |
7 | -- Draw the credits (only in rmCredits)
8 | credits:draw()
9 |
10 | -- draw all weapons
11 | weapons:draw()
12 |
13 | -- Draw the player
14 | player:draw()
15 |
16 | -- Draw spike projectiles
17 | spikes:draw()
18 |
19 | -- Draw enemy projectiles
20 | enemyProjectiles:draw()
21 |
22 | -- Draw eggs from the final boss
23 | eggs:draw()
24 |
25 | -- draw all enemies (except final boss)
26 | enemies:draw(false)
27 |
28 | -- draw final boss (boss must appear over other enemies)
29 | enemies:draw(true)
30 |
31 | -- Draw trails
32 | trails:draw()
33 |
34 | -- Draw vines
35 | vines:draw()
36 |
37 | -- Draw water and ripples after everything else to give underwater objects
38 | -- a blue tint (since the drawing is translucent)
39 | ripples:draw()
40 |
41 | -- Draws all walls (including breakable ones)
42 | local drawWalls = require("source/levels/drawWalls")
43 | drawWalls()
44 |
45 | love.graphics.setColor(1, 1, 1, 1)
46 | mapdata.map:drawLayer(mapdata.map.layers["Main_Tiles"])
47 |
48 | particles:draw()
49 | fires:draw()
50 | blasts:draw()
51 | damages:draw()
52 | pickups:draw()
53 |
54 | -- Draw the player's healthbar
55 | player:drawHealth()
56 |
57 | -- Draw the tutorial text (when applicable)
58 | tutorial:draw()
59 |
60 | if gameState.room == "rmBoss" then
61 |
62 | -- find the boss object
63 | for _,e in ipairs(enemies) do
64 | if e.type == "boss" then
65 | e:bossBar()
66 | end
67 | end
68 |
69 | end
70 |
71 | end
72 |
73 | return drawGameplay
74 |
--------------------------------------------------------------------------------
/source/enemies/bat.lua:
--------------------------------------------------------------------------------
1 | local function batInit(enemy, x, y, arg)
2 |
3 | -- Initialize physics
4 | enemy.physics = world:newBSGRectangleCollider(x, y, 92, 92, 25)
5 | enemy.physics:setCollisionClass('Enemy')
6 | enemy.physics:setLinearDamping(2)
7 | enemy.physics:setFixedRotation(true)
8 | -- We need this to access the table itself given the physics
9 | enemy.physics.parent = enemy
10 |
11 | -- Properties
12 | enemy.health = 12
13 | enemy.hitPower = 5
14 | enemy.moveForce = 17000
15 | enemy.maxSpeed = 800
16 | enemy.barY = 62
17 |
18 | -- Sprite info
19 | enemy.sprite = sprites.enemies.flyerBody
20 | enemy.wingSprite = sprites.enemies.flyerWing1
21 | enemy.spriteTimerBase = 0.001
22 | enemy.spriteTimer = enemy.spriteTimerBase
23 |
24 | enemy.eye = spawnEye(x, y, 0, 1, sprites.enemies.flyerEye)
25 |
26 | -- Final boss can spawn these, and they shoot down
27 | -- if arg is true
28 | if arg then
29 | enemy.physics:applyLinearImpulse(0, 20000)
30 | end
31 |
32 | function enemy:update(dt)
33 |
34 | if self:inSight() then
35 | local speed = speedFromVelocity( self.physics:getLinearVelocity() )
36 | if speed < self.maxSpeed then
37 | local ex, ey = self.physics:getPosition()
38 | local dir = toPlayerVector(ex, ey)
39 | dir = dir * self.moveForce
40 | self.physics:applyForce(dir:unpack())
41 | end
42 | end
43 |
44 | -- Update the animation; alternate between the two frames
45 | self.spriteTimer = updateTimer(self.spriteTimer, dt)
46 | if self.spriteTimer == 0 then
47 | if self.wingSprite == sprites.enemies.flyerWing1 then
48 | self.wingSprite = sprites.enemies.flyerWing2
49 | else
50 | self.wingSprite = sprites.enemies.flyerWing1
51 | end
52 | self.spriteTimer = self.spriteTimerBase
53 | end
54 |
55 | -- Update the eye of the flyer
56 | -- Not to be confused with the Eye of the Tiger
57 | local ex, ey = self.physics:getPosition()
58 | local dir = toPlayerVector(ex, ey)
59 | local offX, offY = (dir * 16):unpack()
60 | self.eye:update(dt, ex + offX, ey + offY, toPlayerRotate(ex, ey))
61 | end
62 |
63 | function enemy:draw()
64 | local sprW = self.wingSprite:getWidth()
65 | local sprH = self.wingSprite:getHeight()
66 | local sprX, sprY = self.physics.body:getPosition()
67 |
68 | local wingOffset = -24
69 | if self.wingSprite == sprites.enemies.flyerWing2 then
70 | wingOffset = wingOffset + 5
71 | end
72 |
73 | -- Draw the wings first
74 | love.graphics.setColor(1, 1, 1, 0.314)
75 | love.graphics.draw(self.wingSprite, sprX, sprY + wingOffset, nil, 1, 1, sprW/2, sprH/2)
76 |
77 | -- Draw the body of the flyer (rotates towards player)
78 | sprW = self.sprite:getWidth()
79 | sprH = self.sprite:getHeight()
80 | love.graphics.setColor(1, 1, 1, 1)
81 | love.graphics.draw(self.sprite, sprX, sprY, toPlayerRotate(sprX, sprY), 1, 1, sprW/2, sprH/2)
82 | end
83 |
84 | return enemy
85 |
86 | end
87 |
88 | return batInit
89 |
--------------------------------------------------------------------------------
/source/enemies/egg.lua:
--------------------------------------------------------------------------------
1 | -- Eggs that the final boss drops
2 | eggs = {}
3 |
4 | function eggs:spawn(x)
5 |
6 | local egg = {}
7 | egg.width = 100
8 | egg.height = 100
9 |
10 | egg.power = 7 -- damage taken by player when colliding
11 |
12 | egg.physics = gravWorld:newBSGRectangleCollider(x, 200, egg.width, egg.height, 10)
13 | egg.physics:setFixedRotation(true)
14 | egg.physics:setCollisionClass('Particle')
15 |
16 | table.insert(eggs, egg)
17 |
18 | end
19 |
20 | function eggs:update(dt)
21 |
22 | for i=#eggs,1,-1 do
23 |
24 | local ex, ey = eggs[i].physics:getPosition()
25 | local width = eggs[i].width
26 | local dmg = eggs[i].power
27 |
28 | -- Check collision with player
29 | -- Note: since eggs are in a different physics world
30 | -- than the player, this is handled differently than
31 | -- most other collisions in the game
32 |
33 | if distToPlayer(ex, ey) < width then
34 | player:hurt(dmg)
35 | end
36 |
37 | -- Crash into the ground
38 |
39 | if ey > 1280 then
40 |
41 | -- Egg has hit the ground, hatch
42 | spawnEnemy(ex-(width/2), 1280, "bat")
43 |
44 | eggs[i].physics:destroy()
45 | table.remove(eggs, i)
46 |
47 | end
48 | end
49 |
50 | end
51 |
52 | function eggs:draw()
53 |
54 | for _,e in ipairs(self) do
55 | local ex, ey = e.physics:getPosition()
56 | local spr = sprites.enemies.egg
57 | love.graphics.draw(spr, ex, ey, nil, 1, 1, spr:getWidth()/2, spr:getHeight()/2)
58 | end
59 |
60 | end
61 |
--------------------------------------------------------------------------------
/source/enemies/enemyProj.lua:
--------------------------------------------------------------------------------
1 | -- projectiles launched by enemies (does not include spikes)
2 | enemyProjectiles = {}
3 |
4 | function spawnEnemyProj(x, y, dir, type)
5 |
6 | local enProj = {}
7 | enProj.dir = dir
8 | enProj.dead = false
9 | enProj.power = 6
10 | enProj.id = math.random()
11 | enProj.rad = 10
12 | enProj.sprite = nil
13 | enProj.impulse = 2000
14 |
15 | if type == "fish" then
16 | enProj.rad = 19
17 | enProj.power = 6
18 | enProj.sprite = sprites.enemies.evilBubble
19 | enProj.impulse = 2500
20 | soundManager:play("starfish")
21 | end
22 |
23 | if type == "bossLaser" then
24 | enProj.rad = 10
25 | enProj.power = 7
26 | enProj.sprite = nil
27 | enProj.impulse = 3000
28 | -- This projectile has a trail, which is spawned here
29 | spawnTrail(enProj.id, 12, 16, {1, 0, 0, 0.706})
30 |
31 | soundManager:play("bossLaser")
32 |
33 | -- Offset the starting location of the laser
34 | -- so it appears in the eye's pupil
35 | local offsetVec = enProj.dir * 100
36 | local wx, wy = offsetVec:unpack()
37 | x = x + wx
38 | y = y + wy
39 | end
40 |
41 | enProj.physics = world:newCircleCollider(x, y, enProj.rad)
42 | enProj.physics:setCollisionClass('E_Weapon')
43 | enProj.dir = enProj.dir:normalized()
44 |
45 | enProj.physics:applyLinearImpulse((enProj.dir * enProj.impulse):unpack())
46 |
47 | function enProj:update(dt)
48 |
49 | -- Hurt player on contact
50 | if self.physics:enter('Player') then
51 | player:hurt(self.power)
52 | self.dead = true
53 | end
54 |
55 | if self.physics:enter('Wall') or self.physics:enter('Breakable')
56 | or self.physics:enter('Transition') then
57 | self.dead = true
58 | end
59 |
60 | end
61 |
62 | table.insert(enemyProjectiles, enProj)
63 |
64 | end
65 |
66 | function enemyProjectiles:update(dt)
67 |
68 | for i,s in ipairs(self) do
69 | s:update(dt)
70 | end
71 |
72 | -- Iterate through all enemyProjectiles in reverse to remove dead ones
73 | -- Update trails before removal
74 | for i=#enemyProjectiles,1,-1 do
75 |
76 | local proj = enemyProjectiles[i]
77 |
78 | -- Updates the projectile's trail (if it has one)
79 | if proj.dead ~= true then
80 | for _,t in ipairs(trails) do
81 | if t.id == proj.id then
82 | t:update(dt, proj)
83 | end
84 | end
85 | end
86 |
87 | if proj.dead then
88 | proj.physics:destroy()
89 | table.remove(enemyProjectiles, i)
90 | end
91 |
92 | end
93 |
94 | end
95 |
96 | function enemyProjectiles:draw()
97 |
98 | love.graphics.setColor(1, 1, 1, 1)
99 |
100 | for i,p in ipairs(self) do
101 |
102 | if p.sprite ~= nil then
103 | local sprX, sprY = p.physics:getPosition()
104 | local sprW = p.sprite:getWidth()
105 | local sprH = p.sprite:getHeight()
106 | love.graphics.draw(p.sprite, sprX, sprY, nil, 1, 1, sprW/2, sprH/2)
107 | end
108 |
109 | end
110 |
111 | end
112 |
--------------------------------------------------------------------------------
/source/enemies/eye.lua:
--------------------------------------------------------------------------------
1 | eyes = {}
2 |
3 | function spawnEye(x, y, rot, scale, spr, args)
4 |
5 | local eye = {}
6 | eye.x = x
7 | eye.y = y
8 | eye.rot = rot
9 | eye.scale = scale
10 | eye.spr = spr
11 | eye.id = id
12 | eye.args = args
13 |
14 | eye.dead = false
15 |
16 | function eye:update(dt, x, y, rot)
17 |
18 | eye.x = x
19 | eye.y = y
20 |
21 | -- Only look at the player if the player is alive
22 | if player.health > 0 then
23 | eye.rot = rot
24 | end
25 |
26 | end
27 |
28 | function eye:draw()
29 |
30 | love.graphics.setColor(1, 1, 1, 1)
31 | love.graphics.draw(self.spr, self.x, self.y, self.rot, self.scale, self.scale, self.spr:getWidth()/2, self.spr:getHeight()/2)
32 |
33 | end
34 |
35 | return eye
36 | --table.insert(eyes, eye)
37 |
38 | end
39 |
--------------------------------------------------------------------------------
/source/enemies/fish.lua:
--------------------------------------------------------------------------------
1 | local function fishInit(enemy, x, y, arg)
2 |
3 | -- Initialize physics
4 | enemy.physics = world:newBSGRectangleCollider(x, y, 106, 106, 26)
5 | enemy.physics:setCollisionClass('Enemy')
6 | enemy.physics:setLinearDamping(2)
7 | enemy.physics:setFixedRotation(true)
8 | -- We need this to access the table itself given the physics
9 | enemy.physics.parent = enemy
10 |
11 | -- Properties
12 | enemy.health = 18
13 | enemy.hitPower = 6
14 | enemy.moveForce = 5000
15 | enemy.maxSpeed = 500
16 | enemy.barY = 80
17 |
18 | enemy.sprite = sprites.enemies.starfish
19 | enemy.rotate = 0
20 | enemy.slowRotSpeed = math.pi/160
21 | enemy.fastRotSpeed = math.pi/10
22 | enemy.rotSpeed = enemy.slowRotSpeed
23 | enemy.rotTween = nil
24 | enemy.timeBetweenShots = 0.8
25 |
26 | enemy.stateTimer = 0
27 | enemy.state = 0
28 |
29 | enemy.eye = spawnEye(x, y, 0, 1, sprites.enemies.flyerEye)
30 |
31 | function enemy:update(dt)
32 |
33 | self.stateTimer = updateTimer(self.stateTimer, dt)
34 |
35 | -- State 0: idle
36 | if self.state == 0 and self:inSight() then
37 | self.state = 1
38 | self.stateTimer = self.timeBetweenShots
39 | self.timeBetweenShots = 1.5
40 | end
41 |
42 | -- State 1: chasing player, start shooting process after timer is up
43 | if self.state == 1 then
44 |
45 | if self.stateTimer <= 0 then
46 | self.state = 1.5
47 | local tweenTo = self.fastRotSpeed
48 | self.rotTween = flux.to(self, 0.5, {rotSpeed = tweenTo}):ease("sineinout")
49 | end
50 |
51 | local speed = speedFromVelocity( self.physics:getLinearVelocity() )
52 | local ex, ey = self.physics:getPosition()
53 | if speed < self.maxSpeed then
54 | local dir = toPlayerVector(ex, ey)
55 | dir = dir * self.moveForce
56 | self.physics:applyForce(dir:unpack())
57 | end
58 |
59 | end
60 |
61 | -- State 1.5: After first tween is done, shoot and slow down rotSpeed
62 | local ex, ey = self.physics:getPosition()
63 | if self.state == 1.5 and self.rotSpeed == self.fastRotSpeed then
64 | self.state = 2
65 | local tweenTo = self.slowRotSpeed
66 | self.rotTween = flux.to(self, 0.5, {rotSpeed = tweenTo}):ease("cubicout")
67 |
68 | -- Shoot bullet
69 | spawnEnemyProj(ex, ey, toPlayerVector(ex, ey), "fish")
70 | end
71 |
72 | -- State 2: After shooting, slowing down
73 | if self.state == 2 and self.rotSpeed == self.slowRotSpeed then
74 | self.state = 0
75 | end
76 |
77 | self.rotate = self.rotate + self.rotSpeed
78 |
79 | self.eye:update(dt, ex, ey, toPlayerRotate(ex, ey))
80 |
81 | end
82 |
83 | function enemy:draw()
84 | local sprX, sprY = self.physics.body:getPosition()
85 |
86 | -- Draw the body
87 | sprW = self.sprite:getWidth()
88 | sprH = self.sprite:getHeight()
89 | love.graphics.setColor(1, 1, 1, 1)
90 | love.graphics.draw(self.sprite, sprX, sprY, self.rotate, 1, 1, sprW/2, sprH/2+6)
91 | end
92 |
93 | return enemy
94 |
95 | end
96 |
97 | return fishInit
98 |
--------------------------------------------------------------------------------
/source/enemies/spikeProj.lua:
--------------------------------------------------------------------------------
1 | -- spikes launched by "spike" enemies
2 | spikes = {}
3 |
4 | function spawnSpike(x, y, num, id, groundDir)
5 |
6 | local spike = {}
7 | spike.dir = vector.new(-1, 0)
8 | spike.dead = false
9 | spike.power = 6
10 | spike.id = id
11 | spike.sprite = sprites.enemies.spikeProj
12 |
13 | -- Originally planned to have spikes pause before launching
14 | -- Since this is no longer needed, just keep this timer at -1
15 | -- so they immediately launch
16 | spike.launchTimer = -1
17 |
18 | -- Assign dir (vector)
19 | if num == 1 then -- left
20 | spike.dir = vector.new(-1, 0)
21 | elseif num == 2 then -- up-left
22 | spike.dir = vector.new(-1, -1)
23 | elseif num == 3 then -- up
24 | spike.dir = vector.new(0, -1)
25 | elseif num == 4 then -- up-right
26 | spike.dir = vector.new(1, -1)
27 | elseif num == 5 then -- right
28 | spike.dir = vector.new(1, 0)
29 | end
30 |
31 | -- Rotate dir based on host's groundDir value
32 | if groundDir == "left" then
33 | spike.dir:rotateInplace(math.pi / 2) -- rotate by 90 degrees
34 | elseif groundDir == "up" then
35 | spike.dir:rotateInplace(math.pi) -- rotate by 180 degrees
36 | elseif groundDir == "right" then
37 | spike.dir:rotateInplace(math.pi / -2) -- rotate by -90 degrees
38 | end
39 |
40 | spike.dir:normalizeInplace()
41 | spike.dir = spike.dir * 64 -- radius of spike enemies
42 |
43 | local tempX, tempY = spike.dir:unpack()
44 | x = x + tempX
45 | y = y + tempY
46 |
47 | spike.physics = world:newCircleCollider(x, y, 16)
48 | spike.physics:setCollisionClass('E_Weapon')
49 | spike.dir = spike.dir:normalized()
50 |
51 | function spike:update(dt)
52 |
53 | --self.launchTimer = updateTimer(self.launchTimer, dt)
54 | if self.launchTimer < 0 then
55 | self.physics:applyLinearImpulse((self.dir * 1500):unpack())
56 | self.launchTimer = 0
57 |
58 | -- self.id is used to destroy spikes that haven't launched yet
59 | -- since this spike has launched, make the id invalid
60 | self.id = -1
61 | end
62 |
63 | -- Hurt player on contact
64 | if self.physics:enter('Player') then
65 | player:hurt(self.power)
66 | self.dead = true
67 | end
68 |
69 | if self.physics:enter('Wall') or self.physics:enter('Breakable')
70 | or self.physics:enter('Transition') then
71 | self.dead = true
72 | end
73 |
74 | end
75 |
76 | table.insert(spikes, spike)
77 |
78 | end
79 |
80 | function spikes:update(dt)
81 |
82 | for i,s in ipairs(self) do
83 | s:update(dt)
84 | end
85 |
86 | -- Iterate through all spikes in reverse to remove dead ones
87 | for i=#spikes,1,-1 do
88 | if spikes[i].dead then
89 | spikes[i].physics:destroy()
90 | table.remove(spikes, i)
91 | end
92 | end
93 |
94 | end
95 |
96 | function spikes:draw()
97 |
98 | love.graphics.setColor(1, 1, 1, 1)
99 |
100 | for i,s in ipairs(self) do
101 | local sprX, sprY = s.physics:getPosition()
102 | local sprW = s.sprite:getWidth()
103 | local sprH = s.sprite:getHeight()
104 |
105 | local vx, vy = s.dir:unpack()
106 | local rotate = math.atan2(vy, vx)
107 |
108 | love.graphics.draw(s.sprite, sprX, sprY, rotate, 1, 1, sprW/2, sprH/2)
109 | end
110 |
111 | end
112 |
--------------------------------------------------------------------------------
/source/environment/blast.lua:
--------------------------------------------------------------------------------
1 | blasts = {}
2 |
3 | function spawnBlast(x, y, size, color, time, rev)
4 | blast = {}
5 | blast.x = x
6 | blast.y = y
7 | blast.color = color
8 | blast.radius = 1
9 | blast.max_radius = size
10 | blast.time = time
11 | blast.timer = time + 1
12 | blast.rev = rev or false
13 | blast.dead = false
14 | blast.maxAlpha = 0.706
15 | blast.alpha = blast.maxAlpha
16 | blast.state = 0
17 |
18 | -- If the blast should happen in reverse
19 | if rev then
20 | blast.radius = size
21 | blast.max_radius = 1
22 | blast.maxAlpha = 0.2
23 | blast.alpha = 0
24 | blast.revTimer = size/2
25 | end
26 |
27 | function blast:update(dt, i)
28 | self.timer = updateTimer(self.timer, dt)
29 |
30 | if self.timer == 0 then
31 | self.dead = true
32 | end
33 |
34 | local max = self.max_radius
35 | local maxAlph = self.maxAlpha
36 |
37 | if self.state == 0 then
38 |
39 | flux.to(self, self.time, { radius = max })
40 |
41 | if self.rev == false then
42 | -- Normal blast, fades out
43 | flux.to(self, self.time, { alpha = 0 })
44 | else
45 | -- Reverse blast, fade in, then out
46 | flux.to(self, self.time/4, { alpha = maxAlph })
47 | end
48 |
49 | self.state = 1
50 | end
51 |
52 | if self.rev and self.alpha == maxAlph then
53 | flux.to(self, self.time/2, { alpha = 0 })
54 | end
55 |
56 | end
57 |
58 | table.insert(blasts, blast)
59 |
60 | end
61 |
62 | function blasts:update(dt)
63 | for i,w in ipairs(blasts) do
64 | w:update(dt, i)
65 | end
66 |
67 | local i = table.getn(blasts)
68 | while i > 0 do
69 | if blasts[i].dead == true then
70 | table.remove(blasts, i)
71 | end
72 | i = i - 1
73 | end
74 | end
75 |
76 | function blasts:draw()
77 | for _,e in ipairs(blasts) do
78 | local r, g, b = unpack(e.color)
79 | love.graphics.setColor(r, g, b, e.alpha)
80 | love.graphics.circle("fill", e.x, e.y, e.radius, math.ceil(e.radius/2))
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/source/environment/breakable.lua:
--------------------------------------------------------------------------------
1 | -- Blocks that can be blown up by the rocket luancher
2 | breakables = {}
3 |
4 | function spawnBreakable(x, y)
5 |
6 | local newBreak = {}
7 | newBreak.breakable = true
8 | newBreak.dead = false
9 |
10 | newBreak.x = x
11 | newBreak.y = y
12 | newBreak.width = 256
13 | newBreak.height = 256
14 |
15 | newBreak.physics = world:newRectangleCollider(x, y, newBreak.width, newBreak.height)
16 | newBreak.physics.parent = newBreak
17 | newBreak.physics:setCollisionClass('Wall')
18 | newBreak.physics:setType('static')
19 |
20 | -- These variables are needed for drawing the rocky surface
21 | newBreak.up = true
22 | newBreak.down = true
23 | newBreak.left = true
24 | newBreak.right = true
25 |
26 | math.randomseed(table.getn(breakables))
27 | newBreak.crackRot = math.random() * 3.14
28 |
29 | table.insert(breakables, newBreak)
30 |
31 | end
32 |
33 | -- spawns all particles for when a wall is blown up
34 | function breakParticles(x, y)
35 |
36 | spawnParticle(x, y, "break", vector(-2500, -4500))
37 | spawnParticle(x, y, "break", vector(2500, -4500))
38 | spawnParticle(x, y, "break", vector(-4500, -2500))
39 | spawnParticle(x, y, "break", vector(4500, -2500))
40 |
41 | end
42 |
43 | function breakables:update(dt)
44 |
45 | -- Iterate through all breakables in reverse to remove dead ones
46 | for i=#breakables,1,-1 do
47 | if breakables[i].dead then
48 |
49 | local bx, by = breakables[i].physics:getPosition()
50 | breakParticles(bx, by)
51 |
52 | breakables[i].physics:destroy()
53 | table.remove(breakables, i)
54 |
55 | end
56 | end
57 |
58 | end
59 |
--------------------------------------------------------------------------------
/source/environment/explosion.lua:
--------------------------------------------------------------------------------
1 | function explode(x, y)
2 |
3 | soundManager:play("explosion")
4 |
5 | local radius = 315
6 |
7 | -- Spawn the particles for the explosion
8 | local scl = 12
9 | local spd = 240
10 | local life = 0.8
11 | for inc=1, 7 do
12 | spd = spd * 0.8
13 | life = life * 1.1
14 | if inc == 7 then
15 | spd = 32 -- This is so the middle particles stay more centralized
16 | end
17 | fires:spawnFire(x, y, life, vector(1, 1), scl, nil, spd)
18 | fires:spawnFire(x, y, life, vector(1, 0), scl, nil, spd)
19 | fires:spawnFire(x, y, life, vector(1, -1), scl, nil, spd)
20 | fires:spawnFire(x, y, life, vector(0, 1), scl, nil, spd)
21 | fires:spawnFire(x, y, life, vector(-1, -1), scl, nil, spd)
22 | fires:spawnFire(x, y, life, vector(-1, 0), scl, nil, spd)
23 | fires:spawnFire(x, y, life, vector(-1, 1), scl, nil, spd)
24 | fires:spawnFire(x, y, life, vector(0, -1), scl, nil, spd)
25 | end
26 |
27 | -- finds all enemies in the blast radius
28 | local ens = world:queryCircleArea(x, y, radius, {'Enemy'})
29 | for i,e in ipairs(ens) do
30 | if e.parent.type ~= "fish" then
31 | e.parent:damage(25)
32 | end
33 | end
34 |
35 | -- finds all breakable walls in the blast radius
36 | local br = world:queryCircleArea(x, y, radius, {'Wall'})
37 | for i,b in ipairs(br) do
38 | if b.parent ~= nil and b.parent.breakable then
39 | b.parent.dead = true
40 | end
41 | end
42 |
43 | shake:start(0.01, 12, 0.005, 0.25)
44 |
45 | expX = x
46 | expY = y
47 |
48 | end
49 |
--------------------------------------------------------------------------------
/source/environment/fire.lua:
--------------------------------------------------------------------------------
1 | -- This "fires" table contains every particle of fire currently in the game
2 | -- The illusion of fire is made by spawning many small blob-shaped sprites
3 | -- that move, change color, and fade away
4 | fires = {}
5 |
6 | function fires:spawnFire(x, y, life, dir, scale, off, speed, smoke, start_alpha)
7 |
8 | -- p is a single fire particle
9 | local p = {}
10 | p.width = 32
11 | p.height = 32
12 |
13 | p.x = x - p.width/2
14 | p.y = y - p.height/2
15 |
16 | p.speed = speed or 60
17 | p.smoke = smoke or false
18 | p.spr_rot = math.random(-3, 2) + math.random() -- 0 to pi radians
19 |
20 | -- sets a random blob sprite for the fire particle
21 | local spriteint = math.random(1, 5)
22 | p.sprite = nil
23 | if spriteint == 1 then
24 | p.sprite = sprites.fire.f1
25 | elseif spriteint == 2 then
26 | p.sprite = sprites.fire.f2
27 | elseif spriteint == 3 then
28 | p.sprite = sprites.fire.f3
29 | elseif spriteint == 4 then
30 | p.sprite = sprites.fire.f4
31 | elseif spriteint == 5 then
32 | p.sprite = sprites.fire.f5
33 | end
34 |
35 | p.dead = false -- if the particle should be destroyed
36 | p.max_life = life -- time the particle is visible
37 | p.life = p.max_life -- current timer counting down
38 | p.scale = scale -- size of the particle
39 | p.front = front or false -- used for draw order (drawn sooner or later)
40 | p.direction = dir or vector(0, -1) -- direction the particle will move
41 |
42 | if dir ~= nil then
43 | -- set the direction of the particle
44 | p.direction = dir:normalized()
45 |
46 | -- This section uses the offset parameter to move the starting position
47 | -- of the particle slightly, based on the direction the particle moves.
48 | -- For example, if the direction was straight up, the particle would be
49 | -- offset randomly to the left or right.
50 | local o = off or 15
51 | local offset = math.random(-1 * o, o)
52 | local randrot = math.random(-3, 2) + math.random()
53 | local perp_vec = p.direction:rotated(randrot) * offset
54 | local ox, oy = perp_vec:unpack()
55 | p.x, p.y = p.x + ox, p.y + oy
56 | end
57 |
58 | -- Gives the particle a bit of random movement by slightly changing its
59 | -- direction. Multi is used to ensure there is an equal amount of clockwise
60 | -- rotation to counterclockwise.
61 | local multi = -1
62 | if table.getn(fires) % 2 == 0 then
63 | multi = 1
64 | end
65 | p.direction:rotateInplace(multi * math.random()/4)
66 | p.direction = p.direction * 3
67 |
68 | p.start_alpha = start_alpha or 0.188
69 | p.alpha = start_alpha or 0.188
70 | --p.scale = 2
71 | p.color = {1, 1, 1}
72 | if p.smoke == true then
73 | p.color = {0.471, 0.471, 0.471}
74 | end
75 |
76 | function p:update(dt)
77 | self.life = updateTimer(self.life, dt)
78 | --if self.life > (self.max_life/6)*5 then
79 | --p.color = {1, 1, 1}
80 | if self.life > (self.max_life/6)*5 then
81 | self.color = {1, 0.988, 0.757}
82 | elseif self.life > (self.max_life/6)*4 then
83 | self.color = {1, 0.788, 0.302}
84 | elseif self.life > (self.max_life/6)*2 then
85 | self.color = {1, 0.635, 0.176}
86 | elseif self.life > (self.max_life/6) then
87 | self.color = {1, 0.569, 0.098}
88 | else
89 | self.color = {1, 1, 1}
90 | end
91 |
92 | -- tints the fire to be smoke
93 | if self.smoke == true then
94 | self.color = {0.471, 0.471, 0.471}
95 | end
96 |
97 | if p.start_alpha == 0.188 then
98 | if self.life > 0.75 then
99 | self.alpha = p.start_alpha
100 | else
101 | self.alpha = self.life/4
102 | end
103 | else
104 | if self.life > 0.75 then
105 | self.alpha = p.start_alpha
106 | else
107 | self.alpha = self.life/9.75
108 | end
109 | end
110 |
111 | local dx, dy = self.direction:unpack()
112 | self.x = self.x + (dx * self.speed * dt)
113 | self.y = self.y + (dy * self.speed * dt)
114 | if self.alpha < 0.001 then
115 | self.dead = true
116 | end
117 | end
118 |
119 | table.insert(fires, p)
120 | end
121 |
122 | function fires:update(dt) do
123 |
124 | for i,f in ipairs(fires) do
125 | f:update(dt)
126 | end
127 |
128 | -- Iterate through all fires in reverse to remove dead ones
129 | for i=#fires,1,-1 do
130 | if fires[i].dead then
131 | table.remove(fires, i)
132 | end
133 | end
134 |
135 | end
136 |
137 | function fires:draw()
138 |
139 | for i,f in ipairs(fires) do
140 | love.graphics.setColor(f.color[1], f.color[2], f.color[3], f.alpha)
141 | love.graphics.draw(f.sprite, f.x+f.width/2, f.y+f.height/2, f.spr_rot, f.scale or 1, f.scale or 1, f.sprite:getWidth()/2, f.sprite:getHeight()/2)
142 | end
143 |
144 | end
145 | end
146 |
--------------------------------------------------------------------------------
/source/environment/trail.lua:
--------------------------------------------------------------------------------
1 | trails = {}
2 |
3 | function spawnTrail(id, max, width, color)
4 | trail = {}
5 | trail.id = id -- used to tell which object to follow
6 | trail.max = max -- length of the trail
7 | trail.width = width -- width of the trail
8 | trail.color = color -- color of the trail
9 |
10 | trail.points = {}
11 | trail.dead = false
12 | trail.updated = false
13 |
14 | trail.arc = {}
15 | trail.poly = {}
16 | trail.triangles = {}
17 |
18 | function trail:update(dt, wep)
19 |
20 | self.updated = true
21 | self.arc = {}
22 |
23 | local t = {}
24 | t.x = wep.physics.body:getX()
25 | t.y = wep.physics.body:getY()
26 | t.dir = nil
27 |
28 | if wep.moving == false then
29 | t.dir = wep.direct:normalized()
30 | else
31 | local vx, vy = wep.physics.body:getLinearVelocity()
32 | t.dir = vector(vx, vy):normalized()
33 | end
34 |
35 | table.insert(self.points, t)
36 | end
37 |
38 | table.insert(trails, trail)
39 |
40 | end
41 |
42 | function trails:update(dt)
43 |
44 | for _,t in ipairs(trails) do
45 | t.updated = false
46 | end
47 |
48 | local i = table.getn(trails)
49 | while i > 0 do
50 | if trails[i].dead == true then
51 | table.remove(trails, i)
52 | end
53 | i = i - 1
54 | end
55 |
56 | end
57 |
58 | -- Call this AFTER weapons get updated
59 | -- This function handles what happens to the trail after the weapon is destroyed
60 | function trails:fadeOut(dt)
61 | for _,t in ipairs(trails) do
62 | if t.updated == false then
63 | t.max = t.max - 1
64 | --create_poly(t)
65 | if t.max < 2 then
66 | t.dead = true
67 | end
68 | end
69 | end
70 | end
71 |
72 | function trails:draw()
73 | for _,t in ipairs(trails) do
74 | if table.getn(t.points) > 1 then
75 | local firstpt = table.getn(t.points) - t.max
76 | if firstpt < 1 then
77 | firstpt = 1
78 | end
79 | love.graphics.setColor(unpack(t.color))
80 | love.graphics.setLineWidth(t.width)
81 | love.graphics.line(t.points[firstpt].x, t.points[firstpt].y, t.points[table.getn(t.points)].x, t.points[table.getn(t.points)].y)
82 | end
83 | end
84 | end
85 |
--------------------------------------------------------------------------------
/source/environment/vine.lua:
--------------------------------------------------------------------------------
1 | -- These are vines that the player shoots, and they sink into the ground
2 | vines = {}
3 |
4 | function spawnVine(x, y)
5 |
6 | local vine = world:newRectangleCollider(x + 32, y + 16, 64, 640)
7 | vine:setCollisionClass('Wall')
8 | vine:setType('static')
9 |
10 | vine.trueX = vine:getX()
11 | vine.trueY = vine:getY()
12 |
13 | vine.sinkTimer = 0
14 | vine.speed = 320
15 | vine.offset = 0
16 | vine.offsetTween = nil
17 |
18 | function vine:update(dt)
19 |
20 | self.sinkTimer = updateTimer(self.sinkTimer, dt)
21 | if self.sinkTimer > 0 then
22 | self.trueY = self.trueY + self.speed * dt
23 | end
24 |
25 | if self:enter('P_Weapon') then
26 | local wep = self:getEnterCollisionData('P_Weapon')
27 | if wep.collider.parent.type == 1 then
28 | self.sinkTimer = 0.35
29 | self.speed = 320
30 | else
31 | self.sinkTimer = 0.8
32 | self.speed = 640
33 | end
34 | end
35 |
36 | if self.offset == 0 then
37 | self.offset = 1
38 | self.offsetTween = flux.to(self, 4, {offset = 32}):ease("quadinout")
39 | elseif self.offset == 32 then
40 | self.offsetTween = flux.to(self, 4, {offset = 0}):ease("quadinout")
41 | end
42 |
43 | self:setPosition(self.trueX, self.trueY + self.offset)
44 |
45 | end
46 |
47 | table.insert(vines, vine)
48 |
49 | end
50 |
51 | function vines:update(dt)
52 |
53 | for i,v in ipairs(self) do
54 | v:update(dt)
55 | end
56 |
57 | end
58 |
59 | function vines:draw()
60 |
61 | for i,v in ipairs(self) do
62 | love.graphics.setColor(1, 1, 1, 1)
63 | local vx, vy = v:getPosition()
64 | love.graphics.draw(sprites.environment.vine, vx-32, vy-320)
65 | end
66 |
67 | end
68 |
--------------------------------------------------------------------------------
/source/environment/water.lua:
--------------------------------------------------------------------------------
1 | -- Ripple objects are small ripple animations on top of all bodies of water
2 | ripples = {}
3 |
4 | -- Spritesheet that ripples use
5 | ripples.spr = sprites.environment.waterSheet
6 |
7 | -- This is the grid that all ripple objects use for animations
8 | local w = ripples.spr:getWidth()
9 | local h = ripples.spr:getHeight()
10 | ripples.grid = anim8.newGrid(64, 64, w, h)
11 |
12 | function spawnRipple(x, y)
13 |
14 | local ripple = {}
15 | ripple.x = x
16 | ripple.y = y
17 |
18 | ripple.animation = anim8.newAnimation(ripples.grid('1-20', 1), 0.1)
19 | ripple.animation:gotoFrame(math.random(1, 20))
20 |
21 | table.insert(ripples, ripple)
22 |
23 | end
24 |
25 | -- Updates the anim8 animation for all ripple objects
26 | function ripples:update(dt)
27 |
28 | for i,r in ipairs(self) do
29 | r.animation:update(dt)
30 | end
31 |
32 | end
33 |
34 | -- Draws the anim8 animation for all ripple objects
35 | function ripples:draw()
36 |
37 | love.graphics.setColor(0.388, 0.502, 0.541, 0.471)
38 |
39 | for i,r in ipairs(self) do
40 | r.animation:draw(self.spr, r.x, r.y)
41 | end
42 |
43 | for i,w in ipairs(mapdata.water) do
44 | love.graphics.rectangle("fill", w.x, w.y, w.width, w.height)
45 | end
46 |
47 | end
48 |
49 | function ripples:destroy()
50 |
51 | for i=#ripples,1,-1 do
52 | table.remove(ripples, i)
53 | end
54 |
55 | end
56 |
--------------------------------------------------------------------------------
/source/global/collision_classes.lua:
--------------------------------------------------------------------------------
1 | -- Collision Classes used with windfield (see the libraries)
2 | function getCollisionClasses()
3 | world:addCollisionClass('Ignore', {ignores = {'Ignore'}})
4 | world:addCollisionClass('Water', {ignores = {'Ignore'}})
5 | world:addCollisionClass('Ripple', {ignores = {'Ignore', 'Water'}})
6 | world:addCollisionClass('Pickup', {ignores = {'Ignore'}})
7 | world:addCollisionClass('Player', {ignores = {'Ignore', 'Water', 'Ripple'}})
8 | world:addCollisionClass('Enemy', {ignores = {'Ignore', 'Pickup', 'Water'}})
9 | world:addCollisionClass('P_Weapon', {ignores = {'Ignore', 'Player', 'Pickup', 'Ripple', 'Water'}})
10 | world:addCollisionClass('E_Weapon', {ignores = {'Ignore', 'Enemy', 'Pickup', 'Ripple', 'Water', 'P_Weapon', 'E_Weapon'}})
11 | world:addCollisionClass('Wall', {ignores = {'Ignore', 'Wall'}})
12 | world:addCollisionClass('Breakable', {ignores = {'Ignore', 'Wall'}})
13 | world:addCollisionClass('Transition', {ignores = {'Ignore'}})
14 |
15 | gravWorld:addCollisionClass('Particle', {ignores = {'Particle'}})
16 | end
17 |
--------------------------------------------------------------------------------
/source/global/gameState.lua:
--------------------------------------------------------------------------------
1 | -- Stores global information about the player's progress
2 | gameState = {}
3 | gameState.player = {}
4 | gameState.pickups = {}
5 |
6 | function gameStateInit()
7 |
8 | -- Number of times the game has been saved.
9 | -- Used to prevent certain save blocks from spawning
10 | gameState.saveCount = 0
11 |
12 | -- State stores if update functions should occur
13 | gameState.state = 1
14 |
15 | -- Stores the current room
16 | gameState.room = "rm1"
17 |
18 | -- Player information
19 | gameState.player.x = 0
20 | gameState.player.y = 0
21 | gameState.player.maxHealth = 20
22 | gameState.player.weapon = 0
23 |
24 | -- Which pickups have been obtained
25 | gameState.pickups.blaster = false
26 | gameState.pickups.rocket = false
27 | gameState.pickups.harpoon = false
28 | gameState.pickups.aquaPack = false
29 | gameState.pickups.health1 = false
30 | gameState.pickups.health2 = false
31 |
32 | -- Changes to false after the tutorial text disappears
33 | gameState.tutorial = true
34 |
35 | end
36 |
--------------------------------------------------------------------------------
/source/global/saveGame.lua:
--------------------------------------------------------------------------------
1 | function saveGame()
2 | gameState.saveCount = gameState.saveCount + 1
3 | gameState.player.x = player.physics:getX()
4 | gameState.player.y = player.physics:getY()
5 | gameState.player.weapon = player.weapon
6 | local temp = Tserial.pack(gameState)
7 | love.filesystem.write("savefile.txt", temp)
8 | saveUtil:startMessage()
9 | saveUtil:destroySave()
10 |
11 | -- Restore the player's health when the game saves
12 | player.health = gameState.player.maxHealth
13 | end
14 |
15 | function loadGame()
16 | if love.filesystem.getInfo("savefile.txt") ~= nil then
17 | local temp = love.filesystem.read("savefile.txt")
18 | gameState = Tserial.unpack(temp, true)
19 | end
20 |
21 | player.physics:setPosition(gameState.player.x, gameState.player.y)
22 | player.health = gameState.player.maxHealth
23 | player.weapon = gameState.player.weapon
24 | player.state = 1
25 | changeToMap(gameState.room)
26 |
27 | if gameState.room ~= "rm27" then
28 | soundManager:startMusic("cavern")
29 | end
30 |
31 | blackScreen:fadeIn(1)
32 | end
33 |
--------------------------------------------------------------------------------
/source/global/saveUtil.lua:
--------------------------------------------------------------------------------
1 | -- Throughout the game, there are invisible save blocks.
2 | -- When the player hits one of these, the game saves.
3 | -- saveUtil stores these blocks and handles the save message.
4 | saveUtil = {}
5 |
6 | -- Block starts offscreen so the player can't hit it.
7 | saveUtil.saveBlock = {}
8 | saveUtil.saveBlock.x = -1000
9 |
10 | -- Info for the "Saving..." message
11 | saveUtil.message = {}
12 | saveUtil.message.state = 0
13 | saveUtil.message.stateTimer = 0
14 | saveUtil.message.text = "Saving..."
15 | saveUtil.message.alpha = 0
16 |
17 | -- Spawn a save block
18 | function saveUtil:spawnSave(x, num)
19 |
20 | -- Do not spawn this block if its value is greater
21 | -- than the number of saves. This prevents blocks
22 | -- that have been used already from spawning again.
23 | if num < gameState.saveCount then
24 | return
25 | end
26 |
27 | saveUtil.saveBlock.x = x
28 |
29 | end
30 |
31 | function saveUtil:destroySave()
32 | -- Move the block offscreen so the player can't hit it
33 | saveUtil.saveBlock.x = -1000
34 | end
35 |
36 | -- Start the "Saving..." message
37 | function saveUtil:startMessage()
38 | saveUtil.message.state = 1
39 | end
40 |
41 | function saveUtil:update(dt)
42 |
43 | -- Collision with player
44 | local px = player.physics:getX()
45 | if math.abs(saveUtil.saveBlock.x - px) < 128 then
46 | saveGame()
47 | end
48 |
49 | saveUtil.message.stateTimer = updateTimer(saveUtil.message.stateTimer, dt)
50 |
51 | -- State 1: Message fades in
52 | if saveUtil.message.state == 1 then
53 |
54 | saveUtil.message.alpha = saveUtil.message.alpha + (dt*1.5)
55 |
56 | if saveUtil.message.alpha >= 1 then
57 |
58 | saveUtil.message.alpha = 1
59 | saveUtil.message.state = 2
60 | saveUtil.message.stateTimer = 1
61 |
62 | end
63 |
64 | end
65 |
66 | -- State 2: Message is fully visible
67 | if saveUtil.message.state == 2 and saveUtil.message.stateTimer == 0 then
68 | saveUtil.message.state = 3
69 | end
70 |
71 | -- State 3: Message fades away
72 | if saveUtil.message.state == 3 then
73 |
74 | saveUtil.message.alpha = saveUtil.message.alpha - dt
75 |
76 | if saveUtil.message.alpha <= 0 then
77 | saveUtil.message.alpha = 0
78 | saveUtil.message.state = 0
79 | end
80 |
81 | end
82 |
83 | end
84 |
85 | function saveUtil:drawMessage()
86 |
87 | -- The message is only visible at states 1 and 2
88 | -- Also, only show the message if the player is in control
89 | if saveUtil.message.state > 0 and player.state == 1 then
90 |
91 | love.graphics.setColor(1, 1, 1, saveUtil.message.alpha)
92 | love.graphics.setFont(fonts.menu.button)
93 | love.graphics.print(saveUtil.message.text, 12 * scale, 4 * scale)
94 |
95 | end
96 |
97 | end
98 |
--------------------------------------------------------------------------------
/source/global/shake.lua:
--------------------------------------------------------------------------------
1 | -- This file handles all things related to screenshake
2 |
3 | shake = {}
4 |
5 | shake.time = 0 -- Timer keeping track of shake duration
6 | shake.fade = true -- Whether or not the shake intensity fades out
7 | shake.fadeSpeed = 10 -- How fast the fade is
8 | shake.intensity = 6 -- Intensity of the shake (in pixels)
9 | shake.speed = 2 -- Duration between camera movements (in seconds)
10 | shake.speedTimer = 2 -- Counts down from speed in real time to change shake dir
11 | shake.dir = 1 -- Tracks if the camera will shift left or right
12 |
13 | function shake:start(t, i, s, f, f_speed)
14 | shake.time = t
15 | shake.intensity = i
16 | shake.speed = s
17 | shake.speedTimer = s
18 | shake.fade = f or true
19 | shake.fadeSpeed = f_speed or 10
20 |
21 | shake.count = 0
22 | shake.dir = 1
23 | end
24 |
25 | function shake:stop()
26 | shake.time = t
27 | shake.intensity = i
28 | shake.speed = s
29 | shake.fade = f or true
30 | shake.fadeSpeed = f_speed or 10
31 | end
32 |
33 | -- be sure to call after cam:update()
34 | function shake:update(dt)
35 |
36 | shake.time = updateTimer(shake.time, dt)
37 |
38 | if shake.time > 0 or (shake.fade and shake.intensity > 0) then
39 |
40 | -- offsets the camera based on the shake's intensity and direction
41 | cam:lookAt(cam.x + (shake.intensity * shake.dir), cam.y)
42 |
43 | if shake.speedTimer <= 0 then
44 | -- When the timer hits zero, change the direction of the camera offset
45 | shake.dir = shake.dir * -1
46 | shake.speedTimer = shake.speed
47 | else
48 | shake.speedTimer = updateTimer(shake.speedTimer, dt)
49 | end
50 |
51 | -- After shake time is up, start fading the intensity
52 | if shake.time <= 0 and shake.fade and shake.intensity > 0 then
53 | shake.intensity = shake.intensity - (dt * shake.fadeSpeed)
54 | end
55 |
56 | else
57 | cam:lookAt(cam.x, cam.y)
58 | end
59 |
60 | end
61 |
--------------------------------------------------------------------------------
/source/global/soundManager.lua:
--------------------------------------------------------------------------------
1 | -- This table is used to play sounds and music.
2 | -- Use this table's functions so that it can check
3 | -- whether or not the sound file should actually
4 | -- be played (it checks if sound is turned off).
5 |
6 | soundManager = {}
7 | soundManager.music = sounds.music.cavern -- replace with menu music
8 | soundManager.volume = 1 -- volume of the music
9 | soundManager.fading = false -- volume will fade down if true
10 |
11 | -- Used for the rooms leading up to the boss
12 | soundManager.danger = false
13 |
14 | -- Used to tell if the ending music has started
15 | soundManager.ending = false
16 |
17 | -- Use for Sound Effects
18 | function soundManager:play(snd)
19 |
20 | if soundOn then
21 | sounds[snd]:play()
22 | end
23 |
24 | end
25 |
26 | function soundManager:startMusic(song)
27 |
28 | if song ~= nil then
29 | self.music = sounds.music[song]
30 | end
31 |
32 | if soundOn then
33 | self.fading = false
34 | self.volume = 1
35 | self.music:setVolume(self.volume)
36 |
37 | if song ~= "ending" and song ~= "intro" then
38 | self.music:setLooping(true)
39 | else
40 | self.music:setLooping(false)
41 | end
42 |
43 | love.audio.play(self.music)
44 | end
45 |
46 | self.danger = false
47 |
48 | end
49 |
50 | function soundManager:musicFade()
51 | self.fading = true
52 | end
53 |
54 | function soundManager:update(dt)
55 | if self.fading then
56 | self.volume = self.volume - dt -- will take 1 second to fade out
57 | if self.volume < 0 then
58 | self.volume = 0
59 | self.fading = false
60 | love.audio.stop(self.music)
61 | end
62 | self.music:setVolume(self.volume)
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/source/global/utilities.lua:
--------------------------------------------------------------------------------
1 | -- Distance formula
2 | function distance(x1, y1, x2, y2)
3 | return math.sqrt((y2 - y1)^2 + (x2 - x1)^2)
4 | end
5 |
6 | -- Speed given X and Y velocity values
7 | function speedFromVelocity(vx, vy)
8 | return math.sqrt(vx * vx + vy * vy)
9 | end
10 |
11 | -- Returns a normalized HUMP vector towards the player from (mx, my)
12 | -- If no parameters are given, it's assumed to be the mouse's position
13 | function toPlayerVector(mx, my)
14 | local px, py = player.physics:getPosition()
15 | local reverse = true
16 | if mx == nil and my == nil then
17 | mx, my = cam:mousePosition()
18 | mx = mx
19 | my = my
20 | reverse = false
21 | end
22 |
23 | if reverse then
24 | return vector.new(px-mx, py-my):normalized()
25 | else
26 | return vector.new(mx-px, my-py):normalized()
27 | end
28 | end
29 |
30 | function toMouseVector(px, py)
31 | local mx, my = cam:mousePosition()
32 | return vector.new(mx-px, my-py):normalized()
33 | end
34 |
35 | -- Gets radians needed to rotate towards the player
36 | function toPlayerRotate(mx, my)
37 | local dir = toPlayerVector(mx, my)
38 | local vx, vy = dir:normalized():unpack()
39 | return math.atan2(vy, vx)
40 | end
41 |
42 | -- Update timer variables
43 | function updateTimer(v, dt)
44 | if v > 0 then
45 | v = v - dt
46 | elseif v < 0 then
47 | v = 0
48 | end
49 |
50 | return v
51 | end
52 |
53 | -- Distance between object and player
54 | function distToPlayer(mx, my)
55 | local px, py = player.physics:getPosition()
56 | return distance(px, py, mx, my)
57 | end
58 |
--------------------------------------------------------------------------------
/source/levels/background.lua:
--------------------------------------------------------------------------------
1 | -- Stores all data about the background
2 | background = {}
3 | background.x = 0
4 | background.y = 0
5 | background.img = sprites.environment.bg
6 | background.tileSize = 512
7 |
8 | -- PARALLAX
9 |
10 | -- Relative position of the background compared to the camera
11 | background.relX = 0
12 | background.relY = 0
13 |
14 | -- Stores the position of the camera in the previous frame
15 | background.oldCamX = 0
16 | background.oldCamY = 0
17 |
18 | -- Used to help with tracking the change in background position
19 | background.diffBufferX = 0
20 | background.diffBufferY = 0
21 |
22 | -- Number of pixels the camera has to move before the background moves
23 | background.speed = 2
24 |
25 | -- calculate parallax positioning
26 | function background:update(dt)
27 |
28 | local camX, camY = cam:position()
29 | local diffX = camX - background.oldCamX
30 | local diffY = camY - background.oldCamY
31 |
32 | -- Background position always at (-768, -768) relative to camera position
33 | background.x = camX - 1920
34 | background.y = camY - 1536
35 |
36 | background.relX = background.relX - (diffX / background.speed)
37 | background.relY = background.relY - (diffY / background.speed)
38 |
39 | if background.relX < -1 * background.tileSize then
40 | background.relX = background.relX + background.tileSize
41 | end
42 |
43 | if background.relX > background.tileSize then
44 | background.relX = background.relY - background.tileSize
45 | end
46 |
47 | if background.relY < -1 * background.tileSize then
48 | background.relY = background.relY + background.tileSize
49 | end
50 |
51 | if background.relY > background.tileSize then
52 | background.relY = background.relY - background.tileSize
53 | end
54 |
55 | background.oldCamX = camX
56 | background.oldCamY = camY
57 |
58 | end
59 |
60 | function background:reset()
61 |
62 | background.relX = 0
63 | background.relY = 0
64 | background.diffBufferX = 0
65 | background.diffBufferY = 0
66 |
67 | local camX, camY = cam:position()
68 | background.x = camX - 1920
69 | background.y = camY - 1536
70 |
71 | background.oldCamX = camX
72 | background.oldCamY = camY
73 |
74 | end
75 |
76 | function background:draw()
77 |
78 | love.graphics.setColor(1, 1, 1, 1)
79 |
80 | if mapdata.map == maps.rmIntro then
81 | love.graphics.setColor(0, 0, 0, 1)
82 | end
83 |
84 | local imgW = self.img:getWidth()
85 | local imgH = self.img:getHeight()
86 |
87 | for i = 0, 8 do -- number of tiles to cover the width of the camera
88 | for j = 0, 6 do -- number of tiles to cover the height of the camera
89 | love.graphics.draw(self.img, background.x + background.relX + (i * imgW),
90 | background.y + background.relY + (j * imgH))
91 | end
92 | end
93 |
94 | end
95 |
--------------------------------------------------------------------------------
/source/levels/destroyAll.lua:
--------------------------------------------------------------------------------
1 | -- This function is called when a new map is loaded.
2 | -- Before the objects in the new map are created, the objects from the old map
3 | -- need to be deleted.
4 |
5 | local function destroyAll()
6 |
7 | -- Destroy all walls that were spawned for the previous map
8 | for i, w in ipairs(mapdata.walls) do
9 | w:destroy()
10 | mapdata.walls[i] = nil
11 | end
12 |
13 | -- Destroy all vines that were spawned for the previous map
14 | for i, v in ipairs(vines) do
15 | v:destroy()
16 | vines[i] = nil
17 | end
18 |
19 | -- Destroy all spikes that were spawned for the previous map
20 | for i, s in ipairs(spikes) do
21 | s.dead = true
22 | end
23 |
24 | -- Destroy all walls that were spawned for the previous map
25 | for i, w in ipairs(mapdata.water) do
26 | if w.ripplePhysics ~= nil then
27 | w.ripplePhysics:destroy()
28 | end
29 | w:destroy()
30 | mapdata.water[i] = nil
31 | end
32 |
33 | -- Destroys all water ripples
34 | ripples:destroy()
35 |
36 | -- Destroy all transition objects from the previous map
37 | for i, t in ipairs(mapdata.transitions) do
38 | t:destroy()
39 | mapdata.transitions[i] = nil
40 | end
41 |
42 | -- Destroy all breakable objects from the previous map
43 | for i, b in ipairs(breakables) do
44 | b.physics:destroy()
45 | breakables[i] = nil
46 | end
47 |
48 | -- Destroy all pickup objects from the previous map
49 | for i, p in ipairs(pickups) do
50 | p.physics:destroy()
51 | pickups[i] = nil
52 | end
53 |
54 | -- Destroy all enemies from the previous map
55 | for i, e in ipairs(enemies) do
56 | e.physics:destroy()
57 | enemies[i] = nil
58 | end
59 |
60 | -- Iterate through all weapons in reverse to remove dead weapons from table
61 | for i=#weapons,1,-1 do
62 | weapons[i].physics:destroy()
63 | table.remove(weapons, i)
64 | end
65 |
66 | for i=#fires,1,-1 do
67 | table.remove(fires, i)
68 | end
69 |
70 | -- Move the saveBlock offscreen so the player can't hit
71 | saveUtil.saveBlock.x = -1000
72 |
73 | end
74 |
75 | return destroyAll
76 |
--------------------------------------------------------------------------------
/source/levels/drawWalls.lua:
--------------------------------------------------------------------------------
1 | local function drawWalls()
2 |
3 | -- This function, given a wall object, will draw the rocks for it
4 | local function singleWallRocks(wall)
5 |
6 | if wall.dontDraw then
7 | return
8 | end
9 |
10 | -- Draw rocks on the surface of all walls
11 | -- Make every wall have direction values to tell which sides need rocks
12 | local surface = sprites.environment.rockySurface
13 | local offY = surface:getHeight()
14 | if wall.up then
15 | for itr=0, (wall.width/128)-1 do
16 | love.graphics.draw(surface, wall.x + (itr * 128), wall.y - offY)
17 | end
18 | end
19 | if wall.down then
20 | for itr=0, (wall.width/128)-1 do
21 | love.graphics.draw(surface, wall.x + (itr * 128), wall.y + wall.height + offY, nil, nil, -1)
22 | end
23 | end
24 | if wall.right then
25 | for itr=0, (wall.height/128)-1 do
26 | love.graphics.draw(surface, wall.x + wall.width + offY, wall.y + (itr * 128), math.pi/2)
27 | end
28 | end
29 | if wall.left then
30 | for itr=0, (wall.height/128)-1 do
31 | love.graphics.draw(surface, wall.x - offY, wall.y + (itr * 128), math.pi/2, nil, -1)
32 | end
33 | end
34 |
35 | end
36 |
37 | -- Draw the ground
38 | love.graphics.setColor(1, 1, 1, 1)
39 |
40 | for i,w in ipairs(mapdata.walls) do
41 | singleWallRocks(w)
42 | end
43 |
44 | for i,b in ipairs(breakables) do
45 | singleWallRocks(b)
46 | end
47 |
48 | for i,w in ipairs(mapdata.walls) do
49 |
50 | if w.dontDraw == false then
51 |
52 | -- Draw the full rectangle for each wall
53 | --love.graphics.rectangle("fill", w.x, w.y, w.width, w.height)
54 |
55 | --[[
56 | love.graphics.setColor(1, 1, 1, 1)
57 |
58 | local spr = sprites.environment.wall
59 |
60 | for itrX=0, (w.width/128)-1 do
61 | for itrY = 0, (w.height/128)-1 do
62 | love.graphics.draw(spr, w.x + (itrX * 128), w.y + (itrY * 128))
63 | end
64 | end
65 |
66 | ]]
67 |
68 | -- Note: this is done in a different for loop than the one above because
69 | -- the ground color needs to be drawn after all the surface rocks have been
70 | -- drawn. This is because some walls extend into the ground, so some walls
71 | -- are drawing rocky surfaces underground. These underground surfaces
72 | -- should not be seen, so these rectangles will cover those surfaces.
73 |
74 | end
75 |
76 | end
77 |
78 | for i,b in ipairs(breakables) do
79 |
80 | love.graphics.setColor(1, 1, 1, 1)
81 |
82 | local spr = sprites.environment.wall
83 |
84 | for itrX=0, 1 do
85 | for itrY = 0, 1 do
86 | love.graphics.draw(spr, b.x + (itrX * 128), b.y + (itrY * 128))
87 | end
88 | end
89 |
90 | love.graphics.setColor(0, 0, 0, 1)
91 | love.graphics.draw(sprites.environment.crack, b.x + 128, b.y + 128, b.crackRot, 1, 1, 90, 90)
92 |
93 | end
94 | end
95 |
96 | return drawWalls
97 |
--------------------------------------------------------------------------------
/source/levels/intro.lua:
--------------------------------------------------------------------------------
1 | -- Handles everything during the intro sequence
2 | -- Also calls loadGame when pressing "Continue"
3 | intro = {}
4 | intro.state = 0
5 | intro.timer = 0
6 | intro.skipMessage = false
7 |
8 | function intro:update(dt)
9 |
10 | if gameState.room ~= "rmIntro" then
11 | return
12 | end
13 |
14 | self.timer = updateTimer(self.timer, dt)
15 |
16 | if self.state == 1 and self.timer == 0 then
17 |
18 | textBox:start("intro")
19 | self.state = 2
20 | self.timer = 1
21 |
22 | end
23 |
24 | if self.state == 2 and self.timer == 0 then
25 |
26 | if scroll.text == "" then
27 | self.state = 2.5
28 | self.timer = 1.25
29 | end
30 |
31 | end
32 |
33 | if self.state == 2.5 and self.timer == 0 then
34 |
35 | soundManager:startMusic("intro")
36 | self.state = 3
37 | self.timer = 1.7
38 |
39 | end
40 |
41 | if self.state == 3 and self.timer == 0 then
42 |
43 | self.state = 4
44 | blackScreen:fadeIn(8)
45 | player.physics:setPosition(512, 260)
46 | player.state = -10
47 | changeToMap("rm1")
48 |
49 | end
50 |
51 | -- Load the saved game (if it exists
52 | if self.state == 100 and self.timer == 0 then
53 |
54 | -- Reset the state
55 | self.state = 0
56 |
57 | if love.filesystem.getInfo("savefile.txt") == nil then
58 |
59 | -- No save file exists, return to the main menu
60 | changeToMap("rmMainMenu")
61 | textBox:start("failedLoad")
62 |
63 | else
64 |
65 | -- Load the game
66 | loadGame()
67 |
68 | end
69 |
70 | end
71 |
72 | end
73 |
74 | function intro:interrupt()
75 |
76 | if gameState.room ~= "rmIntro" then
77 | return
78 | end
79 |
80 | if self.skipMessage then
81 | scroll.text = ""
82 | scroll.messageObj = nil
83 | scroll.charTimer = 0
84 | self.state = 2
85 | self.timer = 0
86 | self.skipMessage = false
87 | else
88 | self.skipMessage = true
89 | end
90 |
91 | end
92 |
93 | function intro:drawInterrupt()
94 |
95 | if self.skipMessage and textBox.active and scroll.text ~= scroll.fullMessage then
96 | local w = gameWidth * scale
97 | local h = gameHeight * scale
98 | love.graphics.setColor(1, 1, 1, 1)
99 | love.graphics.setFont(fonts.menu.intro)
100 | love.graphics.print("Skip?", w - 96 * scale, h - 52 * scale)
101 | end
102 |
103 | end
104 |
--------------------------------------------------------------------------------
/source/libraries/Tserial.lua:
--------------------------------------------------------------------------------
1 | --- Tserial v1.5, a simple table serializer which turns tables into Lua script
2 | -- @author Taehl (SelfMadeSpirit@gmail.com)
3 | Tserial = {}
4 | TSerial = Tserial -- for backwards-compatibility
5 |
6 | --- Serializes a table into a string, in form of Lua script.
7 | -- @param t table to be serialized (may not contain any circular reference)
8 | -- @param drop if true, unserializable types will be silently dropped instead of raising errors
9 | -- if drop is a function, it will be called to serialize unsupported types
10 | -- if drop is a table, it will be used as a serialization table (where {[value] = serial})
11 | -- @param indent if true, output "human readable" mode with newlines and indentation (for debug)
12 | -- @return string recreating given table
13 | function Tserial.pack(t, drop, indent)
14 | assert(type(t) == "table", "Can only Tserial.pack tables.")
15 | local s, empty, indent = "{"..(indent and "\n" or ""), true, indent and math.max(type(indent)=="number" and indent or 0,0)
16 | local function proc(k,v, omitKey) -- encode a key/value pair
17 | empty = nil -- helps ensure empty tables return as "{}"
18 | local tk, tv, skip = type(k), type(v)
19 | if type(drop)=="table" and drop[k] then k = "["..drop[k].."]"
20 | elseif tk == "boolean" then k = k and "[true]" or "[false]"
21 | elseif tk == "string" then local f = string.format("%q",k) if f ~= '"'..k..'"' then k = '['..f..']' end
22 | elseif tk == "number" then k = "["..k.."]"
23 | elseif tk == "table" then k = "["..Tserial.pack(k, drop, indent and indent+1).."]"
24 | elseif type(drop) == "function" then k = "["..string.format("%q",drop(k)).."]"
25 | elseif drop then skip = true
26 | else error("Attempted to Tserial.pack a table with an invalid key: "..tostring(k))
27 | end
28 | if type(drop)=="table" and drop[v] then v = drop[v]
29 | elseif tv == "boolean" then v = v and "true" or "false"
30 | elseif tv == "string" then v = string.format("%q", v)
31 | elseif tv == "number" then -- no change needed
32 | elseif tv == "table" then v = Tserial.pack(v, drop, indent and indent+1)
33 | elseif type(drop) == "function" then v = string.format("%q",drop(v))
34 | elseif drop then skip = true
35 | else error("Attempted to Tserial.pack a table with an invalid value: "..tostring(v))
36 | end
37 | if not skip then return string.rep("\t",indent or 0)..(omitKey and "" or k.."=")..v..","..(indent and "\n" or "") end
38 | return ""
39 | end
40 | local l=-1 repeat l=l+1 until rawget(t,l+1)==nil -- #t "can" lie!
41 | for i=1,l do s = s..proc(i, t[i], true) end -- use ordered values when possible for better string
42 | for k, v in pairs(t) do if not (type(k)=="number" and k<=l) then s = s..proc(k, v) end end
43 | if not empty then s = string.sub(s,1,string.len(s)-1) end
44 | if indent then s = string.sub(s,1,string.len(s)-1).."\n" end
45 | return s..string.rep("\t",(indent or 1)-1).."}"
46 | end
47 |
48 | --- Loads a table into memory from a string (like those output by Tserial.pack)
49 | -- @param s a string of Lua defining a table, such as "{2,4,8,ex='ample'}"
50 | -- @param safe if true, all extraneous parts of the string will be removed, leaving only a table (prevents running anomalous code when unpacking untrusted strings). Will also cause malformed tables to quietly return nil and an error message, instead of throwing an error (so your program can't be crashed with a bad string)
51 | -- @return a table recreated from the given string.
52 | function Tserial.unpack(s, safe)
53 | if safe then s = string.match(s, "(%b{})") end
54 | assert(type(s) == "string", "Can only Tserial.unpack strings.")
55 | local f, result = loadstring("Tserial.table="..s)
56 | if not safe then assert(f,result) elseif not f then return nil, result end
57 | result = f()
58 | local t = Tserial.table
59 | Tserial.table = nil
60 | return t, result
61 | end
--------------------------------------------------------------------------------
/source/libraries/sti/graphics.lua:
--------------------------------------------------------------------------------
1 | local lg = love.graphics
2 | local graphics = { isCreated = lg and true or false }
3 |
4 | function graphics.newSpriteBatch(...)
5 | if graphics.isCreated then
6 | return lg.newSpriteBatch(...)
7 | end
8 | end
9 |
10 | function graphics.newCanvas(...)
11 | if graphics.isCreated then
12 | return lg.newCanvas(...)
13 | end
14 | end
15 |
16 | function graphics.newImage(...)
17 | if graphics.isCreated then
18 | return lg.newImage(...)
19 | end
20 | end
21 |
22 | function graphics.newQuad(...)
23 | if graphics.isCreated then
24 | return lg.newQuad(...)
25 | end
26 | end
27 |
28 | function graphics.getCanvas(...)
29 | if graphics.isCreated then
30 | return lg.getCanvas(...)
31 | end
32 | end
33 |
34 | function graphics.setCanvas(...)
35 | if graphics.isCreated then
36 | return lg.setCanvas(...)
37 | end
38 | end
39 |
40 | function graphics.clear(...)
41 | if graphics.isCreated then
42 | return lg.clear(...)
43 | end
44 | end
45 |
46 | function graphics.push(...)
47 | if graphics.isCreated then
48 | return lg.push(...)
49 | end
50 | end
51 |
52 | function graphics.origin(...)
53 | if graphics.isCreated then
54 | return lg.origin(...)
55 | end
56 | end
57 |
58 | function graphics.scale(...)
59 | if graphics.isCreated then
60 | return lg.scale(...)
61 | end
62 | end
63 |
64 | function graphics.translate(...)
65 | if graphics.isCreated then
66 | return lg.translate(...)
67 | end
68 | end
69 |
70 | function graphics.pop(...)
71 | if graphics.isCreated then
72 | return lg.pop(...)
73 | end
74 | end
75 |
76 | function graphics.draw(...)
77 | if graphics.isCreated then
78 | return lg.draw(...)
79 | end
80 | end
81 |
82 | function graphics.rectangle(...)
83 | if graphics.isCreated then
84 | return lg.rectangle(...)
85 | end
86 | end
87 |
88 | function graphics.getColor(...)
89 | if graphics.isCreated then
90 | return lg.getColor(...)
91 | end
92 | end
93 |
94 | function graphics.setColor(...)
95 | if graphics.isCreated then
96 | return lg.setColor(...)
97 | end
98 | end
99 |
100 | function graphics.line(...)
101 | if graphics.isCreated then
102 | return lg.line(...)
103 | end
104 | end
105 |
106 | function graphics.polygon(...)
107 | if graphics.isCreated then
108 | return lg.polygon(...)
109 | end
110 | end
111 |
112 | function graphics.getWidth()
113 | if graphics.isCreated then
114 | return lg.getWidth()
115 | end
116 | return 0
117 | end
118 |
119 | function graphics.getHeight()
120 | if graphics.isCreated then
121 | return lg.getHeight()
122 | end
123 | return 0
124 | end
125 |
126 | return graphics
--------------------------------------------------------------------------------
/source/libraries/windfield/mlib/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Davis Claiborne
2 |
3 | This software is provided 'as-is', without any express or implied
4 | warranty. In no event will the authors be held liable for any damages
5 | arising from the use of this software.
6 |
7 | Permission is granted to anyone to use this software for any purpose,
8 | including commercial applications, and to alter it and redistribute it
9 | freely, subject to the following restrictions:
10 |
11 | 1. The origin of this software must not be misrepresented; you must not
12 | claim that you wrote the original software. If you use this software
13 | in a product, an acknowledgement in the product documentation would be
14 | appreciated but is not required.
15 | 2. Altered source versions must be plainly marked as such, and must not be
16 | misrepresented as being the original software.
17 | 3. This notice may not be removed or altered from any source distribution.
18 |
--------------------------------------------------------------------------------
/source/pickup.lua:
--------------------------------------------------------------------------------
1 | -- Stores all pickups in the map
2 | pickups = {}
3 |
4 | -- Spawns a pickup, name is used to identify the type of pickup
5 | function spawnPickup(name, x, y)
6 |
7 | -- If we already collected the pickup, don't spawn it
8 | if gameState.pickups[name] then
9 | return nil
10 | end
11 |
12 | local pickup = {}
13 | pickup.name = name
14 | pickup.dead = false
15 | pickup.float = true
16 | pickup.radius = 40
17 | pickup.timer = 0.1
18 | pickup.scale = 0.16
19 |
20 | -- used for floating
21 | pickup.state = 0
22 | pickup.tween = nil
23 | pickup.startY = y
24 | pickup.y = y
25 |
26 | pickup.physics = world:newCircleCollider(x, y, pickup.radius)
27 |
28 | pickup.sprite = sprites.pickups.item
29 |
30 | if name == "blaster" then
31 | pickup.sprite = sprites.pickups.blaster
32 | elseif name == "rocket" then
33 | pickup.sprite = sprites.pickups.rocketLauncher
34 | pickup.scale = 0.2
35 | elseif name == "harpoon" then
36 | pickup.sprite = sprites.pickups.spearGun
37 | elseif name == "aquaPack" then
38 | pickup.sprite = sprites.pickups.aquaPack
39 | pickup.scale = 0.21
40 | elseif name == "health1" or name == "health2" then
41 | pickup.sprite = sprites.pickups.health
42 | end
43 |
44 | -- Set the pickup's collision class
45 | pickup.physics:setCollisionClass('Pickup')
46 |
47 | -- Needed so we can reference the pickup table given its physics
48 | pickup.physics.parent = pickup
49 |
50 | table.insert(pickups, pickup)
51 |
52 | end
53 |
54 | -- Update all pickups currently in the map
55 | function pickups:update(dt)
56 |
57 | for i, p in ipairs(pickups) do
58 |
59 | -- float functionality goes here
60 | if p.state == 0 then
61 | local destY = p.y + 20
62 | p.tween = flux.to(p, 2.5, {y = destY}):ease("quadinout")
63 | p.state = 1
64 | elseif p.state == 1 then
65 | if p.y == p.startY + 20 then
66 | local destY = p.y - 40
67 | p.tween = flux.to(p, 2.5, {y = destY}):ease("quadinout")
68 | p.state = 2
69 | end
70 | elseif p.state == 2 then
71 | if p.y == p.startY - 20 then
72 | local destY = p.y + 40
73 | p.tween = flux.to(p, 2.5, {y = destY}):ease("quadinout")
74 | p.state = 1
75 | end
76 | end
77 |
78 | p.physics:setY(p.y)
79 |
80 | -- colliding with the player
81 | if p.physics:enter('Player') then
82 | -- sets the appropriate pickup value
83 | gameState.pickups[p.name] = true
84 | p.physics:destroy()
85 | p.dead = true
86 | soundManager:play("itemGet")
87 |
88 | if p.name == "blaster" then
89 | player.weapon = 1
90 | textBox:start("blaster")
91 | elseif p.name == "rocket" then
92 | player.weapon = 2
93 | textBox:start("rocket")
94 | elseif p.name == "harpoon" then
95 | player.weapon = 3
96 | textBox:start("harpoon")
97 | elseif p.name == "aquaPack" then
98 | textBox:start("aquaPack")
99 | elseif p.name == "health1" or p.name == "health2" then
100 | gameState.player.maxHealth = gameState.player.maxHealth + 5
101 | player.health = gameState.player.maxHealth
102 | textBox:start("health")
103 | end
104 | end
105 |
106 | p.timer = updateTimer(p.timer, dt)
107 |
108 | --[[
109 |
110 | -- Spawn a sparkle
111 | if p.timer == 0 then
112 |
113 | local px, py = p.physics:getPosition()
114 |
115 | -- Seed the randomness with the pickup's Y position
116 | -- (since it is constantly moving)
117 | math.randomseed(py)
118 |
119 | local moveVector = vector(10, 0)
120 | moveVector:rotateInplace(math.random(0, 2 * math.pi))
121 | spawnParticle(px, py, "pickupSparkle", moveVector)
122 | p.timer = 0.2
123 |
124 | end
125 |
126 | ]]
127 |
128 | end
129 |
130 | -- Iterate through all pickups in reverse to remove dead pickups from table
131 | for i=#pickups,1,-1 do
132 | if pickups[i].dead then
133 | table.remove(pickups, i)
134 | end
135 | end
136 |
137 | end
138 |
139 | -- Draw all pickups
140 | function pickups:draw()
141 | for _,p in ipairs(self) do
142 | local px, py = p.physics:getPosition()
143 | love.graphics.setColor(1, 1, 1, 1)
144 |
145 | -- Background sprite
146 | local bg = sprites.pickups.pickup_back
147 | love.graphics.draw(bg, px, py, nil, 1, 1, bg:getWidth()/2, bg:getHeight()/2)
148 |
149 | -- Actual pickup sprite
150 | love.graphics.draw(p.sprite, px, py, nil, p.scale, nil, p.sprite:getWidth()/2, p.sprite:getHeight()/2)
151 | end
152 | end
153 |
--------------------------------------------------------------------------------
/source/startup/loadFonts.lua:
--------------------------------------------------------------------------------
1 | fonts = {}
2 |
3 | -- fonts.pickup = love.graphics.newFont(34 * scale)
4 | fonts.pickup = love.graphics.newFont("fonts/russoone/RussoOne-Regular.ttf", 42 * scale)
5 | fonts.damage = love.graphics.newFont("fonts/russoone/RussoOne-Regular.ttf", 42)
6 | fonts.boss = love.graphics.newFont("fonts/russoone/RussoOne-Regular.ttf", 52 * scale)
7 |
8 | -- Fonts used during the credits
9 | fonts.credits = {}
10 | fonts.credits.word = love.graphics.newFont("fonts/russoone/RussoOne-Regular.ttf", 48)
11 | fonts.credits.title = love.graphics.newFont("fonts/russoone/RussoOne-Regular.ttf", 118)
12 | fonts.credits.me = love.graphics.newFont("fonts/russoone/RussoOne-Regular.ttf", 56)
13 |
14 | -- Fonts used for the Main Menu and Intro
15 | fonts.menu = {}
16 | fonts.menu.title = love.graphics.newFont("fonts/russoone/RussoOne-Regular.ttf", 146 * scale)
17 | fonts.menu.button = love.graphics.newFont("fonts/russoone/RussoOne-Regular.ttf", 48 * scale)
18 | fonts.menu.message = love.graphics.newFont("fonts/russoone/RussoOne-Regular.ttf", 32 * scale)
19 | fonts.menu.intro = love.graphics.newFont("fonts/vt323/VT323-Regular.ttf", 42 * scale)
20 |
21 | -- Misc fonts
22 | fonts.misc = {}
23 | fonts.misc.save = love.graphics.newFont("fonts/russoone/RussoOne-Regular.ttf", 36 * scale)
24 | fonts.misc.tutorial = love.graphics.newFont("fonts/russoone/RussoOne-Regular.ttf", 49)
25 |
--------------------------------------------------------------------------------
/source/startup/loadSounds.lua:
--------------------------------------------------------------------------------
1 | sounds = {}
2 |
3 | sounds.itemGet = love.audio.newSource("sounds/itemGet.wav", "static")
4 | sounds.playerHurt = love.audio.newSource("sounds/player/playerHurt.wav", "static")
5 | sounds.splash = love.audio.newSource("sounds/player/splash.wav", "static")
6 |
7 | -- There are multiple laser sound effects, was originally planning to have
8 | -- it randomly play one, but I decided I like laser2.wav the best.
9 | sounds.laser = love.audio.newSource("sounds/player/laser2.wav", "static")
10 | sounds.spear = love.audio.newSource("sounds/player/spear.wav", "static")
11 | --sounds.laser = love.audio.newSource({"sounds/player/laser1.wav", "sounds/player/laser2.wav", "sounds/player/laser3.wav"}, "static")
12 | sounds.bombShot = love.audio.newSource("sounds/player/bombShot.wav", "static")
13 | sounds.explosion = love.audio.newSource("sounds/player/explosion.wav", "static")
14 |
15 | sounds.enemyHurt = love.audio.newSource("sounds/enemies/enemyHurt.wav", "static")
16 | sounds.spikes = love.audio.newSource("sounds/enemies/spikes.wav", "static")
17 | sounds.starfish = love.audio.newSource("sounds/enemies/starfish.wav", "static")
18 | sounds.helloBoss = love.audio.newSource("sounds/enemies/helloBoss.wav", "static")
19 | sounds.bossLaser = love.audio.newSource("sounds/enemies/bossLaser.wav", "static")
20 | sounds.bossDie = love.audio.newSource("sounds/enemies/bossDie.wav", "static")
21 | sounds.bossExplode = love.audio.newSource("sounds/enemies/bossExplode.wav", "static")
22 |
23 | sounds.text = love.audio.newSource("sounds/ui/text.wav", "static")
24 | sounds.click = love.audio.newSource("sounds/ui/click.wav", "static")
25 |
26 | sounds.blip = love.audio.newSource("sounds/blip.wav", "static")
27 |
28 |
29 | -- Music
30 | sounds.music = {}
31 | sounds.music.menu = love.audio.newSource("music/menu.ogg", "stream")
32 | sounds.music.cavern = love.audio.newSource("music/cavern.ogg", "stream")
33 | sounds.music.boss = love.audio.newSource("music/boss.ogg", "stream")
34 | sounds.music.danger = love.audio.newSource("music/danger.ogg", "stream")
35 | sounds.music.ending = love.audio.newSource("music/ending.ogg", "stream")
36 |
37 | -- Intro music is static since it is more time-dependent than other songs
38 | sounds.music.intro = love.audio.newSource("music/intro.ogg", "static")
39 |
--------------------------------------------------------------------------------
/source/startup/loadSprites.lua:
--------------------------------------------------------------------------------
1 | sprites = {}
2 |
3 | -- Images for drawing the player
4 | sprites.player = {}
5 | sprites.player.helmet = love.graphics.newImage('sprites/newPlayer2/helmet.png')
6 | sprites.player.body = love.graphics.newImage('sprites/newPlayer2/body.png')
7 | sprites.player.armEmpty = love.graphics.newImage('sprites/newPlayer2/arm.png')
8 | sprites.player.backArm = love.graphics.newImage('sprites/newPlayer2/backArm.png')
9 | sprites.player.armBlaster = love.graphics.newImage('sprites/newPlayer2/armBlaster.png')
10 | sprites.player.armRocket = love.graphics.newImage('sprites/newPlayer2/armRocket.png')
11 | sprites.player.armSpear = love.graphics.newImage('sprites/newPlayer2/armSpear.png')
12 | sprites.player.armSpearLoaded = love.graphics.newImage('sprites/newPlayer2/armSpearLoaded.png')
13 | sprites.player.spear = love.graphics.newImage('sprites/newPlayer/spear.png')
14 | sprites.player.bomb = love.graphics.newImage('sprites/player/bomb.png')
15 | sprites.player.jetpack = love.graphics.newImage('sprites/newPlayer/jetpack.png')
16 | sprites.player.aquaPack = love.graphics.newImage('sprites/newPlayer/aquapack.png')
17 |
18 | sprites.player.newPlayer = love.graphics.newImage('sprites/newPlayer/newPlayer.png')
19 |
20 | -- Images for everything relating to the environment and levels
21 | sprites.environment = {}
22 | sprites.environment.bg = love.graphics.newImage('sprites/environment/bg.png')
23 | sprites.environment.wall = love.graphics.newImage('sprites/environment/wall.png')
24 | sprites.environment.waterSheet = love.graphics.newImage('sprites/environment/waterSheet.png')
25 | sprites.environment.rockySurface = love.graphics.newImage('sprites/environment/rockySurface.png')
26 | sprites.environment.crack = love.graphics.newImage('sprites/environment/crack.png')
27 | sprites.environment.breakParticle = love.graphics.newImage('sprites/environment/breakParticle.png')
28 | sprites.environment.vine = love.graphics.newImage('sprites/environment/vine.png')
29 |
30 | -- Images for enemies
31 | sprites.enemies = {}
32 | sprites.enemies.flyerBody = love.graphics.newImage('sprites/enemies/flyerBody.png')
33 | sprites.enemies.flyerEye = love.graphics.newImage('sprites/enemies/flyerEye.png')
34 | sprites.enemies.flyerWing1 = love.graphics.newImage('sprites/enemies/flyerWing1.png')
35 | sprites.enemies.flyerWing2 = love.graphics.newImage('sprites/enemies/flyerWing2.png')
36 | sprites.enemies.spikeBody = love.graphics.newImage('sprites/enemies/spikeBody.png')
37 | sprites.enemies.spikeProj = love.graphics.newImage('sprites/enemies/spikeProj.png')
38 | sprites.enemies.starfish = love.graphics.newImage('sprites/enemies/starfish.png')
39 | sprites.enemies.evilBubble = love.graphics.newImage('sprites/enemies/evilBubble.png')
40 | sprites.enemies.bossBody = love.graphics.newImage('sprites/enemies/bossBody.png')
41 | sprites.enemies.bigBossEye = love.graphics.newImage('sprites/enemies/bigBossEye.png')
42 | sprites.enemies.egg = love.graphics.newImage('sprites/enemies/egg.png')
43 |
44 | -- Images for items
45 | sprites.pickups = {}
46 | sprites.pickups.health = love.graphics.newImage('sprites/items/healthPickup.png')
47 | sprites.pickups.item = love.graphics.newImage('sprites/items/itemPickup.png')
48 | sprites.pickups.pickup_back = love.graphics.newImage('sprites/items/pickup_back.png')
49 | sprites.pickups.blaster = love.graphics.newImage('sprites/items/blaster.png')
50 | sprites.pickups.rocketLauncher = love.graphics.newImage('sprites/items/rocketLauncher.png')
51 | sprites.pickups.spearGun = love.graphics.newImage('sprites/items/spearGun.png')
52 | sprites.pickups.aquaPack = love.graphics.newImage('sprites/items/aquaPack.png')
53 |
54 | -- Individual blob shapes for drawing fire
55 | sprites.fire = {}
56 | sprites.fire.f1 = love.graphics.newImage('sprites/fire/fire_1.png')
57 | sprites.fire.f2 = love.graphics.newImage('sprites/fire/fire_2.png')
58 | sprites.fire.f3 = love.graphics.newImage('sprites/fire/fire_3.png')
59 | sprites.fire.f4 = love.graphics.newImage('sprites/fire/fire_4.png')
60 | sprites.fire.f5 = love.graphics.newImage('sprites/fire/fire_5.png')
61 |
62 | -- Images for UI
63 | sprites.ui = {}
64 | sprites.ui.sound = love.graphics.newImage('sprites/ui/sound.png')
65 | sprites.ui.github = love.graphics.newImage('sprites/ui/github.png')
66 |
--------------------------------------------------------------------------------
/source/startup/main_require.lua:
--------------------------------------------------------------------------------
1 | function getGlobals()
2 | require("source/libraries/slam")
3 | require("source/startup/loadFonts")
4 | require("source/startup/loadSprites")
5 | require("source/startup/loadSounds")
6 |
7 | require("source/libraries/Tserial")
8 |
9 | require("source/global/utilities")
10 | require("source/global/collision_classes")
11 | require("source/global/gameState")
12 | gameStateInit()
13 |
14 | require("source/global/saveGame")
15 | require("source/global/saveUtil")
16 | require("source/global/soundManager")
17 | getCollisionClasses()
18 |
19 | Camera = require("source/libraries/hump/camera")
20 | vector = require("source/libraries/hump/vector")
21 | flux = require("source/libraries/flux")
22 | anim8 = require("source/libraries/anim8")
23 |
24 | require("source/player")
25 | require("source/weapon")
26 | require("source/pickup")
27 | require("source/enemies/enemy")
28 | require("source/enemies/spikeProj")
29 | require("source/enemies/enemyProj")
30 | require("source/enemies/eye")
31 | require("source/enemies/egg")
32 |
33 | require("source/environment/particle")
34 | require("source/environment/explosion")
35 | require("source/environment/breakable")
36 | require("source/environment/water")
37 | require("source/environment/vine")
38 | require("source/environment/fire")
39 | require("source/environment/trail")
40 | require("source/environment/blast")
41 |
42 | require("source/global/shake")
43 |
44 | require("source/text/messages")
45 | require("source/text/scrollText")
46 | require("source/text/textBox")
47 | require("source/text/damage")
48 | require("source/text/credits")
49 | require("source/text/tutorial")
50 |
51 | require("source/ui/cam")
52 | require("source/ui/blackScreen")
53 | require("source/ui/flash")
54 | require("source/ui/mainMenu")
55 |
56 | require("source/levels/intro")
57 | require("source/levels/background")
58 |
59 | sti = require("source/libraries/sti")
60 | require("source/levels/map_loader")
61 | loadMaps()
62 | end
63 |
--------------------------------------------------------------------------------
/source/startup/startup.lua:
--------------------------------------------------------------------------------
1 | function startup()
2 |
3 | -- GAME WINDOW CONFIGURATION
4 |
5 | -- Window title
6 | love.window.setTitle("CAVERN")
7 |
8 | -- Window icon
9 | local icon = love.image.newImageData('sprites/newPlayer2/helmet.png')
10 | love.window.setIcon(icon)
11 |
12 | -- Game resolution
13 | gameWidth = 1152
14 | gameHeight = 768
15 |
16 | scale = 1 -- adjusts game window to screen size
17 | offset = 0.8 -- window size relative to scale
18 |
19 | local screen_width, screen_height = love.window.getDesktopDimensions()
20 | local w_scale = screen_width / gameWidth
21 | local h_scale = screen_height / gameHeight
22 |
23 | -- scale set to be the lesser of w_scale and h_scale
24 | -- this way, the game window will not ever exceed screen size
25 | if w_scale < h_scale then
26 | scale = w_scale
27 | else
28 | scale = h_scale
29 | end
30 |
31 | scale = scale * offset
32 |
33 | love.window.setMode(gameWidth * scale, gameHeight * scale, {fullscreen = false,
34 | fullscreentype = "desktop", resizable = false, borderless = false,
35 | vsync = true})
36 |
37 |
38 | -- GAME SETUP
39 |
40 | -- Physics setup
41 | -- Windfield is a library that makes LOVE's physics easier to work with
42 | -- https://github.com/SSYGEN/windfield
43 | local wf = require("source/libraries/windfield")
44 | world = wf.newWorld(0, 0, true) -- No gravity, bodies can sleep
45 | gravWorld = wf.newWorld(0, 1000, true)
46 |
47 | -- Global sound variable, game starts with it on
48 | soundOn = true
49 |
50 | -- Requires all global source files
51 | require("source/startup/main_require")
52 | getGlobals()
53 |
54 | -- Sets first map of the game
55 | changeToMap("rmMainMenu")
56 |
57 | -- Start the music!
58 | soundManager:startMusic("menu")
59 | end
60 |
61 | -- This function resets all values after the game is completed
62 | function reinit()
63 | gameStateInit()
64 | player.weapon = 0
65 | player.health = gameState.player.maxHealth
66 | end
67 |
--------------------------------------------------------------------------------
/source/text/credits.lua:
--------------------------------------------------------------------------------
1 | -- This table contains words that will be displayed
2 | -- in the credits room
3 | credits = {}
4 | credits.bottom = 12752
5 |
6 | -- Words follow this format:
7 | -- { Word Text, X position, Y position, Font }
8 |
9 | -- By default, font will be fonts.credits.word unless
10 | -- specified otherwise
11 |
12 | -- X position doesn't use a position value, but it has
13 | -- 3 options: "center", "left", or "right". Items in
14 | -- the credits will either be centered, or put into a
15 | -- left and right column.
16 |
17 | -- Y position is the number of pixels from the bottom
18 | -- Example: if Y position was 100, text would be
19 | -- displayed at credits.bottom - 100
20 |
21 | table.insert(credits, {"CAVERN", "center", 2304, fonts.credits.title})
22 | table.insert(credits, {"Created by Kyle Schaub", "center", 4608, fonts.credits.me})
23 | table.insert(credits, {"All code, art, and music is available on GitHub.", "center", 6912})
24 | table.insert(credits, {"This is an open-source Love2D project.", "center", 7040})
25 | table.insert(credits, {"All of my supportive students on Udemy", "center", 9344})
26 | table.insert(credits, {"Everyone who helped with testing and reporting bugs", "center", 9472})
27 | table.insert(credits, {"Special Thanks to:", "center", 9600})
28 | table.insert(credits, {"Thank you for playing!", "center", 11892, fonts.credits.me})
29 |
30 | function credits:draw()
31 |
32 | -- Only draw if we are in rmCredits
33 | if gameState.room == "rmCredits" then
34 | for _,w in ipairs(self) do
35 |
36 | if w[4] == nil then
37 | love.graphics.setFont(fonts.credits.word)
38 | else
39 | love.graphics.setFont(w[4])
40 | end
41 |
42 | love.graphics.setColor(1, 1, 1, 1)
43 |
44 | local text = w[1]
45 | local x = 384
46 | local y = credits.bottom - w[3]
47 | local width = 1792
48 |
49 | if w[2] ~= "center" then
50 | width = width / 2
51 | end
52 |
53 | if w[2] == "right" then
54 | x = 1280
55 | end
56 |
57 | love.graphics.printf(text, x, y, width, "center")
58 |
59 | end
60 | end
61 |
62 | end
63 |
--------------------------------------------------------------------------------
/source/text/damage.lua:
--------------------------------------------------------------------------------
1 | -- When an enemy takes damage, it'll display the damage amount
2 | damages = {}
3 |
4 | function damages:spawnDamage(x, y, val, who)
5 |
6 | local damage = {}
7 |
8 | -- starting position of the damage text
9 | damage.x = x - 40
10 | if val < 10 then
11 | damage.x = damage.x + 11
12 | end
13 | damage.y = y - 24
14 |
15 | -- Information for the bouncing tween used to animate the text
16 | damage.start_y = damage.y
17 | damage.val = val * -1
18 | damage.jump_tween_x = nil
19 | damage.jump_tween_y = nil
20 | damage.alpha_tween = nil
21 | damage.alpha = 255
22 | damage.dead = false
23 |
24 | -- Random value determining how far left or right the text will bounce
25 | damage.rx = math.random(-100, 100)
26 |
27 | function damage:update()
28 |
29 | local function on_fade_complete()
30 | self.jump_tween_x = nil
31 | self.jump_tween_y = nil
32 | self.alpha_tween = nil
33 | self.dead = true
34 | end
35 |
36 | local function on_y_complete()
37 | local sy = self.start_y
38 | self.jump_tween_y = flux.to(self, 0.5, {y = sy}):ease("quadin")
39 | self.alpha_tween = flux.to(self, 0.5, {alpha = 0}):oncomplete(on_fade_complete):ease("cubicout")
40 | end
41 |
42 | if self.jump_tween_x == nil then
43 | local rx = self.x + self.rx
44 | self.jump_tween_x = flux.to(self, 1, {x = rx})
45 | end
46 | if self.jump_tween_y == nil then
47 | self.jump_tween_y = flux.to(self, 0.5, {y = y - 176}):oncomplete(on_y_complete):ease("quadout")
48 | end
49 | end
50 |
51 | table.insert(damages, damage)
52 |
53 | end
54 |
55 | function damages:update(dt)
56 |
57 | for i,d in ipairs(self) do
58 | d:update(dt)
59 | end
60 |
61 | -- Iterate through all damages in reverse to remove dead ones from table
62 | for i=#damages,1,-1 do
63 | if damages[i].dead then
64 | table.remove(damages, i)
65 | end
66 | end
67 |
68 | end
69 |
70 | function damages:draw()
71 | for _,d in ipairs(damages) do
72 | love.graphics.setColor(1, 0.196, 0.196, d.alpha/255)
73 | love.graphics.setFont(fonts.damage)
74 | love.graphics.print(d.val, d.x, d.y)
75 | love.graphics.setColor(1, 1, 1, 1)
76 | end
77 | end
78 |
--------------------------------------------------------------------------------
/source/text/messages.lua:
--------------------------------------------------------------------------------
1 | -- All messages that get displayed in a scroll
2 |
3 | messages = {}
4 |
5 | messages.blaster = {
6 | "You got the blaster!",
7 | "Use the mouse to aim, and\n to shoot!"
8 | }
9 | messages.blaster.img = sprites.pickups.blaster
10 |
11 | messages.rocket = {
12 | "You got the Rocket Launcher!",
13 | " to launch a missile\nthat explodes upon impact,\ndealing massive damage.",
14 | "Press the or scroll\nthe mouse wheel to switch\nyour equipped weapon."
15 | }
16 | messages.rocket.img = sprites.pickups.rocketLauncher
17 |
18 | messages.harpoon = {
19 | "You got the Spear Gun!",
20 | " to shoot a spear,\neven while underwater.",
21 | }
22 | messages.harpoon.img = sprites.pickups.spearGun
23 |
24 | messages.aquaPack = {
25 | "You got the Aqua Pack!",
26 | "Your suit is now capable of\ngoing underwater.",
27 | }
28 | messages.aquaPack.img = sprites.pickups.aquaPack
29 |
30 | messages.health = {
31 | "You got a Health Upgrade!",
32 | "Your health has been restored,\nand your maximum health has\nincreased by 5.",
33 | }
34 | messages.health.img = sprites.pickups.health
35 |
36 | messages.failedLoad = {
37 | "No save file found.",
38 | }
39 |
40 | messages.intro = {
41 | "1-31-2056 \n"
42 | .. "St. Louis, Missouri, United States of America @\n\n"
43 | .. "Asteroid \"433 Eros\" has crashed into Earth approximately \n30 miles "
44 | .. "southwest of the city.@The impact has uncovered the\nentrance to a series"
45 | .. " of unexplored caves deep underground. @\n\n"
46 | .. "It was discovered in the year 2053 that Eros was home to a\nnew form of "
47 | .. "life.@Movement has been detected beneath the\nsurface of the impact site,"
48 | .. " indicating that these alien\nlifeforms have migrated into the caves."
49 | .. " @\n\n"
50 | .. "We are sending an expert in to investigate."
51 |
52 | }
53 |
54 | messages.tutorial = {
55 | "Use to move.\nUse the mouse to look around.",
56 | }
57 |
--------------------------------------------------------------------------------
/source/text/scrollText.lua:
--------------------------------------------------------------------------------
1 | -- Scroll handles all text that shows text letter-by-letter
2 |
3 | scroll = {}
4 |
5 | scroll.text = "" -- Text currently on screen
6 | scroll.fullMessage = "" -- Full message that will be displayed
7 | scroll.messageObj = nil -- Message object pulled from messages.lua
8 | scroll.charTimer = 0 -- Timer until next letter is displayed
9 | scroll.textSpeed = 0.025 -- Display a new character every ____ seconds
10 | scroll.messageNum = 1 -- Which string from the message object
11 | scroll.charNum = 0 -- Which character of the full message we're on
12 |
13 | function scroll:showMessage(m)
14 |
15 | -- Sets the first set of text for the message
16 | self.fullMessage = messages[m][1]
17 | self.text = ""
18 |
19 | self.messageNum = 1
20 | self.charNum = 0
21 | self.messageObj = messages[m]
22 |
23 | -- The messages table contains other tables, each containing a set of strings
24 | -- which will be displayed in order to the text window.
25 |
26 | end
27 |
28 | function scroll:update(dt)
29 |
30 | if self.messageObj ~= nil then
31 | while self.messageNum <= #self.messageObj do
32 |
33 | if self.fullMessage == "" then
34 | self.fullMessage = self.messageObj[self.messageNum]
35 | end
36 |
37 | if self.charNum < string.len(self.fullMessage) then
38 | self.charTimer = updateTimer(self.charTimer, dt)
39 | if self.charTimer == 0 then
40 | self.charNum = self.charNum + 1
41 | self.text = string.sub(self.fullMessage, 1, self.charNum)
42 |
43 | self.charTimer = self.textSpeed
44 |
45 | -- an "@" represents a spot to wait longer to display the next letter
46 | -- Wait 30 times longer than normal, and replace the @ with a space
47 | -- If the current letter is a comma, wait only slightly longer
48 | local nextLetter = string.sub(self.fullMessage, self.charNum+1, self.charNum+1)
49 | local curLetter = string.sub(self.fullMessage, self.charNum, self.charNum)
50 | if nextLetter == "@" then
51 |
52 | -- Wait longer
53 | self.charTimer = self.textSpeed * 30
54 |
55 | -- replace the @ with a space
56 | self.fullMessage = string.sub(self.fullMessage, 1, self.charNum) ..
57 | " " .. string.sub(self.fullMessage, self.charNum+2,
58 | string.len(self.fullMessage))
59 |
60 | end
61 | if curLetter == "," then
62 | self.charTimer = self.textSpeed * 15
63 | end
64 |
65 | -- The "string.sub" will get the latest character.
66 | -- The "string.byte" will convert that character into an integer
67 | -- that represents its ASCII equivalent
68 | -- All ASCII characters greater than 32 (visible characters) will
69 | -- play the text sound effect
70 | if string.byte( string.sub(self.text, self.charNum, self.charNum) ) > 32 then
71 | soundManager:play("text")
72 | end
73 |
74 | end
75 | else
76 | if love.keyboard.isDown("space","return", 'w', 'a', 's', 'd') or love.mouse.isDown(1,2) then
77 | self.text = ""
78 | self.fullMessage = ""
79 | self.charNum = 0
80 | self.messageNum = self.messageNum + 1
81 | end
82 | end
83 |
84 | return
85 | end
86 |
87 | self.messageObj = nil
88 |
89 | end
90 |
91 | end
92 |
--------------------------------------------------------------------------------
/source/text/textBox.lua:
--------------------------------------------------------------------------------
1 | -- Controls overall display of scrollText messages
2 |
3 | textBox = {}
4 |
5 | textBox.active = false
6 |
7 | -- Box position and dimensions
8 | textBox.x = 100
9 | textBox.y = 100
10 | textBox.width = gameWidth - 200
11 | textBox.height = 400
12 |
13 | -- Where the text will be drawn within the box
14 | textBox.textX = textBox.x + 100
15 | textBox.textY = textBox.y + 100
16 |
17 | -- Misc textBox settings
18 | textBox.font = fonts.pickup
19 | textBox.visible = true
20 |
21 |
22 | function textBox:start(m)
23 |
24 | textBox.active = true
25 |
26 | -- Freezes everything (mostly)
27 | gameState.state = 0
28 |
29 | if m == "blaster" or m == "rocket" or m == "harpoon"
30 | or m == "aquaPack" or m == "health" then
31 | textBox:init("pickup")
32 | end
33 |
34 | if m == "failedLoad" or m == "tutorial" then
35 | textBox:init("failedLoad")
36 | end
37 |
38 | if m == "tutorial" then
39 | textBox:init("tutorial")
40 | end
41 |
42 | if m == "intro" then
43 | textBox:init("intro")
44 | end
45 |
46 | scroll:showMessage(m)
47 |
48 | end
49 |
50 |
51 | function textBox:update(dt)
52 |
53 | if self.active and scroll.messageObj == nil then
54 | self.active = false
55 | gameState.state = 1
56 | end
57 |
58 | end
59 |
60 |
61 | function textBox:draw()
62 |
63 | if self.active then
64 |
65 | -- Draw black background
66 | love.graphics.setColor(0, 0, 0, 1)
67 | love.graphics.rectangle("fill", self.x * scale, self.y * scale,
68 | self.width * scale, self.height * scale)
69 |
70 | -- Draw the border of the box
71 | if self.visible then
72 | love.graphics.setColor(1, 1, 1, 1)
73 | love.graphics.setLineWidth(22 * scale)
74 | love.graphics.rectangle("line", self.x * scale, self.y * scale,
75 | self.width * scale, self.height * scale)
76 | end
77 |
78 | -- Draw text
79 | love.graphics.setColor(1, 1, 1, 1)
80 | love.graphics.setFont(self.font)
81 | love.graphics.print(scroll.text, self.textX * scale, self.textY * scale)
82 |
83 | -- Draw image
84 | if scroll.messageObj ~= nil and scroll.messageObj.img ~= nil then
85 | local spr = scroll.messageObj.img
86 | local w = spr:getWidth()
87 | local h = spr:getHeight()
88 | love.graphics.draw(spr, self.x * scale + (self.width * scale / 2),
89 | -92 * scale + (self.width * scale / 2), nil, scale/4, nil,
90 | w/2, h/2)
91 | end
92 |
93 | end
94 |
95 | end
96 |
97 |
98 | function textBox:init(type)
99 |
100 | if type == "pickup" then
101 | textBox.x = 180
102 | textBox.y = 180
103 | textBox.width = gameWidth - 360
104 | textBox.height = 410
105 | textBox.textX = textBox.x + 40
106 | textBox.textY = textBox.y + 226
107 | textBox.font = fonts.pickup
108 | textBox.visible = true
109 | end
110 |
111 | if type == "failedLoad" then
112 | textBox.x = 180
113 | textBox.y = 265
114 | textBox.width = gameWidth - 360
115 | textBox.height = 170
116 | textBox.textX = textBox.x + 40
117 | textBox.textY = textBox.y + 60
118 | textBox.font = fonts.pickup
119 | textBox.visible = true
120 | end
121 |
122 | if type == "tutorial" then
123 | textBox.x = 180
124 | textBox.y = 60
125 | textBox.width = gameWidth - 360
126 | textBox.height = 189
127 | textBox.textX = textBox.x + 40
128 | textBox.textY = textBox.y + 44
129 | textBox.font = fonts.pickup
130 | textBox.visible = true
131 | end
132 |
133 | if type == "intro" then
134 | textBox.x = -100
135 | textBox.y = -100
136 | textBox.width = gameWidth * scale + 200
137 | textBox.height = gameHeight * scale + 200
138 | textBox.textX = 58
139 | textBox.textY = 104
140 | textBox.font = fonts.menu.intro
141 | textBox.visible = false
142 | end
143 |
144 | end
145 |
--------------------------------------------------------------------------------
/source/text/tutorial.lua:
--------------------------------------------------------------------------------
1 | -- This table controls the text that displays the controls
2 | tutorial = {}
3 | tutorial.active = false -- turns to true when the text should appear
4 | tutorial.alpha = 0 -- transparency of the text
5 | tutorial.speed = 2 -- number of seconds to appear/disappear
6 |
7 | function tutorial:update(dt)
8 |
9 | -- Don't do anything if the tutorial has already finished
10 | if gameState.tutorial == false then
11 | return
12 | end
13 |
14 | if self.active and self.alpha < 1 then
15 | self.alpha = self.alpha + (dt / self.speed)
16 | end
17 |
18 | if self.alpha > 1 then
19 | self.alpha = 1
20 | end
21 |
22 | if self.active == false and self.alpha > 0 then
23 | self.alpha = self.alpha - (dt / self.speed)
24 | end
25 |
26 | if self.active == false and self.alpha < 0 then
27 | self.alpha = 0
28 | gameState.tutorial = false
29 | end
30 |
31 | end
32 |
33 | function tutorial:draw()
34 |
35 | -- Don't draw anything if the tutorial has already finished
36 | if gameState.tutorial == false then
37 | return
38 | end
39 |
40 | local x = 768
41 | local y = 320
42 | local sectionW = 896
43 | love.graphics.setColor(1, 1, 1, self.alpha)
44 | love.graphics.setFont(fonts.misc.tutorial)
45 | love.graphics.printf("Use the arrow keys\nor to move", x, y, sectionW, "center")
46 | love.graphics.printf("Point with the mouse\nto look around", x, y + 224, sectionW, "center")
47 |
48 | end
49 |
50 | function tutorial:start()
51 |
52 | self.active = true
53 | self.alpha = 0.001
54 |
55 | end
56 |
--------------------------------------------------------------------------------
/source/ui/blackScreen.lua:
--------------------------------------------------------------------------------
1 | -- blackScreen is used for fading to black and adding darkness to the screen
2 |
3 | blackScreen = {}
4 | blackScreen.state = 0 -- 0 is stable, 1 is getting dark, -1 is getting lighter
5 | blackScreen.alpha = 0
6 | blackScreen.time = 1 -- time in seconds for the blackScreen fade/unfade
7 |
8 | -- blackScreen is also used for making the screen turn red when drowning
9 | blackScreen.red = false
10 | blackScreen.fullRedAlpha = 0.25
11 |
12 | function blackScreen:update(dt)
13 |
14 | if self.state ~= 0 then
15 | self.alpha = self.alpha + (self.state / self.time * dt)
16 | end
17 |
18 | if self.alpha < 0 then
19 | self.alpha = 0
20 | self.state = 0
21 | self.red = false
22 | end
23 |
24 | if self.alpha > 1 then
25 | self.alpha = 1
26 | self.state = 0
27 | end
28 |
29 | if self.red and self.alpha > self.fullRedAlpha then
30 | self.alpha = self.fullRedAlpha
31 | self.state = 0
32 | end
33 |
34 | end
35 |
36 | function blackScreen:draw()
37 |
38 | local red = 0
39 | if self.red then
40 | red = 1
41 | end
42 | love.graphics.setColor(red, 0, 0, self.alpha)
43 | love.graphics.rectangle("fill", -20, -20, (gameWidth + 40) * scale, (gameHeight + 40) * scale)
44 |
45 | end
46 |
47 | function blackScreen:fadeIn(t)
48 |
49 | self.alpha = 1
50 | self.state = -1
51 | self.time = t or 1
52 | self.red = false
53 |
54 | end
55 |
56 | function blackScreen:fadeOut(t)
57 |
58 | self.alpha = 0
59 | self.state = 1
60 | self.time = t or 1
61 | self.red = false
62 |
63 | end
64 |
65 | function blackScreen:setRed()
66 |
67 | self.alpha = 0
68 | self.state = 1
69 | self.time = 1
70 | self.red = true
71 |
72 | end
73 |
74 | function blackScreen:removeRed()
75 |
76 | self.state = -1
77 | self.time = 1
78 | self.red = true
79 |
80 | end
81 |
--------------------------------------------------------------------------------
/source/ui/cam.lua:
--------------------------------------------------------------------------------
1 | -- 0.5 * scale zooms the camera out. Start at player's position
2 | cam = Camera(player.physics:getX(), player.physics:getY(), 0.5*scale)
3 | cam.x = 0
4 | cam.y = 0
5 |
6 | function cam:update(dt)
7 |
8 | -- player has died, don't update the camera
9 | if player.state < 0 then
10 | return
11 | end
12 |
13 | local lookX = player.physics:getX()
14 | local lookY = player.physics:getY()
15 |
16 | -- Camera can't pan outside the room
17 | if mapdata.room ~= nil then
18 |
19 | -- Normally you would divide gameWidth by 2, but you don't here because
20 | -- the camera is zoomed out, so gameWidth/Height is "doubled"
21 |
22 | local leftBound = mapdata.room.x + gameWidth
23 | if lookX < leftBound then
24 | lookX = leftBound
25 | end
26 |
27 | local rightBound = mapdata.room.x + mapdata.room.width - gameWidth
28 | if lookX > rightBound then
29 | lookX = rightBound
30 | end
31 |
32 | local upperBound = mapdata.room.y + gameHeight
33 | if lookY < upperBound then
34 | lookY = upperBound
35 | end
36 |
37 | local lowerBound = mapdata.room.y + mapdata.room.height - gameHeight
38 | if lookY > lowerBound then
39 | lookY = lowerBound
40 | end
41 |
42 | end
43 |
44 | cam:lookAt(lookX, lookY)
45 |
46 | -- cam.x and cam.y keep track of where the camera is located
47 | -- the lookAt value may be moved if a screenshake is happening, so these
48 | -- values know where the camera should be, regardless of lookAt
49 | cam.x = lookX
50 | cam.y = lookY
51 | end
52 |
--------------------------------------------------------------------------------
/source/ui/flash.lua:
--------------------------------------------------------------------------------
1 | -- Flashes cause the screen to go white
2 | -- Similar to blackScreen, but needs to be its own class
3 |
4 | flash = {}
5 | flash.state = 0 -- 0 is stable, 1 is getting dark, -1 is getting lighter
6 | flash.alpha = 0
7 | flash.time = 1 -- time in seconds for the flash fade/unfade
8 |
9 | -- if this is true, the white screen fades in and immediately out
10 | flash.flashing = false
11 |
12 | function flash:update(dt)
13 |
14 | if self.state ~= 0 then
15 | self.alpha = self.alpha + (self.state / self.time * dt)
16 | end
17 |
18 | if self.alpha < 0 then
19 | self.alpha = 0
20 | self.state = 0
21 | self.flashing = false
22 | end
23 |
24 | if self.alpha > 1 then
25 | self.alpha = 1
26 | self.state = 0
27 |
28 | if self.flashing then
29 | self.state = -1
30 | end
31 | end
32 |
33 | end
34 |
35 | function flash:draw()
36 |
37 | love.graphics.setColor(1, 1, 1, self.alpha)
38 | love.graphics.rectangle("fill", -20, -20, (gameWidth + 40) * scale, (gameHeight + 40) * scale)
39 |
40 | end
41 |
42 | function flash:fadeIn(t)
43 |
44 | self.alpha = 1
45 | self.state = -1
46 | self.time = t or 1
47 |
48 | end
49 |
50 | function flash:fadeOut(t)
51 |
52 | self.alpha = 0
53 | self.state = 1
54 | self.time = t or 1
55 |
56 | end
57 |
58 | function flash:flash(t)
59 |
60 | self.flashing = true
61 | flash:fadeOut(t)
62 |
63 | end
64 |
--------------------------------------------------------------------------------
/source/ui/mainMenu.lua:
--------------------------------------------------------------------------------
1 | -- Stores all clickable buttons on the main menu
2 | -- By index: X, Y, Width, Height, Message
3 | buttons = {}
4 | buttons[1] = {376, 380, 360, 72, "New Game"}
5 | buttons[2] = {376, 476, 360, 72, "Continue"}
6 |
7 | -- Dimensions and offset for the small corner buttons (sound and GitHub)
8 | local smSize = 72
9 | local smOffset = 14
10 |
11 | -- Place buttons 3 and 4 in the left and right corners respectively
12 | buttons[3] = {smOffset, gameHeight - smSize - smOffset, smSize, smSize, ".sound"}
13 | buttons[4] = {gameWidth - smSize - smOffset, gameHeight - smSize - smOffset, smSize, smSize, ".github"}
14 |
15 | -- This value stores the message displayed at the bottom of the menu
16 | buttons.message = ""
17 |
18 | -- This function draws everything on the Main Menu
19 | function menuDraw()
20 |
21 | if gameState.room == "rmMainMenu" then
22 |
23 | love.graphics.setFont(fonts.menu.title)
24 | love.graphics.setColor(1, 1, 1, 1)
25 | love.graphics.printf("CAVERN", 0, 140 * scale, gameWidth * scale, "center")
26 |
27 | -- Start message off as nothing, will be updated if hovering over a button
28 | buttons.message = ""
29 |
30 | for _,b in ipairs(buttons) do
31 |
32 | -- Get attributes stored for the current button
33 | local bX = b[1] * scale;
34 | local bY = b[2] * scale;
35 | local bW = b[3] * scale;
36 | local bH = b[4] * scale;
37 | local bText = b[5];
38 |
39 | if buttons:mouseCheck(b) then -- if the mouse is over the button...
40 |
41 | -- Button border
42 | -- love.graphics.setColor(0.384, 0.604, 0.475) -- enemy color
43 | love.graphics.setColor(1, 1, 1) -- white
44 | love.graphics.setLineWidth(6)
45 | love.graphics.rectangle("line", bX, bY, bW, bH)
46 |
47 | -- Update the button message at the bottom of the screen
48 | if bText == "New Game" then
49 | buttons.message = "Start a new game - erases old save file"
50 | elseif bText == "Continue" then
51 | buttons.message = "Continue from where you left off"
52 | elseif bText == ".sound" then
53 | buttons.message = "Turn music and sound effects on or off"
54 | elseif bText == ".github" then
55 | buttons.message = "View the code on GitHub"
56 | end
57 |
58 | end
59 |
60 | love.graphics.setColor(1, 1, 1, 1)
61 | love.graphics.setFont(fonts.menu.button)
62 |
63 | if bText == ".sound" then
64 | if not soundOn then
65 | love.graphics.setColor(0.35, 0.35, 0.35, 0.5)
66 | end
67 | love.graphics.draw(sprites.ui.sound, bX + 15 * scale, bY + 9 * scale, 0, scale, scale)
68 | elseif bText == ".github" then
69 | love.graphics.draw(sprites.ui.github, bX + 9 * scale, bY + 8 * scale, 0, scale, scale)
70 | else
71 | love.graphics.printf(bText, bX, bY + 8 * scale, bW, "center")
72 | end
73 |
74 | end
75 |
76 | end
77 |
78 | love.graphics.setColor(1, 1, 1, 1)
79 | love.graphics.setFont(fonts.menu.message)
80 | love.graphics.printf(buttons.message, 0, love.graphics.getHeight() - 80, love.graphics.getWidth(), "center")
81 |
82 | end
83 |
84 | -- Check if the mouse is inside the passed button
85 | function buttons:mouseCheck(b)
86 |
87 | -- Get mouse coordinates
88 | local mx, my = love.mouse.getPosition()
89 |
90 | -- Get attributes stored for the passed button
91 | local bX = b[1] * scale;
92 | local bY = b[2] * scale;
93 | local bW = b[3] * scale;
94 | local bH = b[4] * scale;
95 |
96 | -- Compare coordinates to see if mouse is inside button
97 | if mx > bX and mx < bX+bW and my > bY and my < bY+bH then
98 | return true
99 | end
100 |
101 | return false
102 |
103 | end
104 |
105 | -- Called whenever the left mouse button is clicked
106 | -- Checks if it clicked on a button, and does what that
107 | -- button needs to do
108 | function buttons:click()
109 |
110 | for i,b in ipairs(self) do
111 |
112 | -- If the mouse is on the current button...
113 | if buttons:mouseCheck(b) then
114 |
115 | -- The button has been clicked
116 |
117 | if i == 1 then -- New Game button
118 |
119 | -- This is the state for new game sequence
120 | intro.state = 1
121 | intro.timer = 1
122 | buttons.message = ""
123 | soundManager:musicFade()
124 | changeToMap("rmIntro")
125 |
126 | elseif i == 2 then -- Continue button
127 |
128 | -- This is the state for intro's load sequence
129 | intro.state = 100
130 | intro.timer = 1.5
131 | buttons.message = ""
132 | soundManager:musicFade()
133 | changeToMap("rmIntro")
134 |
135 | elseif i == 3 then -- Sound button
136 |
137 | -- Toggle sound to be on/off
138 | soundOn = not soundOn
139 | if soundOn then
140 | soundManager:startMusic("menu")
141 | else
142 | soundManager:musicFade()
143 | end
144 |
145 | elseif i == 4 then -- GitHub button
146 |
147 | -- Open the GitHub page for this game!
148 | love.system.openURL("https://github.com/kyleschaub/cavern")
149 |
150 | end
151 |
152 | soundManager:play("click")
153 |
154 | end
155 |
156 | end
157 |
158 | end
159 |
--------------------------------------------------------------------------------
/source/update.lua:
--------------------------------------------------------------------------------
1 | local function updateGameplay(dt)
2 |
3 | -- Update the main physics world
4 | world:update(dt)
5 |
6 | -- Update the gravity physics world
7 | gravWorld:update(dt)
8 |
9 | -- Update trail table (must be done before weapons are updated)
10 | trails:update(dt)
11 |
12 | -- Update the player
13 | player:update(dt)
14 |
15 | -- Update all weapons
16 | weapons:update(dt)
17 |
18 | -- Update all enemies
19 | enemies:update(dt)
20 |
21 | -- Update all pickup objects in the current map
22 | pickups:update(dt)
23 |
24 | -- Update the saveUtil (for the saveMessage)
25 | saveUtil:update(dt)
26 |
27 | -- Update camera
28 | cam:update(dt)
29 |
30 | -- Update background (for parallax)
31 | background:update(dt)
32 |
33 | -- Update the screen-shake (called right after cam:update)
34 | shake:update(dt)
35 |
36 | -- Update damage text
37 | damages:update(dt)
38 |
39 | -- Update all particles
40 | particles:update(dt)
41 |
42 | -- Update all blasts
43 | blasts:update(dt)
44 |
45 | -- Update water ripple animations
46 | ripples:update(dt)
47 |
48 | -- Update projectiles shot by spike enemies
49 | spikes:update(dt)
50 |
51 | -- Update enemy projectiles
52 | enemyProjectiles:update(dt)
53 |
54 | -- Update eggs from the final boss
55 | eggs:update(dt)
56 |
57 | -- Update the vines that come up from the ground
58 | vines:update(dt)
59 |
60 | -- Update fire particles
61 | fires:update(dt)
62 |
63 | -- Update tweens
64 | flux.update(dt)
65 |
66 | -- Update to remove dead breakable walls
67 | breakables:update(dt)
68 |
69 | -- Update the blackScreen
70 | blackScreen:update(dt)
71 |
72 | -- Update the Sound Manager
73 | soundManager:update(dt)
74 |
75 | -- Update the flash
76 | flash:update(dt)
77 |
78 | -- Handle trail fade-away (when its weapon is destroyed)
79 | trails:fadeOut(dt)
80 |
81 | -- Update the tutorial
82 | tutorial:update(dt)
83 |
84 | -- Update the intro sequence (if necessary)
85 | intro:update(dt)
86 |
87 | end
88 |
89 | return updateGameplay
90 |
--------------------------------------------------------------------------------
/sprites/LICENSE.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/LICENSE.txt
--------------------------------------------------------------------------------
/sprites/enemies/bigBossEye.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/enemies/bigBossEye.png
--------------------------------------------------------------------------------
/sprites/enemies/bossBody.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/enemies/bossBody.png
--------------------------------------------------------------------------------
/sprites/enemies/egg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/enemies/egg.png
--------------------------------------------------------------------------------
/sprites/enemies/evilBubble.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/enemies/evilBubble.png
--------------------------------------------------------------------------------
/sprites/enemies/flyerBody.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/enemies/flyerBody.png
--------------------------------------------------------------------------------
/sprites/enemies/flyerEye.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/enemies/flyerEye.png
--------------------------------------------------------------------------------
/sprites/enemies/flyerWing1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/enemies/flyerWing1.png
--------------------------------------------------------------------------------
/sprites/enemies/flyerWing2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/enemies/flyerWing2.png
--------------------------------------------------------------------------------
/sprites/enemies/spikeBody.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/enemies/spikeBody.png
--------------------------------------------------------------------------------
/sprites/enemies/spikeProj.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/enemies/spikeProj.png
--------------------------------------------------------------------------------
/sprites/enemies/starfish.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/enemies/starfish.png
--------------------------------------------------------------------------------
/sprites/environment/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/bg.png
--------------------------------------------------------------------------------
/sprites/environment/breakParticle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/breakParticle.png
--------------------------------------------------------------------------------
/sprites/environment/crack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/crack.png
--------------------------------------------------------------------------------
/sprites/environment/cracks/crack1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/cracks/crack1.png
--------------------------------------------------------------------------------
/sprites/environment/cracks/crack2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/cracks/crack2.png
--------------------------------------------------------------------------------
/sprites/environment/cracks/crack3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/cracks/crack3.png
--------------------------------------------------------------------------------
/sprites/environment/rockySurface.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/rockySurface.png
--------------------------------------------------------------------------------
/sprites/environment/rockySurface2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/rockySurface2.png
--------------------------------------------------------------------------------
/sprites/environment/rockySurface_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/rockySurface_white.png
--------------------------------------------------------------------------------
/sprites/environment/sinkingWall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/sinkingWall.png
--------------------------------------------------------------------------------
/sprites/environment/vine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/vine.png
--------------------------------------------------------------------------------
/sprites/environment/wall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/wall.png
--------------------------------------------------------------------------------
/sprites/environment/wall2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/wall2.png
--------------------------------------------------------------------------------
/sprites/environment/wall_old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/wall_old.png
--------------------------------------------------------------------------------
/sprites/environment/waterSheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/environment/waterSheet.png
--------------------------------------------------------------------------------
/sprites/fire/fire_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/fire/fire_1.png
--------------------------------------------------------------------------------
/sprites/fire/fire_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/fire/fire_2.png
--------------------------------------------------------------------------------
/sprites/fire/fire_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/fire/fire_3.png
--------------------------------------------------------------------------------
/sprites/fire/fire_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/fire/fire_4.png
--------------------------------------------------------------------------------
/sprites/fire/fire_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/fire/fire_5.png
--------------------------------------------------------------------------------
/sprites/items/aquaPack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/items/aquaPack.png
--------------------------------------------------------------------------------
/sprites/items/blaster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/items/blaster.png
--------------------------------------------------------------------------------
/sprites/items/healthPickup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/items/healthPickup.png
--------------------------------------------------------------------------------
/sprites/items/itemPickup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/items/itemPickup.png
--------------------------------------------------------------------------------
/sprites/items/pickup_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/items/pickup_back.png
--------------------------------------------------------------------------------
/sprites/items/rocketLauncher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/items/rocketLauncher.png
--------------------------------------------------------------------------------
/sprites/items/spearGun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/items/spearGun.png
--------------------------------------------------------------------------------
/sprites/newPlayer/aquapack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/aquapack.png
--------------------------------------------------------------------------------
/sprites/newPlayer/arm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/arm.png
--------------------------------------------------------------------------------
/sprites/newPlayer/armBlaster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/armBlaster.png
--------------------------------------------------------------------------------
/sprites/newPlayer/armRocket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/armRocket.png
--------------------------------------------------------------------------------
/sprites/newPlayer/armSpear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/armSpear.png
--------------------------------------------------------------------------------
/sprites/newPlayer/armSpearLoaded.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/armSpearLoaded.png
--------------------------------------------------------------------------------
/sprites/newPlayer/backArm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/backArm.png
--------------------------------------------------------------------------------
/sprites/newPlayer/backArm_old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/backArm_old.png
--------------------------------------------------------------------------------
/sprites/newPlayer/body.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/body.png
--------------------------------------------------------------------------------
/sprites/newPlayer/helmet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/helmet.png
--------------------------------------------------------------------------------
/sprites/newPlayer/jetpack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/jetpack.png
--------------------------------------------------------------------------------
/sprites/newPlayer/newPlayer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/newPlayer.png
--------------------------------------------------------------------------------
/sprites/newPlayer/spear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer/spear.png
--------------------------------------------------------------------------------
/sprites/newPlayer2/arm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer2/arm.png
--------------------------------------------------------------------------------
/sprites/newPlayer2/armBlaster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer2/armBlaster.png
--------------------------------------------------------------------------------
/sprites/newPlayer2/armRocket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer2/armRocket.png
--------------------------------------------------------------------------------
/sprites/newPlayer2/armSpear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer2/armSpear.png
--------------------------------------------------------------------------------
/sprites/newPlayer2/armSpearLoaded.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer2/armSpearLoaded.png
--------------------------------------------------------------------------------
/sprites/newPlayer2/backArm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer2/backArm.png
--------------------------------------------------------------------------------
/sprites/newPlayer2/body.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer2/body.png
--------------------------------------------------------------------------------
/sprites/newPlayer2/helmet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/newPlayer2/helmet.png
--------------------------------------------------------------------------------
/sprites/player/aquapack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/aquapack.png
--------------------------------------------------------------------------------
/sprites/player/armSpear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/armSpear.png
--------------------------------------------------------------------------------
/sprites/player/arm_blaster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/arm_blaster.png
--------------------------------------------------------------------------------
/sprites/player/arm_blaster2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/arm_blaster2.png
--------------------------------------------------------------------------------
/sprites/player/arm_empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/arm_empty.png
--------------------------------------------------------------------------------
/sprites/player/body.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/body.png
--------------------------------------------------------------------------------
/sprites/player/bomb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/bomb.png
--------------------------------------------------------------------------------
/sprites/player/helmet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/helmet.png
--------------------------------------------------------------------------------
/sprites/player/jetpack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/jetpack.png
--------------------------------------------------------------------------------
/sprites/player/rocketLauncher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/rocketLauncher.png
--------------------------------------------------------------------------------
/sprites/player/rocketLauncherAlt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/rocketLauncherAlt.png
--------------------------------------------------------------------------------
/sprites/player/spear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/player/spear.png
--------------------------------------------------------------------------------
/sprites/ui/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/ui/github.png
--------------------------------------------------------------------------------
/sprites/ui/sound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/challacade/cavern/0d190d6099dfdf47d871d74790d41fae414024f6/sprites/ui/sound.png
--------------------------------------------------------------------------------