mirror of
https://github.com/szymon-jozef/checkers.git
synced 2026-10-27 11:10:48 +01:00
Merge pull request #5 from szymon-jozef/feat/macroquad-ui
feat/macroquad ui
This commit is contained in:
236
Cargo.lock
generated
236
Cargo.lock
generated
@@ -2,6 +2,12 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler2"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.5"
|
version = "1.1.5"
|
||||||
@@ -11,6 +17,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -76,6 +88,12 @@ dependencies = [
|
|||||||
"critical-section",
|
"critical-section",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@@ -94,6 +112,12 @@ version = "3.20.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.25.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -130,6 +154,7 @@ dependencies = [
|
|||||||
"dirs",
|
"dirs",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
|
"macroquad",
|
||||||
"postcard",
|
"postcard",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -147,12 +172,27 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color_quant"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core_maths"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30"
|
||||||
|
dependencies = [
|
||||||
|
"libm",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@@ -162,6 +202,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "critical-section"
|
name = "critical-section"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@@ -271,12 +320,41 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fdeflate"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||||
|
dependencies = [
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fontdue"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7894823fa221401399e2598f8b63f81ac77ff5c63248b7656779bff1632d7d3d"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.15.5",
|
||||||
|
"ttf-parser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.34"
|
version = "0.3.34"
|
||||||
@@ -326,6 +404,12 @@ dependencies = [
|
|||||||
"wasip3",
|
"wasip3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glam"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hash32"
|
name = "hash32"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -341,6 +425,8 @@ version = "0.15.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"allocator-api2",
|
||||||
|
"equivalent",
|
||||||
"foldhash",
|
"foldhash",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -376,6 +462,19 @@ version = "2.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.24.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"byteorder",
|
||||||
|
"color_quant",
|
||||||
|
"num-traits",
|
||||||
|
"png",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.14.0"
|
version = "2.14.0"
|
||||||
@@ -459,6 +558,12 @@ version = "0.2.189"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.20"
|
version = "0.1.20"
|
||||||
@@ -483,12 +588,63 @@ version = "0.4.33"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "macroquad"
|
||||||
|
version = "0.4.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cbd54d99d4d3ac59deed7ee4927bbbf4eb33a55635530af3be9395a55746090"
|
||||||
|
dependencies = [
|
||||||
|
"fontdue",
|
||||||
|
"glam",
|
||||||
|
"image",
|
||||||
|
"macroquad_macro",
|
||||||
|
"miniquad",
|
||||||
|
"quad-rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "macroquad_macro"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64b1d96218903768c1ce078b657c0d5965465c95a60d2682fd97443c9d2483dd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "malloc_buf"
|
||||||
|
version = "0.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.8.3"
|
version = "2.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniquad"
|
||||||
|
version = "0.4.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcbaea978d741f3b60ced666b06a2a1e4a2bc81cb8b9dd29f4a40965e1f59e65"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"ndk-sys",
|
||||||
|
"objc-rs",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||||
|
dependencies = [
|
||||||
|
"adler2",
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
@@ -500,6 +656,30 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ndk-sys"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc-rs"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64a1e7069a2525126bf12a9f1f7916835fafade384fb27cabf698e745e2a1eb8"
|
||||||
|
dependencies = [
|
||||||
|
"malloc_buf",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.4"
|
version = "1.21.4"
|
||||||
@@ -547,6 +727,19 @@ version = "0.2.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "png"
|
||||||
|
version = "0.17.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"crc32fast",
|
||||||
|
"fdeflate",
|
||||||
|
"flate2",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portable-atomic"
|
name = "portable-atomic"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -594,6 +787,12 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quad-rand"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a651516ddc9168ebd67b24afd085a718be02f8858fe406591b013d101ce2f40"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.47"
|
version = "1.0.47"
|
||||||
@@ -764,6 +963,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd-adler32"
|
||||||
|
version = "0.3.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
@@ -910,6 +1115,15 @@ version = "1.1.2+spec-1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ttf-parser"
|
||||||
|
version = "0.25.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
|
||||||
|
dependencies = [
|
||||||
|
"core_maths",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
@@ -1043,6 +1257,28 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ edition = "2024"
|
|||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
env_logger = "0.11.11"
|
env_logger = "0.11.11"
|
||||||
log = "0.4.33"
|
log = "0.4.33"
|
||||||
|
macroquad = "0.4.16"
|
||||||
postcard = { version = "1.1.3", features = ["alloc"] }
|
postcard = { version = "1.1.3", features = ["alloc"] }
|
||||||
rand = "0.10.2"
|
rand = "0.10.2"
|
||||||
serde = { version = "1.0.229", features = ["derive"] }
|
serde = { version = "1.0.229", features = ["derive"] }
|
||||||
|
|||||||
BIN
assets/background.png
Normal file
BIN
assets/background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 533 KiB |
@@ -16,11 +16,26 @@
|
|||||||
# rustc
|
# rustc
|
||||||
# already provided by buildRustPackage :p
|
# already provided by buildRustPackage :p
|
||||||
pkg-config
|
pkg-config
|
||||||
|
makeWrapper
|
||||||
];
|
];
|
||||||
|
|
||||||
# runtime deps
|
# runtime deps
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
|
wayland
|
||||||
|
wayland-protocols
|
||||||
|
libGL
|
||||||
|
alsa-lib
|
||||||
|
|
||||||
|
libX11.out
|
||||||
|
libXi.out
|
||||||
|
libXcursor.out
|
||||||
|
libXrandr.out
|
||||||
|
libxkbcommon.out
|
||||||
|
libxcb
|
||||||
|
libXinerama
|
||||||
|
libXxf86vm
|
||||||
|
|
||||||
|
fontconfig
|
||||||
];
|
];
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
|
|||||||
@@ -1,16 +1,29 @@
|
|||||||
{
|
{
|
||||||
perSystem = { pkgs, config, ... }: {
|
perSystem =
|
||||||
devShells.default = pkgs.mkShell {
|
{
|
||||||
packages = with pkgs; [
|
pkgs,
|
||||||
rustfmt
|
config,
|
||||||
clippy
|
...
|
||||||
];
|
}:
|
||||||
|
{
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
rustfmt
|
||||||
|
clippy
|
||||||
|
];
|
||||||
|
|
||||||
RUST_LOG = "debug";
|
RUST_LOG = "debug";
|
||||||
|
|
||||||
inputsFrom = [
|
LD_LIBRARY_PATH = builtins.concatStringsSep ":" [
|
||||||
(config.packages.default)
|
"${pkgs.libX11}/lib"
|
||||||
];
|
"${pkgs.libXi}/lib"
|
||||||
|
"${pkgs.libGL}/lib"
|
||||||
|
"${pkgs.libxkbcommon}/lib"
|
||||||
|
];
|
||||||
|
|
||||||
|
inputsFrom = [
|
||||||
|
(config.packages.default)
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const QUIT_COMMAND: &str = "/quit";
|
|||||||
const HELP_COMMAND: &str = "/help";
|
const HELP_COMMAND: &str = "/help";
|
||||||
const SEND_COMMAND: &str = "/send";
|
const SEND_COMMAND: &str = "/send";
|
||||||
const READY_COMMAND: &str = "/ready";
|
const READY_COMMAND: &str = "/ready";
|
||||||
|
const UNREADY_COMMAND: &str = "/unready";
|
||||||
const SINGLEPLAYER_COMMAND: &str = "/single";
|
const SINGLEPLAYER_COMMAND: &str = "/single";
|
||||||
|
|
||||||
const CAPTURE_COMMAND: &str = "/capture";
|
const CAPTURE_COMMAND: &str = "/capture";
|
||||||
@@ -26,6 +27,8 @@ enum CliCommands {
|
|||||||
Send,
|
Send,
|
||||||
|
|
||||||
Ready,
|
Ready,
|
||||||
|
Unready,
|
||||||
|
|
||||||
Single,
|
Single,
|
||||||
|
|
||||||
Capture,
|
Capture,
|
||||||
@@ -50,6 +53,7 @@ impl TryFrom<&str> for CliCommands {
|
|||||||
HELP_COMMAND => Ok(CliCommands::Help),
|
HELP_COMMAND => Ok(CliCommands::Help),
|
||||||
SEND_COMMAND => Ok(CliCommands::Send),
|
SEND_COMMAND => Ok(CliCommands::Send),
|
||||||
READY_COMMAND => Ok(CliCommands::Ready),
|
READY_COMMAND => Ok(CliCommands::Ready),
|
||||||
|
UNREADY_COMMAND => Ok(CliCommands::Unready),
|
||||||
SINGLEPLAYER_COMMAND => Ok(CliCommands::Single),
|
SINGLEPLAYER_COMMAND => Ok(CliCommands::Single),
|
||||||
|
|
||||||
CAPTURE_COMMAND => Ok(CliCommands::Capture),
|
CAPTURE_COMMAND => Ok(CliCommands::Capture),
|
||||||
@@ -59,6 +63,7 @@ impl TryFrom<&str> for CliCommands {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
struct ClientContext {
|
struct ClientContext {
|
||||||
pub identity: Option<NetworkIdentity>,
|
pub identity: Option<NetworkIdentity>,
|
||||||
pub is_my_turn: bool,
|
pub is_my_turn: bool,
|
||||||
@@ -67,18 +72,6 @@ struct ClientContext {
|
|||||||
pub available_moves: Option<Vec<MovePath>>,
|
pub available_moves: Option<Vec<MovePath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ClientContext {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
identity: None,
|
|
||||||
is_my_turn: false,
|
|
||||||
|
|
||||||
available_captures: None,
|
|
||||||
available_moves: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run_bot() {
|
async fn run_bot() {
|
||||||
let bot = Bot::new(BotDificulty::Easy).await;
|
let bot = Bot::new(BotDificulty::Easy).await;
|
||||||
bot.game_loop().await;
|
bot.game_loop().await;
|
||||||
@@ -102,10 +95,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
if stdin.read_line(&mut buffer).is_ok() {
|
if stdin.read_line(&mut buffer).is_ok() && stdin_sender.blocking_send(buffer).is_err() {
|
||||||
if stdin_sender.blocking_send(buffer).is_err() {
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -119,6 +110,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
│\t {} - quit
|
│\t {} - quit
|
||||||
│\t {} - show this message
|
│\t {} - show this message
|
||||||
│\t {} - message the server you're ready
|
│\t {} - message the server you're ready
|
||||||
|
│\t {} - revoke readiness
|
||||||
│\t {} - send text message to the server
|
│\t {} - send text message to the server
|
||||||
│\t {} - play alone
|
│\t {} - play alone
|
||||||
│
|
│
|
||||||
@@ -126,7 +118,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
│\t {} <index> <index>- choose move from the list. First number is pawn you want to move and second is position it should take
|
│\t {} <index> <index>- choose move from the list. First number is pawn you want to move and second is position it should take
|
||||||
└───────────────────────────────────────
|
└───────────────────────────────────────
|
||||||
",
|
",
|
||||||
QUIT_COMMAND, HELP_COMMAND, READY_COMMAND, SEND_COMMAND, SINGLEPLAYER_COMMAND , CAPTURE_COMMAND, MOVE_COMMAND
|
QUIT_COMMAND, HELP_COMMAND, READY_COMMAND, UNREADY_COMMAND, SEND_COMMAND, SINGLEPLAYER_COMMAND , CAPTURE_COMMAND, MOVE_COMMAND
|
||||||
);
|
);
|
||||||
|
|
||||||
println!("───────Type /help for help───────");
|
println!("───────Type /help for help───────");
|
||||||
@@ -209,6 +201,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
client.signal_readiness().await;
|
client.signal_readiness().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CliCommands::Unready => {
|
||||||
|
client.revoke_readiness().await;
|
||||||
|
}
|
||||||
|
|
||||||
CliCommands::Single => {
|
CliCommands::Single => {
|
||||||
println!("Tried connecting bot to the server! Make sure to be /ready");
|
println!("Tried connecting bot to the server! Make sure to be /ready");
|
||||||
tokio::spawn(run_bot());
|
tokio::spawn(run_bot());
|
||||||
|
|||||||
169
src/bin/macroquad_client.rs
Normal file
169
src/bin/macroquad_client.rs
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
use checkers::{
|
||||||
|
settings::{
|
||||||
|
client_settings::ClientSettings, general_settings::SettingsLike,
|
||||||
|
server_settings::ServerSettings,
|
||||||
|
},
|
||||||
|
ui::{
|
||||||
|
macroquad::{
|
||||||
|
difficulty_selection::draw_dificulty_selection, game::game::GameClient,
|
||||||
|
main_menu::draw_main_menu, mode_selection::draw_mode_selection,
|
||||||
|
server_selection::draw_server_selection, settings::draw_settings,
|
||||||
|
},
|
||||||
|
state::{GameContext, GuiState},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use macroquad::{
|
||||||
|
color::{BLACK, WHITE},
|
||||||
|
math::vec2,
|
||||||
|
miniquad::{self, conf::LinuxBackend::WaylandWithX11Fallback},
|
||||||
|
text::draw_text,
|
||||||
|
texture::{DrawTextureParams, draw_texture_ex, load_texture},
|
||||||
|
ui::{Skin, root_ui},
|
||||||
|
window::{Conf, next_frame, screen_height, screen_width},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn window_conf() -> Conf {
|
||||||
|
Conf {
|
||||||
|
window_title: "Checkers".to_owned(),
|
||||||
|
window_resizable: true,
|
||||||
|
platform: miniquad::conf::Platform {
|
||||||
|
linux_backend: WaylandWithX11Fallback,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_general_style() -> Skin {
|
||||||
|
let label_style = root_ui()
|
||||||
|
.style_builder()
|
||||||
|
.text_color(WHITE)
|
||||||
|
.font_size(64)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let button_style = root_ui()
|
||||||
|
.style_builder()
|
||||||
|
.text_color(BLACK)
|
||||||
|
.font_size(32)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let checkbox_style = root_ui()
|
||||||
|
.style_builder()
|
||||||
|
.color(BLACK)
|
||||||
|
.text_color(BLACK)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Skin {
|
||||||
|
label_style,
|
||||||
|
button_style,
|
||||||
|
checkbox_style,
|
||||||
|
..root_ui().default_skin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macroquad::main(window_conf)]
|
||||||
|
pub async fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let mut state = Default::default();
|
||||||
|
let mut context = GameContext::default();
|
||||||
|
|
||||||
|
let mut client_settings = ClientSettings::new();
|
||||||
|
let mut server_settings = ServerSettings::new();
|
||||||
|
|
||||||
|
let background = load_texture("assets/background.png").await.unwrap();
|
||||||
|
|
||||||
|
let main_menu_style = get_general_style().await;
|
||||||
|
root_ui().push_skin(&main_menu_style);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
draw_texture_ex(
|
||||||
|
&background,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
WHITE,
|
||||||
|
DrawTextureParams {
|
||||||
|
dest_size: Some(vec2(screen_width(), screen_height())),
|
||||||
|
..DrawTextureParams::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
match &mut state {
|
||||||
|
GuiState::MainMenu => {
|
||||||
|
draw_main_menu(&mut state).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiState::ModeSelection => {
|
||||||
|
draw_mode_selection(&mut state, &mut context).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiState::DificultySelection => {
|
||||||
|
draw_dificulty_selection(&mut state, &mut context).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiState::ServerSelection => {
|
||||||
|
draw_server_selection(&mut state, &mut context).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiState::Connecting(receiver) => {
|
||||||
|
draw_text("Connecting...", 0.0, 0.0, 64.0, BLACK);
|
||||||
|
|
||||||
|
match receiver.try_recv() {
|
||||||
|
Ok(client) => {
|
||||||
|
if let Some(client) = client {
|
||||||
|
draw_text("Connected!", 0.0, 0.0, 64.0, BLACK);
|
||||||
|
let client = GameClient::new(client, &context);
|
||||||
|
if let Some(client) = client {
|
||||||
|
state = GuiState::Game(client);
|
||||||
|
} else {
|
||||||
|
// ugly ass code
|
||||||
|
draw_text("Could not connect!", 0.0, 0.0, 64.0, BLACK);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
draw_text("Could not connect!", 0.0, 0.0, 64.0, BLACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
draw_text("Error", 0.0, 0.0, 64.0, BLACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiState::Settings => {
|
||||||
|
draw_settings(
|
||||||
|
&mut state,
|
||||||
|
&mut context,
|
||||||
|
&mut client_settings,
|
||||||
|
&mut server_settings,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiState::Game(client) => {
|
||||||
|
while !client.should_close {
|
||||||
|
draw_texture_ex(
|
||||||
|
&background,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
WHITE,
|
||||||
|
DrawTextureParams {
|
||||||
|
dest_size: Some(vec2(screen_width(), screen_height())),
|
||||||
|
..DrawTextureParams::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
client.update();
|
||||||
|
client.draw();
|
||||||
|
next_frame().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = GuiState::MainMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiState::Exit => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next_frame().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,3 +2,4 @@ pub mod logic;
|
|||||||
pub mod network;
|
pub mod network;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod super_advanced_ai;
|
pub mod super_advanced_ai;
|
||||||
|
pub mod ui;
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ impl Board {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// For testing
|
/// For testing
|
||||||
|
#[allow(dead_code)]
|
||||||
fn new_empty(player1: &mut Player, player2: &mut Player, size: Option<usize>) -> Self {
|
fn new_empty(player1: &mut Player, player2: &mut Player, size: Option<usize>) -> Self {
|
||||||
debug!("Creating new empty board!");
|
debug!("Creating new empty board!");
|
||||||
let size = size.unwrap_or(8);
|
let size = size.unwrap_or(8);
|
||||||
@@ -104,6 +105,7 @@ impl Board {
|
|||||||
Board { board, size }
|
Board { board, size }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn place_pawn(&mut self, target: Position, owner: &Player) {
|
fn place_pawn(&mut self, target: Position, owner: &Player) {
|
||||||
debug!("Placing new pawn by hand!");
|
debug!("Placing new pawn by hand!");
|
||||||
self[target].pawn = Some(Pawn::new(owner));
|
self[target].pawn = Some(Pawn::new(owner));
|
||||||
@@ -131,6 +133,7 @@ impl Board {
|
|||||||
pos.is_in_range(self.size) && self[pos].pawn.is_none()
|
pos.is_in_range(self.size) && self[pos].pawn.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn is_pawn_movable(&self, pos: Position, player: &Player) -> bool {
|
fn is_pawn_movable(&self, pos: Position, player: &Player) -> bool {
|
||||||
self.get_available_moves(pos, player)
|
self.get_available_moves(pos, player)
|
||||||
.is_some_and(|moves| !moves.is_empty())
|
.is_some_and(|moves| !moves.is_empty())
|
||||||
@@ -218,7 +221,7 @@ impl Board {
|
|||||||
from, to
|
from, to
|
||||||
);
|
);
|
||||||
|
|
||||||
if !self.is_player_owner_of_the_pawn(player, &pawn) {
|
if !self.is_player_owner_of_the_pawn(player, pawn) {
|
||||||
debug!("Player wasn't owner of the pawn...");
|
debug!("Player wasn't owner of the pawn...");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -279,33 +282,32 @@ impl Board {
|
|||||||
let target_option: Option<Position> = current_pos.checked_add(direction);
|
let target_option: Option<Position> = current_pos.checked_add(direction);
|
||||||
|
|
||||||
if let Some(target) = target_option
|
if let Some(target) = target_option
|
||||||
&& self.can_capture(current_pos, target, player, captured_enemies, &pawn)
|
&& self.can_capture(current_pos, target, player, captured_enemies, pawn)
|
||||||
|
&& let Some(path_after_capture) = target.checked_add(direction)
|
||||||
{
|
{
|
||||||
if let Some(path_after_capture) = target.checked_add(direction) {
|
debug!(
|
||||||
debug!(
|
"Found possible capture: {} -> {}",
|
||||||
"Found possible capture: {} -> {}",
|
current_pos, path_after_capture
|
||||||
current_pos, path_after_capture
|
);
|
||||||
);
|
|
||||||
|
|
||||||
found_any_capture = true;
|
found_any_capture = true;
|
||||||
|
|
||||||
let mut new_path = current_path.clone();
|
let mut new_path = current_path.clone();
|
||||||
new_path.push(path_after_capture);
|
new_path.push(path_after_capture);
|
||||||
|
|
||||||
let mut new_enemies = captured_enemies.clone();
|
let mut new_enemies = captured_enemies.clone();
|
||||||
new_enemies.push(target);
|
new_enemies.push(target);
|
||||||
|
|
||||||
self.find_captures(
|
self.find_captures(
|
||||||
start_pos,
|
start_pos,
|
||||||
path_after_capture,
|
path_after_capture,
|
||||||
&mut new_path,
|
&mut new_path,
|
||||||
&new_enemies,
|
&new_enemies,
|
||||||
available_directions,
|
available_directions,
|
||||||
player,
|
player,
|
||||||
all_paths,
|
all_paths,
|
||||||
&pawn,
|
pawn,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,7 +417,7 @@ impl Board {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.is_player_owner_of_the_pawn(&player, self[from].pawn.as_ref().unwrap()) {
|
if !self.is_player_owner_of_the_pawn(player, self[from].pawn.as_ref().unwrap()) {
|
||||||
warn!("Player {} tried moving a pawn which he doesn own!", player);
|
warn!("Player {} tried moving a pawn which he doesn own!", player);
|
||||||
debug!("Tried moving from {} to {}", from, to);
|
debug!("Tried moving from {} to {}", from, to);
|
||||||
return false;
|
return false;
|
||||||
@@ -462,7 +464,7 @@ impl Board {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.is_player_owner_of_the_pawn(player, &pawn) {
|
if !self.is_player_owner_of_the_pawn(player, pawn) {
|
||||||
warn!("Player {} tried moving a pawn which he doesn own!", player);
|
warn!("Player {} tried moving a pawn which he doesn own!", player);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -491,9 +493,9 @@ impl Board {
|
|||||||
impl Index<usize> for Board {
|
impl Index<usize> for Board {
|
||||||
type Output = [Field];
|
type Output = [Field];
|
||||||
|
|
||||||
fn index<'a>(&'a self, i: usize) -> &'a [Field] {
|
fn index(&self, i: usize) -> &[Field] {
|
||||||
let start = i * &self.size;
|
let start = i * self.size;
|
||||||
let end: usize = start + &self.size;
|
let end: usize = start + self.size;
|
||||||
&self.board[start..end]
|
&self.board[start..end]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -507,9 +509,9 @@ impl Index<Position> for Board {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IndexMut<usize> for Board {
|
impl IndexMut<usize> for Board {
|
||||||
fn index_mut<'a>(&'a mut self, i: usize) -> &'a mut [Field] {
|
fn index_mut(&mut self, i: usize) -> &mut [Field] {
|
||||||
let start = i * &self.size;
|
let start = i * self.size;
|
||||||
let end: usize = start + &self.size;
|
let end: usize = start + self.size;
|
||||||
&mut self.board[start..end]
|
&mut self.board[start..end]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use uuid::Uuid;
|
|||||||
use crate::logic::board::{board::Board, field::Field};
|
use crate::logic::board::{board::Board, field::Field};
|
||||||
|
|
||||||
/// View of the board, without any methods. Meant for sending over network
|
/// View of the board, without any methods. Meant for sending over network
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
|
||||||
pub struct BoardView {
|
pub struct BoardView {
|
||||||
pub board: Vec<Field>,
|
pub board: Vec<Field>,
|
||||||
pub size: usize,
|
pub size: usize,
|
||||||
@@ -24,13 +24,31 @@ impl From<Board> for BoardView {
|
|||||||
impl Index<usize> for BoardView {
|
impl Index<usize> for BoardView {
|
||||||
type Output = [Field];
|
type Output = [Field];
|
||||||
|
|
||||||
fn index<'a>(&'a self, i: usize) -> &'a [Field] {
|
fn index(&self, i: usize) -> &[Field] {
|
||||||
let start = i * &self.size;
|
let start = i * self.size;
|
||||||
let end: usize = start + &self.size;
|
let end: usize = start + self.size;
|
||||||
&self.board[start..end]
|
&self.board[start..end]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for BoardView {
|
||||||
|
type Item = Field;
|
||||||
|
type IntoIter = std::vec::IntoIter<Field>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.board.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a BoardView {
|
||||||
|
type Item = &'a Field;
|
||||||
|
type IntoIter = std::slice::Iter<'a, Field>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.board.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BoardView {
|
impl BoardView {
|
||||||
pub fn to_string(&self, owner: &Uuid) -> String {
|
pub fn to_string(&self, owner: &Uuid) -> String {
|
||||||
(0..self.size)
|
(0..self.size)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::logic::{board::pawn::Pawn, math::position::Position};
|
use crate::logic::{board::pawn::Pawn, math::position::Position};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Default)]
|
||||||
pub struct Field {
|
pub struct Field {
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
pub pawn: Option<Pawn>,
|
pub pawn: Option<Pawn>,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
pub mod board;
|
pub mod board;
|
||||||
pub mod board_view;
|
pub mod board_view;
|
||||||
mod field;
|
pub mod field;
|
||||||
pub mod pawn;
|
pub mod pawn;
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ impl GameMaster {
|
|||||||
fn is_player_lost(&self, player: &Player) -> bool {
|
fn is_player_lost(&self, player: &Player) -> bool {
|
||||||
let player_pawns = self.board.get_player_pawns_positions(player);
|
let player_pawns = self.board.get_player_pawns_positions(player);
|
||||||
|
|
||||||
player_pawns.len() == 0
|
player_pawns.is_empty()
|
||||||
|| player_pawns.iter().all(|pos| {
|
|| player_pawns.iter().all(|pos| {
|
||||||
self.board.get_available_moves(*pos, player).is_none()
|
self.board.get_available_moves(*pos, player).is_none()
|
||||||
&& self.board.get_available_captures(*pos, player).is_none()
|
&& self.board.get_available_captures(*pos, player).is_none()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::logic::math::vector::Vector2D;
|
use crate::logic::math::vector::Vector2D;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Default)]
|
||||||
/// Position on the board. Consists of row and column. Should be treated like a point in space on
|
/// Position on the board. Consists of row and column. Should be treated like a point in space on
|
||||||
/// unsigned grid.
|
/// unsigned grid.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ impl Client {
|
|||||||
|
|
||||||
async fn send_message(&mut self, msg: Result<Message<ClientMessage>, postcard::Error>) {
|
async fn send_message(&mut self, msg: Result<Message<ClientMessage>, postcard::Error>) {
|
||||||
debug!("Sending message to the server!");
|
debug!("Sending message to the server!");
|
||||||
let _ = match msg {
|
match msg {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("There was an error while creating the message: {}", e);
|
error!("There was an error while creating the message: {}", e);
|
||||||
return;
|
return;
|
||||||
@@ -137,10 +137,9 @@ impl Client {
|
|||||||
Ok(msg) => {
|
Ok(msg) => {
|
||||||
if let Err(e) = self.conn_sender_outgoing.send(msg).await {
|
if let Err(e) = self.conn_sender_outgoing.send(msg).await {
|
||||||
error!("Error while sending a message to connection thread: {}", e);
|
error!("Error while sending a message to connection thread: {}", e);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_capture(&mut self, capture_path: CapturePath) {
|
pub async fn send_capture(&mut self, capture_path: CapturePath) {
|
||||||
@@ -168,6 +167,12 @@ impl Client {
|
|||||||
self.send_message(msg).await;
|
self.send_message(msg).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn revoke_readiness(&mut self) {
|
||||||
|
debug!("Revoking readiness and sending it to the server...");
|
||||||
|
let msg = Message::new(ClientMessage::SignalUnreadiness);
|
||||||
|
self.send_message(msg).await;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_update_receiver(&mut self) -> Option<Receiver<ServerMessage>> {
|
pub fn get_update_receiver(&mut self) -> Option<Receiver<ServerMessage>> {
|
||||||
self.update_receiver.take()
|
self.update_receiver.take()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,8 +110,7 @@ where
|
|||||||
|
|
||||||
if let Err(e) = tcp.write_all(&msg).await {
|
if let Err(e) = tcp.write_all(&msg).await {
|
||||||
error!("Error while sending content to: {:?}\n{}", self.peer, e);
|
error!("Error while sending content to: {:?}\n{}", self.peer, e);
|
||||||
return;
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start_listening(&mut self) {
|
pub async fn start_listening(&mut self) {
|
||||||
@@ -202,20 +201,20 @@ where
|
|||||||
{
|
{
|
||||||
error!("Erro while sending message to main thread: {}", e);
|
error!("Erro while sending message to main thread: {}", e);
|
||||||
}
|
}
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(
|
error!(
|
||||||
"Error while preparing message in read_content for sender: {}",
|
"Error while preparing message in read_content for sender: {}",
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error while getting the message from bits: {}", e);
|
error!("Error while getting the message from bits: {}", e);
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ pub enum ServerMessage {
|
|||||||
pub enum ClientMessage {
|
pub enum ClientMessage {
|
||||||
AnswerHandshake { player_name: String },
|
AnswerHandshake { player_name: String },
|
||||||
SignalReadiness,
|
SignalReadiness,
|
||||||
|
SignalUnreadiness,
|
||||||
|
|
||||||
ConnectionDead { addr: SocketAddr },
|
ConnectionDead { addr: SocketAddr },
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,9 @@ const MAX_PLAYABLE_CONNECTIONS: usize = 2; // DON'T EVER CHANGE THIS AS GAME OF
|
|||||||
// There will be a spectator mode, hence the name __PLAYABLE__ connection. You will be able to have
|
// There will be a spectator mode, hence the name __PLAYABLE__ connection. You will be able to have
|
||||||
// more connections, but they won't be able to play
|
// more connections, but they won't be able to play
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(Debug, PartialEq, Default)]
|
||||||
enum ServerStage {
|
pub enum ServerStage {
|
||||||
|
#[default]
|
||||||
Lobby,
|
Lobby,
|
||||||
Game,
|
Game,
|
||||||
End,
|
End,
|
||||||
@@ -155,7 +156,7 @@ impl Server {
|
|||||||
let reason = "There is currently game going and it doesn't allow spectators".to_string();
|
let reason = "There is currently game going and it doesn't allow spectators".to_string();
|
||||||
info!("Rejecting {}, because {}", addr, reason);
|
info!("Rejecting {}, because {}", addr, reason);
|
||||||
|
|
||||||
let msg = Message::new(ServerMessage::DeclineHandshake { reason: reason });
|
let msg = Message::new(ServerMessage::DeclineHandshake { reason });
|
||||||
|
|
||||||
if let Ok(msg) = msg {
|
if let Ok(msg) = msg {
|
||||||
let _ = sender.send(msg).await; // we can't use send_message because we
|
let _ = sender.send(msg).await; // we can't use send_message because we
|
||||||
@@ -188,6 +189,10 @@ impl Server {
|
|||||||
self.handle_readiness(addr).await
|
self.handle_readiness(addr).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClientMessage::SignalUnreadiness => {
|
||||||
|
self.handle_unreadiness(addr).await;
|
||||||
|
}
|
||||||
|
|
||||||
ClientMessage::RequestCapture { capture_path } => {
|
ClientMessage::RequestCapture { capture_path } => {
|
||||||
self.process_capture(capture_path).await;
|
self.process_capture(capture_path).await;
|
||||||
}
|
}
|
||||||
@@ -336,6 +341,37 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_unreadiness(&mut self, addr: SocketAddr) {
|
||||||
|
if !self.connections[&addr].is_handshaken {
|
||||||
|
warn!(
|
||||||
|
"Connection: {} tried revoking readiness, but hands were not shaken. Ignoring....",
|
||||||
|
addr
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.state.stage != ServerStage::Lobby {
|
||||||
|
warn!(
|
||||||
|
"Player {} tried revoking readiness, but game already started. Ignoring...",
|
||||||
|
addr
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state.ready_count -= 1;
|
||||||
|
|
||||||
|
self.connections
|
||||||
|
.get_mut(&addr)
|
||||||
|
.expect("Could not get the indentity of the player that wanted to revoke readinessl")
|
||||||
|
.identity
|
||||||
|
.is_ready = false;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Player {} revoked his readiness. Currently ready: {}/{}",
|
||||||
|
addr, self.state.ready_count, MAX_PLAYABLE_CONNECTIONS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/* === Sending messages === */
|
/* === Sending messages === */
|
||||||
|
|
||||||
/// Should be used after every players move
|
/// Should be used after every players move
|
||||||
@@ -464,7 +500,6 @@ impl Server {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error while broadcasting the message: {}", e);
|
error!("Error while broadcasting the message: {}", e);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ use crate::{
|
|||||||
settings::{client_settings::ClientSettings, general_settings::DEFAULT_URL},
|
settings::{client_settings::ClientSettings, general_settings::DEFAULT_URL},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy, Default)]
|
||||||
pub enum BotDificulty {
|
pub enum BotDificulty {
|
||||||
|
#[default]
|
||||||
Easy,
|
Easy,
|
||||||
Normal,
|
Normal,
|
||||||
Hard,
|
Hard,
|
||||||
@@ -65,12 +66,12 @@ impl Bot {
|
|||||||
.choose(&mut rand::rng())
|
.choose(&mut rand::rng())
|
||||||
.expect("Could not get random move");
|
.expect("Could not get random move");
|
||||||
|
|
||||||
client.send_move(rand_pawn.from, rand_move.clone()).await;
|
client.send_move(rand_pawn.from, *rand_move).await;
|
||||||
}
|
}
|
||||||
crate::network::message::ServerMessage::BroadcastBoardState { board: _ } => {
|
crate::network::message::ServerMessage::BroadcastBoardState { board: _ } => {
|
||||||
info!("Got server state!");
|
info!("Got server state!");
|
||||||
|
|
||||||
match dificulty.clone() {
|
match dificulty {
|
||||||
BotDificulty::Easy => {} // we do nothing with it because easy bot
|
BotDificulty::Easy => {} // we do nothing with it because easy bot
|
||||||
// just does random stuff. In future
|
// just does random stuff. In future
|
||||||
// board_view can be used to calculate the
|
// board_view can be used to calculate the
|
||||||
|
|||||||
80
src/ui/macroquad/difficulty_selection.rs
Normal file
80
src/ui/macroquad/difficulty_selection.rs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
use macroquad::{
|
||||||
|
color::GRAY,
|
||||||
|
math::vec2,
|
||||||
|
shapes::draw_rectangle,
|
||||||
|
ui::{hash, root_ui, widgets::Group},
|
||||||
|
window::{screen_height, screen_width},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
settings::{
|
||||||
|
client_settings::ClientSettings,
|
||||||
|
general_settings::{DEFAULT_URL, SettingsLike},
|
||||||
|
},
|
||||||
|
ui::{
|
||||||
|
macroquad::menu_builder::MenuBuilder,
|
||||||
|
state::{
|
||||||
|
GameContext,
|
||||||
|
GuiState::{self},
|
||||||
|
connect_bot, connect_to_server, run_server,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn draw_dificulty_selection(state: &mut GuiState, context: &mut GameContext) {
|
||||||
|
let menu_size = vec2(screen_width() * 0.5, screen_height() * 0.5);
|
||||||
|
let menu_pos = vec2(
|
||||||
|
screen_width() / 2.0 - menu_size.x / 2.0,
|
||||||
|
screen_height() / 2.0 - menu_size.y / 2.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut menu_builder = MenuBuilder::new(menu_size.x, menu_size.y * 0.1);
|
||||||
|
|
||||||
|
draw_rectangle(menu_pos.x, menu_pos.y, menu_size.x, menu_size.y, GRAY);
|
||||||
|
|
||||||
|
Group::new(hash!(), menu_size)
|
||||||
|
.position(menu_pos)
|
||||||
|
.ui(&mut root_ui(), |ui| {
|
||||||
|
menu_builder.label(ui, "Difficulty Selection");
|
||||||
|
|
||||||
|
// TODO! refactor this ugly ass code
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Easy") {
|
||||||
|
context.difficulty = crate::super_advanced_ai::BotDificulty::Easy;
|
||||||
|
start_single(state, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Medium") {
|
||||||
|
context.difficulty = crate::super_advanced_ai::BotDificulty::Normal;
|
||||||
|
start_single(state, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Hard") {
|
||||||
|
context.difficulty = crate::super_advanced_ai::BotDificulty::Hard;
|
||||||
|
start_single(state, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Go back") {
|
||||||
|
*state = GuiState::ModeSelection;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_single(state: &mut GuiState, context: &GameContext) {
|
||||||
|
let mut client_settings = ClientSettings::new();
|
||||||
|
client_settings.server_url = DEFAULT_URL; // we overwrite server_url since we connect locally
|
||||||
|
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
|
run_server(tx);
|
||||||
|
|
||||||
|
let result = rx.recv();
|
||||||
|
|
||||||
|
if let Ok(_result) = result {
|
||||||
|
connect_bot(context.difficulty);
|
||||||
|
}
|
||||||
|
|
||||||
|
*state = GuiState::Connecting(connect_to_server());
|
||||||
|
}
|
||||||
472
src/ui/macroquad/game/board.rs
Normal file
472
src/ui/macroquad/game/board.rs
Normal file
@@ -0,0 +1,472 @@
|
|||||||
|
use std::{
|
||||||
|
ops::{Index, IndexMut},
|
||||||
|
slice::IterMut,
|
||||||
|
};
|
||||||
|
|
||||||
|
use log::debug;
|
||||||
|
use macroquad::{
|
||||||
|
color::{BLACK, Color, GREEN, RED, WHITE, YELLOW},
|
||||||
|
input::{is_mouse_button_pressed, mouse_position},
|
||||||
|
math::{Rect, vec2},
|
||||||
|
shapes::{draw_circle, draw_circle_lines, draw_rectangle},
|
||||||
|
};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::logic::{
|
||||||
|
board::{
|
||||||
|
board_view::BoardView,
|
||||||
|
field::Field,
|
||||||
|
pawn::{CapturePath, MovePath},
|
||||||
|
},
|
||||||
|
math::position::Position,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MOVE_HIGHLITGHT_COLOR: Color = GREEN;
|
||||||
|
const CAPTURE_HIGHLITGHT_COLOR: Color = RED;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct GuiField {
|
||||||
|
rect: Rect,
|
||||||
|
field: Field,
|
||||||
|
pos: Position,
|
||||||
|
is_clickable: bool,
|
||||||
|
color: Color,
|
||||||
|
is_blac: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct GuiFieldVec {
|
||||||
|
gui_fields: Vec<GuiField>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuiFieldVec {
|
||||||
|
pub fn push(&mut self, input: GuiField) {
|
||||||
|
self.gui_fields.push(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.gui_fields.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a GuiFieldVec {
|
||||||
|
type Item = &'a GuiField;
|
||||||
|
type IntoIter = std::slice::Iter<'a, GuiField>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.gui_fields.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a mut GuiFieldVec {
|
||||||
|
type Item = &'a mut GuiField;
|
||||||
|
type IntoIter = IterMut<'a, GuiField>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.gui_fields.iter_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<Position> for GuiFieldVec {
|
||||||
|
type Output = GuiField;
|
||||||
|
|
||||||
|
fn index(&self, index: Position) -> &Self::Output {
|
||||||
|
self.gui_fields
|
||||||
|
.iter()
|
||||||
|
.find(|field| field.pos == index)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<Position> for GuiFieldVec {
|
||||||
|
fn index_mut(&mut self, index: Position) -> &mut Self::Output {
|
||||||
|
self.gui_fields
|
||||||
|
.iter_mut()
|
||||||
|
.find(|field| field.pos == index)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
enum BoardState {
|
||||||
|
#[default]
|
||||||
|
View,
|
||||||
|
ChoosePawn,
|
||||||
|
ChooseMove(Position),
|
||||||
|
ChooseCapture(Position),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Board {
|
||||||
|
rect: Rect,
|
||||||
|
|
||||||
|
board_view: BoardView,
|
||||||
|
fields: GuiFieldVec,
|
||||||
|
|
||||||
|
state: BoardState,
|
||||||
|
|
||||||
|
my_id: Uuid,
|
||||||
|
is_my_turn: bool,
|
||||||
|
|
||||||
|
available_captures: Option<Vec<CapturePath>>,
|
||||||
|
current_deepness: usize,
|
||||||
|
current_capture_path: Vec<Position>,
|
||||||
|
choosen_capture: Option<CapturePath>,
|
||||||
|
|
||||||
|
available_moves: Option<Vec<MovePath>>,
|
||||||
|
choosen_move: Option<MovePath>,
|
||||||
|
|
||||||
|
field_size: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Board {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(Rect::default(), Uuid::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
pub fn new(rect: Rect, player_id: Uuid) -> Self {
|
||||||
|
Self {
|
||||||
|
rect,
|
||||||
|
|
||||||
|
board_view: BoardView::default(),
|
||||||
|
fields: GuiFieldVec::default(),
|
||||||
|
|
||||||
|
state: BoardState::default(),
|
||||||
|
|
||||||
|
my_id: player_id,
|
||||||
|
is_my_turn: false,
|
||||||
|
|
||||||
|
available_captures: None,
|
||||||
|
current_capture_path: vec![],
|
||||||
|
current_deepness: 0,
|
||||||
|
choosen_capture: None,
|
||||||
|
|
||||||
|
available_moves: None,
|
||||||
|
choosen_move: None,
|
||||||
|
|
||||||
|
field_size: 32.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
self.update_dimensions();
|
||||||
|
self.reset_color();
|
||||||
|
|
||||||
|
let clicked_field = self.get_clicked_field();
|
||||||
|
|
||||||
|
match self.state {
|
||||||
|
BoardState::View => {
|
||||||
|
if self.is_my_turn {
|
||||||
|
self.state = BoardState::ChoosePawn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BoardState::ChoosePawn => {
|
||||||
|
if let Some(clicked_field) = clicked_field {
|
||||||
|
self.delegate_move(clicked_field.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.is_back_move_wanted_by_player() {
|
||||||
|
self.state = BoardState::View;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.highlight_current_moves();
|
||||||
|
}
|
||||||
|
|
||||||
|
BoardState::ChooseMove(from) => {
|
||||||
|
if self.is_back_move_wanted_by_player() {
|
||||||
|
self.state = BoardState::ChoosePawn;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.highlight_move_paths(from);
|
||||||
|
|
||||||
|
if let Some(field) = &self.get_clicked_field()
|
||||||
|
&& let Some(available_moves) = &self.available_moves
|
||||||
|
&& available_moves.iter().any(|available_move| {
|
||||||
|
available_move.from == from
|
||||||
|
&& available_move.available_steps.contains(&field.pos)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
self.choosen_move = Some(MovePath {
|
||||||
|
from,
|
||||||
|
available_steps: vec![field.pos],
|
||||||
|
});
|
||||||
|
self.state = BoardState::View;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BoardState::ChooseCapture(from) => {
|
||||||
|
if self.is_back_move_wanted_by_player() {
|
||||||
|
self.reset_capture();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(available_captures) = &self.available_captures else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let possible_moves: Vec<_> = available_captures
|
||||||
|
.iter()
|
||||||
|
.filter_map(|capture| {
|
||||||
|
if capture.from == from
|
||||||
|
&& capture.steps.starts_with(&self.current_capture_path)
|
||||||
|
{
|
||||||
|
Some(&capture.steps)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let current_highlight: Vec<_> = possible_moves
|
||||||
|
.iter()
|
||||||
|
.filter_map(|possible_move| possible_move.get(self.current_deepness))
|
||||||
|
.copied()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let Some(clicked_field) = &self.get_clicked_field().map(|field| field.pos)
|
||||||
|
&& possible_moves.iter().any(|possible_move| {
|
||||||
|
if let Some(valid_move) = possible_move.get(self.current_deepness) {
|
||||||
|
valid_move == clicked_field
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
self.current_capture_path.push(*clicked_field);
|
||||||
|
self.current_deepness += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if current_highlight.is_empty() {
|
||||||
|
debug!("Capture path complete!");
|
||||||
|
self.choosen_capture = available_captures
|
||||||
|
.iter()
|
||||||
|
.find(|capture| {
|
||||||
|
capture.from == from && capture.steps == self.current_capture_path
|
||||||
|
})
|
||||||
|
.cloned();
|
||||||
|
|
||||||
|
self.reset_capture();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.highlight_capture_path(current_highlight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_capture(&mut self) {
|
||||||
|
self.current_capture_path.clear();
|
||||||
|
self.current_deepness = 0;
|
||||||
|
self.state = BoardState::View;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delegate_move(&mut self, from: Position) {
|
||||||
|
if let Some(capture_path) = &self.available_captures
|
||||||
|
&& capture_path.iter().any(|path| path.from == from)
|
||||||
|
{
|
||||||
|
self.state = BoardState::ChooseCapture(from);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(available_moves) = &self.available_moves
|
||||||
|
&& available_moves.iter().any(|path| path.from == from)
|
||||||
|
{
|
||||||
|
self.state = BoardState::ChooseMove(from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_choosen_capture(&mut self) -> Option<CapturePath> {
|
||||||
|
self.choosen_capture.take()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_choosen_move(&mut self) -> Option<MovePath> {
|
||||||
|
self.choosen_move.take()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_board_view(&mut self, board_view: BoardView) {
|
||||||
|
self.board_view = board_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_available_captures(&mut self, captures: Vec<CapturePath>) {
|
||||||
|
self.available_captures = Some(captures);
|
||||||
|
self.available_moves = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_available_moves(&mut self, moves: Vec<MovePath>) {
|
||||||
|
self.state = BoardState::ChoosePawn;
|
||||||
|
|
||||||
|
self.available_moves = Some(moves);
|
||||||
|
self.available_captures = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_board_rect(&mut self, rect: Rect) -> bool {
|
||||||
|
if self.rect != rect {
|
||||||
|
self.rect = rect;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recreate fields vec with new dimensions. Should be used when new board_view arrives
|
||||||
|
pub fn repopulate(&mut self) {
|
||||||
|
self.fields.clear();
|
||||||
|
|
||||||
|
self.field_size = self.rect.w / self.board_view.size as f32;
|
||||||
|
|
||||||
|
for field in &self.board_view {
|
||||||
|
let pos: Position = field.position;
|
||||||
|
|
||||||
|
let is_row_even: bool = pos.row.is_multiple_of(2);
|
||||||
|
let is_column_even: bool = pos.column.is_multiple_of(2);
|
||||||
|
let is_field_black: bool = is_row_even ^ is_column_even;
|
||||||
|
|
||||||
|
let abs_x = self.rect.x + pos.column as f32 * self.field_size;
|
||||||
|
let abs_y = self.rect.y + pos.row as f32 * self.field_size;
|
||||||
|
|
||||||
|
let rect = Rect {
|
||||||
|
x: abs_x,
|
||||||
|
y: abs_y,
|
||||||
|
w: self.field_size,
|
||||||
|
h: self.field_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.fields.push(GuiField {
|
||||||
|
rect,
|
||||||
|
field: field.clone(),
|
||||||
|
is_clickable: false,
|
||||||
|
pos,
|
||||||
|
color: if is_field_black { BLACK } else { WHITE },
|
||||||
|
is_blac: is_field_black,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates dimensions of graphical squares
|
||||||
|
pub fn update_dimensions(&mut self) {
|
||||||
|
self.field_size = self.rect.w / self.board_view.size as f32;
|
||||||
|
|
||||||
|
for field in &mut self.fields {
|
||||||
|
let pos: Position = field.pos;
|
||||||
|
|
||||||
|
let abs_x = self.rect.x + pos.column as f32 * self.field_size;
|
||||||
|
let abs_y = self.rect.y + pos.row as f32 * self.field_size;
|
||||||
|
|
||||||
|
let new_rect = Rect {
|
||||||
|
x: abs_x,
|
||||||
|
y: abs_y,
|
||||||
|
w: self.field_size,
|
||||||
|
h: self.field_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
field.rect = new_rect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_color(&mut self) {
|
||||||
|
for field in &mut self.fields {
|
||||||
|
field.color = if field.is_blac { BLACK } else { WHITE };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self) {
|
||||||
|
for field in &self.fields {
|
||||||
|
draw_rectangle(
|
||||||
|
field.rect.x,
|
||||||
|
field.rect.y,
|
||||||
|
field.rect.w,
|
||||||
|
field.rect.h,
|
||||||
|
field.color,
|
||||||
|
);
|
||||||
|
|
||||||
|
if field.is_clickable {
|
||||||
|
draw_rectangle(
|
||||||
|
field.rect.x,
|
||||||
|
field.rect.y,
|
||||||
|
field.rect.w,
|
||||||
|
field.rect.h,
|
||||||
|
GREEN,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pawn) = &field.field.pawn {
|
||||||
|
let color = if pawn.owner == self.my_id { WHITE } else { RED }; // TODO! This should
|
||||||
|
// be customizable
|
||||||
|
|
||||||
|
let center = field.rect.center();
|
||||||
|
|
||||||
|
let r = field.rect.w * 0.5;
|
||||||
|
let thickness = 0.5;
|
||||||
|
|
||||||
|
draw_circle(center.x, center.y, r, color);
|
||||||
|
match pawn.state {
|
||||||
|
crate::logic::board::pawn::PawnState::Man => {
|
||||||
|
draw_circle_lines(center.x, center.y, r, thickness, BLACK);
|
||||||
|
}
|
||||||
|
crate::logic::board::pawn::PawnState::Dame => {
|
||||||
|
draw_circle_lines(center.x, center.y, r, thickness, YELLOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_clicked_field(&self) -> Option<&GuiField> {
|
||||||
|
let (mouse_x, mouse_y) = mouse_position();
|
||||||
|
let mouse_point = vec2(mouse_x, mouse_y);
|
||||||
|
|
||||||
|
if is_mouse_button_pressed(macroquad::input::MouseButton::Left) {
|
||||||
|
for field in &self.fields {
|
||||||
|
if field.rect.contains(mouse_point) {
|
||||||
|
return Some(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_back_move_wanted_by_player(&self) -> bool {
|
||||||
|
is_mouse_button_pressed(macroquad::input::MouseButton::Right)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_my_turn(&mut self, is_my_turn: bool) {
|
||||||
|
self.is_my_turn = is_my_turn;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn highlight_current_moves(&mut self) {
|
||||||
|
if let Some(available_captures) = &self.available_captures {
|
||||||
|
for capture_path in available_captures {
|
||||||
|
self.fields[capture_path.from].color = MOVE_HIGHLITGHT_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(available_moves) = &self.available_moves {
|
||||||
|
for available_move in available_moves {
|
||||||
|
self.fields[available_move.from].color = MOVE_HIGHLITGHT_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn highlight_move_paths(&mut self, from: Position) {
|
||||||
|
let Some(available_moves) = &self.available_moves else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for available_move in available_moves {
|
||||||
|
if available_move.from == from {
|
||||||
|
for step in &available_move.available_steps {
|
||||||
|
self.fields[*step].color = MOVE_HIGHLITGHT_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn highlight_capture_path(&mut self, fields: Vec<Position>) {
|
||||||
|
for field in fields {
|
||||||
|
self.fields[field].color = CAPTURE_HIGHLITGHT_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
165
src/ui/macroquad/game/chat.rs
Normal file
165
src/ui/macroquad/game/chat.rs
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
use macroquad::{
|
||||||
|
color::{BLACK, WHITE},
|
||||||
|
math::{Rect, vec2},
|
||||||
|
shapes::draw_rectangle,
|
||||||
|
text::measure_text,
|
||||||
|
ui::{
|
||||||
|
Skin, Ui, hash, root_ui,
|
||||||
|
widgets::{Group, InputText},
|
||||||
|
},
|
||||||
|
window::{screen_height, screen_width},
|
||||||
|
};
|
||||||
|
use tokio::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
use crate::ui::macroquad::game::game::GuiCommands;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Message {
|
||||||
|
sender: String,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Chat {
|
||||||
|
area: Rect,
|
||||||
|
messages: Vec<Message>,
|
||||||
|
message_buffer: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chat {
|
||||||
|
pub fn update(&mut self, game_area: &Rect) {
|
||||||
|
self.area = Rect {
|
||||||
|
x: game_area.w,
|
||||||
|
y: 0.0,
|
||||||
|
w: screen_width() * 0.2,
|
||||||
|
h: screen_height(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_message(&mut self, sender: String, content: String) {
|
||||||
|
self.messages.push(Message { sender, content });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_message(&mut self, cmd_sender: Sender<GuiCommands>) {
|
||||||
|
if !self.message_buffer.is_empty() {
|
||||||
|
let _ = // TODO! Check the message before sending
|
||||||
|
cmd_sender
|
||||||
|
.try_send(GuiCommands::Send(self.message_buffer.clone()));
|
||||||
|
self.message_buffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&mut self) {
|
||||||
|
draw_rectangle(self.area.x, self.area.y, self.area.w, self.area.h, BLACK);
|
||||||
|
|
||||||
|
let input_size = vec2(self.area.w, self.area.h * 0.1);
|
||||||
|
let input_pos = vec2(self.area.x, self.area.h - input_size.y);
|
||||||
|
|
||||||
|
InputText::new(hash!())
|
||||||
|
.position(input_pos)
|
||||||
|
.size(input_size)
|
||||||
|
.ui(&mut root_ui(), &mut self.message_buffer);
|
||||||
|
|
||||||
|
let chat_size = vec2(self.area.w, self.area.h - input_size.y);
|
||||||
|
let chat_pos = vec2(self.area.x, self.area.y);
|
||||||
|
|
||||||
|
let font_size = (self.area.w * 0.1) as u16;
|
||||||
|
|
||||||
|
let label_style = root_ui()
|
||||||
|
.style_builder()
|
||||||
|
.font_size(font_size)
|
||||||
|
.text_color(WHITE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let chat_skin: Skin = Skin {
|
||||||
|
label_style,
|
||||||
|
..root_ui().default_skin()
|
||||||
|
};
|
||||||
|
|
||||||
|
root_ui().push_skin(&chat_skin);
|
||||||
|
|
||||||
|
let mut prev_sender: &str = "";
|
||||||
|
|
||||||
|
Group::new(hash!(), chat_size)
|
||||||
|
.position(chat_pos)
|
||||||
|
.ui(&mut root_ui(), |ui| {
|
||||||
|
for message in &self.messages {
|
||||||
|
if prev_sender != message.sender {
|
||||||
|
if !prev_sender.is_empty() {
|
||||||
|
// make space if it's not the first message
|
||||||
|
ui.label(None, "");
|
||||||
|
}
|
||||||
|
ui.label(None, &format!("[{}]", message.sender));
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap_message(ui, &message.content, font_size, chat_size.x);
|
||||||
|
prev_sender = &message.sender;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
root_ui().pop_skin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_message(ui: &mut Ui, msg: &String, font_size: u16, chat_width: f32) {
|
||||||
|
let chat_width = chat_width - 20.0; // we leave some room
|
||||||
|
|
||||||
|
let mut buffer = String::new();
|
||||||
|
|
||||||
|
if measure_text(msg, None, font_size, 1.0).width < chat_width {
|
||||||
|
ui.label(None, msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for word in msg.split_whitespace() {
|
||||||
|
if measure_text(word, None, font_size, 1.0).width >= chat_width {
|
||||||
|
wrap_one_word(ui, word, font_size, chat_width);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let test_string = if buffer.is_empty() {
|
||||||
|
word.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{} {}", buffer, word)
|
||||||
|
};
|
||||||
|
|
||||||
|
let test_width = measure_text(&test_string, None, font_size, 1.0).width;
|
||||||
|
|
||||||
|
if test_width < chat_width {
|
||||||
|
buffer = test_string;
|
||||||
|
} else {
|
||||||
|
if !buffer.is_empty() {
|
||||||
|
ui.label(None, &buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = word.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !buffer.is_empty() {
|
||||||
|
ui.label(None, &buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_one_word(ui: &mut Ui, msg: &str, font_size: u16, chat_width: f32) {
|
||||||
|
if measure_text(msg, None, font_size, 1.0).width < chat_width {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg_half = msg.chars().count() / 2;
|
||||||
|
|
||||||
|
let first_half: String = msg.chars().take(msg_half).collect();
|
||||||
|
let second_half: String = msg.chars().skip(msg_half).collect();
|
||||||
|
|
||||||
|
if measure_text(&first_half, None, font_size, 1.0).width >= chat_width {
|
||||||
|
wrap_one_word(ui, &first_half, font_size, chat_width);
|
||||||
|
} else {
|
||||||
|
ui.label(None, &format!("{}-", first_half));
|
||||||
|
}
|
||||||
|
|
||||||
|
if measure_text(&second_half, None, font_size, 1.0).width >= chat_width {
|
||||||
|
wrap_one_word(ui, &second_half, font_size, chat_width);
|
||||||
|
} else {
|
||||||
|
ui.label(None, &format!("{}-", second_half)); // TODO! Last word always has - at the end.
|
||||||
|
}
|
||||||
|
}
|
||||||
373
src/ui/macroquad/game/game.rs
Normal file
373
src/ui/macroquad/game/game.rs
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
use tokio::sync::mpsc::{self, Sender};
|
||||||
|
|
||||||
|
use log::{debug, error, info};
|
||||||
|
use macroquad::{
|
||||||
|
color::GRAY,
|
||||||
|
input::{KeyCode, get_keys_pressed},
|
||||||
|
math::{Rect, Vec2, vec2},
|
||||||
|
shapes::draw_rectangle,
|
||||||
|
ui::{
|
||||||
|
hash, root_ui,
|
||||||
|
widgets::{Checkbox, Group},
|
||||||
|
},
|
||||||
|
window::{screen_height, screen_width},
|
||||||
|
};
|
||||||
|
use tokio::sync::mpsc::{Receiver, error::TryRecvError::Disconnected};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
logic::board::pawn::{CapturePath, MovePath},
|
||||||
|
network::{
|
||||||
|
client::Client, message::ServerMessage, network_identity::NetworkIdentity,
|
||||||
|
server::ServerStage,
|
||||||
|
},
|
||||||
|
ui::{
|
||||||
|
macroquad::{
|
||||||
|
game::{board::Board, chat::Chat},
|
||||||
|
menu_builder::MenuBuilder,
|
||||||
|
},
|
||||||
|
state::GameContext,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct GameClient {
|
||||||
|
cmd_sender: Sender<GuiCommands>,
|
||||||
|
update_receiver: Receiver<ServerMessage>,
|
||||||
|
|
||||||
|
identity: Option<NetworkIdentity>,
|
||||||
|
|
||||||
|
board: Board,
|
||||||
|
is_my_turn: bool,
|
||||||
|
|
||||||
|
game_state: ServerStage,
|
||||||
|
|
||||||
|
game_area: Rect,
|
||||||
|
board_area: Rect,
|
||||||
|
|
||||||
|
lobby: Lobby,
|
||||||
|
chat: Chat,
|
||||||
|
|
||||||
|
game_result: Option<String>,
|
||||||
|
pub should_close: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Lobby {
|
||||||
|
is_ready: bool,
|
||||||
|
checkbox_pos: Vec2,
|
||||||
|
checkbox_size: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum GuiCommands {
|
||||||
|
Send(String),
|
||||||
|
|
||||||
|
Ready,
|
||||||
|
Unready,
|
||||||
|
|
||||||
|
Capture(CapturePath),
|
||||||
|
Move(MovePath),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameClient {
|
||||||
|
pub fn new(mut client: Client, _context: &GameContext) -> Option<Self> {
|
||||||
|
let Some(update_receiver) = client.get_update_receiver() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let (cmd_sender, mut cmd_recv) = mpsc::channel::<GuiCommands>(10);
|
||||||
|
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap(); // i dont like this
|
||||||
|
|
||||||
|
rt.block_on(async move {
|
||||||
|
loop {
|
||||||
|
match cmd_recv.recv().await {
|
||||||
|
Some(cmd) => match cmd {
|
||||||
|
GuiCommands::Send(content) => {
|
||||||
|
client.send_text_message(content).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiCommands::Ready => {
|
||||||
|
client.signal_readiness().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiCommands::Unready => {
|
||||||
|
client.revoke_readiness().await;
|
||||||
|
}
|
||||||
|
GuiCommands::Capture(capture_path) => {
|
||||||
|
client.send_capture(capture_path).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
GuiCommands::Move(move_path) => {
|
||||||
|
client
|
||||||
|
.send_move(move_path.from, move_path.available_steps[0])
|
||||||
|
.await; // ugly
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
None => {
|
||||||
|
error!("Connection with cmd thread broken");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let board = Board::default();
|
||||||
|
|
||||||
|
let game_stage = ServerStage::default();
|
||||||
|
let mut lobby = Lobby::default();
|
||||||
|
lobby.checkbox_size = vec2(32.0, 16.0);
|
||||||
|
|
||||||
|
let game_area = Rect {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
w: screen_width() * 0.8,
|
||||||
|
h: screen_height(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let board_area = Rect::default();
|
||||||
|
|
||||||
|
let chat = Chat::default();
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
cmd_sender,
|
||||||
|
update_receiver,
|
||||||
|
identity: None,
|
||||||
|
|
||||||
|
board,
|
||||||
|
lobby,
|
||||||
|
|
||||||
|
is_my_turn: false,
|
||||||
|
|
||||||
|
game_state: game_stage,
|
||||||
|
|
||||||
|
game_area,
|
||||||
|
board_area,
|
||||||
|
|
||||||
|
chat,
|
||||||
|
|
||||||
|
game_result: None,
|
||||||
|
should_close: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&mut self) {
|
||||||
|
match self.game_state {
|
||||||
|
ServerStage::Lobby => {
|
||||||
|
self.draw_lobby();
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerStage::Game => {
|
||||||
|
self.draw_game();
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerStage::End => {
|
||||||
|
self.draw_summary_screen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.chat.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_lobby(&mut self) {
|
||||||
|
let lobby = &self.lobby;
|
||||||
|
|
||||||
|
let background_x = lobby.checkbox_size.x * 10.0;
|
||||||
|
let background_y = lobby.checkbox_size.y * 10.0;
|
||||||
|
|
||||||
|
draw_rectangle(
|
||||||
|
lobby.checkbox_pos.x - background_x * 0.5,
|
||||||
|
lobby.checkbox_pos.y - background_y * 0.5,
|
||||||
|
background_x,
|
||||||
|
background_y,
|
||||||
|
GRAY,
|
||||||
|
);
|
||||||
|
|
||||||
|
let before_click: bool = self.lobby.is_ready;
|
||||||
|
Checkbox::new(hash!())
|
||||||
|
.pos(self.lobby.checkbox_pos)
|
||||||
|
.label("Ready")
|
||||||
|
.size(self.lobby.checkbox_size)
|
||||||
|
.ui(&mut root_ui(), &mut self.lobby.is_ready);
|
||||||
|
|
||||||
|
if before_click != self.lobby.is_ready {
|
||||||
|
if self.lobby.is_ready {
|
||||||
|
let _ = self.cmd_sender.try_send(GuiCommands::Ready); // TODO! Maybe handle failure
|
||||||
|
// or something
|
||||||
|
} else {
|
||||||
|
let _ = self.cmd_sender.try_send(GuiCommands::Unready);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_game(&mut self) {
|
||||||
|
self.board.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_summary_screen(&mut self) {
|
||||||
|
// this function doesn't only draw but i'm to lazy to change
|
||||||
|
// this name everywhere
|
||||||
|
let mut menu_builder = MenuBuilder::new(self.game_area.w, self.game_area.h * 0.1);
|
||||||
|
let menu_pos = vec2(self.game_area.x, self.game_area.y);
|
||||||
|
|
||||||
|
draw_rectangle(
|
||||||
|
menu_pos.x,
|
||||||
|
menu_pos.y,
|
||||||
|
self.game_area.w,
|
||||||
|
self.game_area.h,
|
||||||
|
GRAY,
|
||||||
|
);
|
||||||
|
|
||||||
|
Group::new(hash!(), vec2(self.game_area.w, self.game_area.h))
|
||||||
|
.position(menu_pos)
|
||||||
|
.ui(&mut root_ui(), |ui| {
|
||||||
|
menu_builder.label(ui, "Game ended!");
|
||||||
|
|
||||||
|
if let Some(result) = &self.game_result {
|
||||||
|
menu_builder.label(ui, result);
|
||||||
|
} else {
|
||||||
|
menu_builder.label(ui, "Could not get result");
|
||||||
|
}
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Main menu") {
|
||||||
|
// TODO! Should also close the connection
|
||||||
|
// and cleanup
|
||||||
|
self.should_close = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === UPDATING STUFF ==== */
|
||||||
|
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
self.update_rects();
|
||||||
|
self.update_network();
|
||||||
|
|
||||||
|
match self.game_state {
|
||||||
|
ServerStage::Lobby => self.update_lobby(),
|
||||||
|
|
||||||
|
ServerStage::Game => {
|
||||||
|
self.board.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerStage::End => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for key in get_keys_pressed() {
|
||||||
|
if key == KeyCode::Enter {
|
||||||
|
self.chat.send_message(self.cmd_sender.clone()); // not sure about cloning this
|
||||||
|
// every message sent. Maybe
|
||||||
|
// Chat should have it's own
|
||||||
|
// clone?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(capture_path) = self.board.get_choosen_capture() {
|
||||||
|
let _ = self.cmd_sender.try_send(GuiCommands::Capture(capture_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(move_path) = self.board.get_choosen_move() {
|
||||||
|
let _ = self.cmd_sender.try_send(GuiCommands::Move(move_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_network(&mut self) {
|
||||||
|
match self.update_receiver.try_recv() {
|
||||||
|
Ok(msg) => match msg {
|
||||||
|
ServerMessage::GameStart { identity } => {
|
||||||
|
info!("We go identity!");
|
||||||
|
self.identity = Some(identity.clone());
|
||||||
|
self.board = Board::new(self.board_area, identity.id);
|
||||||
|
self.game_state = ServerStage::Game;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerMessage::AvailableCaptures { captures } => {
|
||||||
|
self.board.update_available_captures(captures);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerMessage::AvailableMoves { moves } => {
|
||||||
|
self.board.update_available_moves(moves);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerMessage::BroadcastBoardState { board } => {
|
||||||
|
self.board.update_board_view(board);
|
||||||
|
self.board.repopulate();
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerMessage::BroadcastCurrentTurn { active_player } => {
|
||||||
|
if let Some(identity) = &self.identity
|
||||||
|
&& identity.id == active_player
|
||||||
|
{
|
||||||
|
info!("It's out turn!");
|
||||||
|
self.is_my_turn = true;
|
||||||
|
} else {
|
||||||
|
self.is_my_turn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.board.set_my_turn(self.is_my_turn);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerMessage::BroadCastTextMessage { sender, content } => {
|
||||||
|
debug!("Got message from: {} with content: {}", sender, content);
|
||||||
|
self.chat.push_message(sender, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerMessage::GameEnd { result } => {
|
||||||
|
self.game_result = match result {
|
||||||
|
crate::logic::game_master::GameResult::Lost(uuid) => {
|
||||||
|
if self.identity.clone().unwrap().id == uuid {
|
||||||
|
Some("You lost!".to_string())
|
||||||
|
} else {
|
||||||
|
Some("You won!".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crate::logic::game_master::GameResult::Draw => Some("Draw!".to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.game_state = ServerStage::End;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {} // ignore things that network client handled by itself
|
||||||
|
},
|
||||||
|
|
||||||
|
Err(Disconnected) => {
|
||||||
|
error!("Connection broken!!!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(_) => {} // No messages - nothing wrong (why is this even an error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_lobby(&mut self) {
|
||||||
|
self.lobby.checkbox_pos = vec2(
|
||||||
|
self.game_area.w / 2.0 - self.lobby.checkbox_size.x / 2.0,
|
||||||
|
self.game_area.h / 2.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_rects(&mut self) {
|
||||||
|
self.game_area = Rect {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
w: screen_width() * 0.8,
|
||||||
|
h: screen_height(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.chat.update(&self.game_area);
|
||||||
|
|
||||||
|
let w = self.game_area.w * 0.75;
|
||||||
|
let h = self.game_area.h * 0.75;
|
||||||
|
let side = w.min(h);
|
||||||
|
|
||||||
|
self.board_area = Rect {
|
||||||
|
x: self.game_area.w / 2.0 - (side / 2.0),
|
||||||
|
y: self.game_area.h / 2.0 - (side / 2.0),
|
||||||
|
w: side,
|
||||||
|
h: side,
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.board.update_board_rect(self.board_area) {
|
||||||
|
self.board.update_dimensions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/ui/macroquad/game/mod.rs
Normal file
3
src/ui/macroquad/game/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
mod board;
|
||||||
|
mod chat;
|
||||||
|
pub mod game;
|
||||||
39
src/ui/macroquad/main_menu.rs
Normal file
39
src/ui/macroquad/main_menu.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use macroquad::{
|
||||||
|
color::GRAY,
|
||||||
|
math::vec2,
|
||||||
|
shapes::draw_rectangle,
|
||||||
|
ui::{hash, root_ui, widgets::Group},
|
||||||
|
window::{screen_height, screen_width},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::ui::{macroquad::menu_builder::MenuBuilder, state::GuiState};
|
||||||
|
|
||||||
|
pub async fn draw_main_menu(state: &mut GuiState) {
|
||||||
|
let menu_size = vec2(screen_width() * 0.5, screen_height() * 0.5);
|
||||||
|
let menu_pos = vec2(
|
||||||
|
screen_width() / 2.0 - menu_size.x / 2.0,
|
||||||
|
screen_height() / 2.0 - menu_size.y / 2.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut menu_builder = MenuBuilder::new(menu_size.x, menu_size.y * 0.1);
|
||||||
|
|
||||||
|
draw_rectangle(menu_pos.x, menu_pos.y, menu_size.x, menu_size.y, GRAY);
|
||||||
|
|
||||||
|
Group::new(hash!(), menu_size)
|
||||||
|
.position(menu_pos)
|
||||||
|
.ui(&mut root_ui(), |ui| {
|
||||||
|
menu_builder.label(ui, "Main menu");
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Play") {
|
||||||
|
*state = GuiState::ModeSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Settings") {
|
||||||
|
*state = GuiState::Settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Quit") {
|
||||||
|
*state = GuiState::Exit;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
58
src/ui/macroquad/menu_builder.rs
Normal file
58
src/ui/macroquad/menu_builder.rs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
use macroquad::{
|
||||||
|
math::{Vec2, vec2},
|
||||||
|
ui::{
|
||||||
|
Ui, hash,
|
||||||
|
widgets::{Button, InputText, Label},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct MenuBuilder {
|
||||||
|
center_x: f32,
|
||||||
|
current_y: f32,
|
||||||
|
button_size: Vec2,
|
||||||
|
spacing: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MenuBuilder {
|
||||||
|
pub fn new(window_width: f32, start_y: f32) -> Self {
|
||||||
|
let button_size = vec2(200.0, 50.0);
|
||||||
|
Self {
|
||||||
|
center_x: (window_width - button_size.x) / 2.0,
|
||||||
|
current_y: start_y,
|
||||||
|
button_size,
|
||||||
|
spacing: 15.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text_input(&mut self, ui: &mut Ui, id: &str, buffer: &mut String) {
|
||||||
|
InputText::new(hash!(id))
|
||||||
|
.position(vec2(self.center_x, self.current_y))
|
||||||
|
.size(self.button_size)
|
||||||
|
.ui(ui, buffer);
|
||||||
|
|
||||||
|
self.update_height();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn label(&mut self, ui: &mut Ui, label: &str) {
|
||||||
|
Label::new(label)
|
||||||
|
.position(vec2(self.center_x, self.current_y))
|
||||||
|
.size(self.button_size)
|
||||||
|
.ui(ui);
|
||||||
|
|
||||||
|
self.update_height();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn button(&mut self, ui: &mut Ui, label: &str) -> bool {
|
||||||
|
let clicked = Button::new(label)
|
||||||
|
.position(vec2(self.center_x, self.current_y))
|
||||||
|
.size(self.button_size)
|
||||||
|
.ui(ui);
|
||||||
|
|
||||||
|
self.update_height();
|
||||||
|
clicked
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_height(&mut self) {
|
||||||
|
self.current_y += self.button_size.y + self.spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/ui/macroquad/mod.rs
Normal file
7
src/ui/macroquad/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod difficulty_selection;
|
||||||
|
pub mod game;
|
||||||
|
pub mod main_menu;
|
||||||
|
mod menu_builder;
|
||||||
|
pub mod mode_selection;
|
||||||
|
pub mod server_selection;
|
||||||
|
pub mod settings;
|
||||||
46
src/ui/macroquad/mode_selection.rs
Normal file
46
src/ui/macroquad/mode_selection.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
use macroquad::{
|
||||||
|
color::GRAY,
|
||||||
|
math::vec2,
|
||||||
|
shapes::draw_rectangle,
|
||||||
|
ui::{hash, root_ui, widgets::Group},
|
||||||
|
window::{screen_height, screen_width},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::ui::{
|
||||||
|
macroquad::menu_builder::MenuBuilder,
|
||||||
|
state::{
|
||||||
|
GameContext,
|
||||||
|
GuiState::{self, ServerSelection},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn draw_mode_selection(state: &mut GuiState, context: &mut GameContext) {
|
||||||
|
let menu_size = vec2(screen_width() * 0.5, screen_height() * 0.5);
|
||||||
|
let menu_pos = vec2(
|
||||||
|
screen_width() / 2.0 - menu_size.x / 2.0,
|
||||||
|
screen_height() / 2.0 - menu_size.y / 2.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut menu_builder = MenuBuilder::new(menu_size.x, menu_size.y * 0.1);
|
||||||
|
|
||||||
|
draw_rectangle(menu_pos.x, menu_pos.y, menu_size.x, menu_size.y, GRAY);
|
||||||
|
|
||||||
|
Group::new(hash!(), menu_size)
|
||||||
|
.position(menu_pos)
|
||||||
|
.ui(&mut root_ui(), |ui| {
|
||||||
|
menu_builder.label(ui, "Mode selection");
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Singleplayer") {
|
||||||
|
context.gamemode = crate::ui::state::GameMode::Singleplayer;
|
||||||
|
*state = GuiState::DificultySelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Multiplayer") {
|
||||||
|
*state = ServerSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Go back") {
|
||||||
|
*state = GuiState::MainMenu;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
63
src/ui/macroquad/server_selection.rs
Normal file
63
src/ui/macroquad/server_selection.rs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
use macroquad::{
|
||||||
|
color::GRAY,
|
||||||
|
math::vec2,
|
||||||
|
shapes::draw_rectangle,
|
||||||
|
ui::{hash, root_ui, widgets::Group},
|
||||||
|
window::{screen_height, screen_width},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::ui::{
|
||||||
|
macroquad::menu_builder::MenuBuilder,
|
||||||
|
state::{
|
||||||
|
GameContext,
|
||||||
|
GuiState::{self},
|
||||||
|
connect_to_server, run_server,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn draw_server_selection(state: &mut GuiState, context: &mut GameContext) {
|
||||||
|
let menu_size = vec2(screen_width() * 0.5, screen_height() * 0.5);
|
||||||
|
let menu_pos = vec2(
|
||||||
|
screen_width() / 2.0 - menu_size.x / 2.0,
|
||||||
|
screen_height() / 2.0 - menu_size.y / 2.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut menu_builder = MenuBuilder::new(menu_size.x, menu_size.y * 0.1);
|
||||||
|
|
||||||
|
draw_rectangle(menu_pos.x, menu_pos.y, menu_size.x, menu_size.y, GRAY);
|
||||||
|
|
||||||
|
Group::new(hash!(), menu_size)
|
||||||
|
.position(menu_pos)
|
||||||
|
.ui(&mut root_ui(), |ui| {
|
||||||
|
menu_builder.label(ui, "Host");
|
||||||
|
if menu_builder.button(ui, "Start") {
|
||||||
|
context.gamemode = crate::ui::state::GameMode::Multiplayer { is_hosting: true };
|
||||||
|
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
run_server(tx);
|
||||||
|
|
||||||
|
let result = rx.recv();
|
||||||
|
|
||||||
|
if let Ok(_result) = result {
|
||||||
|
*state = GuiState::Connecting(connect_to_server());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menu_builder.label(ui, "Server selection");
|
||||||
|
|
||||||
|
menu_builder.text_input(ui, "server url", &mut context.server_url_buffer);
|
||||||
|
//context.normalise_url();
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Connect") {
|
||||||
|
context.gamemode = crate::ui::state::GameMode::Multiplayer { is_hosting: false };
|
||||||
|
*state = GuiState::Connecting(connect_to_server());
|
||||||
|
// TODO Validate url before connecting or something like that
|
||||||
|
}
|
||||||
|
|
||||||
|
if menu_builder.button(ui, "Go back") {
|
||||||
|
*state = GuiState::ModeSelection;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
85
src/ui/macroquad/settings.rs
Normal file
85
src/ui/macroquad/settings.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use log::error;
|
||||||
|
use macroquad::{
|
||||||
|
color::GRAY,
|
||||||
|
math::vec2,
|
||||||
|
shapes::draw_rectangle,
|
||||||
|
ui::{hash, root_ui, widgets::Group},
|
||||||
|
window::{screen_height, screen_width},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
settings::{
|
||||||
|
client_settings::ClientSettings, general_settings::SettingsLike,
|
||||||
|
server_settings::ServerSettings,
|
||||||
|
},
|
||||||
|
ui::{
|
||||||
|
macroquad::menu_builder::MenuBuilder,
|
||||||
|
state::{GameContext, GuiState},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn draw_settings(
|
||||||
|
state: &mut GuiState,
|
||||||
|
context: &mut GameContext,
|
||||||
|
client_settings: &mut ClientSettings,
|
||||||
|
server_settings: &mut ServerSettings,
|
||||||
|
) {
|
||||||
|
let menu_size = vec2(screen_width() * 0.5, screen_height() * 0.5);
|
||||||
|
let menu_pos = vec2(
|
||||||
|
screen_width() / 2.0 - menu_size.x / 2.0,
|
||||||
|
screen_height() / 2.0 - menu_size.y / 2.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut menu_builder = MenuBuilder::new(menu_size.x, menu_size.y * 0.1);
|
||||||
|
draw_rectangle(menu_pos.x, menu_pos.y, menu_size.x, menu_size.y, GRAY);
|
||||||
|
|
||||||
|
Group::new(hash!(), menu_size)
|
||||||
|
.position(menu_pos)
|
||||||
|
.ui(&mut root_ui(), |ui| {
|
||||||
|
// Client
|
||||||
|
menu_builder.label(ui, "Game settings");
|
||||||
|
|
||||||
|
menu_builder.label(ui, "Name");
|
||||||
|
menu_builder.text_input(ui, "name input", &mut client_settings.name);
|
||||||
|
|
||||||
|
menu_builder.label(ui, "Default server url");
|
||||||
|
menu_builder.text_input(ui, "url input", &mut context.server_url_buffer);
|
||||||
|
|
||||||
|
menu_builder.label(ui, "");
|
||||||
|
|
||||||
|
// Server
|
||||||
|
menu_builder.label(ui, "Server settings");
|
||||||
|
menu_builder.label(ui, "TODO");
|
||||||
|
|
||||||
|
if let Some(error_msg) = &context.latest_error_message {
|
||||||
|
menu_builder.label(ui, error_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit
|
||||||
|
if menu_builder.button(ui, "Save and back") {
|
||||||
|
client_settings.server_url = match context.server_url_buffer.parse() {
|
||||||
|
Ok(result) => result,
|
||||||
|
|
||||||
|
Err(e) => {
|
||||||
|
context.latest_error_message = Some(e.to_string());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = client_settings.save_to_file() {
|
||||||
|
context.latest_error_message = Some(e.to_string());
|
||||||
|
error!("Error while saving client settings to file: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = server_settings.save_to_file() {
|
||||||
|
context.latest_error_message = Some(e.to_string());
|
||||||
|
error!("Error while saving server settings to file: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.latest_error_message = None;
|
||||||
|
*state = GuiState::MainMenu;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
2
src/ui/mod.rs
Normal file
2
src/ui/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod macroquad;
|
||||||
|
pub mod state;
|
||||||
92
src/ui/state.rs
Normal file
92
src/ui/state.rs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
use std::sync::mpsc::{self, Receiver, Sender};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
network::{client::Client, server::Server},
|
||||||
|
super_advanced_ai::{Bot, BotDificulty},
|
||||||
|
ui::macroquad::game::game::GameClient,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub enum GuiState {
|
||||||
|
#[default]
|
||||||
|
MainMenu,
|
||||||
|
|
||||||
|
ModeSelection,
|
||||||
|
DificultySelection,
|
||||||
|
ServerSelection,
|
||||||
|
|
||||||
|
Connecting(Receiver<Option<Client>>),
|
||||||
|
|
||||||
|
Settings,
|
||||||
|
Game(GameClient),
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Copy)]
|
||||||
|
pub enum GameMode {
|
||||||
|
#[default]
|
||||||
|
Singleplayer,
|
||||||
|
Multiplayer {
|
||||||
|
is_hosting: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GameContext {
|
||||||
|
pub difficulty: BotDificulty,
|
||||||
|
pub gamemode: GameMode,
|
||||||
|
pub server_url_buffer: String,
|
||||||
|
|
||||||
|
pub latest_error_message: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GameContext {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
difficulty: BotDificulty::default(),
|
||||||
|
gamemode: GameMode::default(),
|
||||||
|
server_url_buffer: "127.0.0.1:6767".to_string(),
|
||||||
|
latest_error_message: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO! Move this
|
||||||
|
pub fn connect_to_server() -> Receiver<Option<Client>> {
|
||||||
|
let (tx, rc) = mpsc::channel::<Option<Client>>();
|
||||||
|
|
||||||
|
let _ = std::thread::spawn(move || {
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
|
||||||
|
let client_option = rt.block_on(async { Client::new(None).await });
|
||||||
|
|
||||||
|
let _ = tx.send(client_option);
|
||||||
|
|
||||||
|
std::thread::park();
|
||||||
|
});
|
||||||
|
|
||||||
|
rc
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_server(sender: Sender<()>) {
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
|
||||||
|
let mut server = rt.block_on(Server::new());
|
||||||
|
rt.block_on(server.start());
|
||||||
|
|
||||||
|
let _ = sender.send(());
|
||||||
|
|
||||||
|
rt.block_on(server.update());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect_bot(dificluty: BotDificulty) {
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
|
||||||
|
let bot = rt.block_on(Bot::new(dificluty));
|
||||||
|
|
||||||
|
rt.block_on(bot.game_loop());
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user