Merge branch 'master' into shop
This commit is contained in:
commit
f77a8755b8
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ludum-dare-48"
|
name = "one-breath"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Evan Pratten <ewpratten@gmail.com>"]
|
authors = ["Evan Pratten <ewpratten@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
BIN
assets/img/character/diveNormal.aseprite
Normal file
BIN
assets/img/character/diveNormal.aseprite
Normal file
Binary file not shown.
82
assets/img/character/diveNormal.json
Normal file
82
assets/img/character/diveNormal.json
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{ "frames": {
|
||||||
|
"Sprite-0005 0.": {
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
"Sprite-0005 1.": {
|
||||||
|
"frame": { "x": 11, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
"Sprite-0005 2.": {
|
||||||
|
"frame": { "x": 22, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
"Sprite-0005 3.": {
|
||||||
|
"frame": { "x": 33, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
"Sprite-0005 4.": {
|
||||||
|
"frame": { "x": 44, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
"Sprite-0005 5.": {
|
||||||
|
"frame": { "x": 55, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
"Sprite-0005 6.": {
|
||||||
|
"frame": { "x": 66, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
"Sprite-0005 7.": {
|
||||||
|
"frame": { "x": 77, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"app": "http://www.aseprite.org/",
|
||||||
|
"version": "1.2.27-x64",
|
||||||
|
"image": "diveNormal.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": { "w": 88, "h": 21 },
|
||||||
|
"scale": "1",
|
||||||
|
"frameTags": [
|
||||||
|
],
|
||||||
|
"layers": [
|
||||||
|
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
|
||||||
|
],
|
||||||
|
"slices": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
BIN
assets/img/character/diveNormal.png
Normal file
BIN
assets/img/character/diveNormal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 657 B |
BIN
assets/img/character/diveStroke.aseprite
Normal file
BIN
assets/img/character/diveStroke.aseprite
Normal file
Binary file not shown.
186
assets/img/character/diveStroke.json
Normal file
186
assets/img/character/diveStroke.json
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
{ "frames": {
|
||||||
|
"diveStroke 0.aseprite": {
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 1.aseprite": {
|
||||||
|
"frame": { "x": 17, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 2.aseprite": {
|
||||||
|
"frame": { "x": 34, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 3.aseprite": {
|
||||||
|
"frame": { "x": 51, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 4.aseprite": {
|
||||||
|
"frame": { "x": 68, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 5.aseprite": {
|
||||||
|
"frame": { "x": 85, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 6.aseprite": {
|
||||||
|
"frame": { "x": 102, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 7.aseprite": {
|
||||||
|
"frame": { "x": 119, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 8.aseprite": {
|
||||||
|
"frame": { "x": 136, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 9.aseprite": {
|
||||||
|
"frame": { "x": 153, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 10.aseprite": {
|
||||||
|
"frame": { "x": 170, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 11.aseprite": {
|
||||||
|
"frame": { "x": 187, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 12.aseprite": {
|
||||||
|
"frame": { "x": 204, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 13.aseprite": {
|
||||||
|
"frame": { "x": 221, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 14.aseprite": {
|
||||||
|
"frame": { "x": 238, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 15.aseprite": {
|
||||||
|
"frame": { "x": 255, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 16.aseprite": {
|
||||||
|
"frame": { "x": 272, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 17.aseprite": {
|
||||||
|
"frame": { "x": 289, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 18.aseprite": {
|
||||||
|
"frame": { "x": 306, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 19.aseprite": {
|
||||||
|
"frame": { "x": 323, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
},
|
||||||
|
"diveStroke 20.aseprite": {
|
||||||
|
"frame": { "x": 340, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 },
|
||||||
|
"sourceSize": { "w": 17, "h": 21 },
|
||||||
|
"duration": 75
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"app": "http://www.aseprite.org/",
|
||||||
|
"version": "1.2.27-x64",
|
||||||
|
"image": "diveStroke.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": { "w": 357, "h": 21 },
|
||||||
|
"scale": "1",
|
||||||
|
"frameTags": [
|
||||||
|
],
|
||||||
|
"layers": [
|
||||||
|
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
|
||||||
|
],
|
||||||
|
"slices": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
BIN
assets/img/character/diveStroke.png
Normal file
BIN
assets/img/character/diveStroke.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/img/character/diveStrokeCharge.aseprite
Normal file
BIN
assets/img/character/diveStrokeCharge.aseprite
Normal file
Binary file not shown.
50
assets/img/character/diveStrokeCharge.json
Normal file
50
assets/img/character/diveStrokeCharge.json
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{ "frames": {
|
||||||
|
"diveStroke 0.aseprite": {
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
"diveStroke 1.aseprite": {
|
||||||
|
"frame": { "x": 11, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
"diveStroke 2.aseprite": {
|
||||||
|
"frame": { "x": 22, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
},
|
||||||
|
"diveStroke 3.aseprite": {
|
||||||
|
"frame": { "x": 33, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": false,
|
||||||
|
"spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 },
|
||||||
|
"sourceSize": { "w": 11, "h": 21 },
|
||||||
|
"duration": 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"app": "http://www.aseprite.org/",
|
||||||
|
"version": "1.2.27-x64",
|
||||||
|
"image": "diveStrokeCharge.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": { "w": 44, "h": 21 },
|
||||||
|
"scale": "1",
|
||||||
|
"frameTags": [
|
||||||
|
],
|
||||||
|
"layers": [
|
||||||
|
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
|
||||||
|
],
|
||||||
|
"slices": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
BIN
assets/img/character/diveStrokeCharge.png
Normal file
BIN
assets/img/character/diveStrokeCharge.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 666 B |
BIN
assets/img/character/walk1.png
Normal file
BIN
assets/img/character/walk1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 402 B |
1
assets/img/map/cave.json
Normal file
1
assets/img/map/cave.json
Normal file
File diff suppressed because one or more lines are too long
BIN
assets/img/map/cave.png
Normal file
BIN
assets/img/map/cave.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 292 KiB |
BIN
assets/img/map/tileset.png
Normal file
BIN
assets/img/map/tileset.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 894 B |
@ -3,6 +3,10 @@
|
|||||||
"x": 10000.0,
|
"x": 10000.0,
|
||||||
"y": 10000.0
|
"y": 10000.0
|
||||||
},
|
},
|
||||||
|
"player_spawn": {
|
||||||
|
"x": 220.0,
|
||||||
|
"y": 50.0
|
||||||
|
},
|
||||||
"fish": [
|
"fish": [
|
||||||
{
|
{
|
||||||
"x": 500.0,
|
"x": 500.0,
|
||||||
|
@ -9,10 +9,10 @@ set -e
|
|||||||
# Make a uni-bundle
|
# Make a uni-bundle
|
||||||
echo "Creating a fat bundle for all platforms"
|
echo "Creating a fat bundle for all platforms"
|
||||||
rm -rf ./bundle/release
|
rm -rf ./bundle/release
|
||||||
rm -rf ./bundle/ludum-dare-48.zip
|
rm -rf ./bundle/one-breath.zip
|
||||||
mkdir -p ./bundle/release
|
mkdir -p ./bundle/release
|
||||||
cp -r ./assets ./bundle/release
|
cp -r ./assets ./bundle/release
|
||||||
cp ./bundle/linux/release/ludum-dare-48 ./bundle/release/ludum-dare-48
|
cp ./bundle/linux/release/one-breath ./bundle/release/one-breath
|
||||||
cp ./bundle/windows/release/ludum-dare-48.exe ./bundle/release/ludum-dare-48.exe
|
cp ./bundle/windows/release/one-breath.exe ./bundle/release/one-breath.exe
|
||||||
cd ./bundle/release
|
cd ./bundle/release
|
||||||
zip -r ../ludum-dare-48.zip ./
|
zip -r ../one-breath.zip ./
|
@ -12,7 +12,7 @@ rm -rf ./bundle/linux/release-x86_64-unknown-linux-gnu.zip
|
|||||||
mkdir -p ./bundle/linux/release
|
mkdir -p ./bundle/linux/release
|
||||||
|
|
||||||
echo "Copying binary"
|
echo "Copying binary"
|
||||||
cp ./target/x86_64-unknown-linux-gnu/release/ludum-dare-48 ./bundle/linux/release
|
cp ./target/x86_64-unknown-linux-gnu/release/one-breath ./bundle/linux/release
|
||||||
|
|
||||||
echo "Copying assets"
|
echo "Copying assets"
|
||||||
cp -r ./assets ./bundle/linux/release
|
cp -r ./assets ./bundle/linux/release
|
||||||
|
@ -11,7 +11,7 @@ rm -rf ./bundle/windows/release-x86_64-pc-windows-gnu.zip
|
|||||||
mkdir -p ./bundle/windows/release
|
mkdir -p ./bundle/windows/release
|
||||||
|
|
||||||
echo "Copying binary"
|
echo "Copying binary"
|
||||||
cp ./target/x86_64-pc-windows-gnu/release/ludum-dare-48.exe ./bundle/windows/release
|
cp ./target/x86_64-pc-windows-gnu/release/one-breath.exe ./bundle/windows/release
|
||||||
|
|
||||||
echo "Copying assets"
|
echo "Copying assets"
|
||||||
cp -r ./assets ./bundle/windows/release
|
cp -r ./assets ./bundle/windows/release
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use rand::{Rng, prelude::ThreadRng};
|
use rand::{Rng, prelude::ThreadRng};
|
||||||
use raylib::prelude::*;
|
use raylib::prelude::*;
|
||||||
|
|
||||||
use crate::{gamecore::GameCore, lib::utils::triangles::rotate_vector, player::Player};
|
use crate::{gamecore::GameCore, lib::utils::triangles::rotate_vector, player::Player, world::World};
|
||||||
|
|
||||||
const FISH_FOLLOW_PLAYER_DISTANCE: f32 = 80.0;
|
const FISH_FOLLOW_PLAYER_DISTANCE: f32 = 30.0;
|
||||||
const FISH_FOLLOW_PLAYER_SPEED: f32 = 2.0;
|
const FISH_FOLLOW_PLAYER_SPEED: f32 = 1.8;
|
||||||
const FISH_FOLLOW_PLAYER_SPEED_FAST: f32 = FISH_FOLLOW_PLAYER_SPEED * 3.0;
|
const FISH_FOLLOW_PLAYER_SPEED_FAST: f32 = FISH_FOLLOW_PLAYER_SPEED * 3.0;
|
||||||
const FISH_ATTACH_RADIUS: f32 = 20.0;
|
const FISH_ATTACH_RADIUS: f32 = 20.0;
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ const FISH_ATTACH_RADIUS: f32 = 20.0;
|
|||||||
pub struct FishEntity {
|
pub struct FishEntity {
|
||||||
position: Vector2,
|
position: Vector2,
|
||||||
direction: Vector2,
|
direction: Vector2,
|
||||||
following_player: bool,
|
pub following_player: bool,
|
||||||
size: Vector2,
|
size: Vector2,
|
||||||
rng: ThreadRng
|
rng: ThreadRng
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ impl FishEntity {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_follow_player(&mut self, player: &Player, dt: f64) {
|
pub fn handle_follow_player(&mut self, player: &Player, dt: f64, other_fish: &Vec<FishEntity>) {
|
||||||
// Distance and direction to player
|
// Distance and direction to player
|
||||||
let dist_to_player = player.position - self.position;
|
let dist_to_player = player.position - self.position;
|
||||||
let dist_to_player_lin = self.position.distance_to(player.position);
|
let dist_to_player_lin = self.position.distance_to(player.position);
|
||||||
@ -66,7 +66,7 @@ impl FishEntity {
|
|||||||
self.position += movement;
|
self.position += movement;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_free_movement(&mut self, player: &Player, dt: f64) {
|
pub fn handle_free_movement(&mut self, player: &mut Player, dt: f64) {
|
||||||
// Distance and direction to player
|
// Distance and direction to player
|
||||||
let dist_to_player = player.position - self.position;
|
let dist_to_player = player.position - self.position;
|
||||||
let dist_to_player_lin = self.position.distance_to(player.position);
|
let dist_to_player_lin = self.position.distance_to(player.position);
|
||||||
@ -74,8 +74,11 @@ impl FishEntity {
|
|||||||
direction_to_player.normalize();
|
direction_to_player.normalize();
|
||||||
|
|
||||||
// Handle player picking up fish
|
// Handle player picking up fish
|
||||||
if player.position.distance_to(self.position).abs() <= player.size.y * 1.2 {
|
if player.position.distance_to(self.position).abs() <= player.size.y * 2.2 {
|
||||||
self.following_player = true;
|
self.following_player = true;
|
||||||
|
|
||||||
|
// Add currency to the player
|
||||||
|
player.coins += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look at the player;
|
// Look at the player;
|
||||||
@ -83,9 +86,9 @@ impl FishEntity {
|
|||||||
self.direction = direction_to_player;
|
self.direction = direction_to_player;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_position(&mut self, player: &Player, dt: f64) {
|
pub fn update_position(&mut self, player: &mut Player, dt: f64, other_fish: &Vec<FishEntity>) {
|
||||||
if self.following_player {
|
if self.following_player {
|
||||||
self.handle_follow_player(player, dt);
|
self.handle_follow_player(player, dt, other_fish);
|
||||||
} else {
|
} else {
|
||||||
self.handle_free_movement(player, dt);
|
self.handle_free_movement(player, dt);
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,7 @@ impl GameCore {
|
|||||||
world: World,
|
world: World,
|
||||||
progress: GameProgress,
|
progress: GameProgress,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let player = Player::new(&world.player_spawn);
|
||||||
Self {
|
Self {
|
||||||
state: GameState::Loading,
|
state: GameState::Loading,
|
||||||
last_state: GameState::Loading,
|
last_state: GameState::Loading,
|
||||||
@ -117,13 +118,13 @@ impl GameCore {
|
|||||||
.expect("Failed to load game assets. Can not launch!"),
|
.expect("Failed to load game assets. Can not launch!"),
|
||||||
master_camera: Camera2D {
|
master_camera: Camera2D {
|
||||||
offset: Vector2::zero(),
|
offset: Vector2::zero(),
|
||||||
target: Vector2::zero(),
|
target: world.player_spawn,
|
||||||
rotation: 0.0,
|
rotation: 0.0,
|
||||||
zoom: 1.0,
|
zoom: 2.0,
|
||||||
},
|
},
|
||||||
show_simple_debug_info: false,
|
show_simple_debug_info: false,
|
||||||
world: world,
|
world: world,
|
||||||
player: Player::new(),
|
player,
|
||||||
progress: progress,
|
progress: progress,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ impl GameProfiler {
|
|||||||
w_type: None,
|
w_type: None,
|
||||||
},
|
},
|
||||||
DataSet {
|
DataSet {
|
||||||
title: Some("Breath".to_string()),
|
title: Some("Boost".to_string()),
|
||||||
value: json!(self.data.player_boost_percent),
|
value: json!(self.data.player_boost_percent),
|
||||||
graph: Some(false),
|
graph: Some(false),
|
||||||
unit: Some("%".to_string()),
|
unit: Some("%".to_string()),
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
use raylib::{
|
use raylib::{core::color::Color, math::{Rectangle, Vector2}, prelude::{RaylibDraw, RaylibDrawHandle, RaylibMode2D}, texture::Texture2D};
|
||||||
core::color::Color,
|
|
||||||
math::{Rectangle, Vector2},
|
|
||||||
prelude::{RaylibDraw, RaylibDrawHandle},
|
|
||||||
texture::Texture2D,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A wrapper around an animation spritesheet
|
/// A wrapper around an animation spritesheet
|
||||||
pub struct FrameAnimationWrapper {
|
pub struct FrameAnimationWrapper {
|
||||||
@ -29,7 +24,9 @@ impl FrameAnimationWrapper {
|
|||||||
|
|
||||||
/// Start the animation
|
/// Start the animation
|
||||||
pub fn start(&mut self, handle: &RaylibDrawHandle) {
|
pub fn start(&mut self, handle: &RaylibDrawHandle) {
|
||||||
self.start_time_seconds = handle.get_time();
|
if self.start_time_seconds == 0.0 {
|
||||||
|
self.start_time_seconds = handle.get_time();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop (and reset) the animation
|
/// Stop (and reset) the animation
|
||||||
@ -48,16 +45,17 @@ impl FrameAnimationWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the next frame to the screen at `position`
|
/// Draw the next frame to the screen at `position`
|
||||||
pub fn draw(&mut self, handle: &mut RaylibDrawHandle, position: Vector2) {
|
pub fn draw(&mut self, handle: &mut RaylibMode2D<RaylibDrawHandle>, position: Vector2, rotation: f32) {
|
||||||
let frame_id = self.get_current_frame_id(handle);
|
let frame_id = self.get_current_frame_id(handle);
|
||||||
self.draw_frame(handle, position, frame_id);
|
self.draw_frame(handle, position, rotation, frame_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw a specified frame to the screen at `position`
|
/// Draw a specified frame to the screen at `position`
|
||||||
pub fn draw_frame(
|
pub fn draw_frame(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &mut RaylibDrawHandle,
|
handle: &mut RaylibMode2D<RaylibDrawHandle>,
|
||||||
position: Vector2,
|
position: Vector2,
|
||||||
|
rotation: f32,
|
||||||
frame_number: u32,
|
frame_number: u32,
|
||||||
) {
|
) {
|
||||||
// Determine the col number
|
// Determine the col number
|
||||||
@ -75,8 +73,20 @@ impl FrameAnimationWrapper {
|
|||||||
width: self.size.x,
|
width: self.size.x,
|
||||||
height: self.size.y,
|
height: self.size.y,
|
||||||
};
|
};
|
||||||
|
let frame_dest = Rectangle {
|
||||||
|
x: position.x,
|
||||||
|
y: position.y,
|
||||||
|
width: self.size.x,
|
||||||
|
height: self.size.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rotation origin
|
||||||
|
let origin = Vector2 {
|
||||||
|
x: self.size.x / 2.0,
|
||||||
|
y: self.size.y / 2.0
|
||||||
|
};
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
handle.draw_texture_rec(&mut self.sprite_sheet, frame_box, position, Color::WHITE);
|
handle.draw_texture_pro(&mut self.sprite_sheet, frame_box, frame_dest, origin, rotation, Color::WHITE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
src/lib/wrappers/complexanimation.rs
Normal file
32
src/lib/wrappers/complexanimation.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
pub mod audio;
|
pub mod audio;
|
||||||
pub mod animation;
|
pub mod animation;
|
||||||
|
pub mod complexanimation;
|
@ -7,6 +7,9 @@ use raylib::prelude::*;
|
|||||||
use crate::{gamecore::{self, GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer};
|
use crate::{gamecore::{self, GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer};
|
||||||
|
|
||||||
use self::shop::Shop;
|
use self::shop::Shop;
|
||||||
|
use crate::{
|
||||||
|
pallette::{WATER},
|
||||||
|
};
|
||||||
|
|
||||||
use super::screen::Screen;
|
use super::screen::Screen;
|
||||||
|
|
||||||
@ -34,7 +37,48 @@ impl InGameScreen {
|
|||||||
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
|
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
|
||||||
game_core: &mut GameCore,
|
game_core: &mut GameCore,
|
||||||
) {
|
) {
|
||||||
context_2d.draw_circle(0, 0, 10.0, Color::BLACK);
|
// Build source bounds
|
||||||
|
let source_bounds = Rectangle {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: game_core.resources.cave_mid_layer.width as f32,
|
||||||
|
height: game_core.resources.cave_mid_layer.height as f32,
|
||||||
|
};
|
||||||
|
let world_bounds = Rectangle {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: game_core.resources.cave_mid_layer.width as f32,
|
||||||
|
height: game_core.resources.cave_mid_layer.height as f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clear the background
|
||||||
|
context_2d.draw_rectangle_rec(world_bounds, WATER);
|
||||||
|
|
||||||
|
// Render the world texture
|
||||||
|
context_2d.draw_texture_rec(
|
||||||
|
&game_core.resources.cave_mid_layer,
|
||||||
|
source_bounds,
|
||||||
|
Vector2 {
|
||||||
|
x: world_bounds.x,
|
||||||
|
y: world_bounds.y,
|
||||||
|
},
|
||||||
|
Color::WHITE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_colliders(
|
||||||
|
&mut self,
|
||||||
|
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
|
||||||
|
game_core: &mut GameCore,
|
||||||
|
) {
|
||||||
|
// Render every collider
|
||||||
|
for collider in game_core.world.colliders.iter() {
|
||||||
|
context_2d.draw_rectangle_lines_ex(
|
||||||
|
collider,
|
||||||
|
1,
|
||||||
|
Color::RED,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,27 +107,24 @@ impl Screen for InGameScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear frame
|
// Clear frame
|
||||||
draw_handle.clear_background(Color::BLUE);
|
draw_handle.clear_background(Color::BLACK);
|
||||||
|
|
||||||
// Handle the pause menu being opened
|
// Handle the pause menu being opened
|
||||||
if draw_handle.is_key_pressed(KeyboardKey::KEY_ESCAPE) {
|
if draw_handle.is_key_pressed(KeyboardKey::KEY_ESCAPE) {
|
||||||
return Some(GameState::PauseMenu);
|
return Some(GameState::PauseMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
};
|
||||||
|
let camera_window_center = window_center * (1.0 / game_core.master_camera.zoom);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -94,11 +135,14 @@ impl Screen for InGameScreen {
|
|||||||
|
|
||||||
// Render the world
|
// Render the world
|
||||||
self.render_world(&mut context_2d, game_core);
|
self.render_world(&mut context_2d, game_core);
|
||||||
|
if game_core.show_simple_debug_info{
|
||||||
|
self.render_colliders(&mut context_2d, game_core);
|
||||||
|
}
|
||||||
|
|
||||||
// Render entities
|
// Render entities
|
||||||
let mut fish = &mut game_core.world.fish;
|
let fish_clone = game_core.world.fish.clone();
|
||||||
for fish in fish.iter_mut() {
|
for fish in game_core.world.fish.iter_mut() {
|
||||||
fish.update_position(&game_core.player, dt);
|
fish.update_position(&mut game_core.player, dt, &fish_clone);
|
||||||
fish.render(&mut context_2d);
|
fish.render(&mut context_2d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +150,18 @@ impl Screen for InGameScreen {
|
|||||||
playerlogic::render_player(&mut context_2d, game_core);
|
playerlogic::render_player(&mut context_2d, 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
|
// Render the hud
|
||||||
hud::render_hud(draw_handle, game_core, window_center);
|
hud::render_hud(draw_handle, game_core, window_center);
|
||||||
|
|
||||||
|
@ -5,11 +5,11 @@ use crate::{
|
|||||||
pallette::{TRANSLUCENT_WHITE_128, TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96},
|
pallette::{TRANSLUCENT_WHITE_128, TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96},
|
||||||
};
|
};
|
||||||
|
|
||||||
const NORMAL_PLAYER_SPEED: i32 = 4;
|
const NORMAL_PLAYER_SPEED: i32 = 3;
|
||||||
const BOOST_PLAYER_SPEED: i32 = NORMAL_PLAYER_SPEED * 2;
|
const BOOST_PLAYER_SPEED: i32 = NORMAL_PLAYER_SPEED * 2;
|
||||||
const CAMERA_FOLLOW_SPEED: f32 = 0.7;
|
const CAMERA_FOLLOW_SPEED: f32 = 0.7;
|
||||||
const TURN_SPEED: f32 = 0.05;
|
const TURN_SPEED: f32 = 0.15;
|
||||||
const BOOST_DECREASE_PER_SECOND: f32 = 0.75;
|
const BOOST_DECREASE_PER_SECOND: f32 = 0.65;
|
||||||
const BOOST_REGEN_PER_SECOND: f32 = 0.25;
|
const BOOST_REGEN_PER_SECOND: f32 = 0.25;
|
||||||
const BREATH_DECREASE_PER_SECOND: f32 = 0.01;
|
const BREATH_DECREASE_PER_SECOND: f32 = 0.01;
|
||||||
|
|
||||||
@ -27,45 +27,45 @@ pub fn update_player_movement(
|
|||||||
let raw_movement_direction = mouse_world_pose - game_core.player.position;
|
let raw_movement_direction = mouse_world_pose - game_core.player.position;
|
||||||
let mut normalized_movement_direction = raw_movement_direction;
|
let mut normalized_movement_direction = raw_movement_direction;
|
||||||
normalized_movement_direction.normalize();
|
normalized_movement_direction.normalize();
|
||||||
|
|
||||||
let tau: f32 = PI as f32 * 2.0;
|
|
||||||
// get angles as floats
|
|
||||||
let mut player_angle: f32 = Vector2::zero().angle_to(game_core.player.direction);
|
|
||||||
let mut desired_angle: f32 = Vector2::zero().angle_to(normalized_movement_direction);
|
|
||||||
|
|
||||||
// make angle positive
|
let tau: f32 = PI as f32 * 2.0;
|
||||||
if desired_angle < 0.0 {
|
// get angles as floats
|
||||||
desired_angle += tau;
|
let mut player_angle: f32 = Vector2::zero().angle_to(game_core.player.direction);
|
||||||
}
|
let mut desired_angle: f32 = Vector2::zero().angle_to(normalized_movement_direction);
|
||||||
|
|
||||||
// turn towards mouse at turn speed
|
// make angle positive
|
||||||
if player_angle % tau > desired_angle {
|
if desired_angle < 0.0 {
|
||||||
if (player_angle % tau) - desired_angle > PI as f32 {
|
desired_angle += tau;
|
||||||
player_angle += TURN_SPEED;
|
}
|
||||||
} else {
|
|
||||||
player_angle -= TURN_SPEED;
|
// turn towards mouse at turn speed
|
||||||
}
|
if player_angle % tau > desired_angle {
|
||||||
|
if (player_angle % tau) - desired_angle > PI as f32 {
|
||||||
|
player_angle += TURN_SPEED;
|
||||||
} else {
|
} else {
|
||||||
if desired_angle - (player_angle % tau) > PI as f32 {
|
player_angle -= TURN_SPEED;
|
||||||
player_angle -= TURN_SPEED;
|
|
||||||
} else {
|
|
||||||
player_angle += TURN_SPEED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if desired_angle - (player_angle % tau) > PI as f32 {
|
||||||
|
player_angle -= TURN_SPEED;
|
||||||
|
} else {
|
||||||
|
player_angle += TURN_SPEED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// snap to mouse if close enough
|
// snap to mouse if close enough
|
||||||
if f32::abs(player_angle - desired_angle) < (TURN_SPEED * 1.1) {
|
if f32::abs(player_angle - desired_angle) < (TURN_SPEED * 1.1) {
|
||||||
player_angle = desired_angle;
|
player_angle = desired_angle;
|
||||||
}
|
}
|
||||||
if player_angle > tau {
|
if player_angle > tau {
|
||||||
player_angle -= tau;
|
player_angle -= tau;
|
||||||
}
|
}
|
||||||
if player_angle < 0.0 {
|
if player_angle < 0.0 {
|
||||||
player_angle += tau;
|
player_angle += tau;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set angle
|
// set angle
|
||||||
game_core.player.direction = Vector2::new(f32::cos(player_angle), f32::sin(player_angle));
|
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
|
// In the case the player is in "null", just jump the camera to them
|
||||||
if game_core.player.position == Vector2::zero() {
|
if game_core.player.position == Vector2::zero() {
|
||||||
@ -84,10 +84,36 @@ pub fn update_player_movement(
|
|||||||
|
|
||||||
// Decrease the boost
|
// Decrease the boost
|
||||||
game_core.player.boost_percent -= BOOST_DECREASE_PER_SECOND * dt as f32;
|
game_core.player.boost_percent -= BOOST_DECREASE_PER_SECOND * dt as f32;
|
||||||
|
game_core.player.is_boosting = true;
|
||||||
|
if game_core.player.boost_percent >= 0.9 {
|
||||||
|
game_core
|
||||||
|
.resources
|
||||||
|
.player_animation_boost_charge
|
||||||
|
.start(draw_handle);
|
||||||
|
game_core.resources.player_animation_regular.stop();
|
||||||
|
game_core.player.is_boost_charging = true;
|
||||||
|
} else {
|
||||||
|
game_core.resources.player_animation_boost_charge.stop();
|
||||||
|
game_core
|
||||||
|
.resources
|
||||||
|
.player_animation_boost
|
||||||
|
.start(draw_handle);
|
||||||
|
game_core.player.is_boost_charging = false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Set the speed multiplier
|
// Set the speed multiplier
|
||||||
speed_multiplier = NORMAL_PLAYER_SPEED as f32;
|
speed_multiplier = NORMAL_PLAYER_SPEED as f32;
|
||||||
|
|
||||||
|
// Reset boost animation
|
||||||
|
game_core.player.is_boosting = false;
|
||||||
|
game_core.player.is_boost_charging = false;
|
||||||
|
game_core.resources.player_animation_boost_charge.stop();
|
||||||
|
game_core.resources.player_animation_boost.stop();
|
||||||
|
game_core
|
||||||
|
.resources
|
||||||
|
.player_animation_regular
|
||||||
|
.start(draw_handle);
|
||||||
|
|
||||||
// Handle boost regen
|
// Handle boost regen
|
||||||
if !user_request_boost {
|
if !user_request_boost {
|
||||||
game_core.player.boost_percent = (game_core.player.boost_percent
|
game_core.player.boost_percent = (game_core.player.boost_percent
|
||||||
@ -103,7 +129,22 @@ pub fn update_player_movement(
|
|||||||
// Only do this if the mouse is far enough away
|
// Only do this if the mouse is far enough away
|
||||||
let player_real_movement = game_core.player.direction * speed_multiplier;
|
let player_real_movement = game_core.player.direction * speed_multiplier;
|
||||||
if raw_movement_direction.distance_to(Vector2::zero()) > game_core.player.size.y / 2.0 {
|
if raw_movement_direction.distance_to(Vector2::zero()) > game_core.player.size.y / 2.0 {
|
||||||
|
game_core.player.is_moving = true;
|
||||||
game_core.player.position += player_real_movement;
|
game_core.player.position += player_real_movement;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !game_core.player.is_moving {
|
||||||
|
game_core.player.position -= player_real_movement;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
game_core.player.is_moving = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the camera to follow the player
|
// Move the camera to follow the player
|
||||||
@ -113,9 +154,19 @@ pub fn update_player_movement(
|
|||||||
draw_handle.get_world_to_screen2D(game_core.player.position, game_core.master_camera);
|
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
|
// Camera only moves if you get close to the edge of the screen
|
||||||
if player_screen_position.distance_to(window_center).abs() > (window_center.y - 40.0) {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the player is not on screen, snap the camera to them
|
||||||
|
if player_screen_position.distance_to(window_center).abs() > window_center.y {
|
||||||
|
game_core.master_camera.target = game_core.player.position - (window_center / 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Clamp camera target y to 0
|
||||||
|
// if game_core.master_camera.target.y < -100.0 {
|
||||||
|
// game_core.master_camera.target.y = -100.0;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_player(context_2d: &mut RaylibMode2D<RaylibDrawHandle>, game_core: &mut GameCore) {
|
pub fn render_player(context_2d: &mut RaylibMode2D<RaylibDrawHandle>, game_core: &mut GameCore) {
|
||||||
@ -135,25 +186,43 @@ pub fn render_player(context_2d: &mut RaylibMode2D<RaylibDrawHandle>, game_core:
|
|||||||
TRANSLUCENT_WHITE_64,
|
TRANSLUCENT_WHITE_64,
|
||||||
);
|
);
|
||||||
context_2d.draw_ring(
|
context_2d.draw_ring(
|
||||||
player.position,
|
Vector2 {
|
||||||
boost_ring_max_radius - 2.0,
|
x: player.position.x as i32 as f32,
|
||||||
boost_ring_max_radius + 2.0,
|
y: player.position.y as i32 as f32,
|
||||||
|
},
|
||||||
|
boost_ring_max_radius,
|
||||||
|
boost_ring_max_radius + 1.0,
|
||||||
0,
|
0,
|
||||||
(360.0 * player.breath_percent) as i32,
|
(360.0 * player.breath_percent) as i32,
|
||||||
0,
|
0,
|
||||||
TRANSLUCENT_WHITE_96,
|
TRANSLUCENT_WHITE_96,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: tmp rect
|
// Render the player based on what is happening
|
||||||
context_2d.draw_rectangle_pro(
|
if player.is_boost_charging {
|
||||||
Rectangle {
|
game_core.resources.player_animation_boost_charge.draw(
|
||||||
x: player.position.x,
|
context_2d,
|
||||||
y: player.position.y,
|
player.position,
|
||||||
width: player.size.x,
|
player_rotation.to_degrees() - 90.0,
|
||||||
height: player.size.y,
|
);
|
||||||
},
|
} else if player.is_boosting {
|
||||||
player.size / 2.0,
|
game_core.resources.player_animation_boost.draw(
|
||||||
player_rotation.to_degrees() + 90.0,
|
context_2d,
|
||||||
Color::BLACK,
|
player.position,
|
||||||
);
|
player_rotation.to_degrees() - 90.0,
|
||||||
|
);
|
||||||
|
} else if player.is_moving {
|
||||||
|
game_core.resources.player_animation_regular.draw(
|
||||||
|
context_2d,
|
||||||
|
player.position,
|
||||||
|
player_rotation.to_degrees() - 90.0,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
game_core.resources.player_animation_regular.draw_frame(
|
||||||
|
context_2d,
|
||||||
|
player.position,
|
||||||
|
player_rotation.to_degrees() - 90.0,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,11 +28,11 @@ impl Screen for MainMenuScreen {
|
|||||||
let win_width = draw_handle.get_screen_width();
|
let win_width = draw_handle.get_screen_width();
|
||||||
|
|
||||||
// Clear frame
|
// Clear frame
|
||||||
draw_handle.clear_background(Color::WHITE);
|
draw_handle.clear_background(Color::BLUE);
|
||||||
|
|
||||||
// Render title
|
// Render title
|
||||||
draw_handle.draw_text(
|
draw_handle.draw_text(
|
||||||
"TMP TITLE",
|
"ONE BREATH",
|
||||||
(win_height / 2) - 80,
|
(win_height / 2) - 80,
|
||||||
win_width / 4,
|
win_width / 4,
|
||||||
40,
|
40,
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::screen::Screen;
|
use super::screen::Screen;
|
||||||
|
|
||||||
const SCREEN_PANEL_SIZE: Vector2 = Vector2 { x: 300.0, y: 300.0 };
|
const SCREEN_PANEL_SIZE: Vector2 = Vector2 { x: 300.0, y: 380.0 };
|
||||||
|
|
||||||
pub struct PauseMenuScreen {}
|
pub struct PauseMenuScreen {}
|
||||||
|
|
||||||
@ -118,6 +118,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",
|
||||||
|
(win_width / 2) - (SCREEN_PANEL_SIZE.x as i32 / 2) + 10,
|
||||||
|
(win_height / 2) - (SCREEN_PANEL_SIZE.y as i32 / 2) + 120,
|
||||||
|
20,
|
||||||
|
Color::BLACK,
|
||||||
|
);
|
||||||
|
|
||||||
// Close and quit buttons
|
// Close and quit buttons
|
||||||
let bottom_left_button_dimensions = Rectangle {
|
let bottom_left_button_dimensions = Rectangle {
|
||||||
x: (win_width as f32 / 2.0) - (SCREEN_PANEL_SIZE.x / 2.0) + 5.0,
|
x: (win_width as f32 / 2.0) - (SCREEN_PANEL_SIZE.x / 2.0) + 5.0,
|
||||||
|
@ -13,14 +13,14 @@ use lib::{utils::profiler::GameProfiler, wrappers::audio::player::AudioPlayer};
|
|||||||
use log::info;
|
use log::info;
|
||||||
use logic::{gameend::GameEndScreen, ingame::InGameScreen, loadingscreen::LoadingScreen, mainmenu::MainMenuScreen, pausemenu::PauseMenuScreen, screen::Screen};
|
use logic::{gameend::GameEndScreen, ingame::InGameScreen, loadingscreen::LoadingScreen, mainmenu::MainMenuScreen, pausemenu::PauseMenuScreen, screen::Screen};
|
||||||
use raylib::prelude::*;
|
use raylib::prelude::*;
|
||||||
use world::World;
|
use world::{World, load_world_colliders};
|
||||||
|
|
||||||
// Game Launch Configuration
|
// Game Launch Configuration
|
||||||
const DEFAULT_WINDOW_DIMENSIONS: Vector2 = Vector2 {
|
const DEFAULT_WINDOW_DIMENSIONS: Vector2 = Vector2 {
|
||||||
x: 1080.0,
|
x: 1080.0,
|
||||||
y: 720.0,
|
y: 720.0,
|
||||||
};
|
};
|
||||||
const WINDOW_TITLE: &str = r"Ludum Dare 48";
|
const WINDOW_TITLE: &str = r"One Breath";
|
||||||
const MAX_FPS: u32 = 60;
|
const MAX_FPS: u32 = 60;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -32,7 +32,7 @@ fn main() {
|
|||||||
.size(
|
.size(
|
||||||
DEFAULT_WINDOW_DIMENSIONS.x as i32,
|
DEFAULT_WINDOW_DIMENSIONS.x as i32,
|
||||||
DEFAULT_WINDOW_DIMENSIONS.y as i32,
|
DEFAULT_WINDOW_DIMENSIONS.y as i32,
|
||||||
)
|
).msaa_4x()
|
||||||
.title(WINDOW_TITLE)
|
.title(WINDOW_TITLE)
|
||||||
.build();
|
.build();
|
||||||
raylib.set_target_fps(MAX_FPS);
|
raylib.set_target_fps(MAX_FPS);
|
||||||
@ -41,7 +41,8 @@ fn main() {
|
|||||||
raylib.set_exit_key(None);
|
raylib.set_exit_key(None);
|
||||||
|
|
||||||
// Load the world
|
// Load the world
|
||||||
let world = World::load_from_json("./assets/worlds/mainworld.json".to_string()).expect("Failed to load main world JSON");
|
let world_colliders = load_world_colliders("./assets/img/map/cave.json".to_string()).expect("Failed to load world colliders");
|
||||||
|
let world = World::load_from_json("./assets/worlds/mainworld.json".to_string(), world_colliders).expect("Failed to load main world JSON");
|
||||||
|
|
||||||
// Load the game progress
|
// Load the game progress
|
||||||
let game_progress = GameProgress::try_from_file("./assets/savestate.json".to_string());
|
let game_progress = GameProgress::try_from_file("./assets/savestate.json".to_string());
|
||||||
|
@ -19,4 +19,18 @@ pub const TRANSLUCENT_WHITE_64: Color = Color {
|
|||||||
g: 255,
|
g: 255,
|
||||||
b: 255,
|
b: 255,
|
||||||
a: 64,
|
a: 64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SKY: Color = Color {
|
||||||
|
r: 15,
|
||||||
|
g: 193,
|
||||||
|
b: 217,
|
||||||
|
a: 255
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const WATER: Color = Color {
|
||||||
|
r: 24,
|
||||||
|
g: 66,
|
||||||
|
b: 143,
|
||||||
|
a: 255
|
||||||
};
|
};
|
@ -1,6 +1,6 @@
|
|||||||
use raylib::math::Vector2;
|
use raylib::math::{Rectangle, Vector2};
|
||||||
|
|
||||||
|
|
||||||
|
use crate::lib::utils::triangles::rotate_vector;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
@ -9,20 +9,51 @@ pub struct Player {
|
|||||||
pub size: Vector2,
|
pub size: Vector2,
|
||||||
pub coins: u32,
|
pub coins: u32,
|
||||||
pub boost_percent: f32,
|
pub boost_percent: f32,
|
||||||
pub breath_percent: f32
|
pub breath_percent: f32,
|
||||||
|
pub is_moving: bool,
|
||||||
|
pub is_boosting: bool,
|
||||||
|
pub is_boost_charging: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
pub fn new() -> Self {
|
pub fn new(spawn: &Vector2) -> Self {
|
||||||
Self {
|
Self {
|
||||||
boost_percent: 1.0,
|
boost_percent: 1.0,
|
||||||
size: Vector2 {
|
size: Vector2 { x: 11.0, y: 21.0 },
|
||||||
x: 11.0 * 4.0,
|
|
||||||
y: 21.0 * 4.0
|
|
||||||
},
|
|
||||||
breath_percent: 1.0,
|
breath_percent: 1.0,
|
||||||
|
position: spawn.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
pub fn collides_with_rec(&self, rectangle: &Rectangle) -> bool {
|
||||||
|
// // Build a bounding box of the player by their corners
|
||||||
|
// let top_left_corner = self.position - (self.size / 2.0);
|
||||||
|
// let bottom_right_corner = self.position + (self.size / 2.0);
|
||||||
|
// let top_right_corner = Vector2 {
|
||||||
|
// x: bottom_right_corner.x,
|
||||||
|
// y: top_left_corner.y,
|
||||||
|
// };
|
||||||
|
// let bottom_left_corner = Vector2 {
|
||||||
|
// x: top_left_corner.x,
|
||||||
|
// y: bottom_right_corner.y,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Get the rotation
|
||||||
|
// let rotation = Vector2::zero().angle_to(self.direction);
|
||||||
|
|
||||||
|
// // Rotate the bounds
|
||||||
|
// let top_left_corner = rotate_vector(top_left_corner, rotation);
|
||||||
|
// let bottom_right_corner = rotate_vector(bottom_right_corner, rotation);
|
||||||
|
// let top_right_corner = rotate_vector(top_right_corner, rotation);
|
||||||
|
// let bottom_left_corner = rotate_vector(bottom_left_corner, rotation);
|
||||||
|
|
||||||
|
// // Check for collisions
|
||||||
|
// return rectangle.check_collision_point_rec(top_left_corner)
|
||||||
|
// || rectangle.check_collision_point_rec(bottom_right_corner)
|
||||||
|
// || rectangle.check_collision_point_rec(top_right_corner)
|
||||||
|
// || rectangle.check_collision_point_rec(bottom_left_corner);
|
||||||
|
|
||||||
|
return rectangle.check_collision_circle_rec(self.position, (self.size.y * 0.5) / 2.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,19 +1,68 @@
|
|||||||
use failure::Error;
|
use failure::Error;
|
||||||
use raylib::{RaylibHandle, RaylibThread, texture::{Image, Texture2D}};
|
use raylib::{
|
||||||
|
math::Vector2,
|
||||||
|
texture::{Image, Texture2D},
|
||||||
|
RaylibHandle, RaylibThread,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::lib::wrappers::animation::FrameAnimationWrapper;
|
||||||
|
|
||||||
/// This struct contains all textures and sounds that must be loaded into (V)RAM at the start of the game
|
/// This struct contains all textures and sounds that must be loaded into (V)RAM at the start of the game
|
||||||
pub struct GlobalResources {
|
pub struct GlobalResources {
|
||||||
|
|
||||||
// Branding
|
// Branding
|
||||||
pub game_logo: Texture2D
|
pub game_logo: Texture2D,
|
||||||
|
|
||||||
|
// Player
|
||||||
|
pub player_animation_regular: FrameAnimationWrapper,
|
||||||
|
pub player_animation_boost_charge: FrameAnimationWrapper,
|
||||||
|
pub player_animation_boost: FrameAnimationWrapper,
|
||||||
|
|
||||||
|
// Cave
|
||||||
|
pub cave_mid_layer: Texture2D
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalResources {
|
impl GlobalResources {
|
||||||
/// Load all resources. **THIS WILL HANG!**
|
/// Load all resources. **THIS WILL HANG!**
|
||||||
pub fn load_all(raylib: &mut RaylibHandle, thread: &RaylibThread) -> Result<GlobalResources, String> {
|
pub fn load_all(
|
||||||
|
raylib: &mut RaylibHandle,
|
||||||
|
thread: &RaylibThread,
|
||||||
|
) -> Result<GlobalResources, String> {
|
||||||
Ok(GlobalResources {
|
Ok(GlobalResources {
|
||||||
game_logo: raylib.load_texture_from_image(&thread, &Image::load_image("./assets/img/logos/game-logo.png")?)?
|
game_logo: raylib.load_texture_from_image(
|
||||||
|
&thread,
|
||||||
|
&Image::load_image("./assets/img/logos/game-logo.png")?,
|
||||||
|
)?,
|
||||||
|
player_animation_regular: FrameAnimationWrapper::new(
|
||||||
|
raylib.load_texture_from_image(
|
||||||
|
&thread,
|
||||||
|
&Image::load_image("./assets/img/character/diveNormal.png")?,
|
||||||
|
)?,
|
||||||
|
Vector2 { x: 11.0, y: 21.0 },
|
||||||
|
8,
|
||||||
|
100 / 8,
|
||||||
|
),
|
||||||
|
player_animation_boost_charge: FrameAnimationWrapper::new(
|
||||||
|
raylib.load_texture_from_image(
|
||||||
|
&thread,
|
||||||
|
&Image::load_image("./assets/img/character/diveStrokeCharge.png")?,
|
||||||
|
)?,
|
||||||
|
Vector2 { x: 11.0, y: 21.0 },
|
||||||
|
21,
|
||||||
|
100 / 4,
|
||||||
|
),
|
||||||
|
player_animation_boost: FrameAnimationWrapper::new(
|
||||||
|
raylib.load_texture_from_image(
|
||||||
|
&thread,
|
||||||
|
&Image::load_image("./assets/img/character/diveStroke.png")?,
|
||||||
|
)?,
|
||||||
|
Vector2 { x: 17.0, y: 21.0 },
|
||||||
|
21,
|
||||||
|
30,
|
||||||
|
),
|
||||||
|
cave_mid_layer: raylib.load_texture_from_image(
|
||||||
|
&thread,
|
||||||
|
&Image::load_image("./assets/img/map/cave.png")?,
|
||||||
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
43
src/world.rs
43
src/world.rs
@ -1,6 +1,6 @@
|
|||||||
use std::{fs::File, io::BufReader};
|
use std::{fs::File, io::BufReader};
|
||||||
|
|
||||||
use raylib::math::Vector2;
|
use raylib::math::{Rectangle, Vector2};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
@ -10,16 +10,20 @@ use crate::entities::fish::FishEntity;
|
|||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub end_position: Vector2,
|
pub end_position: Vector2,
|
||||||
|
pub player_spawn: Vector2,
|
||||||
|
|
||||||
#[serde(rename = "fish")]
|
#[serde(rename = "fish")]
|
||||||
pub fish_positions: Vec<Vector2>,
|
pub fish_positions: Vec<Vector2>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub fish: Vec<FishEntity>
|
pub fish: Vec<FishEntity>,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub colliders: Vec<Rectangle>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn load_from_json(file: String) -> Result<Self, Error> {
|
pub fn load_from_json(file: String, colliders: Vec<Rectangle>) -> Result<Self, Error> {
|
||||||
// Load the file
|
// Load the file
|
||||||
let file = File::open(file)?;
|
let file = File::open(file)?;
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
@ -30,6 +34,39 @@ impl World {
|
|||||||
// Init all fish
|
// Init all fish
|
||||||
result.fish = FishEntity::new_from_positions(&result.fish_positions);
|
result.fish = FishEntity::new_from_positions(&result.fish_positions);
|
||||||
|
|
||||||
|
// Init colliders
|
||||||
|
result.colliders = Vec::new();
|
||||||
|
for collider in colliders.iter(){
|
||||||
|
result.colliders.push(Rectangle {
|
||||||
|
x: collider.x - (collider.width / 2.0),
|
||||||
|
y: collider.y - (collider.height / 2.0),
|
||||||
|
width: collider.width,
|
||||||
|
height: collider.height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn spend_coins(&mut self, count: usize) {
|
||||||
|
for _ in 0..count {
|
||||||
|
self.fish.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
for fish in self.fish.iter_mut() {
|
||||||
|
fish.following_player = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn load_world_colliders(file: String) -> Result<Vec<Rectangle>, Error> {
|
||||||
|
// Load the file
|
||||||
|
let file = File::open(file)?;
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
|
||||||
|
// Deserialize
|
||||||
|
Ok(serde_json::from_reader(reader)?)
|
||||||
|
}
|
Reference in New Issue
Block a user