Compare commits

..

160 Commits
shop ... master

Author SHA1 Message Date
rsninja722
930696783d done 2021-04-26 20:19:13 -04:00
rsninja722
60d6511035 tutorial 2021-04-26 20:18:29 -04:00
rsninja722
0abe049061 Merge remote-tracking branch 'origin/master' into assets 2021-04-26 20:02:46 -04:00
rsninja722
1401094b13 balancing 2021-04-26 19:51:49 -04:00
2591cb85de Merge branch 'master' of github.com:Ewpratten/ludum-dare-48 2021-04-26 19:45:05 -04:00
2653c9e127 loading logo 2021-04-26 19:44:55 -04:00
wm-c
24fae60c4c tutorial images 2021-04-26 19:41:52 -04:00
rsninja722
3ba8505104
Merge pull request #48 from Ewpratten/push-mob
Push mob
2021-04-26 18:44:22 -04:00
wm-c
6c22f05a30 Merge branch 'master' into push-mob 2021-04-26 18:25:39 -04:00
wm-c
67899182a4 Pufferfish added 2021-04-26 18:24:16 -04:00
8753436eb9
Merge pull request #47 from Ewpratten/assets
map and more assets
2021-04-26 18:19:52 -04:00
b9ea9b9a78 Merge branch 'assets' of github.com:Ewpratten/ludum-dare-48 into assets 2021-04-26 17:20:36 -04:00
8b7ee82387 deref mut 2021-04-26 17:20:28 -04:00
rsninja722
ce2e028c72 balancing, stated audio 2021-04-26 17:16:43 -04:00
wm-c
1494b188c8 Merge branch 'assets' into push-mob 2021-04-26 16:28:56 -04:00
wm-c
086eb1d86f Merge branch 'master' into push-mob 2021-04-26 16:23:59 -04:00
wm-c
b765513075 Expanding pufferfish 2021-04-26 16:23:33 -04:00
rsninja722
0d453ed708 music 2021-04-26 16:15:27 -04:00
rsninja722
eed8f324af enemies placed, suck range decreased 2021-04-26 15:20:47 -04:00
a271875d73
Merge pull request #45 from Ewpratten/rename
Rename and README
2021-04-26 14:57:51 -04:00
c5cc13f9e7 5by4 2021-04-26 14:57:21 -04:00
rsninja722
0a69b80b34 pufferfish, started real map 2021-04-26 14:51:37 -04:00
055ac10ca0 round readme 2021-04-26 14:51:23 -04:00
f81920162d the 2021-04-26 14:46:15 -04:00
003f27a06e less colon 2021-04-26 14:45:37 -04:00
22fd05f13b more links 2021-04-26 14:45:12 -04:00
4c113824c7 raylib-rs link 2021-04-26 14:43:39 -04:00
eed3f3d752 renaming and readme 2021-04-26 14:42:23 -04:00
e9583a7266 tweak the transponder win distance 2021-04-26 14:06:13 -04:00
wm-c
38e63414b0 Merge branch 'master' into push-mob 2021-04-26 14:05:25 -04:00
wm-c
e8a00b9907 pufferfish handling stub 2021-04-26 14:03:36 -04:00
wm-c
cf64fb4851 added pufferfish mob 2021-04-26 14:02:02 -04:00
rsninja722
53a746c551 Merge branch 'master' into assets 2021-04-26 13:59:55 -04:00
rsninja722
3fbb50f985
Merge pull request #44 from Ewpratten/whirlpooltweaks
added velocity with friction, translucent shop back
2021-04-26 13:59:25 -04:00
rsninja722
eb49b08bab added velocity with friction, translucent shop back 2021-04-26 13:26:58 -04:00
6b77476d09 remove useless audio update 2021-04-26 12:35:54 -04:00
f9091fbce2 fix segfault on quit 2021-04-26 12:35:12 -04:00
3da8bb06ed
Merge pull request #43 from Ewpratten/suck-monster
Suck monster
2021-04-25 21:36:26 -04:00
wm-c
3c1d56a46b Added comments 2021-04-25 20:58:34 -04:00
wm-c
3007f96ca4 Whirlpools have spirites 2021-04-25 20:49:48 -04:00
wm-c
1928d0d292 Merge branch 'master' into suck-monster 2021-04-25 20:49:30 -04:00
wm-c
dfeb6dfbf7 Merge branch 'assets' into suck-monster 2021-04-25 20:20:09 -04:00
wm-c
c8da93bfdb added whirlpool 2021-04-25 20:18:11 -04:00
rsninja722
0c9005939c some sounds 2021-04-25 19:53:44 -04:00
a32c20fdee
Merge pull request #42 from Ewpratten/reduce_warnings
Wrangle warnings, and speed up compile time
2021-04-25 19:47:29 -04:00
e4ae9b18b3 Clean up deps 2021-04-25 19:22:46 -04:00
d729ae811e Reset title 2021-04-25 19:16:10 -04:00
733839ca10 clean up the rest of the files 2021-04-25 19:15:02 -04:00
5e1ae73b2b more warning removal 2021-04-25 19:12:37 -04:00
7edfe6f132 clean root ingame 2021-04-25 19:09:28 -04:00
068c8be089 rm complexanimation 2021-04-25 19:08:42 -04:00
8c8d0ab595 Clean jellyfish 2021-04-25 19:08:16 -04:00
602f600379 clean octopus 2021-04-25 19:07:48 -04:00
fdb93c03fd clean unused code for fish 2021-04-25 19:07:10 -04:00
rsninja722
b24c09fde3 whirlpool (rotate texture in code) 2021-04-25 18:40:54 -04:00
rsninja722
a8884757f5 Merge remote-tracking branch 'origin/master' into assets 2021-04-25 18:39:53 -04:00
a74ba44e77
Merge pull request #41 from Ewpratten/shader2
Wave shading
2021-04-25 18:27:33 -04:00
2758d0de55 make the waves less wavy 2021-04-25 18:27:05 -04:00
5497d8e279 Only load shader memory location once 2021-04-25 18:22:55 -04:00
dbc620aa03 Big fancy shaders 2021-04-25 18:15:45 -04:00
86603bf729 pixelate the wave shader 2021-04-25 17:57:34 -04:00
07d678ffba working on wavs 2021-04-25 17:25:48 -04:00
d2a14ce650
Merge pull request #40 from Ewpratten/assets
Background
2021-04-25 17:08:37 -04:00
rsninja722
0b1cd6c678 Merge remote-tracking branch 'origin/assets' into assets 2021-04-25 17:07:16 -04:00
rsninja722
b85c61d532 cave background 2021-04-25 17:07:04 -04:00
e759e0ddbe fix transponder positioning 2021-04-25 16:48:55 -04:00
2014ad42c8
Merge pull request #39 from Ewpratten/assets
Assets
2021-04-25 16:38:12 -04:00
wm-c
ed70f6ac8c Merge branch 'master' into assets 2021-04-25 16:28:22 -04:00
wm-c
8411b60c5c Added transponder 2021-04-25 16:27:07 -04:00
rsninja722
5f2ad0b87c Merge branch 'master' into assets 2021-04-25 16:25:55 -04:00
b954e79287
Merge pull request #37 from Ewpratten/assets
Assets
2021-04-25 16:21:36 -04:00
4adadbe8cd
Merge pull request #38 from Ewpratten/readme
Readme
2021-04-25 16:19:38 -04:00
fcf8226f65 templating 2021-04-25 16:17:09 -04:00
1ec856e1f5 badge urls 2021-04-25 16:13:38 -04:00
22dc823f17 Badges 2021-04-25 16:12:37 -04:00
afe9d64c2f readme image 2021-04-25 16:09:38 -04:00
wm-c
f1a071f5da Merge branch 'master' into assets 2021-04-25 16:06:50 -04:00
623b6dbad3 Rename the game 2021-04-25 16:06:25 -04:00
wm-c
41c1ba7774 Merge branch 'assets' of github.com:Ewpratten/ludum-dare-48 into assets 2021-04-25 16:05:48 -04:00
wm-c
e11686fbe0 added item assests 2021-04-25 16:05:38 -04:00
f54a88cfd5 remove savestate from assets 2021-04-25 15:50:47 -04:00
9bd4cb4122
Merge pull request #36 from Ewpratten/octopus_asset
Octopus asset
2021-04-25 15:45:02 -04:00
b332f7ffb8 animate octopus 2021-04-25 15:44:42 -04:00
2fec1a9765 Merge remote-tracking branch 'origin/assets' into octopus_asset 2021-04-25 15:36:50 -04:00
dda941ba3a
Merge pull request #35 from Ewpratten/shaders
Use a pixel shader
2021-04-25 15:32:11 -04:00
1534883d78 Pixel shading 2021-04-25 15:31:02 -04:00
rsninja722
a4448775a7 octopus 2021-04-25 15:22:39 -04:00
a5614c54b2
Merge pull request #34 from Ewpratten/outofbreathscreen
added win screen
2021-04-25 15:14:44 -04:00
wm-c
4073923edf Merge branch 'master' into assets 2021-04-25 15:14:01 -04:00
25c98de3ba fix pause menu graphics 2021-04-25 15:13:29 -04:00
wm-c
976b230986 resources loading 2021-04-25 15:12:26 -04:00
d985cb2fad fix texture trait 2021-04-25 15:10:42 -04:00
be91f70053 blooooom 2021-04-25 15:06:27 -04:00
810ebc72e1 Rendering with shaders 2021-04-25 14:47:33 -04:00
rsninja722
bc8d03a261 Merge remote-tracking branch 'origin/master' into assets 2021-04-25 14:38:43 -04:00
rsninja722
ccfa199f13 items + transponder 2021-04-25 14:38:29 -04:00
e1d9100613 stop player from wasting money 2021-04-25 14:30:18 -04:00
wm-c
74ba0ffab9 Merge branch 'master' into outofbreathscreen 2021-04-25 14:27:28 -04:00
wm-c
e809f9838e added win screen 2021-04-25 14:24:17 -04:00
8e8cf7bf4c raylib logo 2021-04-25 13:51:41 -04:00
10949850f6
Merge pull request #32 from Ewpratten/assets
Assets
2021-04-25 13:48:34 -04:00
c91d82080a
Merge pull request #33 from Ewpratten/backgrounds
add backgrounds to menus
2021-04-25 13:48:17 -04:00
319405e761 add backgrounds to menus 2021-04-25 13:48:00 -04:00
rsninja722
3b9eebbb3c fish fix, less js 2021-04-25 13:45:50 -04:00
2f1a275465
Merge pull request #31 from Ewpratten/outofbreathscreen
Death screen added
2021-04-25 13:43:55 -04:00
846b75d6e5 Merge remote-tracking branch 'origin/master' into outofbreathscreen 2021-04-25 13:43:35 -04:00
wm-c
fcaeee4877 Death screen added 2021-04-25 13:37:23 -04:00
rsninja722
c5f716cca1 Merge branch 'master' into assets 2021-04-25 13:36:58 -04:00
c207966b75
Merge pull request #30 from Ewpratten/fish_fix
Fish fix
2021-04-25 13:34:29 -04:00
50baf53b9e fix fish direction 2021-04-25 13:33:10 -04:00
e7cd020666 Merge remote-tracking branch 'origin/assets' into fish_fix 2021-04-25 13:30:17 -04:00
c4fec8c835 Merge remote-tracking branch 'origin/master' into fish_fix 2021-04-25 13:29:29 -04:00
021e597747
Merge pull request #29 from Ewpratten/item_impl
Implement items
2021-04-25 13:28:59 -04:00
rsninja722
67d76dab08 fish?? 2021-04-25 13:28:37 -04:00
2d6d1f3629 air bag impl 2021-04-25 13:28:34 -04:00
da3b52e395 Add flipper logic 2021-04-25 13:25:05 -04:00
97449cbc44
Merge pull request #28 from Ewpratten/shop_tooltip
tooltips in the shop
2021-04-25 13:20:19 -04:00
rsninja722
b1a1b2e105 fish? 2021-04-25 13:15:06 -04:00
53b3a33b4e Store tooltips 2021-04-25 13:15:05 -04:00
1abc80be64 placeholder item rendering 2021-04-25 13:06:48 -04:00
5d019bf39f
Merge pull request #27 from Ewpratten/shop2
Merge shop code with master, while doing a bunch of UI updates
2021-04-25 12:45:06 -04:00
cc4affdbd1 remove player extra coins 2021-04-25 12:39:53 -04:00
7979295f7a Add world resetting and state saving 2021-04-25 12:39:10 -04:00
7dac0bbfd1 user can now buy items! 2021-04-25 12:23:46 -04:00
a88cae2875 buy button 2021-04-25 11:55:35 -04:00
82d5b336c6 Full game navigation 2021-04-25 11:51:38 -04:00
289befc802 much improved item buy button rendering 2021-04-25 11:45:54 -04:00
5bb8261119 Item wrapper 2021-04-25 11:37:53 -04:00
0d862760bd item trait 2021-04-25 11:26:25 -04:00
198edc6556 more items 2021-04-25 11:17:48 -04:00
ad5d6bc1ab background image 2021-04-25 11:12:37 -04:00
560329fa2b Merge remote-tracking branch 'origin/assets' into shop2 2021-04-25 11:10:01 -04:00
3a2caecfff working on buy buttons 2021-04-25 11:09:38 -04:00
rsninja722
a4f2ee84d4 shop background + tile map editor 2021-04-25 10:49:39 -04:00
rsninja722
9035b1d872 Merge branch 'master' into assets 2021-04-25 10:47:20 -04:00
010ad7ce26 move shop file 2021-04-25 10:42:03 -04:00
fa7040f626 Shop screen layout 2021-04-25 10:38:41 -04:00
eff489d9ff Adding UI boxes 2021-04-25 10:33:11 -04:00
83aaa01133 button 2021-04-25 10:18:23 -04:00
1bc5aa966d improved menu navigation 2021-04-25 10:05:35 -04:00
bed3f407d6 tmp disable shop code 2021-04-25 10:01:02 -04:00
2a24d45cce Pull in shop logic
Co-authored-by: wm-c <wcmeathrel@gmail.com>
2021-04-25 09:58:43 -04:00
a68d3e8e70 lvl3 flippers
Co-authored-by: wm-c <wcmeathrel@gmail.com>
2021-04-25 09:54:23 -04:00
6c288e9178 Add flashlight
Co-authored-by: wm-c <wcmeathrel@gmail.com>
2021-04-25 09:53:56 -04:00
73ac4d25a9 lvl3 stun gun
Co-authored-by: wm-c <wcmeathrel@gmail.com>
2021-04-25 09:53:24 -04:00
c4dd17f25d Add airbag item data
Co-authored-by: wm-c <wcmeathrel@gmail.com>
2021-04-25 09:52:15 -04:00
7452803ba9 Create a base screen for the shop 2021-04-25 09:49:16 -04:00
43c60c6dfc
Merge pull request #26 from Ewpratten/darkness
Make the world dark, using an image now!
2021-04-25 09:34:27 -04:00
8b15d8fcaf Better darkness logic 2021-04-25 09:33:32 -04:00
f67b654088 proper background rendering with scale 2021-04-25 09:23:31 -04:00
0c4928b892 dark png 2021-04-24 21:45:54 -04:00
dcbe3eab2e
Merge pull request #22 from Ewpratten/darkness
darkness is taking over
2021-04-24 21:16:14 -04:00
43bcfe8623 darkness is taking over 2021-04-24 21:15:57 -04:00
d972238574 Main menu button fixes 2021-04-24 20:49:35 -04:00
ff36b1c2c3
Merge pull request #21 from Ewpratten/octopus
Octopus logic
2021-04-24 20:35:34 -04:00
3d29a971cf octobreath 2021-04-24 20:33:28 -04:00
c5a2877efd suck air bubble animation 2021-04-24 20:07:19 -04:00
cf342aa932 octopus movement 2021-04-24 19:39:09 -04:00
1585521fe3 background gradient 2021-04-24 19:09:28 -04:00
561bb5abf5
Merge pull request #20 from Ewpratten/ouchies
Add jellyfish as an enemy
2021-04-24 19:03:58 -04:00
110 changed files with 3826 additions and 823 deletions

3
.gitignore vendored
View File

@ -15,4 +15,5 @@ Cargo.lock
/target
.project
.project
savestate.json

View File

@ -1,5 +1,5 @@
[package]
name = "one-breath"
name = "ldgame"
version = "0.1.0"
authors = ["Evan Pratten <ewpratten@gmail.com>"]
edition = "2018"
@ -13,9 +13,4 @@ serialstudio = "0.1.0"
serde = "1.0.125"
serde_json = "1.0.64"
failure = "0.1.8"
parry2d = "0.4.0"
log = "0.4.14"
env_logger = "0.8.3"
nalgebra = "0.26.1"
rand = "0.8.3"
tiled = "0.9.4"

View File

@ -1,8 +1,19 @@
# ludum-dare-48
<img src="./assets/img/logos/readme.png" width="100%">
# Deep Breath
[![Build](https://github.com/Ewpratten/ludum-dare-48/actions/workflows/build.yml/badge.svg)](https://github.com/Ewpratten/ludum-dare-48/actions/workflows/build.yml)
[![Bundle](https://github.com/Ewpratten/ludum-dare-48/actions/workflows/bundle.yml/badge.svg)](https://github.com/Ewpratten/ludum-dare-48/actions/workflows/bundle.yml)
[![Ludum Dare 48](https://img.shields.io/badge/Ludum%20Dare-48-orange)](https://ldjam.com/events/ludum-dare/48/$236526)
[![Rust 1.51](https://img.shields.io/badge/Rust-1.51-orange)](https://www.rust-lang.org/)
[![Made with Raylib](https://img.shields.io/badge/Made%20With-raylib-blue)](https://www.raylib.com/)
**Deep Breath** is an exploration game where you explore an underwater cave in hopes of finding your lost transponder. Items and upgrades can be acquired along the way to assist your search.
This game was written in [Rust](https://www.rust-lang.org/), on top of [Rust bindings](https://github.com/deltaphc/raylib-rs) to the [`raylib`](https://github.com/raysan5/raylib) graphics library. For most of the team, this has been our first big Rust project.
This has been our second game produced for Ludum Dare. Check out the first [here](https://ldjam.com/events/ludum-dare/46/micromanaged-mike).
## Development Resources
@ -21,6 +32,10 @@ Core libraries:
- [`serde`](https://serde.rs/)
- [`serialstudio-rs`](https://github.com/Ewpratten/serialstudio-rs)
Sound Samples:
- [JavierZumer](https://freesound.org/people/JavierZumer/sounds/257236/)
- [Noted451](https://freesound.org/people/Noted451/sounds/531015/)
### VSCode Setup
If using VSCode, disable the `Rust` extension, and install everything in the **Workspace Recommendations** (You will see this list by searching `@recommended` in the extensions panel)

BIN
assets/audio/breath.mp3 Normal file

Binary file not shown.

BIN
assets/audio/die.mp3 Normal file

Binary file not shown.

BIN
assets/audio/fishPickup.mp3 Normal file

Binary file not shown.

BIN
assets/audio/shopSong.mp3 Normal file

Binary file not shown.

BIN
assets/audio/succ.mp3 Normal file

Binary file not shown.

BIN
assets/audio/swim1.mp3 Normal file

Binary file not shown.

BIN
assets/audio/swim2.mp3 Normal file

Binary file not shown.

BIN
assets/audio/swim3.mp3 Normal file

Binary file not shown.

BIN
assets/audio/swim4.mp3 Normal file

Binary file not shown.

BIN
assets/audio/swimSong.mp3 Normal file

Binary file not shown.

BIN
assets/audio/uiBuy.mp3 Normal file

Binary file not shown.

BIN
assets/audio/uiClick.mp3 Normal file

Binary file not shown.

Binary file not shown.

BIN
assets/audio/zap.mp3 Normal file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,66 @@
{ "frames": {
"octopus 0.aseprite": {
"frame": { "x": 0, "y": 0, "w": 20, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 },
"sourceSize": { "w": 20, "h": 20 },
"duration": 100
},
"octopus 1.aseprite": {
"frame": { "x": 20, "y": 0, "w": 20, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 },
"sourceSize": { "w": 20, "h": 20 },
"duration": 100
},
"octopus 2.aseprite": {
"frame": { "x": 40, "y": 0, "w": 20, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 },
"sourceSize": { "w": 20, "h": 20 },
"duration": 100
},
"octopus 3.aseprite": {
"frame": { "x": 60, "y": 0, "w": 20, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 },
"sourceSize": { "w": 20, "h": 20 },
"duration": 100
},
"octopus 4.aseprite": {
"frame": { "x": 80, "y": 0, "w": 20, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 },
"sourceSize": { "w": 20, "h": 20 },
"duration": 100
},
"octopus 5.aseprite": {
"frame": { "x": 100, "y": 0, "w": 20, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 },
"sourceSize": { "w": 20, "h": 20 },
"duration": 100
}
},
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.2.27-x64",
"image": "octopus.png",
"format": "RGBA8888",
"size": { "w": 120, "h": 20 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

View File

@ -0,0 +1,50 @@
{ "frames": {
"octopusSuck 0.aseprite": {
"frame": { "x": 0, "y": 0, "w": 30, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 30, "h": 20 },
"sourceSize": { "w": 30, "h": 20 },
"duration": 100
},
"octopusSuck 1.aseprite": {
"frame": { "x": 30, "y": 0, "w": 30, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 30, "h": 20 },
"sourceSize": { "w": 30, "h": 20 },
"duration": 100
},
"octopusSuck 2.aseprite": {
"frame": { "x": 60, "y": 0, "w": 30, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 30, "h": 20 },
"sourceSize": { "w": 30, "h": 20 },
"duration": 100
},
"octopusSuck 3.aseprite": {
"frame": { "x": 90, "y": 0, "w": 30, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 30, "h": 20 },
"sourceSize": { "w": 30, "h": 20 },
"duration": 100
}
},
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.2.27-x64",
"image": "octopusSuck.png",
"format": "RGBA8888",
"size": { "w": 120, "h": 20 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

View File

@ -0,0 +1,50 @@
{ "frames": {
"pufferFish 0.aseprite": {
"frame": { "x": 0, "y": 0, "w": 39, "h": 25 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 39, "h": 25 },
"sourceSize": { "w": 39, "h": 25 },
"duration": 100
},
"pufferFish 1.aseprite": {
"frame": { "x": 39, "y": 0, "w": 39, "h": 25 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 39, "h": 25 },
"sourceSize": { "w": 39, "h": 25 },
"duration": 100
},
"pufferFish 2.aseprite": {
"frame": { "x": 78, "y": 0, "w": 39, "h": 25 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 39, "h": 25 },
"sourceSize": { "w": 39, "h": 25 },
"duration": 100
},
"pufferFish 3.aseprite": {
"frame": { "x": 117, "y": 0, "w": 39, "h": 25 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 39, "h": 25 },
"sourceSize": { "w": 39, "h": 25 },
"duration": 100
}
},
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.2.27-x64",
"image": "pufferFishAttack.png",
"format": "RGBA8888",
"size": { "w": 156, "h": 25 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

View File

@ -0,0 +1,42 @@
{ "frames": {
"pufferFish 0.aseprite": {
"frame": { "x": 0, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
},
"pufferFish 1.aseprite": {
"frame": { "x": 19, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
},
"pufferFish 2.aseprite": {
"frame": { "x": 38, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
}
},
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.2.27-x64",
"image": "pufferFishBigIdle.png",
"format": "RGBA8888",
"size": { "w": 57, "h": 19 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

View File

@ -0,0 +1,50 @@
{ "frames": {
"pufferFish 0.aseprite": {
"frame": { "x": 0, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
},
"pufferFish 1.aseprite": {
"frame": { "x": 19, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
},
"pufferFish 2.aseprite": {
"frame": { "x": 38, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
},
"pufferFish 3.aseprite": {
"frame": { "x": 57, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
}
},
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.2.27-x64",
"image": "pufferFishExpand.png",
"format": "RGBA8888",
"size": { "w": 76, "h": 19 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

View File

@ -0,0 +1,66 @@
{ "frames": {
"pufferFish 0.aseprite": {
"frame": { "x": 0, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
},
"pufferFish 1.aseprite": {
"frame": { "x": 19, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
},
"pufferFish 2.aseprite": {
"frame": { "x": 38, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
},
"pufferFish 3.aseprite": {
"frame": { "x": 57, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
},
"pufferFish 4.aseprite": {
"frame": { "x": 76, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
},
"pufferFish 5.aseprite": {
"frame": { "x": 95, "y": 0, "w": 19, "h": 19 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 19, "h": 19 },
"sourceSize": { "w": 19, "h": 19 },
"duration": 100
}
},
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.2.27-x64",
"image": "pufferFish.png",
"format": "RGBA8888",
"size": { "w": 114, "h": 19 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

View File

@ -0,0 +1,51 @@
{ "frames": {
"whirlpool 0.aseprite": {
"frame": { "x": 0, "y": 0, "w": 20, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 },
"sourceSize": { "w": 20, "h": 20 },
"duration": 300
},
"whirlpool 1.aseprite": {
"frame": { "x": 20, "y": 0, "w": 20, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 },
"sourceSize": { "w": 20, "h": 20 },
"duration": 300
},
"whirlpool 2.aseprite": {
"frame": { "x": 40, "y": 0, "w": 20, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 },
"sourceSize": { "w": 20, "h": 20 },
"duration": 300
},
"whirlpool 3.aseprite": {
"frame": { "x": 60, "y": 0, "w": 20, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 },
"sourceSize": { "w": 20, "h": 20 },
"duration": 300
}
},
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.2.27-x64",
"image": "whirlpool.png",
"format": "RGBA8888",
"size": { "w": 80, "h": 20 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 2", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
assets/img/items/air1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

BIN
assets/img/items/air2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

BIN
assets/img/items/air3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

BIN
assets/img/items/stun1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

BIN
assets/img/items/stun2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

BIN
assets/img/items/stun3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
assets/img/logos/readme.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
assets/img/map/backBack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 323 KiB

BIN
assets/img/map/darkness.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

26
assets/img/map/fish.json Normal file
View File

@ -0,0 +1,26 @@
{ "frames": {
"fish 62.aseprite": {
"frame": { "x": 806, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 50
}
},
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.2.27-x64",
"image": "fish.png",
"format": "RGBA8888",
"size": { "w": 819, "h": 9 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

BIN
assets/img/map/fish.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

View File

@ -0,0 +1,130 @@
{ "frames": {
"fishIdle 0.aseprite": {
"frame": { "x": 0, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 1.aseprite": {
"frame": { "x": 13, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 2.aseprite": {
"frame": { "x": 26, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 3.aseprite": {
"frame": { "x": 39, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 4.aseprite": {
"frame": { "x": 52, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 5.aseprite": {
"frame": { "x": 65, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 6.aseprite": {
"frame": { "x": 78, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 7.aseprite": {
"frame": { "x": 91, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 8.aseprite": {
"frame": { "x": 104, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 9.aseprite": {
"frame": { "x": 117, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 10.aseprite": {
"frame": { "x": 130, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 11.aseprite": {
"frame": { "x": 143, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 12.aseprite": {
"frame": { "x": 156, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
},
"fishIdle 13.aseprite": {
"frame": { "x": 169, "y": 0, "w": 13, "h": 9 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 13, "h": 9 },
"sourceSize": { "w": 13, "h": 9 },
"duration": 100
}
},
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.2.27-x64",
"image": "fishIdle.png",
"format": "RGBA8888",
"size": { "w": 182, "h": 9 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

BIN
assets/img/map/shop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -0,0 +1,67 @@
{ "frames": {
"Sprite-0002 0.": {
"frame": { "x": 0, "y": 0, "w": 10, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 20 },
"sourceSize": { "w": 10, "h": 20 },
"duration": 100
},
"Sprite-0002 1.": {
"frame": { "x": 10, "y": 0, "w": 10, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 20 },
"sourceSize": { "w": 10, "h": 20 },
"duration": 100
},
"Sprite-0002 2.": {
"frame": { "x": 20, "y": 0, "w": 10, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 20 },
"sourceSize": { "w": 10, "h": 20 },
"duration": 100
},
"Sprite-0002 3.": {
"frame": { "x": 30, "y": 0, "w": 10, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 20 },
"sourceSize": { "w": 10, "h": 20 },
"duration": 100
},
"Sprite-0002 4.": {
"frame": { "x": 40, "y": 0, "w": 10, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 20 },
"sourceSize": { "w": 10, "h": 20 },
"duration": 100
},
"Sprite-0002 5.": {
"frame": { "x": 50, "y": 0, "w": 10, "h": 20 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 20 },
"sourceSize": { "w": 10, "h": 20 },
"duration": 100
}
},
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.2.27-x64",
"image": "transponder.png",
"format": "RGBA8888",
"size": { "w": 60, "h": 20 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" },
{ "name": "Layer 2", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

BIN
assets/img/map/tut1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

BIN
assets/img/map/tut2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

50
assets/shaders/pixel.fs Normal file
View File

@ -0,0 +1,50 @@
#version 330
// Input vertex attributes (from vertex shader)
in vec2 fragTexCoord;
in vec4 fragColor;
// Input uniform values
uniform sampler2D texture0;
uniform vec4 colDiffuse;
// Time fed from CPU
uniform float time = 0.0;
// Output fragment color
out vec4 finalColor;
// Viewport dimensions
const vec2 viewport = vec2(1080.0, 720.0);
// Pixel scaling
const vec2 pixelScale = vec2(2.0, 2.0);
void main()
{
// Calculate the distance to merge pixels
float dx = pixelScale.x * (1.0 / viewport.x);
float dy = pixelScale.y * (1.0 / viewport.y);
// Get the base UV coordinate of the pixel
vec2 baseUV = fragTexCoord;
// Use a wave function to translate the pixel UV
float X = baseUV.x*0.5+time;
float Y = baseUV.y*0.25+time;
baseUV.y += cos(X+Y)*0.0025*cos(Y);
baseUV.x += sin(X-Y)*0.012*sin(Y);
// Calculate a UV for this new blocky pixel
vec2 pixelatedUV = vec2(dx * floor(baseUV.x / dx), dy * floor(baseUV.y / dy));
// Rebuild the texture with the new UVs
vec3 tc = texture(texture0, pixelatedUV).rgb;
// Apply a color filter
tc = tc + vec3(0, 0.05, 0.15);
// Build the final pixel
finalColor = vec4(tc, 1.0);
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
<style>
html {
background-color: #1e1e1e;
}
</style>
<canvas id="game" width="1080" height="720"></canvas>
<script src="game.js"></script>
<script src="main.js"></script>

314
assets/tileEditor/main.js Normal file
View File

@ -0,0 +1,314 @@
images = ["", "tileset.png"];
var map = [];
var boolMap = [];
// 0 1 2 3
// 4 5 6 7
// 8 9 1011
// 12131415
// up right down left
var types = [
[0, 0, 0, 0, 15],
[0, 0, 0, 1, 14],
[0, 0, 1, 0, 3],
[0, 0, 1, 1, 2],
[0, 1, 0, 0, 12],
[0, 1, 0, 1, 13],
[0, 1, 1, 0, 0],
[0, 1, 1, 1, 1],
[1, 0, 0, 0, 11],
[1, 0, 0, 1, 10],
[1, 0, 1, 0, 7],
[1, 0, 1, 1, 6],
[1, 1, 0, 0, 8],
[1, 1, 0, 1, 9],
[1, 1, 1, 0, 4],
[1, 1, 1, 1, 5]
];
var brushSize = 1;
var collisions = [];
for (var y = 0; y < 216; y++) {
var arr = [];
var arr2 = [];
for (var x = 0; x < 72; x++) {
arr.push(0);
arr2.push(false);
}
map.push(arr);
boolMap.push(arr2);
}
setup(60);
function onAssetsLoaded() {
for (var y = 0; y < 4; y++) {
for (var x = 0; x < 4; x++) {
var newCanv = document.createElement("canvas");
newCanv.width = 10;
newCanv.height = 10;
var newCtx = newCanv.getContext("2d");
newCtx.drawImage(sprites.tileset.spr, x * 10, y * 10, 10, 10, 0, 0, 10, 10);
sprites[x + y * 4] = { spr: newCanv, drawLimitSize: 10 };
}
}
parseMap();
}
function update() {}
function input() {
if (keyDown[k.w]) {
moveCamera(0, -5);
}
if (keyDown[k.s]) {
moveCamera(0, 5);
}
if (keyDown[k.a]) {
moveCamera(-5, 0);
}
if (keyDown[k.d]) {
moveCamera(5, 0);
}
if (keyPress[k.q]) {
if (brushSize > 1) {
brushSize--;
}
}
if (keyPress[k.e]) {
brushSize++;
}
camera.zoom += scroll;
if (camera.zoom < 1) {
camera.zoom = 1;
}
if (keyPress[k.ENTER]) {
renderMap();
}
if (keyPress[k.SPACE]) {
collisions = makeOptimizedCollision();
}
var x = Math.floor(mousePosition().x / 10);
var y = Math.floor(mousePosition().y / 10);
x = x < 0 ? 0 : x;
y = y < 0 ? 0 : y;
x = x > map[0].length - 1 ? map[0].length - 1 : x;
y = y > map.length - 1 ? map.length - 1 : y;
if (mouseDown[0]) {
for (var yy = -brushSize / 2; yy < brushSize / 2; yy++) {
for (var xx = -brushSize / 2; xx < brushSize / 2; xx++) {
var posX = ~~(1 + xx + x);
var posY = ~~(1 + yy + y);
if (posX > -1 && posY > -1 && posY < map.length && posX < map[0].length) {
boolMap[posY][posX] = true;
}
}
}
}
if (mouseDown[2]) {
for (var yy = -brushSize / 2; yy < brushSize / 2; yy++) {
for (var xx = -brushSize / 2; xx < brushSize / 2; xx++) {
var posX = ~~(1 + xx + x);
var posY = ~~(1 + yy + y);
if (posX > -1 && posY > -1 && posY < map.length && posX < map[0].length) {
boolMap[posY][posX] = false;
}
}
}
}
}
function draw() {
parseMap();
var w = map[0].length * 10;
var h = map.length * 10;
rect(w / 2, h / 2, w, h, "blue");
for (var y = 0; y < map.length; y++) {
for (var x = 0; x < map[0].length; x++) {
if (map[y][x] !== -1) {
img(sprites[map[y][x]], x * 10 + 5, y * 10 + 5);
}
}
}
for(var i=0;i<collisions.length;i++) {
rectOut(collisions[i].x,collisions[i].y,collisions[i].width,collisions[i].height,"#ff0000");
}
}
function renderMap() {
var renderCvs = document.createElement("canvas");
renderCvs.width = map[0].length * 10;
renderCvs.height = map.length * 10;
var renderCtx = renderCvs.getContext("2d");
camera.x = 0;
camera.y = 0;
drawMode = 0;
absDraw = true;
curCtx = renderCtx;
for (var y = 0; y < map.length; y++) {
for (var x = 0; x < map[0].length; x++) {
if (map[y][x] !== -1) {
img(sprites[map[y][x]], x * 10 + 5, y * 10 + 5);
if (map[y][x] === 5) {
var closest = 6;
for (var y2 = -4; y2 < 5; y2++) {
for (var x2 = -4; x2 < 5; x2++) {
if(y2 === 0 && x2 === 0) {
continue;
}
var posX = x + x2;
var posY = y + y2;
if (posX > -1 && posY > -1 && posY < map.length && posX < map[0].length) {
if (boolMap[posY][posX] === false) {
var distance = dist({ x: x, y: y }, { x: posX, y: posY });
if (distance < closest) {
closest = distance;
}
}
}
}
}
if (closest > 1) {
rect(x * 10 + 5, y * 10 + 5, 10, 10, `#000000${Math.round(map_range(closest, 1, 6, 50, 255)).toString(16)}`);
}
}
}
}
}
document.body.appendChild(renderCvs);
collisions = makeOptimizedCollision();
var txt = document.createElement("textarea");
txt.innerText = JSON.stringify(collisions);
document.body.appendChild(txt);
}
function map_range(value, low1, high1, low2, high2) {
return low2 + ((high2 - low2) * (value - low1)) / (high1 - low1);
}
function parseMap() {
for (var y = 0; y < map.length; y++) {
for (var x = 0; x < map[0].length; x++) {
if (boolMap[y][x] === false) {
map[y][x] = -1;
continue;
}
var t = 0,
b = 0,
l = 0,
r = 0;
if (y === 0) {
t = 1;
} else {
t = boolMap[y - 1][x] ? 1 : 0;
}
if (x === 0) {
l = 1;
} else {
l = boolMap[y][x - 1] ? 1 : 0;
}
if (y === map.length - 1) {
b = 1;
} else {
b = boolMap[y + 1][x] ? 1 : 0;
}
if (x === map[0].length - 1) {
r = 1;
} else {
r = boolMap[y][x + 1] ? 1 : 0;
}
for (var i = 0; i < 16; i++) {
if (types[i][0] === t && types[i][1] === r && types[i][2] === b && types[i][3] === l) {
map[y][x] = types[i][4];
break;
}
}
}
}
}
function makeOptimizedCollision() {
var tileCount = 0;
var worldW = map[0].length;
var worldH = map.length;
cols = [];
// 2d array of booleans for if a tile has gotten a collision made for it
var hasCollision = [];
// fill arrays
for (var y = 0; y < worldH; y++) {
var hasCollisionRow = [];
for (var x = 0; x < worldW; x++) {
hasCollisionRow.push(false);
}
hasCollision.push(hasCollisionRow);
}
// try to make large rectangles that cover multiple walls to make collision more efficient
for (var y = 0; y < worldH; y++) {
for (var x = 0; x < worldW; x++) {
if (!hasCollision[y][x] && boolMap[y][x]) {
// find right limit
var xPos = x;
while (xPos < worldW && boolMap[y][xPos]) {
xPos++;
}
xPos--;
// find bottom limit
var yPos = y;
var fullRow = true;
// go down row by row
while (yPos < worldH-1 && boolMap[yPos][xPos] && fullRow) {
yPos++;
// go through the whole row, make sure it is full
var rowX = xPos;
while (rowX > -1 && boolMap[yPos][rowX]) {
rowX--;
}
// if the row is not full, stop
if (rowX + 1 !== x) {
fullRow = false;
yPos--;
}
}
// track what tiles have gotten collision
for (var y2 = y; y2 < yPos + 1; y2++) {
for (var x2 = x; x2 < xPos + 1; x2++) {
hasCollision[y2][x2] = true;
tileCount++;
}
}
// find collider dimensions
var colX = (x + xPos + 1) / 2;
var colY = (y + yPos + 1) / 2;
var colW = xPos - x + 1;
var colH = yPos - y + 1;
// add collider
cols.push({ x: colX * 10, y: colY * 10, width: colW * 10, height: colH * 10 });
}
}
}
console.log(`tiles: ${tileCount}, boxes: ${cols.length}`)
return cols;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

File diff suppressed because one or more lines are too long

View File

@ -9,10 +9,10 @@ set -e
# Make a uni-bundle
echo "Creating a fat bundle for all platforms"
rm -rf ./bundle/release
rm -rf ./bundle/one-breath.zip
rm -rf ./bundle/ldgame.zip
mkdir -p ./bundle/release
cp -r ./assets ./bundle/release
cp ./bundle/linux/release/one-breath ./bundle/release/one-breath
cp ./bundle/windows/release/one-breath.exe ./bundle/release/one-breath.exe
cp ./bundle/linux/release/ldgame ./bundle/release/ldgame
cp ./bundle/windows/release/ldgame.exe ./bundle/release/ldgame.exe
cd ./bundle/release
zip -r ../one-breath.zip ./
zip -r ../ldgame.zip ./

View File

@ -12,7 +12,7 @@ rm -rf ./bundle/linux/release-x86_64-unknown-linux-gnu.zip
mkdir -p ./bundle/linux/release
echo "Copying binary"
cp ./target/x86_64-unknown-linux-gnu/release/one-breath ./bundle/linux/release
cp ./target/x86_64-unknown-linux-gnu/release/ldgame ./bundle/linux/release
echo "Copying assets"
cp -r ./assets ./bundle/linux/release

View File

@ -11,7 +11,7 @@ rm -rf ./bundle/windows/release-x86_64-pc-windows-gnu.zip
mkdir -p ./bundle/windows/release
echo "Copying binary"
cp ./target/x86_64-pc-windows-gnu/release/one-breath.exe ./bundle/windows/release
cp ./target/x86_64-pc-windows-gnu/release/ldgame.exe ./bundle/windows/release
echo "Copying assets"
cp -r ./assets ./bundle/windows/release

View File

@ -1,7 +1,15 @@
use raylib::prelude::*;
use crate::{player::Player, resources::GlobalResources};
pub trait EnemyBase {
fn render();
fn handle_logic();
fn handle_getting_attacked();
}
fn render(
&mut self,
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
player: &mut Player,
resources: &mut GlobalResources,
dt: f64,
);
fn handle_logic(&mut self, player: &mut Player, dt: f64) -> u8;
fn handle_getting_attacked(&mut self, stun_duration: f64, current_time: f64);
}

View File

@ -6,7 +6,7 @@ use crate::{
use raylib::prelude::*;
use serde::{Deserialize, Serialize};
const JELLYFISH_STUN_DURATION: f64 = 0.75;
const JELLYFISH_STUN_DURATION: f64 = 1.5;
const JELLYFISH_STUN_REACH: f32 = 20.0;
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
@ -28,6 +28,7 @@ impl EnemyBase for JellyFish {
fn render(
&mut self,
context_2d: &mut raylib::prelude::RaylibMode2D<raylib::prelude::RaylibDrawHandle>,
_player: &mut Player,
resources: &mut GlobalResources,
dt: f64,
) {
@ -76,16 +77,17 @@ impl EnemyBase for JellyFish {
&& !is_jelly_stunned;
}
fn handle_logic(&mut self, player: &mut Player, dt: f64) {
fn handle_logic(&mut self, player: &mut Player, _dt: f64) -> u8 {
// Handle stunning the player
if self.do_stun_player {
if self.position.distance_to(player.position).abs() <= JELLYFISH_STUN_REACH {
player.set_stun_seconds(JELLYFISH_STUN_DURATION);
}
}
return 0;
}
fn handle_getting_attacked(&mut self, stun_duration: f64) {
fn handle_getting_attacked(&mut self, stun_duration: f64, _current_time: f64) {
self.stunned_timer = stun_duration;
self.max_stunned_time = stun_duration;
}

View File

@ -1 +1,5 @@
pub mod base;
pub mod base;
pub mod jellyfish;
pub mod octopus;
pub mod whirlpool;
pub mod pufferfish;

View File

@ -0,0 +1,145 @@
use crate::{
lib::utils::calculate_linear_slide,
pallette::{TRANSLUCENT_RED_64, TRANSLUCENT_WHITE_128},
player::Player,
};
use super::base::EnemyBase;
use rand::Rng;
use raylib::prelude::*;
use serde::{Deserialize, Serialize};
const OCTOPUS_SUCK_AIR_DELAY: f64 = 3.5;
const OCTOPUS_SUCK_AIR_RANGE: f32 = 40.0;
const OCTOPUS_SUCK_AIR_DURATION: f64 = 1.0;
const OCTOPUS_SUCK_AIR_AMOUNT: f32 = 0.1;
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
struct OctopusAirBubble {
position: Vector2,
speed: f32,
}
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
pub struct Octopus {
pub position_a: Vector2,
pub position_b: Vector2,
#[serde(skip)]
pub current_position: Vector2,
#[serde(skip)]
pub stunned_timer: f64,
#[serde(skip)]
pub max_stunned_time: f64,
#[serde(skip)]
pub suck_air_time_remaining: f64,
#[serde(skip)]
suck_air_bubbles: Vec<OctopusAirBubble>,
#[serde(skip)]
has_taken_air_from_player: bool,
}
impl Octopus {}
impl EnemyBase for Octopus {
fn render(
&mut self,
context_2d: &mut raylib::prelude::RaylibMode2D<raylib::prelude::RaylibDrawHandle>,
player: &mut Player,
resources: &mut crate::resources::GlobalResources,
dt: f64,
) {
let is_octopus_stunned = self.stunned_timer > 0.0;
// Simple sine position
let h_trans = (context_2d.get_time() / 8.0).sin().abs() as f32;
// Modify the current pose
let dist_a_to_b = self.position_b - self.position_a;
self.current_position = (dist_a_to_b * h_trans) + self.position_a;
// Render the stun ring
if self.max_stunned_time > 0.0 && self.stunned_timer > 0.0 {
let stun_ring_alpha =
calculate_linear_slide(self.stunned_timer / self.max_stunned_time);
context_2d.draw_circle_v(
self.current_position,
20.0,
TRANSLUCENT_RED_64.fade(0.55 * stun_ring_alpha as f32),
);
self.stunned_timer -= dt;
}
// Every once in a while, start sucking air
if (context_2d.get_time() % OCTOPUS_SUCK_AIR_DELAY) < 0.1
&& self.suck_air_time_remaining == 0.0
&& !is_octopus_stunned
{
self.suck_air_time_remaining = OCTOPUS_SUCK_AIR_DURATION;
self.has_taken_air_from_player = false;
// Spawn a few air bubbles if the player is in range
if player.position.distance_to(self.current_position).abs() <= OCTOPUS_SUCK_AIR_RANGE {
for _ in 0..5 {
self.suck_air_bubbles.push(OctopusAirBubble {
position: player.position,
speed: rand::thread_rng().gen_range(0.8..1.3),
});
}
}
}
// Handle sucking air bubble animation
if self.suck_air_time_remaining > 0.0 {
// Render and update all bubbles
for bubble in self.suck_air_bubbles.iter_mut() {
// Get the direction from the bubble to the octopus
let direction = (self.current_position - bubble.position).normalized();
// Render the bubble
context_2d.draw_circle_v(bubble.position, 2.0, TRANSLUCENT_WHITE_128);
// Move the bubble
bubble.position += direction * bubble.speed;
}
// Reduce time
self.suck_air_time_remaining = (self.suck_air_time_remaining - dt).max(0.0);
} else {
self.suck_air_bubbles.clear();
}
// Render animation
if self.suck_air_time_remaining > 0.0 {
resources
.octopus_animation_attack
.draw(context_2d, self.current_position, 0.0);
} else {
resources
.octopus_animation_regular
.draw(context_2d, self.current_position, 0.0);
}
}
fn handle_logic(&mut self, player: &mut crate::player::Player, _dt: f64) -> u8 {
if self.suck_air_time_remaining > 0.0 && !self.has_taken_air_from_player {
if player.position.distance_to(self.current_position).abs() <= OCTOPUS_SUCK_AIR_RANGE {
// Take air from the player
player.breath_percent -= OCTOPUS_SUCK_AIR_AMOUNT;
// Set the flag
self.has_taken_air_from_player = true;
return 1;
}
}
return 0;
}
fn handle_getting_attacked(&mut self, stun_duration: f64, _current_time: f64) {
self.stunned_timer = stun_duration;
self.max_stunned_time = stun_duration;
}
}

View File

@ -0,0 +1,156 @@
use raylib::prelude::*;
use serde::{Deserialize, Serialize};
use crate::{lib::utils::calculate_linear_slide, pallette::TRANSLUCENT_RED_64};
use super::base::EnemyBase;
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub enum PufferState {
SmallIdle,
Growing,
LargeIdle,
Blowing,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Pufferfish {
pub position: Vector2,
pub is_knocking_back: bool,
pub time_knocking_back: f64,
pub inflate_timer: f64,
pub is_large: bool,
pub stun_timer: f64,
pub puffer_state: PufferState,
}
impl EnemyBase for Pufferfish {
fn render(
&mut self,
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
player: &mut crate::player::Player,
resources: &mut crate::resources::GlobalResources,
dt: f64,
) {
let is_stunned = self.stun_timer > 0.0;
// Render the stun ring
if is_stunned {
// println!("Stunned");
let stun_ring_alpha =
calculate_linear_slide(self.stun_timer / 1.0);
context_2d.draw_circle_v(
self.position,
12.0,
TRANSLUCENT_RED_64.fade(0.55 * stun_ring_alpha as f32),
);
self.stun_timer -= dt;
}
let angle = player.position.angle_to(self.position).to_degrees();
match self.puffer_state {
PufferState::SmallIdle => {
resources.pufferfish_small.draw(
context_2d,
Vector2 {
x: self.position.x,
y: self.position.y,
},
angle,
);
if self.position.distance_to(player.position).abs() <= 100.0 && self.inflate_timer > 2.0{
self.puffer_state = PufferState::Growing;
}
self.is_large = false;
},
PufferState::Growing => {
self.inflate_timer = 0.0;
resources.pufferfish_expand.draw(
context_2d,
Vector2 {
x: self.position.x,
y: self.position.y,
},
angle,
);
if resources.pufferfish_expand.get_current_frame_id(context_2d) == 3 {
self.puffer_state = PufferState::LargeIdle;
}
self.is_large = true;
},
PufferState::LargeIdle => {
self.inflate_timer = 0.0;
resources.pufferfish_big.draw(
context_2d,
Vector2 {
x: self.position.x,
y: self.position.y,
},
angle,
);
if self.position.distance_to(player.position).abs() <= 65.0{
self.puffer_state = PufferState::Blowing;
self.is_knocking_back = true;
self.time_knocking_back = 0.0;
}
self.is_large = true;
},
PufferState::Blowing => {
resources.pufferfish_attack.draw(
context_2d,
Vector2 {
x: self.position.x,
y: self.position.y,
},
angle,
);
if resources.pufferfish_expand.get_current_frame_id(context_2d) == 3 && self.inflate_timer > 1.0{
self.puffer_state = PufferState::SmallIdle;
self.inflate_timer = 0.0;
}
self.is_large = false;
},
}
}
fn handle_logic(&mut self, player: &mut crate::player::Player, dt: f64) -> u8 {
self.inflate_timer += dt;
self.time_knocking_back += dt;
if self.time_knocking_back >= 0.5{
self.is_knocking_back = false;
}
if self.position.distance_to(player.position).abs() > 120.0 && self.is_large {
self.puffer_state = PufferState::Blowing;
self.inflate_timer = 0.0;
}
return 0;
}
fn handle_getting_attacked(&mut self, stun_duration: f64, current_time: f64) {
self.stun_timer = stun_duration;
}
}

View File

@ -0,0 +1,52 @@
use raylib::prelude::*;
use super::base::EnemyBase;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
pub struct Whirlpool{
pub position: Vector2,
// Track if it needs removing
pub should_remove: bool,
// variable for tracking rotation
pub rotation: f32,
}
impl Whirlpool{
// hook to see if item needs removing
pub fn should_remove(&self) -> bool{
return self.should_remove;
}
}
impl EnemyBase for Whirlpool{
fn render(
&mut self,
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
player: &mut crate::player::Player,
resources: &mut crate::resources::GlobalResources,
dt: f64,
) {
resources.whirlpool.draw(context_2d, Vector2{x: self.position.x, y: self.position.y}, self.rotation);
self.rotation += 1.0;
}
fn handle_logic(&mut self, player: &mut crate::player::Player, dt: f64) -> u8 {
return 0;
}
// Whirlpool removed if shoot
fn handle_getting_attacked(&mut self, stun_duration: f64, current_time: f64) {
self.should_remove = true;
}
}

View File

@ -1,14 +1,7 @@
use rand::{prelude::ThreadRng, Rng};
use raylib::prelude::*;
use crate::{
gamecore::GameCore, lib::utils::triangles::rotate_vector, player::Player, world::World,
};
const FISH_FOLLOW_PLAYER_DISTANCE: f32 = 30.0;
const FISH_FOLLOW_PLAYER_SPEED: f32 = 1.8;
const FISH_FOLLOW_PLAYER_SPEED_FAST: f32 = FISH_FOLLOW_PLAYER_SPEED * 3.0;
const FISH_ATTACH_RADIUS: f32 = 20.0;
use crate::{gamecore::{self, GameCore}, lib::wrappers::audio::player::AudioPlayer, player::Player, resources::GlobalResources};
const FISH_VISION: f32 = 25.0;
const FISH_MAX_SPEED: f32 = 2.0;
@ -19,15 +12,18 @@ const FISH_FACTOR_COHESION: f32 = 0.1;
const FISH_SEPARATION_DISTANCE: f32 = 15.0;
const FISH_FACTOR_SEPARATION: f32 = 1.5;
#[derive(Debug, Clone)]
pub struct FishEntity {
position: Vector2,
direction: Vector2,
velocity: Vector2,
pub following_player: bool,
current_frame: u8,
animation_counter: u32,
size: Vector2,
color: u8,
rng: ThreadRng,
color: Color,
}
impl FishEntity {
@ -35,16 +31,16 @@ impl FishEntity {
let mut rng = rand::thread_rng();
Self {
position: position,
direction: Vector2::zero(),
direction: Vector2 {
x: rng.gen_range(0.0..1.0),
y: rng.gen_range(0.0..1.0),
},
velocity: Vector2::zero(),
following_player: false,
animation_counter: 0,
current_frame: 0,
size: Vector2 { x: 5.0, y: 8.0 },
color: Color {
r: rng.gen_range(128..225),
g: rng.gen_range(128..225),
b: rng.gen_range(128..225),
a: 140,
},
color: rng.gen_range(0..6),
rng,
}
}
@ -57,7 +53,12 @@ impl FishEntity {
return output;
}
pub fn handle_follow_player(&mut self, player: &Player, dt: f64, other_fish: &Vec<FishEntity>) {
pub fn handle_follow_player(
&mut self,
player: &Player,
_dt: f64,
other_fish: &Vec<FishEntity>,
) {
let mut acceleration: Vector2 = Vector2::zero();
let mut steer: Vector2 = Vector2::zero();
@ -134,69 +135,72 @@ impl FishEntity {
self.position += self.velocity;
}
pub fn handle_free_movement(&mut self, player: &mut Player, dt: f64) {
// Distance and direction to player
let dist_to_player = player.position - self.position;
let dist_to_player_lin = self.position.distance_to(player.position);
let mut direction_to_player = dist_to_player;
direction_to_player.normalize();
pub fn handle_free_movement(&mut self, player: &mut Player, _dt: f64) -> bool {
// Handle player picking up fish
if player.position.distance_to(self.position).abs() <= player.size.y * 2.2 {
self.following_player = true;
self.velocity = self.direction.normalized();
self.current_frame = 0;
self.animation_counter = 0;
// Add currency to the player
player.coins += 1;
return true;
}
// Look at the player;
self.position = self.position;
self.direction = direction_to_player;
return false
}
pub fn update_position(&mut self, player: &mut Player, dt: f64, other_fish: &Vec<FishEntity>) {
pub fn update_position(&mut self, player: &mut Player, dt: f64, other_fish: &Vec<FishEntity>) -> bool{
if self.following_player {
self.handle_follow_player(player, dt, other_fish);
} else {
self.handle_free_movement(player, dt);
if self.handle_free_movement(player, dt) {
return true;
}
}
return false;
}
pub fn render(
&mut self,
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
resources: &mut GlobalResources,
) {
// Direction
let direction = (Vector2::zero().angle_to(self.direction.normalized())).to_degrees();
self.animation_counter += 1;
// swimming
if self.following_player {
if self.animation_counter % 3 == 0 {
self.current_frame += 1;
if self.current_frame == 8 {
self.current_frame = 0;
}
}
resources.fish_animation_swim.draw_frame(
context_2d,
self.position,
direction,
(self.current_frame + self.color * 9) as u32,
);
// idle
} else {
if self.animation_counter % 10 == 0 {
if self.current_frame == 0 {
self.current_frame = 1;
} else {
self.current_frame = 0;
}
}
resources.fish_animation_idle.draw_frame(
context_2d,
self.position,
direction,
(self.current_frame + self.color * 2) as u32,
);
}
}
pub fn render(&self, context_2d: &mut RaylibMode2D<RaylibDrawHandle>) {
// Direction
let direction =
Vector2::zero().angle_to(self.direction.normalized()) + (90.0 as f32).to_radians();
// Get the corners of the fish
let fish_front = rotate_vector(
Vector2 {
x: 0.0,
y: (self.size.y / 2.0) * -1.0,
},
direction,
);
let fish_bl = rotate_vector(
Vector2 {
x: (self.size.x / 2.0) * -1.0,
y: (self.size.y / 2.0),
},
direction,
);
let fish_br = rotate_vector(
Vector2 {
x: (self.size.x / 2.0),
y: (self.size.y / 2.0),
},
direction,
);
// Draw the fish as a triangle with rotation
context_2d.draw_triangle(
self.position + fish_front,
self.position + fish_bl,
self.position + fish_br,
self.color,
);
}
}

View File

@ -5,15 +5,13 @@ use std::{fmt, fs::File, io::BufReader};
use raylib::{
camera::Camera2D, math::Vector2, prelude::RaylibDrawHandle, RaylibHandle, RaylibThread,
};
use failure::Error;
use crate::{
player::{Player, PlayerInventory},
resources::GlobalResources,
world::World,
};
use failure::Error;
use log::debug;
use serde::{Deserialize, Serialize};
/// Overall states for the game
@ -25,6 +23,8 @@ pub enum GameState {
GameQuit,
InGame,
GameEnd,
InShop,
WinGame,
}
impl fmt::Display for GameState {
@ -76,6 +76,20 @@ impl GameProgress {
Ok(())
}
pub fn update(&mut self, new_progress: &GameProgress) {
// Bring in new data
self.coins = new_progress.coins;
self.inventory = new_progress.inventory.clone();
self.max_depth = self.max_depth.max(new_progress.max_depth);
// self.fastest_time = self.fastest_time.min(new_progress.fastest_time);
// Write to file
let result = self.to_file("./savestate.json".to_string());
if result.is_err() {
println!("Could not save game state. Holding in RAM");
}
}
}
/// This structure contains the entire game state, and should be passed around to various logic functions.
@ -134,8 +148,6 @@ impl GameCore {
}
pub fn switch_state(&mut self, new_state: GameState, draw_handle: Option<&RaylibDrawHandle>) {
debug!("Switching global state to: {}", new_state);
self.last_state = self.state;
self.state = new_state;

View File

@ -1,161 +1,325 @@
use raylib::{
color::Color,
math::{Rectangle, Vector2},
prelude::{RaylibDraw, RaylibDrawHandle},
};
use serde::{Deserialize, Serialize};
use crate::resources::GlobalResources;
pub trait ItemBase {
fn get_cost(&self) -> u32;
fn get_level(&self) -> u8;
fn get_name(&self) -> String;
fn get_description(&self) -> String;
fn get_texture(
&self,
draw_handle: &mut RaylibDrawHandle,
resources: &GlobalResources,
dest: Rectangle,
);
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct StunGun {
pub range: f32,
pub duration: f64,
pub level: u8,
cost: u32,
}
impl StunGun {
pub fn lvl1() -> Self {
Self {
range: 30.0,
duration: 0.5,
duration: 2.0,
level: 1,
cost: 15,
}
}
pub fn lvl2() -> Self {
Self {
range: 60.0,
duration: 0.75,
duration: 2.5,
level: 2,
cost: 25,
}
}
pub fn lvl3() -> Self {
pub fn lvl3() -> Self {
Self {
range: 80.0,
duration: 1.0,
duration: 3.0,
level: 3,
cost: 40,
}
}
}
impl ItemBase for StunGun {
fn get_cost(&self) -> u32 {
self.cost
}
fn get_name(&self) -> String {
return "Stun Bomb".to_string();
}
fn get_description(&self) -> String {
return "Right click to stun enemies\n one use per dive".to_string();
}
fn get_texture(
&self,
draw_handle: &mut RaylibDrawHandle,
resources: &GlobalResources,
dest: Rectangle,
) {
let texture = match self.get_level() {
1 => (&resources.stun_gun_one),
2 => (&resources.stun_gun_two),
3 | _ => (&resources.stun_gun_three),
};
draw_handle.draw_texture_pro(
texture,
Rectangle {
x: 0.0,
y: 0.0,
width: texture.width as f32,
height: texture.height as f32,
},
dest,
Vector2 { x: 0.0, y: 0.0 },
0.0,
Color::WHITE,
);
}
fn get_level(&self) -> u8 {
self.level
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct AirBag{
extra_oxygen: u32,
pub struct AirBag {
pub extra_oxygen: f32,
pub level: u8,
cost: u32,
}
impl AirBag {
pub fn lvl1() -> Self {
Self {
extra_oxygen: 15,
extra_oxygen: 0.15,
level: 1,
cost: 25,
}
}
pub fn lvl2() -> Self {
Self {
extra_oxygen: 30,
extra_oxygen: 0.30,
level: 2,
cost: 35,
}
}
pub fn lvl3() -> Self {
pub fn lvl3() -> Self {
Self {
extra_oxygen: 45,
extra_oxygen: 0.45,
level: 3,
cost: 50,
}
}
}
impl ItemBase for AirBag {
fn get_cost(&self) -> u32 {
self.cost
}
fn get_name(&self) -> String {
return "Bag of Air".to_string();
}
fn get_description(&self) -> String {
return "Its.. a bag.\nFilled with air. Duh".to_string();
}
fn get_texture(
&self,
draw_handle: &mut RaylibDrawHandle,
resources: &GlobalResources,
dest: Rectangle,
) {
let texture = match self.get_level() {
1 => (&resources.air_one),
2 => (&resources.air_two),
3 | _ => (&resources.air_three),
};
draw_handle.draw_texture_pro(
texture,
Rectangle {
x: 0.0,
y: 0.0,
width: texture.width as f32,
height: texture.height as f32,
},
dest,
Vector2 { x: 0.0, y: 0.0 },
0.0,
Color::WHITE,
);
}
fn get_level(&self) -> u8 {
self.level
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct Flashlight{
power_level: f32,
pub struct Flashlight {
pub radius: f32,
pub level: u8,
cost: u32,
}
impl Flashlight{
impl Flashlight {
pub fn lvl1() -> Self {
Self {
power_level: 0.25,
radius: 0.25,
level: 1,
cost: 20,
}
}
pub fn lvl2() -> Self {
Self {
power_level: 0.5,
radius: 0.5,
level: 2,
cost: 30,
}
}
pub fn lvl3() -> Self {
pub fn lvl3() -> Self {
Self {
power_level: 1.0,
radius: 1.0,
level: 3,
cost: 50,
}
}
}
impl ItemBase for Flashlight {
fn get_cost(&self) -> u32 {
self.cost
}
fn get_name(&self) -> String {
return "Flashlight".to_string();
}
fn get_description(&self) -> String {
return "See better for longer".to_string();
}
fn get_texture(
&self,
draw_handle: &mut RaylibDrawHandle,
resources: &GlobalResources,
dest: Rectangle,
) {
let texture = match self.get_level() {
1 => (&resources.flashlight_one),
2 => (&resources.flashlight_two),
3 | _ => (&resources.flashlight_three),
};
draw_handle.draw_texture_pro(
texture,
Rectangle {
x: 0.0,
y: 0.0,
width: texture.width as f32,
height: texture.height as f32,
},
dest,
Vector2 { x: 0.0, y: 0.0 },
0.0,
Color::WHITE,
);
}
fn get_level(&self) -> u8 {
self.level
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct Flippers {
pub speed_increase: f32,
pub level: u8,
cost: u32,
}
impl Flippers {
pub fn lvl1() -> Self {
Self {
speed_increase: 1.2
speed_increase: 1.1,
level: 1,
cost: 30,
}
}
pub fn lvl2() -> Self {
Self {
speed_increase: 1.5
speed_increase: 1.2,
level: 2,
cost: 40,
}
}
pub fn lvl3() -> Self {
pub fn lvl3() -> Self {
Self {
speed_increase: 1.8
speed_increase: 1.3,
level: 3,
cost: 50,
}
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(tag = "t", content = "c")]
pub enum ShopItems {
StunGun(u8, u8, String),
AirBag(u8, u8, String),
Flashlight(u8, u8, String),
Flippers(u8, u8, String)
impl ItemBase for Flippers {
fn get_cost(&self) -> u32 {
self.cost
}
fn get_name(&self) -> String {
return "Flippers".to_string();
}
fn get_description(&self) -> String {
return "Swim faster, and look stupid\nat the same time!".to_string();
}
fn get_texture(
&self,
draw_handle: &mut RaylibDrawHandle,
resources: &GlobalResources,
dest: Rectangle,
) {
let texture = match self.get_level() {
1 => (&resources.flippers_one),
2 => (&resources.flippers_two),
3 | _ => (&resources.flippers_three),
};
draw_handle.draw_texture_pro(
texture,
Rectangle {
x: 0.0,
y: 0.0,
width: texture.width as f32,
height: texture.height as f32,
},
dest,
Vector2 { x: 0.0, y: 0.0 },
0.0,
Color::WHITE,
);
}
fn get_level(&self) -> u8 {
self.level
}
}
impl ShopItems{
pub fn get_inital_items() -> [ShopItems; 4]{
[ShopItems::StunGun(0, 5, String::from("Stun Gun")), ShopItems::AirBag(0, 5, String::from("Air Bag")),
ShopItems::Flashlight(0, 5, String::from("Flash Light")), ShopItems::Flippers(0, 5, String::from("Flippers"))]
}
pub fn get_level(item: &ShopItems) -> u8{
match item {
ShopItems::StunGun(x, _, _) => *x,
ShopItems::AirBag(x, _, _) => *x,
ShopItems::Flashlight(x, _, _) => *x,
ShopItems::Flippers(x, _, _) => *x
}
}
pub fn get_cost(item: &ShopItems) -> u8{
match item {
ShopItems::StunGun(_, x, _) => *x,
ShopItems::AirBag(_, x, _) => *x,
ShopItems::Flashlight(_, x, _) => *x,
ShopItems::Flippers(_, x, _) => *x
}
}
pub fn get_name(item: &ShopItems) -> String{
match item {
ShopItems::StunGun(_, _, x) => x.to_string(),
ShopItems::AirBag(_, _, x) => x.to_string(),
ShopItems::Flashlight(_, _, x) => x.to_string(),
ShopItems::Flippers(_, _, x) => x.to_string()
}
}
}

71
src/lib/utils/button.rs Normal file
View File

@ -0,0 +1,71 @@
use raylib::prelude::*;
pub struct OnScreenButton {
bounds: Rectangle,
text: String,
background: Color,
border: Color,
border_hover: Color,
font_px: i32,
draw_border: bool,
}
impl OnScreenButton {
pub fn new(
text: String,
bounds: Rectangle,
background: Color,
border: Color,
border_hover: Color,
font_px: i32,
draw_border: bool,
) -> Self {
Self {
bounds,
text,
background,
border,
border_hover,
font_px,
draw_border,
}
}
pub fn is_hovered(&self, draw_handle: &RaylibDrawHandle) -> bool {
return self
.bounds
.check_collision_point_rec(draw_handle.get_mouse_position());
}
pub fn render(&self, draw_handle: &mut RaylibDrawHandle) {
// Draw the button background
draw_handle.draw_rectangle_rec(self.bounds, self.background);
// Check mouse info
let is_being_hovered = self.is_hovered(draw_handle);
// Render the border
if self.draw_border {
draw_handle.draw_rectangle_lines_ex(
self.bounds,
3,
match is_being_hovered {
true => self.border_hover,
false => self.border,
},
);
}
// Render the text
draw_handle.draw_text(
&self.text,
self.bounds.x as i32 + 10,
self.bounds.y as i32 + ((self.bounds.height as i32 - self.font_px) / 2),
self.font_px,
match is_being_hovered && !self.draw_border {
true => self.border_hover,
false => self.border,
},
)
}
}

View File

@ -1,5 +1,6 @@
pub mod profiler;
pub mod triangles;
pub mod button;
pub fn calculate_linear_slide(playthrough_percent: f64) -> f64 {
if playthrough_percent < 0.25 {

View File

@ -2,7 +2,7 @@ use raylib::{core::color::Color, math::{Rectangle, Vector2}, prelude::{RaylibDra
/// A wrapper around an animation spritesheet
pub struct FrameAnimationWrapper {
sprite_sheet: Texture2D,
pub sprite_sheet: Texture2D,
size: Vector2,
frame_count: u32,
frames_per_second: u8,

View File

@ -39,3 +39,10 @@ impl std::ops::Deref for AudioPlayer {
&self.backend
}
}
impl std::ops::DerefMut for AudioPlayer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.backend
}
}

View File

@ -1,32 +0,0 @@
use std::usize;
use raylib::prelude::*;
pub struct FrameRange {
pub min: usize,
pub max: usize,
}
pub struct ComplexAnimationTool {
sprite_sheet: Texture2D,
frames_per_second: f32,
frame_size: Vector2,
sprite_sheet_size_frames: Vector2
}
impl ComplexAnimationTool {
pub fn render_loop(&self, context_2d: &mut RaylibMode2D<RaylibDrawHandle>, bounds: Rectangle, rotation: f32, range: &FrameRange) {
}
pub fn render_frame(&self, context_2d: &mut RaylibMode2D<RaylibDrawHandle>, bounds: Rectangle, rotation: f32, id: usize) {
// Convert the ID to an xy
let col_id = id % self.sprite_sheet_size_frames.x as usize;
let row_id = id / self.sprite_sheet_size_frames.y as usize;
}
}

View File

@ -1,3 +1,2 @@
pub mod audio;
pub mod animation;
pub mod complexanimation;
pub mod animation;

View File

@ -2,7 +2,7 @@ use raylib::prelude::*;
use crate::{
gamecore::{GameCore, GameState},
lib::wrappers::audio::player::AudioPlayer,
lib::{utils::button::OnScreenButton, wrappers::audio::player::AudioPlayer},
};
use super::screen::Screen;
@ -22,13 +22,14 @@ impl Screen for GameEndScreen {
&mut self,
draw_handle: &mut RaylibDrawHandle,
_thread: &RaylibThread,
audio_system: &mut AudioPlayer,
_audio_system: &mut AudioPlayer,
game_core: &mut GameCore,
) -> Option<GameState> {
let mouse_position = draw_handle.get_mouse_position();
draw_handle.clear_background(Color::GRAY);
// TODO: Maybe we can stick some art here?
// Render the background
draw_handle.draw_texture(&game_core.resources.shop_background, 0, 0, Color::WHITE);
// Window dimensions
let win_height = draw_handle.get_screen_height();
@ -53,75 +54,48 @@ impl Screen for GameEndScreen {
// Render heading text
draw_handle.draw_text(
"OUT OF BREATH",
(win_width / 2) - 80,
(win_width / 2) - ((SCREEN_PANEL_SIZE.x as i32 + 6) / 2) + 25,
(win_height / 2) - (SCREEN_PANEL_SIZE.y as i32 / 2) + 10,
40,
30,
Color::BLACK,
);
// TODO: Save game progress
// Render message
draw_handle.draw_text(
"Your clone can now buy items ",
((win_width / 2) - ((SCREEN_PANEL_SIZE.x as i32 + 6) / 2))
+ (0.15 * SCREEN_PANEL_SIZE.x) as i32,
(win_height / 2) - (SCREEN_PANEL_SIZE.y as i32 / 2) + 50,
15,
Color::BLACK,
);
// Creates
let go_to_menu_button = OnScreenButton::new(
String::from("Return to shop"),
Rectangle {
x: (((win_width / 2) - ((SCREEN_PANEL_SIZE.x as i32 + 6) / 2) + 5)
+ (0.15 * SCREEN_PANEL_SIZE.x) as i32) as f32,
y: (((win_height / 2) - (SCREEN_PANEL_SIZE.y as i32 / 2) + 90) as f32) + 100.0,
width: 210.0,
height: 50.0,
},
Color::WHITE,
Color::BLACK,
Color::GRAY,
25,
true,
);
// // Close and quit buttons
// let bottom_left_button_dimensions = Rectangle {
// x: (win_width as f32 / 2.0) - (SCREEN_PANEL_SIZE.x / 2.0) + 5.0,
// y: (win_height as f32 / 2.0) + (SCREEN_PANEL_SIZE.y / 2.0) - 50.0,
// width: (SCREEN_PANEL_SIZE.x / 2.0) - 15.0,
// height: 40.0,
// };
// let bottom_right_button_dimensions = Rectangle {
// x: (win_width as f32 / 2.0) + 5.0,
// y: bottom_left_button_dimensions.y,
// width: bottom_left_button_dimensions.width,
// height: bottom_left_button_dimensions.height,
// };
// render button
go_to_menu_button.render(draw_handle);
// // Check if the mouse is over either button
// let mouse_over_bottom_left_button =
// bottom_left_button_dimensions.check_collision_point_rec(mouse_position);
// let mouse_over_bottom_right_button =
// bottom_right_button_dimensions.check_collision_point_rec(mouse_position);
// // Render buttons
// draw_handle.draw_rectangle_lines_ex(
// bottom_left_button_dimensions,
// 3,
// match mouse_over_bottom_left_button {
// true => Color::GRAY,
// false => Color::BLACK,
// },
// );
// draw_handle.draw_text(
// "Quit",
// bottom_left_button_dimensions.x as i32 + 15,
// bottom_left_button_dimensions.y as i32 + 5,
// 30,
// Color::BLACK,
// );
// draw_handle.draw_rectangle_lines_ex(
// bottom_right_button_dimensions,
// 3,
// match mouse_over_bottom_right_button {
// true => Color::GRAY,
// false => Color::BLACK,
// },
// );
// draw_handle.draw_text(
// "Close",
// bottom_right_button_dimensions.x as i32 + 15,
// bottom_right_button_dimensions.y as i32 + 5,
// 30,
// Color::BLACK,
// );
// // Handle click actions on the buttons
// if draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) {
// if mouse_over_bottom_left_button {
// return Some(GameState::GameQuit);
// } else if mouse_over_bottom_right_button {
// return Some(game_core.last_state);
// }
// }
// If the player clicks on the button send them to shop
if go_to_menu_button.is_hovered(draw_handle)
&& draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON)
{
return Some(GameState::InShop);
}
return None;
}

View File

@ -18,6 +18,9 @@ pub fn render_hud(
+ slider_bound_height,
};
draw_handle.draw_rectangle(10, 10, 20, 700, Color::new(0, 50, 255, 50));
draw_handle.draw_rectangle( 10, 710 - (game_core.player.breath_percent * 450.0) as i32, 20, (game_core.player.breath_percent * 450.0) as i32, Color::new(0, 50, 255, 125));
// Render the base of the slider
draw_handle.draw_rectangle(
(progress_slider_position.x - slider_bound_height) as i32,

View File

@ -1,34 +1,25 @@
mod hud;
mod playerlogic;
mod shop;
use raylib::prelude::*;
use crate::{gamecore::{self, GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer};
use self::shop::Shop;
use crate::{
pallette::{WATER},
};
use crate::{entities::enemy::{base::EnemyBase, whirlpool::Whirlpool}, gamecore::{self, GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer};
use super::screen::Screen;
#[derive(PartialEq)]
pub enum InGameState {
BUYING,
SWIMMING,
}
use crate::entities::fish::FishEntity;
pub struct InGameScreen {
current_state: InGameState,
shop: Shop,
shader_time_var_location: i32,
swim_playing: bool,
}
impl InGameScreen {
pub fn new() -> Self {
pub unsafe fn new(game_core: &GameCore) -> Self {
Self {
current_state: InGameState::BUYING,
shop: Shop::new(),
shader_time_var_location: raylib::ffi::GetShaderLocation(
*game_core.resources.pixel_shader,
rstr!("time").as_ptr(),
),
swim_playing: false,
}
}
@ -36,6 +27,8 @@ impl InGameScreen {
&mut self,
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
game_core: &mut GameCore,
dt: f64,
audio_system: &mut AudioPlayer,
) {
// Build source bounds
let source_bounds = Rectangle {
@ -52,7 +45,101 @@ impl InGameScreen {
};
// Clear the background
context_2d.draw_rectangle_rec(world_bounds, WATER);
// context_2d.draw_rectangle_gradient_v(
// world_bounds.x as i32,
// world_bounds.y as i32,
// world_bounds.width as i32,
// world_bounds.height as i32,
// WATER,
// WATER_DARK,
// );
context_2d.draw_texture_pro(
&game_core.resources.background_back,
Rectangle {
x: 0.0,
y: 0.0,
width: game_core.resources.background_back.width as f32,
height: game_core.resources.background_back.height as f32,
},
Rectangle::new(
0.0,
0.0,
(game_core.resources.background_back.width * 2) as f32,
(game_core.resources.background_back.height * 2) as f32,
),
Vector2 { x: 0.0, y: 0.0 },
0.0,
Color::WHITE,
);
context_2d.draw_texture_pro(
&game_core.resources.background_front,
Rectangle {
x: 0.0,
y: 0.0,
width: game_core.resources.background_front.width as f32,
height: game_core.resources.background_front.height as f32,
},
Rectangle::new(
0.0,
0.0,
(game_core.resources.background_front.width * 2) as f32,
(game_core.resources.background_front.height * 2) as f32,
),
Vector2 { x: 0.0, y: 0.0 },
0.0,
Color::WHITE,
);
// Render fish
let fish_clone = game_core.world.fish.clone();
for fish in game_core.world.fish.iter_mut() {
if fish.update_position(&mut game_core.player, dt, &fish_clone) {
audio_system.play_sound(&game_core.resources.fish_pickup);
}
fish.render(context_2d, &mut game_core.resources);
}
context_2d.draw_texture_pro(
&game_core.resources.tut1,
Rectangle {
x: 0.0,
y: 0.0,
width: 44.0,
height: 41.0,
},
Rectangle {
x: 110.0,
y: 100.0,
width: 44.0,
height: 41.0,
},
Vector2 {
x: 0.0,
y: 0.0,
},
0.0,
Color::WHITE,
);
context_2d.draw_texture_pro(
&game_core.resources.tut2,
Rectangle {
x: 0.0,
y: 0.0,
width: 44.0,
height: 41.0,
},
Rectangle {
x: 160.0,
y: 110.0,
width: 44.0,
height: 41.0,
},
Vector2 {
x: 0.0,
y: 0.0,
},
0.0,
Color::WHITE,
);
// Render the world texture
context_2d.draw_texture_rec(
@ -73,50 +160,86 @@ impl InGameScreen {
) {
// Render every collider
for collider in game_core.world.colliders.iter() {
context_2d.draw_rectangle_lines_ex(
collider,
1,
Color::RED,
);
context_2d.draw_rectangle_lines_ex(collider, 1, Color::RED);
}
}
fn render_darkness(&mut self, draw_handle: &mut RaylibDrawHandle, game_core: &mut GameCore) {
// Calculate the min view radius based on the current flashlight
let mut min_radius = 0.0;
if game_core.player.inventory.flashlight.is_some() {
min_radius = game_core
.player
.inventory
.flashlight
.as_ref()
.unwrap()
.radius;
}
// Get the window center
let win_height = draw_handle.get_screen_height();
let win_width = draw_handle.get_screen_width();
// Calculate the occusion radius based on depth
let radius = (1.0
- (game_core.player.calculate_depth_percent(&game_core.world) * 1.3).clamp(0.0, 1.0))
.max(min_radius);
// Determine width and height scales
// This is clamped to make the rendering logic below easier by removing the need to overdraw
let width_scale = (5.0 * radius).max(0.5);
let height_scale = (5.0 * radius).max(0.5);
// Get the base sizes of everything
let texture_width = game_core.resources.darkness_overlay.width as f32;
let texture_height = game_core.resources.darkness_overlay.height as f32;
let texture_width_scaled = texture_width * width_scale;
let texture_height_scaled = texture_height * height_scale;
// Render the overlay
draw_handle.draw_texture_pro(
&game_core.resources.darkness_overlay,
Rectangle {
x: 0.0,
y: 0.0,
width: texture_width,
height: texture_height,
},
Rectangle {
x: (win_width as f32 - texture_width_scaled) / 2.0,
y: (win_height as f32 - texture_height_scaled) / 2.0,
width: texture_width_scaled,
height: texture_height_scaled,
},
Vector2 { x: 0.0, y: 0.0 },
0.0,
Color::WHITE,
);
}
}
impl Screen for InGameScreen {
fn render(
&mut self,
draw_handle: &mut RaylibDrawHandle,
thread: &RaylibThread,
audio_system: &mut AudioPlayer,
_thread: &RaylibThread,
_audio_system: &mut AudioPlayer,
game_core: &mut GameCore,
) -> Option<GameState> {
// Calculate DT
let dt = draw_handle.get_time() - game_core.last_frame_time;
// Window dimensions
let win_height = draw_handle.get_screen_height();
let win_width = draw_handle.get_screen_width();
let window_center = Vector2 {
x: (win_width as f32 / 2.0),
y: (win_height as f32 / 2.0),
};
// Creates items for shop
if draw_handle.get_time() - game_core.last_state_change_time <= 0.05{
self.shop.create_items(Vector2::new(win_width as f32, win_height as f32));
}
// Clear frame
draw_handle.clear_background(Color::BLACK);
// Handle the pause menu being opened
if draw_handle.is_key_pressed(KeyboardKey::KEY_ESCAPE) {
return Some(GameState::PauseMenu);
}
// music
if !_audio_system.is_sound_playing(&game_core.resources.song_swim) {
_audio_system.play_sound(&game_core.resources.song_swim);
}
// Window dimensions
let win_height = draw_handle.get_screen_height();
let win_width = draw_handle.get_screen_width();
@ -124,55 +247,155 @@ impl Screen for InGameScreen {
x: (win_width as f32 / 2.0),
y: (win_height as f32 / 2.0),
};
let camera_window_center = window_center * (1.0 / game_core.master_camera.zoom);
// Update player movement
playerlogic::update_player_movement(draw_handle, game_core, window_center, _audio_system);
if draw_handle.get_time() % 10.0 <= 0.1 && !_audio_system.is_sound_playing(&game_core.resources.breath){
_audio_system.set_sound_volume(&game_core.resources.breath, 0.5);
_audio_system.play_sound(&game_core.resources.breath);
}
// Open a 2D context
{
let mut context_2d = draw_handle.begin_mode2D(game_core.master_camera);
// Render the world
self.render_world(&mut context_2d, game_core);
if game_core.show_simple_debug_info{
self.render_colliders(&mut context_2d, game_core);
unsafe {
raylib::ffi::BeginTextureMode(*game_core.resources.shader_texture);
}
{
let mut context_2d = draw_handle.begin_mode2D(game_core.master_camera);
// Render entities
let fish_clone = game_core.world.fish.clone();
for fish in game_core.world.fish.iter_mut() {
fish.update_position(&mut game_core.player, dt, &fish_clone);
fish.render(&mut context_2d);
// Clear frame
context_2d.clear_background(Color::BLACK);
// Render the world
self.render_world(&mut context_2d, game_core, dt, _audio_system);
if game_core.show_simple_debug_info {
self.render_colliders(&mut context_2d, game_core);
}
// Render entities
for jellyfish in game_core.world.jellyfish.iter_mut() {
jellyfish.handle_logic(&mut game_core.player, dt);
jellyfish.render(
&mut context_2d,
&mut game_core.player,
&mut game_core.resources,
dt,
);
}
for octopus in game_core.world.octopus.iter_mut() {
if octopus.handle_logic(&mut game_core.player, dt) == 1 {
_audio_system.play_sound(&game_core.resources.succ);
}
octopus.render(
&mut context_2d,
&mut game_core.player,
&mut game_core.resources,
dt,
);
}
// Iterates over whirlpools and runs render and logic funcs
for whirlpool_mob in game_core.world.whirlpool.iter_mut(){
whirlpool_mob.handle_logic(&mut game_core.player, dt);
whirlpool_mob.render(&mut context_2d, &mut game_core.player, &mut game_core.resources, dt);
// Spawns 10 fish on spawn
if whirlpool_mob.should_remove(){
for _ in 0..10{
game_core.world.fish.push(FishEntity::new(whirlpool_mob.position));
}
}
}
// Iterates over pufferfish
for pufferfish in game_core.world.pufferfish.iter_mut(){
pufferfish.handle_logic(&mut game_core.player, dt);
pufferfish.render(&mut context_2d, &mut game_core.player, &mut game_core.resources, dt);
}
// Removes whirlpools set for removal
game_core.world.whirlpool.retain(|x| !x.should_remove());
// Render transponder
game_core.resources.transponder.draw(
&mut context_2d,
game_core.world.end_position,
0.0,
);
// Render Player
game_core
.player
.render(&mut context_2d, &mut game_core.resources, dt);
}
unsafe {
raylib::ffi::EndTextureMode();
}
// Render Player
// playerlogic::render_player(&mut context_2d, game_core);
game_core.player.render(&mut context_2d, &mut game_core.resources, dt);
}
// Update the shader's internal time
game_core
.resources
.pixel_shader
.set_shader_value(self.shader_time_var_location, draw_handle.get_time() as f32);
// Render the 2D context via the ripple shader
{
let mut shader_context =
draw_handle.begin_shader_mode(&game_core.resources.pixel_shader);
// Blit the texture
shader_context.draw_texture_pro(
&game_core.resources.shader_texture,
Rectangle {
x: 0.0,
y: 0.0,
width: game_core.resources.shader_texture.width() as f32,
height: (game_core.resources.shader_texture.height() as f32) * -1.0,
},
Rectangle {
x: -10.0,
y: -10.0,
width: win_width as f32 + 20.0,
height: win_height as f32 + 20.0,
},
Vector2::zero(),
0.0,
Color::WHITE,
);
}
// Render the darkness layer
self.render_darkness(draw_handle, game_core);
// Only render shop in shop period, otherwise allow player movement
if draw_handle.get_time() - game_core.last_state_change_time >= 0.05
&& self.current_state == InGameState::BUYING{
shop::render_shop(draw_handle, game_core, self);
}else{
// Update player movement
playerlogic::update_player_movement(draw_handle, game_core, window_center);
}
// Render the hud
hud::render_hud(draw_handle, game_core, window_center);
// Handle player out of breath
if game_core.player.breath_percent == 0.0 {
return Some(GameState::GameEnd);
}
if game_core
.world
.end_position
.distance_to(game_core.player.position)
<= 40.0
{
return Some(GameState::WinGame);
}
return None;
}
}

View File

@ -1,30 +1,33 @@
use raylib::core::audio::RaylibAudio;
use raylib::prelude::*;
use rand::{prelude::ThreadRng, Rng};
use crate::{gamecore::GameCore, lib::wrappers::audio::player::AudioPlayer};
use crate::{
gamecore::GameCore,
pallette::{TRANSLUCENT_WHITE_128, TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96},
};
const NORMAL_PLAYER_SPEED: i32 = 3;
const NORMAL_PLAYER_SPEED: i32 = 1;
const BOOST_PLAYER_SPEED: i32 = NORMAL_PLAYER_SPEED * 2;
const CAMERA_FOLLOW_SPEED: f32 = 0.7;
const PLAYER_FRICTION: f32 = 1.05;
const WHIRLPOOL_PULL: f32 = 3.0;
const TURN_SPEED: f32 = 0.15;
const BOOST_DECREASE_PER_SECOND: f32 = 0.65;
const BOOST_REGEN_PER_SECOND: f32 = 0.25;
const BREATH_DECREASE_PER_SECOND: f32 = 0.01;
const BREATH_DECREASE_PER_SECOND: f32 = 0.02;
pub fn update_player_movement(
draw_handle: &mut RaylibDrawHandle,
game_core: &mut GameCore,
window_center: Vector2,
audio_system: &mut AudioPlayer
) {
// Calculate DT
let dt = draw_handle.get_time() - game_core.last_frame_time;
// Handle player movement
let mouse_pose = draw_handle.get_mouse_position();
let mouse_world_pose = draw_handle.get_screen_to_world2D(mouse_pose, game_core.master_camera);
let raw_movement_direction = mouse_world_pose - game_core.player.position;
let mut raw_movement_direction = mouse_world_pose - game_core.player.position;
let mut normalized_movement_direction = raw_movement_direction;
normalized_movement_direction.normalize();
@ -65,7 +68,9 @@ pub fn update_player_movement(
}
// set angle
game_core.player.direction = Vector2::new(f32::cos(player_angle), f32::sin(player_angle));
if !game_core.player.is_stunned() {
game_core.player.direction = Vector2::new(f32::cos(player_angle), f32::sin(player_angle));
}
// In the case the player is in "null", just jump the camera to them
if game_core.player.position == Vector2::zero() {
@ -76,16 +81,49 @@ pub fn update_player_movement(
let user_request_boost = draw_handle.is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON);
let user_request_action = draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON);
if user_request_action && !game_core.player.has_stunned {
game_core.player.has_stunned = true;
game_core
.player
.begin_attack(&mut game_core.world, draw_handle.get_time());
}
if user_request_action {
game_core.player.begin_attack();
// println!("{{\"x\":{}, \"y\":{}}},",f32::round(game_core.player.position.x),f32::round(game_core.player.position.y));
}
// die sound
if game_core.player.breath_percent <= 0.06 {
if !audio_system.is_sound_playing(&game_core.resources.die) {
audio_system.play_sound(&game_core.resources.die);
}
}
if (game_core.player.stun_timer > 0.0 || game_core.player.is_stun_gun_active()) && !audio_system.is_sound_playing(&game_core.resources.zap) {
audio_system.play_sound(&game_core.resources.zap);
}
// Move the player in their direction
let speed_multiplier;
let mut speed_multiplier;
if user_request_boost && game_core.player.boost_percent >= 0.0 {
// Set the speed multiplier
speed_multiplier = BOOST_PLAYER_SPEED as f32;
// swim sound
if !audio_system.is_sound_playing(&game_core.resources.swim1) && !audio_system.is_sound_playing(&game_core.resources.swim2) && !audio_system.is_sound_playing(&game_core.resources.swim3) && !audio_system.is_sound_playing(&game_core.resources.swim4) {
let mut rng = rand::thread_rng();
let num = rng.gen_range(0..3);
if num == 0 {
audio_system.play_sound(&game_core.resources.swim1);
} else if num == 1 {
audio_system.play_sound(&game_core.resources.swim2);
} else if num == 2 {
audio_system.play_sound(&game_core.resources.swim3);
} else {
audio_system.play_sound(&game_core.resources.swim4);
}
}
// Decrease the boost
game_core.player.boost_percent -= BOOST_DECREASE_PER_SECOND * dt as f32;
game_core.player.is_boosting = true;
@ -128,49 +166,179 @@ pub fn update_player_movement(
// Handle flippers doing a speed increase
if game_core.player.inventory.flippers.is_some() {
let speed_multiplier = speed_multiplier * game_core.player.inventory.flippers.as_ref().unwrap().speed_increase;
speed_multiplier = speed_multiplier
* game_core
.player
.inventory
.flippers
.as_ref()
.unwrap()
.speed_increase;
}
// Update the player's breath
game_core.player.breath_percent =
(game_core.player.breath_percent - BREATH_DECREASE_PER_SECOND * dt as f32).clamp(0.0, 1.0);
(game_core.player.breath_percent - BREATH_DECREASE_PER_SECOND * dt as f32).max(0.0);
// Only do this if the mouse is far enough away
let player_real_movement = game_core.player.direction * speed_multiplier;
let player_stunned = game_core.player.stun_timer > 0.0;
if raw_movement_direction.distance_to(Vector2::zero()) > game_core.player.size.y / 2.0 || player_stunned{
game_core.player.is_moving = true;
game_core.player.position += player_real_movement;
let mut player_real_movement = game_core.player.direction * speed_multiplier;
// Check for any collisions
for collider in game_core.world.colliders.iter() {
if game_core.player.collides_with_rec(collider) {
game_core.player.is_moving = false;
break;
}
// Handle the player wearing flippers
if game_core.player.inventory.flippers.is_some() {
player_real_movement *= game_core
.player
.inventory
.flippers
.as_ref()
.unwrap()
.speed_increase;
}
let mut should_apply_friction: bool = true;
// Check each whirlpool for effects
for whirlpool in game_core.world.whirlpool.iter_mut(){
// check if its in range and not to close
if game_core.player.position.distance_to(whirlpool.position) <= 50.0 && game_core.player.position.distance_to(whirlpool.position) >= 10.0{
// Calculates info for formulas
// Deltas between positions
let net_pose = game_core.player.position - whirlpool.position;
// Angle between: UNITS: RADIANS
let angle = net_pose.y.atan2(net_pose.x);
// Calculates force
let force = WHIRLPOOL_PULL / game_core.player.position.distance_to(whirlpool.position);
// Calculates componets of force
let mut force_x = (force as f32 * angle.cos()).clamp(-5.0, 5.0);
let mut force_y = (force as f32 * angle.sin()).clamp(-5.0, 5.0);
// Prevents Nan erros
if force_x.is_nan(){
force_x = 5.0 * net_pose.x;
}
if force_y.is_nan(){
force_y = 5.0 * net_pose.y;
}
// Adds values to drift tracker
game_core.player.additional_vel.x -= force_x;
game_core.player.additional_vel.y -= force_y;
should_apply_friction = false;
}
}
for pufferfish in game_core.world.pufferfish.iter_mut(){
if pufferfish.is_knocking_back{
// Calculates info for formulas
// Deltas between positions
let net_pose = game_core.player.position - pufferfish.position;
// Angle between: UNITS: RADIANS
let angle = net_pose.y.atan2(net_pose.x);
// Calculates force
let force = 0.2;
// Calculates componets of force
let mut force_x = (force as f32 * angle.cos()).clamp(-1.0, 1.0);
let mut force_y = (force as f32 * angle.sin()).clamp(-1.0, 1.0);
// Prevents Nan erros
if force_x.is_nan(){
force_x = 1.0 * net_pose.x;
}
if force_y.is_nan(){
force_y = 1.0 * net_pose.y;
}
game_core.player.additional_vel.x += force_x;
game_core.player.additional_vel.y += force_y;
should_apply_friction = false;
}
}
if should_apply_friction {
game_core.player.additional_vel.x /= PLAYER_FRICTION;
game_core.player.additional_vel.y /= PLAYER_FRICTION;
if f32::round(game_core.player.additional_vel.x * 10.0) == 0.0 {
game_core.player.additional_vel.x = 0.0;
}
if !game_core.player.is_moving {
game_core.player.position -= player_real_movement;
}
} else {
game_core.player.is_moving = false;
// Handle updating the stun timer
if player_stunned {
game_core.player.stun_timer -= dt;
if f32::round(game_core.player.additional_vel.y * 10.0) == 0.0 {
game_core.player.additional_vel.y = 0.0;
}
}
if !(raw_movement_direction.distance_to(Vector2::zero()) > game_core.player.size.y / 2.0) {
player_real_movement = Vector2::zero();
}
// Handle movement and collisions
if !game_core.player.is_stunned() {
if game_core.player.is_moving {
// move in x
game_core.player.position.x += player_real_movement.x + game_core.player.additional_vel.x;
// Check for any collisions
for collider in game_core.world.colliders.iter() {
if game_core.player.collides_with_rec(collider) {
game_core.player.position.x -= player_real_movement.x + game_core.player.additional_vel.x;
player_real_movement.x = 0.0;
game_core.player.additional_vel.x = 0.0;
break;
}
}
// move in y
game_core.player.position.y += player_real_movement.y + game_core.player.additional_vel.y;
// Check for any collisions
for collider in game_core.world.colliders.iter() {
if game_core.player.collides_with_rec(collider) {
game_core.player.position.y -= player_real_movement.y + game_core.player.additional_vel.y;
player_real_movement.y = 0.0;
game_core.player.additional_vel.y = 0.0;
break;
}
}
}
}
// Handle updating the stun timer
if player_stunned {
game_core.player.stun_timer -= dt;
}
// Move the camera to follow the player
let direction_from_cam_to_player =
(game_core.player.position - window_center) - game_core.master_camera.target;
let player_screen_position =
draw_handle.get_world_to_screen2D(game_core.player.position, game_core.master_camera);
// Camera only moves if you get close to the edge of the screen
if player_screen_position.distance_to(window_center).abs() > 100.0 {
game_core.master_camera.target += player_real_movement;
game_core.master_camera.target += player_real_movement + game_core.player.additional_vel;
}
// If the player is not on screen, snap the camera to them
@ -183,4 +351,3 @@ pub fn update_player_movement(
// game_core.master_camera.target.y = -100.0;
// }
}

View File

@ -1,205 +0,0 @@
use std::u8;
use raylib::prelude::*;
use crate::{gamecore::GameCore, items, items::ShopItems};
use super::{InGameScreen, InGameState};
pub struct Item {
x_pose: i32,
y_pose: i32,
width: i32,
height: i32,
cost: u8,
level: u8,
name: String,
}
pub struct Shop {
shop_items: Vec<Item>,
}
impl Shop {
pub fn new() -> Self {
Self {
shop_items: Vec::new(),
}
}
// Creates all the items
pub fn create_items(&mut self, screen_dimension: Vector2) {
// gets every item.. hacky
let items = ShopItems::get_inital_items();
// sets sizes any random number is just a number I think looks good
let screen_width = screen_dimension.x as f32;
let screen_height = screen_dimension.y as f32;
let box_height = screen_height * 0.15;
let box_width = screen_width * 0.1;
let start_width = screen_width - (box_width * 4.0) - 40.0;
let draw_height = screen_height - 20.0 - box_height;
let mut item_vec = Vec::new();
for box_num in 0..4 {
let x_pose = start_width + box_width * box_num as f32;
// adds an item struct to the item list
item_vec.push(Item {
x_pose: ((x_pose + (5 * box_num) as f32) as i32),
y_pose: (draw_height as i32),
width: (box_width as i32),
height: (box_height as i32),
// Crazy hacky but this gets the data from the enum
cost: (ShopItems::get_cost(&items.get(box_num).unwrap())),
level: (ShopItems::get_level(&items.get(box_num).unwrap())),
name: (ShopItems::get_name(&items.get(box_num).unwrap())),
});
}
self.shop_items = item_vec;
}
}
pub fn render_shop(
draw_handle: &mut RaylibDrawHandle,
game_core: &mut GameCore,
inGameScreen: &mut InGameScreen,
) {
// Pressing F exits from buying
if draw_handle.is_key_pressed(KeyboardKey::KEY_F) {
inGameScreen.current_state = InGameState::SWIMMING;
}
let mouse_position = draw_handle.get_mouse_position();
draw_handle.draw_text(
&format!("Coins: {}", game_core.player.coins),
15,
15,
30,
Color::WHITE,
);
// Draws shop boxes
for mut item in inGameScreen.shop.shop_items.iter_mut() {
// If hovering on square draw full
if mouse_position.x >= item.x_pose as f32
&& mouse_position.x <= item.x_pose as f32 + item.width as f32
&& mouse_position.y >= item.y_pose as f32
&& mouse_position.y <= item.y_pose as f32 + item.width as f32
{
// Draw rect
draw_handle.draw_rectangle(
item.x_pose,
item.y_pose,
item.width,
item.height,
Color::BLACK,
);
// Preform purchasing functions
if draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON)
&& game_core.player.coins >= item.cost as u32
{
// Remove currency
game_core.world.spend_coins(item.cost.into());
game_core.player.coins -= item.cost as u32;
// Upgrade item in inventory
match &(item.name)[..] {
"Stun Gun" => {
match item.level {
0 => {
game_core.player.inventory.stun_gun = Some(items::StunGun::lvl1())
}
1 => {
game_core.player.inventory.stun_gun = Some(items::StunGun::lvl2())
}
2 => {
game_core.player.inventory.stun_gun = Some(items::StunGun::lvl3())
}
_ => (return),
};
item.cost += 5;
item.level += 1;
}
"Air Bag" => {
match item.level {
0 => {
game_core.player.inventory.air_bag = Some(items::AirBag::lvl1());
}
1 => {
game_core.player.inventory.air_bag = Some(items::AirBag::lvl2());
}
2 => {
game_core.player.inventory.air_bag = Some(items::AirBag::lvl3());
}
_ => (return),
};
item.cost += 5;
item.level += 1;
}
"Flash Light" => {
match item.level {
0 => {
game_core.player.inventory.flashlight = Some(items::Flashlight::lvl1());
}
1 => {
game_core.player.inventory.flashlight = Some(items::Flashlight::lvl2());
}
2 => {
game_core.player.inventory.flashlight = Some(items::Flashlight::lvl3());
}
_ => (return),
};
item.cost += 5;
item.level += 1;
},
"Flippers" => {
match item.level {
0 => {
game_core.player.inventory.flippers = Some(items::Flippers::lvl1());
}
1 => {
game_core.player.inventory.flippers = Some(items::Flippers::lvl2());
}
2 => {
game_core.player.inventory.flippers = Some(items::Flippers::lvl3());
}
_ => (return),
};
item.cost += 5;
item.level += 1;
},
_ => (return),
};
}
} else {
// outlines if not hovered
draw_handle.draw_rectangle_lines(
item.x_pose,
item.y_pose,
item.width,
item.height,
Color::BLACK,
);
}
// Draw text about object
draw_handle.draw_text(
&format!("{}: ${}", item.name, item.cost),
item.x_pose + 5,
item.y_pose + 5,
12,
Color::BLACK,
);
}
}

View File

@ -1,11 +1,13 @@
use raylib::prelude::*;
use crate::{gamecore::{GameCore, GameState}, lib::{utils::calculate_linear_slide, wrappers::audio::player::AudioPlayer}};
use crate::{
gamecore::{GameCore, GameState},
lib::{utils::calculate_linear_slide, wrappers::audio::player::AudioPlayer},
};
use super::screen::Screen;
const SECONDS_PER_LOGO: f64 = 4.0;
const RUST_ORANGE: Color = Color::new(222, 165, 132, 255);
#[derive(Debug, PartialEq)]
enum LoadingScreenState {
@ -47,30 +49,30 @@ impl LoadingScreen {
win_height: i32,
win_width: i32,
) {
// Determine how far through rendering this logo we are
// This value is used to determine the logo alpha
let playthrough_percent =
(draw_handle.get_time() - self.last_state_switch_time) / SECONDS_PER_LOGO;
// // Determine how far through rendering this logo we are
// // This value is used to determine the logo alpha
// let playthrough_percent =
// (draw_handle.get_time() - self.last_state_switch_time) / SECONDS_PER_LOGO;
// Build a color mask
let mask = self.get_logo_mask(playthrough_percent);
// // Build a color mask
// let mask = self.get_logo_mask(playthrough_percent);
// Get the logo
let logo = &game_core.resources.game_logo;
// // Get the logo
// let logo = &game_core.resources.game_logo;
// Render the logo
draw_handle.draw_texture(
logo,
(win_width / 2) - (logo.width / 2),
(win_height / 2) - (logo.height / 2),
mask,
);
// // Render the logo
// draw_handle.draw_texture(
// logo,
// (win_width / 2) - (logo.width / 2),
// (win_height / 2) - (logo.height / 2),
// mask,
// );
// Move on to next logo if needed
if playthrough_percent >= 1.0 {
// if playthrough_percent >= 1.0 {
self.state = LoadingScreenState::RaylibLogo;
self.last_state_switch_time = draw_handle.get_time();
}
// }
}
fn show_raylib_logo(
@ -89,10 +91,10 @@ impl LoadingScreen {
let mask = self.get_logo_mask(playthrough_percent);
// Create modified colors
let alpha_orange = Color {
r: RUST_ORANGE.r,
g: RUST_ORANGE.g,
b: RUST_ORANGE.b,
let alpha_black = Color {
r: 0,
g: 0,
b: 0,
a: mask.a,
};
@ -102,7 +104,7 @@ impl LoadingScreen {
win_height / 2 - 128,
256,
256,
alpha_orange,
alpha_black,
);
draw_handle.draw_rectangle(
win_width / 2 - 112,
@ -111,19 +113,19 @@ impl LoadingScreen {
224,
Color::WHITE,
);
draw_handle.draw_text(
"rust",
win_width / 2 - 69,
win_height / 2 + 18,
50,
alpha_orange,
);
// draw_handle.draw_text(
// "rust",
// win_width / 2 - 69,
// win_height / 2 + 18,
// 50,
// alpha_orange,
// );
draw_handle.draw_text(
"raylib",
win_width / 2 - 44,
win_height / 2 + 48,
50,
alpha_orange,
alpha_black,
);
// Move on to next logo if needed
@ -138,7 +140,7 @@ impl Screen for LoadingScreen {
fn render(
&mut self,
draw_handle: &mut RaylibDrawHandle,
thread: &RaylibThread,
_thread: &RaylibThread,
_audio_system: &mut AudioPlayer,
game_core: &mut GameCore,
) -> Option<GameState> {

View File

@ -19,55 +19,84 @@ impl Screen for MainMenuScreen {
fn render(
&mut self,
draw_handle: &mut RaylibDrawHandle,
thread: &RaylibThread,
audio_system: &mut AudioPlayer,
_thread: &RaylibThread,
_audio_system: &mut AudioPlayer,
game_core: &mut GameCore,
) -> Option<GameState> {
// Window dimensions
let win_height = draw_handle.get_screen_height();
let win_width = draw_handle.get_screen_width();
// Clear frame
draw_handle.clear_background(Color::BLUE);
// // Clear frame
// draw_handle.clear_background(Color::BLUE);
// Render the background
draw_handle.draw_texture(&game_core.resources.shop_background, 0, 0, Color::WHITE);
// Render title
draw_handle.draw_text(
"ONE BREATH",
(win_height / 2) - 80,
win_width / 4,
40,
"DEEP BREATH",
(win_height / 2) - 100,
win_width / 8,
80,
Color::BLACK,
);
// Get mouse position data
let mouse_position = draw_handle.get_mouse_position();
let hovering_play_button = mouse_position.y > (win_width as f32 / 4.0)
&& mouse_position.y < (win_width as f32 / 4.0) + 60.0;
let hovering_shop_button = mouse_position.y > (win_width as f32 / 4.0) + 100.0
&& mouse_position.y < (win_width as f32 / 4.0) + 160.0;
let hovering_quit_button = mouse_position.y > (win_width as f32 / 4.0) + 200.0
&& mouse_position.y < (win_width as f32 / 4.0) + 260.0;
// Play and quit
draw_handle.draw_text(
"Play",
(win_height / 2) - 80,
(win_height / 2) + 120,
win_width / 4,
60,
match hovering_play_button {
true => Color::BLUE,
false => Color::BLACK,
},
);
draw_handle.draw_text(
"Shop",
(win_height / 2) + 120,
(win_width / 4) + 100,
20,
Color::BLACK,
60,
match hovering_shop_button {
true => Color::BLUE,
false => Color::BLACK,
},
);
draw_handle.draw_text(
"Quit",
(win_height / 2) - 80,
(win_width / 4) + 140,
20,
Color::BLACK,
(win_height / 2) + 130,
(win_width / 4) + 200,
60,
match hovering_quit_button {
true => Color::RED,
false => Color::BLACK,
},
);
// Handle button presses
let mouse_position = draw_handle.get_mouse_position();
let mouse_clicked = draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON);
// Check clicks
if mouse_clicked {
if mouse_position.y > (win_width as f32 / 4.0) + 100.0
&& mouse_position.y < (win_width as f32 / 4.0) + 120.0
{
if hovering_play_button {
// Reset the world
game_core.world.reset(&mut game_core.player);
_audio_system.play_sound(&game_core.resources.ui_click);
// Start playing
return Some(GameState::InGame);
} else if mouse_position.y > (win_width as f32 / 4.0) + 140.0
&& mouse_position.y < (win_width as f32 / 4.0) + 180.0
{
} else if hovering_shop_button {
_audio_system.play_sound(&game_core.resources.ui_click);
return Some(GameState::InShop);
} else if hovering_quit_button {
return Some(GameState::GameQuit);
}
}

View File

@ -3,4 +3,6 @@ pub mod loadingscreen;
pub mod mainmenu;
pub mod pausemenu;
pub mod ingame;
pub mod gameend;
pub mod gameend;
pub mod shop;
pub mod winscreen;

View File

@ -2,7 +2,7 @@ use raylib::prelude::*;
use crate::{
gamecore::{GameCore, GameState},
lib::wrappers::audio::player::AudioPlayer,
lib::{utils::button::OnScreenButton, wrappers::audio::player::AudioPlayer},
};
use super::screen::Screen;
@ -26,8 +26,11 @@ impl Screen for PauseMenuScreen {
game_core: &mut GameCore,
) -> Option<GameState> {
let mouse_position = draw_handle.get_mouse_position();
draw_handle.clear_background(Color::GRAY);
// TODO: Maybe we can stick some art here?
// draw_handle.clear_background(Color::GRAY);
// // TODO: Maybe we can stick some art here?
// Render the background
draw_handle.draw_texture(&game_core.resources.shop_background, 0, 0, Color::WHITE);
// If escape is pressed again, return to the previous render state
if draw_handle.is_key_pressed(KeyboardKey::KEY_ESCAPE) {
@ -120,14 +123,15 @@ impl Screen for PauseMenuScreen {
// Render credits
draw_handle.draw_text(
"Credits:\n\t- @ewpratten\n\t- @rsninja722\n\t- @wm-c\n\t- @catarinaburghi",
"Credits:\n\t- @ewpratten\n\t- @rsninja722\n\t- @wm-c\n\t- @catarinaburghi\n\t- @kondroel",
(win_width / 2) - (SCREEN_PANEL_SIZE.x as i32 / 2) + 10,
(win_height / 2) - (SCREEN_PANEL_SIZE.y as i32 / 2) + 120,
(win_height / 2) - (SCREEN_PANEL_SIZE.y as i32 / 2) + 150,
20,
Color::BLACK,
);
// Close and quit buttons
// Bottom buttons
let bottom_left_button_dimensions = Rectangle {
x: (win_width as f32 / 2.0) - (SCREEN_PANEL_SIZE.x / 2.0) + 5.0,
y: (win_height as f32 / 2.0) + (SCREEN_PANEL_SIZE.y / 2.0) - 50.0,
@ -141,49 +145,36 @@ impl Screen for PauseMenuScreen {
height: bottom_left_button_dimensions.height,
};
// Check if the mouse is over either button
let mouse_over_bottom_left_button =
bottom_left_button_dimensions.check_collision_point_rec(mouse_position);
let mouse_over_bottom_right_button =
bottom_right_button_dimensions.check_collision_point_rec(mouse_position);
// Render buttons
draw_handle.draw_rectangle_lines_ex(
let menu_button = OnScreenButton::new(
"Menu".to_string(),
bottom_left_button_dimensions,
3,
match mouse_over_bottom_left_button {
true => Color::GRAY,
false => Color::BLACK,
},
);
draw_handle.draw_text(
"Quit",
bottom_left_button_dimensions.x as i32 + 15,
bottom_left_button_dimensions.y as i32 + 5,
30,
Color::WHITE,
Color::BLACK,
Color::GRAY,
30,
true,
);
draw_handle.draw_rectangle_lines_ex(
let close_button = OnScreenButton::new(
"Close".to_string(),
bottom_right_button_dimensions,
3,
match mouse_over_bottom_right_button {
true => Color::GRAY,
false => Color::BLACK,
},
);
draw_handle.draw_text(
"Close",
bottom_right_button_dimensions.x as i32 + 15,
bottom_right_button_dimensions.y as i32 + 5,
30,
Color::WHITE,
Color::BLACK,
Color::GRAY,
30,
true,
);
// Render both
menu_button.render(draw_handle);
close_button.render(draw_handle);
// Handle click actions on the buttons
if draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) {
if mouse_over_bottom_left_button {
return Some(GameState::GameQuit);
} else if mouse_over_bottom_right_button {
if menu_button.is_hovered(draw_handle) {
audio_system.play_sound(&game_core.resources.ui_click);
return Some(GameState::MainMenu);
} else if close_button.is_hovered(draw_handle) {
audio_system.play_sound(&game_core.resources.ui_click);
return Some(game_core.last_state);
}
}

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