kill_counters #109
356
Cargo.lock
generated
356
Cargo.lock
generated
@ -28,15 +28,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.4.7"
|
version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
|
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ahash"
|
|
||||||
version = "0.6.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "796540673305a66d127804eef19ad696f1f204b8c1025aaca4958c17eab32877"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.6",
|
"getrandom 0.2.6",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -178,6 +172,17 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-recursion"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-std"
|
name = "async-std"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
@ -309,15 +314,6 @@ version = "1.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-buffer"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
@ -370,12 +366,6 @@ version = "1.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bytes"
|
|
||||||
version = "0.5.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@ -388,28 +378,6 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
|
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cargo-platform"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cargo_metadata"
|
|
||||||
version = "0.12.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f"
|
|
||||||
dependencies = [
|
|
||||||
"cargo-platform",
|
|
||||||
"semver 0.11.0",
|
|
||||||
"semver-parser",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.73"
|
version = "1.0.73"
|
||||||
@ -514,15 +482,20 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crc"
|
||||||
version = "0.5.4"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
|
checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"crc-catalog",
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc-catalog"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-queue"
|
name = "crossbeam-queue"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@ -553,16 +526,6 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crypto-mac"
|
|
||||||
version = "0.10.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
"subtle",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctor"
|
name = "ctor"
|
||||||
version = "0.1.22"
|
version = "0.1.22"
|
||||||
@ -586,26 +549,37 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "digest"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
|
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer 0.10.2",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dotenv"
|
name = "dotenv"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
@ -624,16 +598,18 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ages-prs",
|
"ages-prs",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"async-recursion",
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"barrel",
|
"barrel",
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"chrono",
|
"chrono",
|
||||||
"crc",
|
"crc 1.8.1",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"enum-utils",
|
"enum-utils",
|
||||||
"fern",
|
"fern",
|
||||||
|
"fix-hidden-lifetime-bug",
|
||||||
"futures",
|
"futures",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libpso",
|
"libpso",
|
||||||
@ -716,6 +692,26 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fix-hidden-lifetime-bug"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4ae9c2016a663983d4e40a9ff967d6dcac59819672f0b47f2b17574e99c33c8"
|
||||||
|
dependencies = [
|
||||||
|
"fix-hidden-lifetime-bug-proc_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fix-hidden-lifetime-bug-proc_macros"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e4c81935e123ab0741c4c4f0d9b8377e5fb21d3de7e062fa4b1263b1fbcba1ea"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -789,6 +785,17 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-intrusive"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
@ -901,28 +908,22 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
|
||||||
dependencies = [
|
|
||||||
"ahash 0.4.7",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashlink"
|
name = "hashlink"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8"
|
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.9.1",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -934,6 +935,15 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
@ -950,13 +960,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hkdf"
|
||||||
version = "0.10.1"
|
version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
|
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-mac",
|
"hmac",
|
||||||
"digest 0.9.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -965,7 +974,7 @@ version = "0.12.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest 0.10.3",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -986,7 +995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.1.0",
|
"autocfg 1.1.0",
|
||||||
"hashbrown 0.11.2",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1007,12 +1016,6 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "0.4.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -1091,24 +1094,13 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "md-5"
|
|
||||||
version = "0.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
|
|
||||||
dependencies = [
|
|
||||||
"block-buffer 0.9.0",
|
|
||||||
"digest 0.9.0",
|
|
||||||
"opaque-debug",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "md-5"
|
name = "md-5"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582"
|
checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest 0.10.3",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1306,21 +1298,18 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pest"
|
|
||||||
version = "2.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
|
||||||
dependencies = [
|
|
||||||
"ucd-trie",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
@ -1376,7 +1365,7 @@ version = "0.19.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb76d6535496f633fa799bb872ffb4790e9cbdedda9d35564ca0252f930c0dd5"
|
checksum = "eb76d6535496f633fa799bb872ffb4790e9cbdedda9d35564ca0252f930c0dd5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes",
|
||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
"futures",
|
"futures",
|
||||||
"log",
|
"log",
|
||||||
@ -1392,13 +1381,13 @@ checksum = "79ec03bce71f18b4a27c4c64c6ba2ddf74686d69b91d8714fb32ead3adaed713"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes 1.1.0",
|
"bytes",
|
||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
"hmac 0.12.1",
|
"hmac",
|
||||||
"md-5 0.10.1",
|
"md-5",
|
||||||
"memchr",
|
"memchr",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"sha2 0.10.2",
|
"sha2",
|
||||||
"stringprep",
|
"stringprep",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1408,7 +1397,7 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04619f94ba0cc80999f4fc7073607cb825bc739a883cb6d20900fc5e009d6b0d"
|
checksum = "04619f94ba0cc80999f4fc7073607cb825bc739a883cb6d20900fc5e009d6b0d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes",
|
||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
"postgres-protocol",
|
"postgres-protocol",
|
||||||
]
|
]
|
||||||
@ -1642,6 +1631,17 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.6",
|
||||||
|
"redox_syscall",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "refinery"
|
name = "refinery"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@ -1735,7 +1735,7 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"semver 1.0.7",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1792,31 +1792,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
|
||||||
dependencies = [
|
|
||||||
"semver-parser",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
|
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "semver-parser"
|
|
||||||
version = "0.10.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
|
||||||
dependencies = [
|
|
||||||
"pest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.136"
|
version = "1.0.136"
|
||||||
@ -1854,36 +1835,20 @@ version = "1.0.79"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"itoa",
|
||||||
"itoa 1.0.1",
|
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha-1"
|
name = "sha-1"
|
||||||
version = "0.9.8"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
|
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer 0.9.0",
|
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"digest 0.9.0",
|
"digest",
|
||||||
"opaque-debug",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sha2"
|
|
||||||
version = "0.9.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
|
||||||
dependencies = [
|
|
||||||
"block-buffer 0.9.0",
|
|
||||||
"cfg-if",
|
|
||||||
"cpufeatures",
|
|
||||||
"digest 0.9.0",
|
|
||||||
"opaque-debug",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1894,7 +1859,7 @@ checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"digest 0.10.3",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1957,9 +1922,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx"
|
name = "sqlx"
|
||||||
version = "0.4.2"
|
version = "0.5.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e1a98f9bf17b690f026b6fec565293a995b46dfbd6293debcb654dcffd2d1b34"
|
checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-macros",
|
"sqlx-macros",
|
||||||
@ -1967,41 +1932,44 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-core"
|
name = "sqlx-core"
|
||||||
version = "0.4.2"
|
version = "0.5.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36bb6a2ca3345a86493bc3b71eabc2c6c16a8bb1aa476cf5303bee27f67627d7"
|
checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.6.3",
|
"ahash",
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64",
|
"base64",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes 0.5.6",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
"crc",
|
"crc 2.1.0",
|
||||||
"crossbeam-channel",
|
|
||||||
"crossbeam-queue",
|
"crossbeam-queue",
|
||||||
"crossbeam-utils",
|
"dirs",
|
||||||
"either",
|
"either",
|
||||||
|
"event-listener",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-intrusive",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashlink",
|
"hashlink",
|
||||||
"hex",
|
"hex",
|
||||||
"hmac 0.10.1",
|
"hkdf",
|
||||||
"itoa 0.4.8",
|
"hmac",
|
||||||
|
"indexmap",
|
||||||
|
"itoa",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"md-5 0.9.1",
|
"md-5",
|
||||||
"memchr",
|
"memchr",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot",
|
"paste",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rand 0.7.3",
|
"rand 0.8.5",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha-1",
|
"sha-1",
|
||||||
"sha2 0.9.9",
|
"sha2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlformat",
|
"sqlformat",
|
||||||
"sqlx-rt",
|
"sqlx-rt",
|
||||||
@ -2013,20 +1981,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-macros"
|
name = "sqlx-macros"
|
||||||
version = "0.4.2"
|
version = "0.5.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b5ada8b3b565331275ce913368565a273a74faf2a34da58c4dc010ce3286844"
|
checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cargo_metadata",
|
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"either",
|
"either",
|
||||||
"futures",
|
"heck 0.4.0",
|
||||||
"heck",
|
"once_cell",
|
||||||
"lazy_static",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.9.9",
|
"sha2",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-rt",
|
"sqlx-rt",
|
||||||
"syn",
|
"syn",
|
||||||
@ -2035,9 +2001,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-rt"
|
name = "sqlx-rt"
|
||||||
version = "0.2.0"
|
version = "0.5.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "63fc5454c9dd7aaea3a0eeeb65ca40d06d0d8e7413a8184f7c3a3ffa5056190b"
|
checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-native-tls",
|
"async-native-tls",
|
||||||
"async-std",
|
"async-std",
|
||||||
@ -2066,7 +2032,7 @@ version = "0.19.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e61bb0be289045cb80bfce000512e32d09f8337e54c186725da381377ad1f8d5"
|
checksum = "e61bb0be289045cb80bfce000512e32d09f8337e54c186725da381377ad1f8d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.3.3",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
@ -2154,7 +2120,7 @@ version = "1.18.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f48b6d60512a392e34dbf7fd456249fd2de3c83669ab642e021903f4015185b"
|
checksum = "0f48b6d60512a392e34dbf7fd456249fd2de3c83669ab642e021903f4015185b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"mio",
|
"mio",
|
||||||
@ -2172,7 +2138,7 @@ checksum = "4b6c8b33df661b548dcd8f9bf87debb8c56c05657ed291122e1188698c2ece95"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes 1.1.0",
|
"bytes",
|
||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
"futures",
|
"futures",
|
||||||
"log",
|
"log",
|
||||||
@ -2193,7 +2159,7 @@ version = "0.6.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
|
checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.1.0",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"log",
|
"log",
|
||||||
@ -2216,12 +2182,6 @@ version = "1.15.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ucd-trie"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
@ -25,11 +25,12 @@ derive_more = { version = "0.99.3", features = ["display"]}
|
|||||||
thiserror = "1.0.15"
|
thiserror = "1.0.15"
|
||||||
ages-prs = "0.1"
|
ages-prs = "0.1"
|
||||||
async-trait = "0.1.51"
|
async-trait = "0.1.51"
|
||||||
|
async-recursion= "1.0.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
barrel = { version = "0.6.5", features = ["pg"] }
|
barrel = { version = "0.6.5", features = ["pg"] }
|
||||||
refinery = { version = "0.5.0", features = ["postgres"] }
|
refinery = { version = "0.5.0", features = ["postgres"] }
|
||||||
sqlx = { version = "0.4.0", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] }
|
sqlx = { version = "0.5.10", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] }
|
||||||
strum = "0.19.5"
|
strum = "0.19.5"
|
||||||
strum_macros = "0.19"
|
strum_macros = "0.19"
|
||||||
anyhow = { version = "1.0.47", features = ["backtrace"] }
|
anyhow = { version = "1.0.47", features = ["backtrace"] }
|
||||||
|
fix-hidden-lifetime-bug = "0.2.4"
|
||||||
|
@ -69,14 +69,14 @@ fn main() {
|
|||||||
character.name = format!("Test Char {}", i*2);
|
character.name = format!("Test Char {}", i*2);
|
||||||
let character = entity_gateway.create_character(character).await.unwrap();
|
let character = entity_gateway.create_character(character).await.unwrap();
|
||||||
entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap();
|
entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap();
|
||||||
entity_gateway.set_bank_meseta(&character.id, item::BankName("".into()), item::Meseta(999999)).await.unwrap();
|
entity_gateway.set_bank_meseta(&character.id, &item::BankName("".into()), item::Meseta(999999)).await.unwrap();
|
||||||
let mut character = NewCharacterEntity::new(fake_user.id, 1);
|
let mut character = NewCharacterEntity::new(fake_user.id, 1);
|
||||||
character.slot = 2;
|
character.slot = 2;
|
||||||
character.name = "ItemRefactor".into();
|
character.name = "ItemRefactor".into();
|
||||||
character.exp = 80000000;
|
character.exp = 80000000;
|
||||||
let character = entity_gateway.create_character(character).await.unwrap();
|
let character = entity_gateway.create_character(character).await.unwrap();
|
||||||
entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap();
|
entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap();
|
||||||
entity_gateway.set_bank_meseta(&character.id, item::BankName("".into()), item::Meseta(999999)).await.unwrap();
|
entity_gateway.set_bank_meseta(&character.id, &item::BankName("".into()), item::Meseta(999999)).await.unwrap();
|
||||||
|
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
entity_gateway.create_item(
|
entity_gateway.create_item(
|
||||||
@ -88,6 +88,7 @@ fn main() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
@ -108,13 +109,14 @@ fn main() {
|
|||||||
NewItemEntity {
|
NewItemEntity {
|
||||||
item: ItemDetail::Weapon(
|
item: ItemDetail::Weapon(
|
||||||
item::weapon::Weapon {
|
item::weapon::Weapon {
|
||||||
weapon: item::weapon::WeaponType::Raygun,
|
weapon: item::weapon::WeaponType::SealedJSword,
|
||||||
grind: 5,
|
grind: 5,
|
||||||
special: Some(item::weapon::WeaponSpecial::Hell),
|
special: None,
|
||||||
attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}),
|
attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}),
|
||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
|
||||||
None,],
|
None,],
|
||||||
tekked: false,
|
tekked: true,
|
||||||
|
kills: Some(22998),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
@ -122,13 +124,14 @@ fn main() {
|
|||||||
NewItemEntity {
|
NewItemEntity {
|
||||||
item: ItemDetail::Weapon(
|
item: ItemDetail::Weapon(
|
||||||
item::weapon::Weapon {
|
item::weapon::Weapon {
|
||||||
weapon: item::weapon::WeaponType::Handgun,
|
weapon: item::weapon::WeaponType::Club,
|
||||||
grind: 5,
|
grind: 10,
|
||||||
special: Some(item::weapon::WeaponSpecial::Charge),
|
special: Some(item::weapon::WeaponSpecial::Charge),
|
||||||
attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}),
|
attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}),
|
||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
|
||||||
None,],
|
None,],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
@ -143,6 +146,7 @@ fn main() {
|
|||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
||||||
None,],
|
None,],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
@ -157,6 +161,7 @@ fn main() {
|
|||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
||||||
None,],
|
None,],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
@ -171,6 +176,7 @@ fn main() {
|
|||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
@ -197,7 +203,7 @@ fn main() {
|
|||||||
item::NewItemEntity {
|
item::NewItemEntity {
|
||||||
item: ItemDetail::Tool (
|
item: ItemDetail::Tool (
|
||||||
item::tool::Tool {
|
item::tool::Tool {
|
||||||
tool: item::tool::ToolType::CellOfMag502,
|
tool: item::tool::ToolType::MagicRockMoola,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
@ -213,8 +219,17 @@ fn main() {
|
|||||||
|
|
||||||
let item6_1 = entity_gateway.create_item(
|
let item6_1 = entity_gateway.create_item(
|
||||||
NewItemEntity {
|
NewItemEntity {
|
||||||
item: ItemDetail::ESWeapon(
|
item: ItemDetail::Weapon(
|
||||||
item::esweapon::ESWeapon::new(item::esweapon::ESWeaponType::Saber)
|
item::weapon::Weapon {
|
||||||
|
weapon: item::weapon::WeaponType::Autogun,
|
||||||
|
grind: 5,
|
||||||
|
special: Some(item::weapon::WeaponSpecial::Hell),
|
||||||
|
attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 70}),
|
||||||
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 80}),
|
||||||
|
None,],
|
||||||
|
tekked: false,
|
||||||
|
kills: None,
|
||||||
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
let item7_a = entity_gateway.create_item(
|
let item7_a = entity_gateway.create_item(
|
||||||
@ -244,8 +259,9 @@ fn main() {
|
|||||||
NewItemEntity {
|
NewItemEntity {
|
||||||
item: ItemDetail::Unit(
|
item: ItemDetail::Unit(
|
||||||
item::unit::Unit {
|
item::unit::Unit {
|
||||||
unit: item::unit::UnitType::PriestMind,
|
unit: item::unit::UnitType::Limiter,
|
||||||
modifier: Some(item::unit::UnitModifier::PlusPlus),
|
modifier: None,
|
||||||
|
kills: Some(19999),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -255,7 +271,8 @@ fn main() {
|
|||||||
item: ItemDetail::Unit(
|
item: ItemDetail::Unit(
|
||||||
item::unit::Unit {
|
item::unit::Unit {
|
||||||
unit: item::unit::UnitType::PriestMind,
|
unit: item::unit::UnitType::PriestMind,
|
||||||
modifier: Some(item::unit::UnitModifier::Plus),
|
modifier: Some(item::unit::UnitModifier::Minus),
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -266,6 +283,7 @@ fn main() {
|
|||||||
item::unit::Unit {
|
item::unit::Unit {
|
||||||
unit: item::unit::UnitType::PriestMind,
|
unit: item::unit::UnitType::PriestMind,
|
||||||
modifier: Some(item::unit::UnitModifier::Minus),
|
modifier: Some(item::unit::UnitModifier::Minus),
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -276,6 +294,7 @@ fn main() {
|
|||||||
item::unit::Unit {
|
item::unit::Unit {
|
||||||
unit: item::unit::UnitType::PriestMind,
|
unit: item::unit::UnitType::PriestMind,
|
||||||
modifier: Some(item::unit::UnitModifier::MinusMinus),
|
modifier: Some(item::unit::UnitModifier::MinusMinus),
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -298,6 +317,7 @@ fn main() {
|
|||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
@ -327,7 +347,7 @@ fn main() {
|
|||||||
|
|
||||||
let inventory = item::InventoryEntity::new(vec![InventoryItemEntity::from(item0), item1.into(), item2_w.into(), item3.into(), item4.into(), item5_m.into(), item6.into(), item6_1.into(), item7_a.into(), item8_s.into(), item9_u0.into(), item10_u1.into(), item11_u2.into(), item12_u3.into(), item13.into(), item14.into(), monomates.into()]);
|
let inventory = item::InventoryEntity::new(vec![InventoryItemEntity::from(item0), item1.into(), item2_w.into(), item3.into(), item4.into(), item5_m.into(), item6.into(), item6_1.into(), item7_a.into(), item8_s.into(), item9_u0.into(), item10_u1.into(), item11_u2.into(), item12_u3.into(), item13.into(), item14.into(), monomates.into()]);
|
||||||
entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap();
|
entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap();
|
||||||
entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), &item::BankName("".into())).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("[patch] starting server");
|
info!("[patch] starting server");
|
||||||
|
@ -3,7 +3,7 @@ use std::fs::File;
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use crate::entity::character::CharacterClass;
|
use crate::entity::character::CharacterClass;
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, Debug, PartialEq)]
|
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct CharacterStats {
|
pub struct CharacterStats {
|
||||||
pub hp: u16,
|
pub hp: u16,
|
||||||
pub atp: u16,
|
pub atp: u16,
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
use std::convert::From;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use futures::Future;
|
||||||
|
|
||||||
use crate::entity::account::*;
|
use crate::entity::account::*;
|
||||||
use crate::entity::character::*;
|
use crate::entity::character::*;
|
||||||
@ -14,17 +16,34 @@ pub enum GatewayError {
|
|||||||
PgError(#[from] sqlx::Error)
|
PgError(#[from] sqlx::Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait EntityGateway: Send + Sync + Clone {
|
pub trait EntityGateway: Send + Sync {
|
||||||
|
async fn transaction<'a>(&'a mut self) -> Result<Box<dyn EntityGatewayTransaction + 'a>, GatewayError>
|
||||||
|
{
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, _func: F) -> Result<R, E>
|
||||||
|
where
|
||||||
|
Fut: Future<Output = Result<(Box<dyn EntityGatewayTransaction + 'a>, R), E>> + Send + 'a,
|
||||||
|
F: FnOnce(Box<dyn EntityGatewayTransaction + 'a>) -> Fut + Send,
|
||||||
|
R: Send,
|
||||||
|
E: From<GatewayError>,
|
||||||
|
Self: Sized
|
||||||
|
{
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
async fn create_user(&mut self, _user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
|
async fn create_user(&mut self, _user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user_by_id(&self, _id: UserAccountId) -> Result<UserAccountEntity, GatewayError> {
|
async fn get_user_by_id(&mut self, _id: UserAccountId) -> Result<UserAccountEntity, GatewayError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user_by_name(&self, _username: String) -> Result<UserAccountEntity, GatewayError> {
|
async fn get_user_by_name(&mut self, _username: String) -> Result<UserAccountEntity, GatewayError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +55,7 @@ pub trait EntityGateway: Send + Sync + Clone {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user_settings_by_user(&self, _user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> {
|
async fn get_user_settings_by_user(&mut self, _user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +68,7 @@ pub trait EntityGateway: Send + Sync + Clone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: just make this a vec sorted by slot order?
|
// TODO: just make this a vec sorted by slot order?
|
||||||
async fn get_characters_by_user(&self, _user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError> {
|
async fn get_characters_by_user(&mut self, _user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +76,7 @@ pub trait EntityGateway: Send + Sync + Clone {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_guild_card_data_by_user(&self, _user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> {
|
async fn get_guild_card_data_by_user(&mut self, _user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +104,9 @@ pub trait EntityGateway: Send + Sync + Clone {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn add_unit_modifier(&mut self, _item_id: &ItemEntityId, _modifier: unit::UnitModifier) -> Result<(), GatewayError> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
async fn get_items_by_character(&self, _char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> {
|
async fn get_items_by_character(&self, _char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> {
|
||||||
@ -96,7 +118,7 @@ pub trait EntityGateway: Send + Sync + Clone {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_character_bank(&mut self, _char_id: &CharacterEntityId, _bank_name: BankName) -> Result<BankEntity, GatewayError> {
|
async fn get_character_bank(&mut self, _char_id: &CharacterEntityId, _bank_name: &BankName) -> Result<BankEntity, GatewayError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +126,7 @@ pub trait EntityGateway: Send + Sync + Clone {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_character_bank(&mut self, _char_id: &CharacterEntityId, _inventory: &BankEntity, _bank_name: BankName) -> Result<(), GatewayError> {
|
async fn set_character_bank(&mut self, _char_id: &CharacterEntityId, _inventory: &BankEntity, _bank_name: &BankName) -> Result<(), GatewayError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,11 +146,27 @@ pub trait EntityGateway: Send + Sync + Clone {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: BankName) -> Result<Meseta, GatewayError> {
|
async fn get_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: &BankName) -> Result<Meseta, GatewayError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: BankName, _amount: Meseta) -> Result<(), GatewayError> {
|
async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: &BankName, _amount: Meseta) -> Result<(), GatewayError> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_trade(&mut self, _char_id1: &CharacterEntityId, _char_id2: &CharacterEntityId) -> Result<TradeEntity, GatewayError> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait EntityGatewayTransaction: Send + Sync {
|
||||||
|
fn gateway(&mut self) -> &mut dyn EntityGateway {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn commit(self: Box<Self>) -> Result<(), GatewayError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,13 +1,68 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
use futures::Future;
|
||||||
|
|
||||||
use crate::entity::account::*;
|
use crate::entity::account::*;
|
||||||
use crate::entity::character::*;
|
use crate::entity::character::*;
|
||||||
use crate::entity::gateway::{EntityGateway, GatewayError};
|
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError};
|
||||||
use crate::entity::item::*;
|
use crate::entity::item::*;
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
// TODO: implement multiple banks
|
||||||
|
|
||||||
|
pub struct InMemoryGatewayTransaction<'a> {
|
||||||
|
working_gateway: InMemoryGateway,
|
||||||
|
original_gateway: &'a mut InMemoryGateway,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<'a> EntityGatewayTransaction for InMemoryGatewayTransaction<'a> {
|
||||||
|
fn gateway(&mut self) -> &mut dyn EntityGateway {
|
||||||
|
&mut self.working_gateway
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn commit(mut self: Box<Self>) -> Result<(), GatewayError> {
|
||||||
|
self.original_gateway.users.lock().unwrap().clear();
|
||||||
|
self.original_gateway.users.lock().unwrap().extend(self.working_gateway.users.lock().unwrap().clone());
|
||||||
|
|
||||||
|
self.original_gateway.user_settings.lock().unwrap().clear();
|
||||||
|
self.original_gateway.user_settings.lock().unwrap().extend(self.working_gateway.user_settings.lock().unwrap().clone());
|
||||||
|
|
||||||
|
self.original_gateway.characters.lock().unwrap().clear();
|
||||||
|
self.original_gateway.characters.lock().unwrap().extend(self.working_gateway.characters.lock().unwrap().clone());
|
||||||
|
|
||||||
|
self.original_gateway.character_meseta.lock().unwrap().clear();
|
||||||
|
self.original_gateway.character_meseta.lock().unwrap().extend(self.working_gateway.character_meseta.lock().unwrap().clone());
|
||||||
|
|
||||||
|
self.original_gateway.bank_meseta.lock().unwrap().clear();
|
||||||
|
self.original_gateway.bank_meseta.lock().unwrap().extend(self.working_gateway.bank_meseta.lock().unwrap().clone());
|
||||||
|
|
||||||
|
self.original_gateway.items.lock().unwrap().clear();
|
||||||
|
self.original_gateway.items.lock().unwrap().extend(self.working_gateway.items.lock().unwrap().clone());
|
||||||
|
|
||||||
|
self.original_gateway.inventories.lock().unwrap().clear();
|
||||||
|
self.original_gateway.inventories.lock().unwrap().extend(self.working_gateway.inventories.lock().unwrap().clone());
|
||||||
|
|
||||||
|
self.original_gateway.banks.lock().unwrap().clear();
|
||||||
|
self.original_gateway.banks.lock().unwrap().extend(self.working_gateway.banks.lock().unwrap().clone());
|
||||||
|
|
||||||
|
self.original_gateway.equips.lock().unwrap().clear();
|
||||||
|
self.original_gateway.equips.lock().unwrap().extend(self.working_gateway.equips.lock().unwrap().clone());
|
||||||
|
|
||||||
|
self.original_gateway.mag_modifiers.lock().unwrap().clear();
|
||||||
|
self.original_gateway.mag_modifiers.lock().unwrap().extend(self.working_gateway.mag_modifiers.lock().unwrap().clone());
|
||||||
|
|
||||||
|
self.original_gateway.weapon_modifiers.lock().unwrap().clear();
|
||||||
|
self.original_gateway.weapon_modifiers.lock().unwrap().extend(self.working_gateway.weapon_modifiers.lock().unwrap().clone());
|
||||||
|
|
||||||
|
self.original_gateway.trades.lock().unwrap().clear();
|
||||||
|
self.original_gateway.trades.lock().unwrap().extend(self.working_gateway.trades.lock().unwrap().clone());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct InMemoryGateway {
|
pub struct InMemoryGateway {
|
||||||
users: Arc<Mutex<BTreeMap<UserAccountId, UserAccountEntity>>>,
|
users: Arc<Mutex<BTreeMap<UserAccountId, UserAccountEntity>>>,
|
||||||
@ -21,6 +76,9 @@ pub struct InMemoryGateway {
|
|||||||
equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>,
|
equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>,
|
||||||
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
|
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
|
||||||
weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>,
|
weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>,
|
||||||
|
unit_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<unit::UnitModifier>>>>,
|
||||||
|
trades: Arc<Mutex<Vec<TradeEntity>>>,
|
||||||
|
unit_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<unit::UnitModifier>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for InMemoryGateway {
|
impl Default for InMemoryGateway {
|
||||||
@ -37,6 +95,9 @@ impl Default for InMemoryGateway {
|
|||||||
equips: Arc::new(Mutex::new(BTreeMap::new())),
|
equips: Arc::new(Mutex::new(BTreeMap::new())),
|
||||||
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
|
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
|
||||||
weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
|
weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
|
||||||
|
unit_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
|
||||||
|
trades: Arc::new(Mutex::new(Vec::new())),
|
||||||
|
unit_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,6 +111,9 @@ impl InMemoryGateway {
|
|||||||
item.item = match item.item {
|
item.item = match item.item {
|
||||||
ItemDetail::Weapon(mut weapon) => {
|
ItemDetail::Weapon(mut weapon) => {
|
||||||
if let Some(weapon_modifiers) = self.weapon_modifiers.lock().unwrap().get(&item.id) {
|
if let Some(weapon_modifiers) = self.weapon_modifiers.lock().unwrap().get(&item.id) {
|
||||||
|
if weapon.weapon.has_counter() {
|
||||||
|
weapon.kills = Some(0);
|
||||||
|
}
|
||||||
for weapon_modifier in weapon_modifiers.iter() {
|
for weapon_modifier in weapon_modifiers.iter() {
|
||||||
weapon.apply_modifier(weapon_modifier);
|
weapon.apply_modifier(weapon_modifier);
|
||||||
}
|
}
|
||||||
@ -74,7 +138,7 @@ impl InMemoryGateway {
|
|||||||
mag::MagModifier::MagCell(mag_cell_id) => {
|
mag::MagModifier::MagCell(mag_cell_id) => {
|
||||||
if let Some(mag_cell) = items.get(mag_cell_id) {
|
if let Some(mag_cell) = items.get(mag_cell_id) {
|
||||||
if let ItemDetail::Tool(mag_cell) = mag_cell.item {
|
if let ItemDetail::Tool(mag_cell) = mag_cell.item {
|
||||||
mag.apply_mag_cell(mag_cell.tool.try_into().unwrap())
|
mag.apply_mag_cell(mag_cell.tool.try_into().unwrap()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -83,7 +147,18 @@ impl InMemoryGateway {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ItemDetail::Mag(mag)
|
ItemDetail::Mag(mag)
|
||||||
}
|
},
|
||||||
|
ItemDetail::Unit(mut unit) => {
|
||||||
|
if let Some(unit_modifiers) = self.unit_modifiers.lock().unwrap().get(&item.id) {
|
||||||
|
if unit.unit.has_counter() {
|
||||||
|
unit.kills = Some(0);
|
||||||
|
}
|
||||||
|
for unit_modifier in unit_modifiers.iter() {
|
||||||
|
unit.apply_modifier(unit_modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemDetail::Unit(unit)
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
item.item
|
item.item
|
||||||
}
|
}
|
||||||
@ -99,6 +174,91 @@ impl InMemoryGateway {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl EntityGateway for InMemoryGateway {
|
impl EntityGateway for InMemoryGateway {
|
||||||
|
async fn transaction<'a>(&'a mut self) -> Result<Box<dyn EntityGatewayTransaction + 'a>, GatewayError>
|
||||||
|
{
|
||||||
|
let working_gateway = {
|
||||||
|
let users = self.users.lock().unwrap().clone();
|
||||||
|
let user_settings = self.user_settings.lock().unwrap().clone();
|
||||||
|
let characters = self.characters.lock().unwrap().clone();
|
||||||
|
let character_meseta = self.character_meseta.lock().unwrap().clone();
|
||||||
|
let bank_meseta = self.bank_meseta.lock().unwrap().clone();
|
||||||
|
let items = self.items.lock().unwrap().clone();
|
||||||
|
let inventories = self.inventories.lock().unwrap().clone();
|
||||||
|
let banks = self.banks.lock().unwrap().clone();
|
||||||
|
let equips = self.equips.lock().unwrap().clone();
|
||||||
|
let mag_modifiers = self.mag_modifiers.lock().unwrap().clone();
|
||||||
|
let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone();
|
||||||
|
let trades = self.trades.lock().unwrap().clone();
|
||||||
|
|
||||||
|
InMemoryGateway {
|
||||||
|
users: Arc::new(Mutex::new(users)),
|
||||||
|
user_settings: Arc::new(Mutex::new(user_settings)),
|
||||||
|
characters: Arc::new(Mutex::new(characters)),
|
||||||
|
character_meseta: Arc::new(Mutex::new(character_meseta)),
|
||||||
|
bank_meseta: Arc::new(Mutex::new(bank_meseta)),
|
||||||
|
items: Arc::new(Mutex::new(items)),
|
||||||
|
inventories: Arc::new(Mutex::new(inventories)),
|
||||||
|
banks: Arc::new(Mutex::new(banks)),
|
||||||
|
equips: Arc::new(Mutex::new(equips)),
|
||||||
|
mag_modifiers: Arc::new(Mutex::new(mag_modifiers)),
|
||||||
|
weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)),
|
||||||
|
trades: Arc::new(Mutex::new(trades)),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Box::new(InMemoryGatewayTransaction {
|
||||||
|
working_gateway,
|
||||||
|
original_gateway: self,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, func: F) -> Result<R, E>
|
||||||
|
where
|
||||||
|
Fut: Future<Output = Result<(Box<dyn EntityGatewayTransaction + 'a>, R), E>> + Send + 'a,
|
||||||
|
F: FnOnce(Box<dyn EntityGatewayTransaction + 'a>) -> Fut + Send,
|
||||||
|
R: Send,
|
||||||
|
E: From<GatewayError>,
|
||||||
|
{
|
||||||
|
let users = self.users.lock().unwrap().clone();
|
||||||
|
let user_settings = self.user_settings.lock().unwrap().clone();
|
||||||
|
let characters = self.characters.lock().unwrap().clone();
|
||||||
|
let character_meseta = self.character_meseta.lock().unwrap().clone();
|
||||||
|
let bank_meseta = self.bank_meseta.lock().unwrap().clone();
|
||||||
|
let items = self.items.lock().unwrap().clone();
|
||||||
|
let inventories = self.inventories.lock().unwrap().clone();
|
||||||
|
let banks = self.banks.lock().unwrap().clone();
|
||||||
|
let equips = self.equips.lock().unwrap().clone();
|
||||||
|
let mag_modifiers = self.mag_modifiers.lock().unwrap().clone();
|
||||||
|
let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone();
|
||||||
|
let trades = self.trades.lock().unwrap().clone();
|
||||||
|
|
||||||
|
let working_gateway = InMemoryGateway {
|
||||||
|
users: Arc::new(Mutex::new(users)),
|
||||||
|
user_settings: Arc::new(Mutex::new(user_settings)),
|
||||||
|
characters: Arc::new(Mutex::new(characters)),
|
||||||
|
character_meseta: Arc::new(Mutex::new(character_meseta)),
|
||||||
|
bank_meseta: Arc::new(Mutex::new(bank_meseta)),
|
||||||
|
items: Arc::new(Mutex::new(items)),
|
||||||
|
inventories: Arc::new(Mutex::new(inventories)),
|
||||||
|
banks: Arc::new(Mutex::new(banks)),
|
||||||
|
equips: Arc::new(Mutex::new(equips)),
|
||||||
|
mag_modifiers: Arc::new(Mutex::new(mag_modifiers)),
|
||||||
|
weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)),
|
||||||
|
trades: Arc::new(Mutex::new(trades)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let transaction = Box::new(InMemoryGatewayTransaction {
|
||||||
|
working_gateway,
|
||||||
|
original_gateway: self,
|
||||||
|
});
|
||||||
|
|
||||||
|
let (transaction, result) = func(transaction).await?;
|
||||||
|
|
||||||
|
transaction.commit().await?;
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
|
async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
|
||||||
let mut users = self.users.lock().unwrap();
|
let mut users = self.users.lock().unwrap();
|
||||||
let id = users
|
let id = users
|
||||||
@ -124,12 +284,12 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user_by_id(&self, id: UserAccountId) -> Result<UserAccountEntity, GatewayError> {
|
async fn get_user_by_id(&mut self, id: UserAccountId) -> Result<UserAccountEntity, GatewayError> {
|
||||||
let users = self.users.lock().unwrap();
|
let users = self.users.lock().unwrap();
|
||||||
users.get(&id).cloned().ok_or(GatewayError::Error)
|
users.get(&id).cloned().ok_or(GatewayError::Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user_by_name(&self, username: String) -> Result<UserAccountEntity, GatewayError> {
|
async fn get_user_by_name(&mut self, username: String) -> Result<UserAccountEntity, GatewayError> {
|
||||||
let users = self.users.lock().unwrap();
|
let users = self.users.lock().unwrap();
|
||||||
users
|
users
|
||||||
.iter()
|
.iter()
|
||||||
@ -159,7 +319,7 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
Ok(new_settings)
|
Ok(new_settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> {
|
async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> {
|
||||||
let user_settings = self.user_settings.lock().unwrap();
|
let user_settings = self.user_settings.lock().unwrap();
|
||||||
user_settings
|
user_settings
|
||||||
.iter()
|
.iter()
|
||||||
@ -168,7 +328,7 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
.ok_or(GatewayError::Error)
|
.ok_or(GatewayError::Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_characters_by_user(&self, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError> {
|
async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError> {
|
||||||
let characters = self.characters.lock().unwrap();
|
let characters = self.characters.lock().unwrap();
|
||||||
const NONE: Option<CharacterEntity> = None;
|
const NONE: Option<CharacterEntity> = None;
|
||||||
let mut chars = [NONE; 4];
|
let mut chars = [NONE; 4];
|
||||||
@ -215,7 +375,7 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> {
|
async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> {
|
||||||
Ok(GuildCardDataEntity::new(user.id))
|
Ok(GuildCardDataEntity::new(user.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +431,14 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn add_unit_modifier(&mut self, item_id: &ItemEntityId, modifier: unit::UnitModifier) -> Result<(), GatewayError> {
|
||||||
|
self.unit_modifiers.lock().unwrap()
|
||||||
|
.entry(*item_id)
|
||||||
|
.or_insert_with(Vec::new)
|
||||||
|
.push(modifier);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
|
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
|
||||||
let inventories = self.inventories.lock().unwrap();
|
let inventories = self.inventories.lock().unwrap();
|
||||||
Ok(inventories
|
Ok(inventories
|
||||||
@ -281,7 +449,7 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
.unwrap_or_default())
|
.unwrap_or_default())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_character_bank(&mut self, char_id: &CharacterEntityId, _bank_name: BankName) -> Result<BankEntity, GatewayError> {
|
async fn get_character_bank(&mut self, char_id: &CharacterEntityId, _bank_name: &BankName) -> Result<BankEntity, GatewayError> {
|
||||||
let banks = self.banks.lock().unwrap();
|
let banks = self.banks.lock().unwrap();
|
||||||
Ok(banks
|
Ok(banks
|
||||||
.iter()
|
.iter()
|
||||||
@ -297,7 +465,7 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TOOD: impl bank name
|
// TOOD: impl bank name
|
||||||
async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, _bank_name: BankName) -> Result<(), GatewayError> {
|
async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, _bank_name: &BankName) -> Result<(), GatewayError> {
|
||||||
let mut banks = self.banks.lock().unwrap();
|
let mut banks = self.banks.lock().unwrap();
|
||||||
banks.insert(*char_id, bank.clone());
|
banks.insert(*char_id, bank.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -334,19 +502,46 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> {
|
async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> {
|
||||||
let mut bank_meseta = self.bank_meseta.lock().unwrap();
|
let mut bank_meseta = self.bank_meseta.lock().unwrap();
|
||||||
bank_meseta.insert((*char_id, bank), meseta);
|
bank_meseta.insert((*char_id, bank.clone()), meseta);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName) -> Result<Meseta, GatewayError> {
|
async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName) -> Result<Meseta, GatewayError> {
|
||||||
let mut bank_meseta = self.bank_meseta.lock().unwrap();
|
let mut bank_meseta = self.bank_meseta.lock().unwrap();
|
||||||
if let Some(meseta) = bank_meseta.get_mut(&(*char_id, bank)) {
|
if let Some(meseta) = bank_meseta.get_mut(&(*char_id, bank.clone())) {
|
||||||
Ok(*meseta)
|
Ok(*meseta)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Err(GatewayError::Error)
|
Err(GatewayError::Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result<TradeEntity, GatewayError> {
|
||||||
|
let mut trades = self.trades.lock().unwrap();
|
||||||
|
let id = trades.len() as u32;
|
||||||
|
let new_trade = TradeEntity {
|
||||||
|
id: TradeId(id),
|
||||||
|
character1: *char_id1,
|
||||||
|
character2: *char_id2,
|
||||||
|
};
|
||||||
|
trades.push(new_trade.clone());
|
||||||
|
Ok(new_trade)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn increment_kill_counter(&mut self, item_id: &ItemEntityId) -> Result<(), GatewayError> {
|
||||||
|
if let Some(item_entity) = self.items.lock().unwrap().get_mut(item_id) {
|
||||||
|
item_entity.increase_kill_counter();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_kill_counter() {
|
||||||
|
println!("src/entity/gateway/inmemory.rs::get_kill_counter() - unimplemented!");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_kill_counter() {
|
||||||
|
println!("src/entity/gateway/inmemory.rs::set_kill_counter() - unimplemented!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,6 @@ pub mod entitygateway;
|
|||||||
pub mod inmemory;
|
pub mod inmemory;
|
||||||
pub mod postgres;
|
pub mod postgres;
|
||||||
|
|
||||||
pub use entitygateway::{EntityGateway, GatewayError};
|
pub use entitygateway::{EntityGateway, EntityGatewayTransaction, GatewayError};
|
||||||
pub use inmemory::InMemoryGateway;
|
pub use inmemory::InMemoryGateway;
|
||||||
pub use self::postgres::PostgresGateway;
|
pub use self::postgres::PostgresGateway;
|
||||||
|
5
src/entity/gateway/postgres/migrations/V0005__trade.sql
Normal file
5
src/entity/gateway/postgres/migrations/V0005__trade.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
create table trades (
|
||||||
|
id serial primary key not null,
|
||||||
|
character1 integer references character (id) not null,
|
||||||
|
character2 integer references character (id) not null,
|
||||||
|
);
|
@ -291,6 +291,7 @@ pub struct PgWeapon {
|
|||||||
grind: u8,
|
grind: u8,
|
||||||
attrs: HashMap<weapon::Attribute, i8>,
|
attrs: HashMap<weapon::Attribute, i8>,
|
||||||
tekked: bool,
|
tekked: bool,
|
||||||
|
kills: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<weapon::Weapon> for PgWeapon {
|
impl From<weapon::Weapon> for PgWeapon {
|
||||||
@ -301,6 +302,7 @@ impl From<weapon::Weapon> for PgWeapon {
|
|||||||
grind: other.grind,
|
grind: other.grind,
|
||||||
attrs: other.attrs.iter().flatten().map(|attr| (attr.attr, attr.value)).collect(),
|
attrs: other.attrs.iter().flatten().map(|attr| (attr.attr, attr.value)).collect(),
|
||||||
tekked: other.tekked,
|
tekked: other.tekked,
|
||||||
|
kills: other.kills,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,6 +323,7 @@ impl From<PgWeapon> for weapon::Weapon {
|
|||||||
grind: other.grind,
|
grind: other.grind,
|
||||||
attrs,
|
attrs,
|
||||||
tekked: other.tekked,
|
tekked: other.tekked,
|
||||||
|
kills: other.kills,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -392,6 +395,7 @@ impl From<PgShield> for shield::Shield {
|
|||||||
pub struct PgUnit {
|
pub struct PgUnit {
|
||||||
unit: unit::UnitType,
|
unit: unit::UnitType,
|
||||||
modifier: Option<unit::UnitModifier>,
|
modifier: Option<unit::UnitModifier>,
|
||||||
|
kills: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<unit::Unit> for PgUnit {
|
impl From<unit::Unit> for PgUnit {
|
||||||
@ -399,6 +403,7 @@ impl From<unit::Unit> for PgUnit {
|
|||||||
PgUnit {
|
PgUnit {
|
||||||
unit: other.unit,
|
unit: other.unit,
|
||||||
modifier: other.modifier,
|
modifier: other.modifier,
|
||||||
|
kills: other.kills,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,6 +413,7 @@ impl From<PgUnit> for unit::Unit {
|
|||||||
unit::Unit {
|
unit::Unit {
|
||||||
unit: other.unit,
|
unit: other.unit,
|
||||||
modifier: other.modifier,
|
modifier: other.modifier,
|
||||||
|
kills: other.kills,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -591,6 +597,7 @@ pub enum PgItemNoteDetail {
|
|||||||
character_id: u32,
|
character_id: u32,
|
||||||
},
|
},
|
||||||
PlayerDrop {
|
PlayerDrop {
|
||||||
|
character_id: u32,
|
||||||
map_area: MapArea,
|
map_area: MapArea,
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
@ -605,10 +612,18 @@ pub enum PgItemNoteDetail {
|
|||||||
},
|
},
|
||||||
SoldToShop,
|
SoldToShop,
|
||||||
Trade {
|
Trade {
|
||||||
id: u32,
|
trade_id: u32,
|
||||||
character_to: u32,
|
character_to: u32,
|
||||||
character_from: u32,
|
character_from: u32,
|
||||||
},
|
},
|
||||||
|
Withdraw {
|
||||||
|
character_id: u32,
|
||||||
|
bank: String,
|
||||||
|
},
|
||||||
|
Deposit {
|
||||||
|
character_id: u32,
|
||||||
|
bank: String,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ItemNote> for PgItemNoteDetail {
|
impl From<ItemNote> for PgItemNoteDetail {
|
||||||
@ -625,7 +640,8 @@ impl From<ItemNote> for PgItemNoteDetail {
|
|||||||
ItemNote::Pickup{character_id} => PgItemNoteDetail::Pickup {
|
ItemNote::Pickup{character_id} => PgItemNoteDetail::Pickup {
|
||||||
character_id: character_id.0,
|
character_id: character_id.0,
|
||||||
},
|
},
|
||||||
ItemNote::PlayerDrop{map_area, x, y, z} => PgItemNoteDetail::PlayerDrop {
|
ItemNote::PlayerDrop{character_id, map_area, x, y, z} => PgItemNoteDetail::PlayerDrop {
|
||||||
|
character_id: character_id.0,
|
||||||
map_area,
|
map_area,
|
||||||
x,y,z,
|
x,y,z,
|
||||||
},
|
},
|
||||||
@ -637,10 +653,22 @@ impl From<ItemNote> for PgItemNoteDetail {
|
|||||||
character_id: character_id.0,
|
character_id: character_id.0,
|
||||||
},
|
},
|
||||||
ItemNote::SoldToShop => PgItemNoteDetail::SoldToShop,
|
ItemNote::SoldToShop => PgItemNoteDetail::SoldToShop,
|
||||||
ItemNote::Trade{id, character_to, character_from} => PgItemNoteDetail::Trade {
|
ItemNote::Trade{trade_id, character_to, character_from} => PgItemNoteDetail::Trade {
|
||||||
id: id.0,
|
trade_id: trade_id.0,
|
||||||
character_to: character_to.0,
|
character_to: character_to.0,
|
||||||
character_from: character_from.0,
|
character_from: character_from.0,
|
||||||
|
},
|
||||||
|
ItemNote::Withdraw{character_id, bank} => {
|
||||||
|
PgItemNoteDetail::Withdraw {
|
||||||
|
character_id: character_id.0,
|
||||||
|
bank: bank.0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ItemNote::Deposit{character_id, bank} => {
|
||||||
|
PgItemNoteDetail::Deposit {
|
||||||
|
character_id: character_id.0,
|
||||||
|
bank: bank.0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -660,7 +688,8 @@ impl From<PgItemNoteDetail> for ItemNote {
|
|||||||
PgItemNoteDetail::Pickup{character_id} => ItemNote::Pickup {
|
PgItemNoteDetail::Pickup{character_id} => ItemNote::Pickup {
|
||||||
character_id: CharacterEntityId(character_id as u32),
|
character_id: CharacterEntityId(character_id as u32),
|
||||||
},
|
},
|
||||||
PgItemNoteDetail::PlayerDrop{map_area, x, y, z} => ItemNote::PlayerDrop {
|
PgItemNoteDetail::PlayerDrop{character_id, map_area, x, y, z} => ItemNote::PlayerDrop {
|
||||||
|
character_id: CharacterEntityId(character_id as u32),
|
||||||
map_area,
|
map_area,
|
||||||
x,y,z,
|
x,y,z,
|
||||||
},
|
},
|
||||||
@ -672,11 +701,19 @@ impl From<PgItemNoteDetail> for ItemNote {
|
|||||||
character_id: CharacterEntityId(character_id),
|
character_id: CharacterEntityId(character_id),
|
||||||
},
|
},
|
||||||
PgItemNoteDetail::SoldToShop => ItemNote::SoldToShop,
|
PgItemNoteDetail::SoldToShop => ItemNote::SoldToShop,
|
||||||
PgItemNoteDetail::Trade {id, character_to, character_from} => ItemNote::Trade {
|
PgItemNoteDetail::Trade {trade_id, character_to, character_from} => ItemNote::Trade {
|
||||||
id: TradeId(id as u32),
|
trade_id: TradeId(trade_id as u32),
|
||||||
character_to: CharacterEntityId(character_to as u32),
|
character_to: CharacterEntityId(character_to as u32),
|
||||||
character_from: CharacterEntityId(character_from as u32),
|
character_from: CharacterEntityId(character_from as u32),
|
||||||
}
|
},
|
||||||
|
PgItemNoteDetail::Withdraw{character_id, bank} => ItemNote::Withdraw {
|
||||||
|
character_id: CharacterEntityId(character_id as u32),
|
||||||
|
bank: BankName(bank),
|
||||||
|
},
|
||||||
|
PgItemNoteDetail::Deposit{character_id, bank} => ItemNote::Deposit {
|
||||||
|
character_id: CharacterEntityId(character_id as u32),
|
||||||
|
bank: BankName(bank),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -827,3 +864,19 @@ impl From<(CharacterEntityId, EquippedEntity)> for PgEquipped {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, sqlx::FromRow)]
|
||||||
|
pub struct PgTradeEntity {
|
||||||
|
id: i32,
|
||||||
|
character1: i32,
|
||||||
|
character2: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PgTradeEntity> for TradeEntity {
|
||||||
|
fn from(other: PgTradeEntity) -> TradeEntity {
|
||||||
|
TradeEntity {
|
||||||
|
id: TradeId(other.id as u32),
|
||||||
|
character1: CharacterEntityId(other.character1 as u32),
|
||||||
|
character2: CharacterEntityId(other.character2 as u32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -289,7 +289,7 @@ impl ArmorType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum ArmorModifier {
|
pub enum ArmorModifier {
|
||||||
AddSlot {
|
AddSlot {
|
||||||
addslot: ItemEntityId,
|
addslot: ItemEntityId,
|
||||||
@ -297,7 +297,7 @@ pub enum ArmorModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Armor {
|
pub struct Armor {
|
||||||
pub armor: ArmorType,
|
pub armor: ArmorType,
|
||||||
pub dfp: u8,
|
pub dfp: u8,
|
||||||
|
@ -121,7 +121,7 @@ impl ESWeaponType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, strum_macros::EnumIter)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, strum_macros::EnumIter)]
|
||||||
pub enum ESWeaponSpecial {
|
pub enum ESWeaponSpecial {
|
||||||
Jellen = 1,
|
Jellen = 1,
|
||||||
Zalure,
|
Zalure,
|
||||||
@ -169,7 +169,7 @@ impl ESWeaponSpecial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct ESWeapon {
|
pub struct ESWeapon {
|
||||||
pub esweapon: ESWeaponType,
|
pub esweapon: ESWeaponType,
|
||||||
pub special: Option<ESWeaponSpecial>,
|
pub special: Option<ESWeaponSpecial>,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use thiserror::Error;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use crate::entity::item::tool::ToolType;
|
use crate::entity::item::tool::ToolType;
|
||||||
@ -419,9 +420,9 @@ pub enum MagCell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::TryFrom<ToolType> for MagCell {
|
impl std::convert::TryFrom<ToolType> for MagCell {
|
||||||
type Error = ();
|
type Error = MagCellError;
|
||||||
|
|
||||||
fn try_from(tool: ToolType) -> Result<MagCell, ()> {
|
fn try_from(tool: ToolType) -> Result<MagCell, MagCellError> {
|
||||||
match tool {
|
match tool {
|
||||||
ToolType::CellOfMag502 => Ok(MagCell::CellOfMag502),
|
ToolType::CellOfMag502 => Ok(MagCell::CellOfMag502),
|
||||||
ToolType::CellOfMag213 => Ok(MagCell::CellOfMag213),
|
ToolType::CellOfMag213 => Ok(MagCell::CellOfMag213),
|
||||||
@ -448,7 +449,7 @@ impl std::convert::TryFrom<ToolType> for MagCell {
|
|||||||
ToolType::YahoosEngine => Ok(MagCell::YahoosEngine),
|
ToolType::YahoosEngine => Ok(MagCell::YahoosEngine),
|
||||||
ToolType::DPhotonCore => Ok(MagCell::DPhotonCore),
|
ToolType::DPhotonCore => Ok(MagCell::DPhotonCore),
|
||||||
ToolType::LibertaKit => Ok(MagCell::LibertaKit),
|
ToolType::LibertaKit => Ok(MagCell::LibertaKit),
|
||||||
_ => Err(()),
|
_ => Err(MagCellError::IsNotMagCell),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -509,7 +510,16 @@ impl MagAttributeOrdering {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum MagCellError {
|
||||||
|
#[error("not a mag cell")]
|
||||||
|
IsNotMagCell,
|
||||||
|
#[error("mag is rare")]
|
||||||
|
IsRareMag,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum MagModifier {
|
pub enum MagModifier {
|
||||||
FeedMag{
|
FeedMag{
|
||||||
food: ItemEntityId,
|
food: ItemEntityId,
|
||||||
@ -519,7 +529,7 @@ pub enum MagModifier {
|
|||||||
OwnerChange(CharacterClass, SectionID)
|
OwnerChange(CharacterClass, SectionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, enum_utils::FromStr)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, enum_utils::FromStr)]
|
||||||
pub enum PhotonBlast {
|
pub enum PhotonBlast {
|
||||||
Farlla,
|
Farlla,
|
||||||
Estlla,
|
Estlla,
|
||||||
@ -529,7 +539,7 @@ pub enum PhotonBlast {
|
|||||||
MyllaYoulla,
|
MyllaYoulla,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Mag {
|
pub struct Mag {
|
||||||
pub mag: MagType,
|
pub mag: MagType,
|
||||||
def: u16,
|
def: u16,
|
||||||
@ -1047,7 +1057,10 @@ impl Mag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this needs more checks on validity
|
// TODO: this needs more checks on validity
|
||||||
pub fn apply_mag_cell(&mut self, mag_cell: MagCell) {
|
pub fn apply_mag_cell(&mut self, mag_cell: MagCell) -> Result<(), MagCellError> {
|
||||||
|
if self.is_rare_item() {
|
||||||
|
return Err(MagCellError::IsRareMag)
|
||||||
|
}
|
||||||
self.mag = match mag_cell {
|
self.mag = match mag_cell {
|
||||||
MagCell::CellOfMag502 => {
|
MagCell::CellOfMag502 => {
|
||||||
match self.id {
|
match self.id {
|
||||||
@ -1097,11 +1110,11 @@ impl Mag {
|
|||||||
MagCell::YahoosEngine => MagType::Yahoo,
|
MagCell::YahoosEngine => MagType::Yahoo,
|
||||||
MagCell::DPhotonCore => MagType::GaelGiel,
|
MagCell::DPhotonCore => MagType::GaelGiel,
|
||||||
MagCell::LibertaKit => MagType::Agastya,
|
MagCell::LibertaKit => MagType::Agastya,
|
||||||
}
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is this even needed? mags are not shop sellable...yet
|
pub fn is_rare_item(&self) -> bool {
|
||||||
pub fn is_rare_item(self) -> bool {
|
|
||||||
matches!(
|
matches!(
|
||||||
self.mag,
|
self.mag,
|
||||||
MagType::Pitri
|
MagType::Pitri
|
||||||
|
@ -13,13 +13,13 @@ use crate::entity::character::CharacterEntityId;
|
|||||||
use crate::ship::map::MapArea;
|
use crate::ship::map::MapArea;
|
||||||
use crate::ship::drops::ItemDropType;
|
use crate::ship::drops::ItemDropType;
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone, Debug, Hash, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct ItemEntityId(pub u32);
|
pub struct ItemEntityId(pub u32);
|
||||||
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
|
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
|
||||||
pub struct ItemId(u32);
|
pub struct ItemId(u32);
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, derive_more::Display)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, derive_more::Display)]
|
||||||
pub struct BankName(pub String);
|
pub struct BankName(pub String);
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct TradeId(pub u32);
|
pub struct TradeId(pub u32);
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
@ -40,13 +40,15 @@ pub enum ItemNote {
|
|||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
},
|
},
|
||||||
PlayerDrop {
|
PlayerDrop {
|
||||||
|
character_id: CharacterEntityId,
|
||||||
map_area: MapArea,
|
map_area: MapArea,
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
z: f32,
|
z: f32,
|
||||||
},
|
},
|
||||||
Consumed,
|
Consumed, // TODO: character_id
|
||||||
FedToMag {
|
FedToMag {
|
||||||
|
//character_id: CharacterEntityId,
|
||||||
mag: ItemEntityId,
|
mag: ItemEntityId,
|
||||||
},
|
},
|
||||||
BoughtAtShop {
|
BoughtAtShop {
|
||||||
@ -54,13 +56,21 @@ pub enum ItemNote {
|
|||||||
},
|
},
|
||||||
SoldToShop,
|
SoldToShop,
|
||||||
Trade {
|
Trade {
|
||||||
id: TradeId,
|
trade_id: TradeId,
|
||||||
character_to: CharacterEntityId,
|
character_to: CharacterEntityId,
|
||||||
character_from: CharacterEntityId,
|
character_from: CharacterEntityId,
|
||||||
},
|
},
|
||||||
|
Withdraw {
|
||||||
|
character_id: CharacterEntityId,
|
||||||
|
bank: BankName,
|
||||||
|
},
|
||||||
|
Deposit {
|
||||||
|
character_id: CharacterEntityId,
|
||||||
|
bank: BankName,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct Meseta(pub u32);
|
pub struct Meseta(pub u32);
|
||||||
|
|
||||||
impl Meseta {
|
impl Meseta {
|
||||||
@ -85,12 +95,12 @@ pub enum ItemType {
|
|||||||
ESWeapon(esweapon::ESWeaponType),
|
ESWeapon(esweapon::ESWeaponType),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum ItemParseError {
|
pub enum ItemParseError {
|
||||||
InvalidBytes
|
InvalidBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum ItemDetail {
|
pub enum ItemDetail {
|
||||||
Weapon(weapon::Weapon),
|
Weapon(weapon::Weapon),
|
||||||
Armor(armor::Armor),
|
Armor(armor::Armor),
|
||||||
@ -169,6 +179,19 @@ impl ItemDetail {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_kill_counter(self) -> bool {
|
||||||
|
match self {
|
||||||
|
ItemDetail::Weapon(w) => w.kills.is_some(),
|
||||||
|
ItemDetail::Armor(_a) => false,
|
||||||
|
ItemDetail::Shield(_s) => false,
|
||||||
|
ItemDetail::Unit(u) => u.kills.is_some(),
|
||||||
|
ItemDetail::Tool(_t) => false,
|
||||||
|
ItemDetail::TechniqueDisk(_d) => false,
|
||||||
|
ItemDetail::Mag(_m) => false,
|
||||||
|
ItemDetail::ESWeapon(_e) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -176,13 +199,12 @@ pub struct NewItemEntity {
|
|||||||
pub item: ItemDetail,
|
pub item: ItemDetail,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct ItemEntity {
|
pub struct ItemEntity {
|
||||||
pub id: ItemEntityId,
|
pub id: ItemEntityId,
|
||||||
pub item: ItemDetail,
|
pub item: ItemDetail,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum InventoryItemEntity {
|
pub enum InventoryItemEntity {
|
||||||
Individual(ItemEntity),
|
Individual(ItemEntity),
|
||||||
@ -317,3 +339,15 @@ impl BankEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct TradeEntity {
|
||||||
|
pub id: TradeId,
|
||||||
|
pub character1: CharacterEntityId,
|
||||||
|
pub character2: CharacterEntityId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ItemModifier {
|
||||||
|
WeaponModifier(weapon::WeaponModifier),
|
||||||
|
}
|
||||||
|
@ -519,7 +519,7 @@ impl ShieldType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Shield {
|
pub struct Shield {
|
||||||
pub shield: ShieldType,
|
pub shield: ShieldType,
|
||||||
pub dfp: u8,
|
pub dfp: u8,
|
||||||
|
@ -642,7 +642,7 @@ impl ToolType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Tool {
|
pub struct Tool {
|
||||||
pub tool: ToolType,
|
pub tool: ToolType,
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
use crate::ship::monster::MonsterType;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum ItemParseError {
|
pub enum ItemParseError {
|
||||||
@ -321,20 +323,29 @@ impl UnitType {
|
|||||||
_ => Err(ItemParseError::InvalidUnitType),
|
_ => Err(ItemParseError::InvalidUnitType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_counter(&self) -> bool {
|
||||||
|
matches!(self, UnitType::Limiter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum UnitModifier {
|
pub enum UnitModifier {
|
||||||
PlusPlus,
|
PlusPlus,
|
||||||
Plus,
|
Plus,
|
||||||
Minus,
|
Minus,
|
||||||
MinusMinus,
|
MinusMinus,
|
||||||
|
AddKill {
|
||||||
|
enemy: MonsterType,
|
||||||
|
// attack: u32, // maybe one day for TURBO logging?
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Unit {
|
pub struct Unit {
|
||||||
pub unit: UnitType,
|
pub unit: UnitType,
|
||||||
pub modifier: Option<UnitModifier>,
|
pub modifier: Option<UnitModifier>,
|
||||||
|
pub kills: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -359,13 +370,19 @@ impl Unit {
|
|||||||
result[6] = 0xFE;
|
result[6] = 0xFE;
|
||||||
result[7] = 0xFF;
|
result[7] = 0xFF;
|
||||||
},
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.unit.has_counter() {
|
||||||
|
result[10..12].copy_from_slice(&self.kills.unwrap_or(0u16).to_be_bytes());
|
||||||
|
result[10] += 0x80;
|
||||||
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(data: [u8; 16]) -> Result<Unit, ItemParseError> {
|
pub fn from_bytes(data: [u8; 16]) -> Result<Unit, ItemParseError> {
|
||||||
let u = UnitType::parse_type([data[0], data[1], data[2]]);
|
let u = UnitType::parse_type([data[0], data[1], data[2]]);
|
||||||
|
let mut k = None;
|
||||||
if let Ok(u) = u {
|
if let Ok(u) = u {
|
||||||
let m = match u16::from_le_bytes([data[6], data[7]]) {
|
let m = match u16::from_le_bytes([data[6], data[7]]) {
|
||||||
0x02 => Some(UnitModifier::PlusPlus),
|
0x02 => Some(UnitModifier::PlusPlus),
|
||||||
@ -375,9 +392,14 @@ impl Unit {
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if data[10] & 0x80 == 0x80 {
|
||||||
|
k = Some(u16::from_be_bytes([data[10] - 0x80, data[11]]));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Unit{
|
Ok(Unit{
|
||||||
unit: u,
|
unit: u,
|
||||||
modifier: m,
|
modifier: m,
|
||||||
|
kills: k,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -456,4 +478,12 @@ impl Unit {
|
|||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn apply_modifier(&mut self, modifier: &UnitModifier) {
|
||||||
|
if let UnitModifier::AddKill{enemy: _} = modifier {
|
||||||
|
if let Some(kills) = self.kills {
|
||||||
|
self.kills = Some(kills + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::entity::item::ItemEntityId;
|
use crate::entity::item::ItemEntityId;
|
||||||
|
use crate::ship::monster::MonsterType;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@ -10,7 +11,7 @@ pub enum ItemParseError {
|
|||||||
InvalidWeaponAttribute,
|
InvalidWeaponAttribute,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq, Ord, PartialOrd, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub enum Attribute {
|
pub enum Attribute {
|
||||||
Native = 1,
|
Native = 1,
|
||||||
ABeast,
|
ABeast,
|
||||||
@ -32,7 +33,7 @@ impl Attribute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct WeaponAttribute {
|
pub struct WeaponAttribute {
|
||||||
pub attr: Attribute,
|
pub attr: Attribute,
|
||||||
pub value: i8,
|
pub value: i8,
|
||||||
@ -45,7 +46,7 @@ impl WeaponAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, strum_macros::EnumIter)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, strum_macros::EnumIter)]
|
||||||
pub enum WeaponSpecial {
|
pub enum WeaponSpecial {
|
||||||
Draw = 1,
|
Draw = 1,
|
||||||
Drain,
|
Drain,
|
||||||
@ -1421,17 +1422,21 @@ impl WeaponType {
|
|||||||
_ => Err(ItemParseError::InvalidWeaponType),
|
_ => Err(ItemParseError::InvalidWeaponType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_counter(&self) -> bool {
|
||||||
|
matches!(self, WeaponType::SealedJSword | WeaponType::LameDArgent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum TekSpecialModifier {
|
pub enum TekSpecialModifier {
|
||||||
Plus,
|
Plus,
|
||||||
Neutral,
|
Neutral,
|
||||||
Minus,
|
Minus,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum TekPercentModifier {
|
pub enum TekPercentModifier {
|
||||||
PlusPlus,
|
PlusPlus,
|
||||||
Plus,
|
Plus,
|
||||||
@ -1440,7 +1445,7 @@ pub enum TekPercentModifier {
|
|||||||
MinusMinus,
|
MinusMinus,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum WeaponModifier {
|
pub enum WeaponModifier {
|
||||||
AddPercents {
|
AddPercents {
|
||||||
attr: WeaponAttribute,
|
attr: WeaponAttribute,
|
||||||
@ -1455,15 +1460,20 @@ pub enum WeaponModifier {
|
|||||||
percent: TekPercentModifier,
|
percent: TekPercentModifier,
|
||||||
grind: i32,
|
grind: i32,
|
||||||
},
|
},
|
||||||
|
AddKill {
|
||||||
|
enemy: MonsterType,
|
||||||
|
// attack: u32, // maybe one day for TURBO logging?
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Weapon {
|
pub struct Weapon {
|
||||||
pub weapon: WeaponType,
|
pub weapon: WeaponType,
|
||||||
pub special: Option<WeaponSpecial>,
|
pub special: Option<WeaponSpecial>,
|
||||||
pub grind: u8,
|
pub grind: u8,
|
||||||
pub attrs: [Option<WeaponAttribute>; 3],
|
pub attrs: [Option<WeaponAttribute>; 3],
|
||||||
pub tekked: bool,
|
pub tekked: bool,
|
||||||
|
pub kills: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1475,49 +1485,60 @@ impl Weapon {
|
|||||||
grind: 0,
|
grind: 0,
|
||||||
attrs: [None; 3],
|
attrs: [None; 3],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: apply other modifiers
|
||||||
pub fn apply_modifier(&mut self, modifier: &WeaponModifier) {
|
pub fn apply_modifier(&mut self, modifier: &WeaponModifier) {
|
||||||
if let WeaponModifier::Tekked{special, percent, grind} = modifier {
|
match modifier {
|
||||||
match special {
|
WeaponModifier::AddPercents{attr: _, pds: _} => {},
|
||||||
TekSpecialModifier::Plus => {
|
WeaponModifier::AddGrind{amount: _, grinder: _} => {},
|
||||||
self.special = self.special.map(|special| {
|
WeaponModifier::Tekked{special, percent, grind} => {
|
||||||
special.rank_up()
|
match special {
|
||||||
});
|
TekSpecialModifier::Plus => {
|
||||||
},
|
self.special = self.special.map(|special| {
|
||||||
TekSpecialModifier::Minus => {
|
special.rank_up()
|
||||||
self.special = self.special.map(|special| {
|
});
|
||||||
special.rank_down()
|
},
|
||||||
});
|
TekSpecialModifier::Minus => {
|
||||||
},
|
self.special = self.special.map(|special| {
|
||||||
TekSpecialModifier::Neutral => {
|
special.rank_down()
|
||||||
},
|
});
|
||||||
}
|
},
|
||||||
for i in 0..3 {
|
TekSpecialModifier::Neutral => {
|
||||||
self.attrs[i] = self.attrs[i].map(|mut attr| {
|
},
|
||||||
match percent {
|
}
|
||||||
TekPercentModifier::PlusPlus => {
|
for i in 0..3 {
|
||||||
attr.value += 10;
|
self.attrs[i] = self.attrs[i].map(|mut attr| {
|
||||||
},
|
match percent {
|
||||||
TekPercentModifier::Plus => {
|
TekPercentModifier::PlusPlus => {
|
||||||
attr.value += 5;
|
attr.value += 10;
|
||||||
},
|
},
|
||||||
TekPercentModifier::MinusMinus => {
|
TekPercentModifier::Plus => {
|
||||||
attr.value -= 10;
|
attr.value += 5;
|
||||||
},
|
},
|
||||||
TekPercentModifier::Minus => {
|
TekPercentModifier::MinusMinus => {
|
||||||
attr.value -= 5;
|
attr.value -= 10;
|
||||||
},
|
},
|
||||||
TekPercentModifier::Neutral => {
|
TekPercentModifier::Minus => {
|
||||||
|
attr.value -= 5;
|
||||||
|
},
|
||||||
|
TekPercentModifier::Neutral => {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
attr
|
||||||
attr
|
});
|
||||||
});
|
}
|
||||||
}
|
self.grind = std::cmp::max(self.grind as i32 + grind, 0) as u8;
|
||||||
self.grind = std::cmp::max(self.grind as i32 + grind, 0) as u8;
|
self.tekked = true;
|
||||||
self.tekked = true;
|
},
|
||||||
}
|
WeaponModifier::AddKill{enemy: _} => {
|
||||||
|
if let Some(kills) = self.kills {
|
||||||
|
self.kills = Some(kills + 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> [u8; 16] {
|
pub fn as_bytes(&self) -> [u8; 16] {
|
||||||
@ -1532,7 +1553,12 @@ impl Weapon {
|
|||||||
|
|
||||||
result[6..8].copy_from_slice(&self.attrs[0].map(|s| s.value()).unwrap_or([0,0]));
|
result[6..8].copy_from_slice(&self.attrs[0].map(|s| s.value()).unwrap_or([0,0]));
|
||||||
result[8..10].copy_from_slice(&self.attrs[1].map(|s| s.value()).unwrap_or([0,0]));
|
result[8..10].copy_from_slice(&self.attrs[1].map(|s| s.value()).unwrap_or([0,0]));
|
||||||
result[10..12].copy_from_slice(&self.attrs[2].map(|s| s.value()).unwrap_or([0,0]));
|
if self.weapon.has_counter() {
|
||||||
|
result[10..12].copy_from_slice(&self.kills.unwrap_or(0u16).to_be_bytes());
|
||||||
|
result[10] += 0x80;
|
||||||
|
} else {
|
||||||
|
result[10..12].copy_from_slice(&self.attrs[2].map(|s| s.value()).unwrap_or([0,0]));
|
||||||
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@ -1543,6 +1569,7 @@ impl Weapon {
|
|||||||
if let Ok(weapon) = wep {
|
if let Ok(weapon) = wep {
|
||||||
let mut special = None;
|
let mut special = None;
|
||||||
let mut tekked = true;
|
let mut tekked = true;
|
||||||
|
let mut kills = None;
|
||||||
let grind = data[3];
|
let grind = data[3];
|
||||||
|
|
||||||
if data[4] >= 0x81 && data[4] <= 0xA8 {
|
if data[4] >= 0x81 && data[4] <= 0xA8 {
|
||||||
@ -1575,12 +1602,18 @@ impl Weapon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if data[10] & 0x80 == 0x80 {
|
||||||
|
attrs[2] = None;
|
||||||
|
kills = Some(u16::from_be_bytes([data[10] - 0x80, data[11]]));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Weapon {
|
Ok(Weapon {
|
||||||
weapon,
|
weapon,
|
||||||
special,
|
special,
|
||||||
grind,
|
grind,
|
||||||
attrs,
|
attrs,
|
||||||
tekked,
|
tekked,
|
||||||
|
kills,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#![feature(drain_filter)]
|
#![feature(drain_filter)]
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
|
|
||||||
|
extern crate fix_hidden_lifetime_bug;
|
||||||
|
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
@ -220,6 +220,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None; 3],
|
attrs: [None; 3],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
})}).await?;
|
})}).await?;
|
||||||
|
|
||||||
entity_gateway.add_item_note(&weapon.id, ItemNote::CharacterCreation {
|
entity_gateway.add_item_note(&weapon.id, ItemNote::CharacterCreation {
|
||||||
@ -257,41 +258,44 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
|
|||||||
character_id: character.id,
|
character_id: character.id,
|
||||||
}).await?;
|
}).await?;
|
||||||
|
|
||||||
let (monomates, monofluids) = futures::future::join_all((0..4usize).map(|_| {
|
let mut monomates = Vec::new();
|
||||||
let mut eg = entity_gateway.clone();
|
for _ in 0..4usize {
|
||||||
let character_id = character.id;
|
let monomate = entity_gateway.create_item(
|
||||||
async move {
|
NewItemEntity {
|
||||||
let monomate = eg.create_item(
|
item: ItemDetail::Tool (
|
||||||
NewItemEntity {
|
Tool {
|
||||||
item: ItemDetail::Tool (
|
tool: item::tool::ToolType::Monomate,
|
||||||
Tool {
|
})}).await?;
|
||||||
tool: item::tool::ToolType::Monomate,
|
|
||||||
})}).await?;
|
|
||||||
|
|
||||||
eg.add_item_note(&monomate.id, ItemNote::CharacterCreation {
|
entity_gateway.add_item_note(&monomate.id, ItemNote::CharacterCreation {
|
||||||
character_id
|
character_id: character.id
|
||||||
}).await?;
|
}).await?;
|
||||||
|
|
||||||
let monofluid = eg.create_item(
|
monomates.push(monomate);
|
||||||
NewItemEntity {
|
}
|
||||||
item: ItemDetail::Tool (
|
|
||||||
Tool {
|
|
||||||
tool: item::tool::ToolType::Monofluid,
|
|
||||||
})}).await?;
|
|
||||||
|
|
||||||
eg.add_item_note(&monofluid.id, ItemNote::CharacterCreation {
|
let mut monofluids = Vec::new();
|
||||||
character_id
|
for _ in 0..4usize {
|
||||||
}).await?;
|
let monofluid = entity_gateway.create_item(
|
||||||
|
NewItemEntity {
|
||||||
|
item: ItemDetail::Tool (
|
||||||
|
Tool {
|
||||||
|
tool: item::tool::ToolType::Monofluid,
|
||||||
|
})}).await?;
|
||||||
|
|
||||||
Ok((monomate, monofluid))
|
entity_gateway.add_item_note(&monofluid.id, ItemNote::CharacterCreation {
|
||||||
}})).await.into_iter().collect::<Result<Vec<_>, GatewayError>>()?.into_iter().unzip();
|
character_id: character.id
|
||||||
|
}).await?;
|
||||||
|
|
||||||
|
monofluids.push(monofluid);
|
||||||
|
}
|
||||||
|
|
||||||
let inventory = InventoryEntity {
|
let inventory = InventoryEntity {
|
||||||
items: vec![InventoryItemEntity::Individual(weapon.clone()), InventoryItemEntity::Individual(armor.clone()), InventoryItemEntity::Individual(mag.clone()),
|
items: vec![InventoryItemEntity::Individual(weapon.clone()), InventoryItemEntity::Individual(armor.clone()), InventoryItemEntity::Individual(mag.clone()),
|
||||||
InventoryItemEntity::Stacked(monomates), InventoryItemEntity::Stacked(monofluids)],
|
InventoryItemEntity::Stacked(monomates), InventoryItemEntity::Stacked(monofluids)],
|
||||||
};
|
};
|
||||||
entity_gateway.set_character_inventory(&character.id, &inventory).await?;
|
entity_gateway.set_character_inventory(&character.id, &inventory).await?;
|
||||||
entity_gateway.set_character_bank(&character.id, &BankEntity::default(), BankName("".into())).await?;
|
entity_gateway.set_character_bank(&character.id, &BankEntity::default(), &BankName("".into())).await?;
|
||||||
let equipped = EquippedEntity {
|
let equipped = EquippedEntity {
|
||||||
weapon: Some(weapon.id),
|
weapon: Some(weapon.id),
|
||||||
armor: Some(armor.id),
|
armor: Some(armor.id),
|
||||||
@ -326,7 +330,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||||
match get_login_status(&self.entity_gateway, pkt).await {
|
match get_login_status(&mut self.entity_gateway, pkt).await {
|
||||||
Ok(user) => {
|
Ok(user) => {
|
||||||
if let Some(connected_client) = self.connected_clients.get(&user.id) {
|
if let Some(connected_client) = self.connected_clients.get(&user.id) {
|
||||||
if let Some(expires) = connected_client.expires {
|
if let Some(expires) = connected_client.expires {
|
||||||
@ -809,7 +813,7 @@ mod test {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl EntityGateway for TestData {
|
impl EntityGateway for TestData {
|
||||||
async fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> {
|
async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result<UserSettingsEntity, GatewayError> {
|
||||||
Ok(UserSettingsEntity {
|
Ok(UserSettingsEntity {
|
||||||
id: UserSettingsId(0),
|
id: UserSettingsId(0),
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
@ -865,7 +869,7 @@ mod test {
|
|||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_character_create() {
|
async fn test_character_create() {
|
||||||
let test_data = InMemoryGateway::default();
|
let mut test_data = InMemoryGateway::default();
|
||||||
let mut fake_user = ClientState::new();
|
let mut fake_user = ClientState::new();
|
||||||
fake_user.user = Some(UserAccountEntity {
|
fake_user.user = Some(UserAccountEntity {
|
||||||
id: UserAccountId(3),
|
id: UserAccountId(3),
|
||||||
|
@ -59,7 +59,8 @@ impl SendServerPacket for SendLoginPacket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_login_status(entity_gateway: &impl EntityGateway, pkt: &Login) -> Result<UserAccountEntity, AccountStatus> {
|
// TODO: MORE impl EntityGateway?
|
||||||
|
pub async fn get_login_status(entity_gateway: &mut impl EntityGateway, pkt: &Login) -> Result<UserAccountEntity, AccountStatus> {
|
||||||
let username = array_to_utf8(pkt.username).map_err(|_err| AccountStatus::Error)?;
|
let username = array_to_utf8(pkt.username).map_err(|_err| AccountStatus::Error)?;
|
||||||
let password = array_to_utf8(pkt.password).map_err(|_err| AccountStatus::Error)?;
|
let password = array_to_utf8(pkt.password).map_err(|_err| AccountStatus::Error)?;
|
||||||
let user = entity_gateway.get_user_by_name(username).await.map_err(|_| AccountStatus::InvalidUser)?;
|
let user = entity_gateway.get_user_by_name(username).await.map_err(|_| AccountStatus::InvalidUser)?;
|
||||||
@ -108,7 +109,7 @@ impl<EG: EntityGateway> LoginServerState<EG> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendLoginPacket>, anyhow::Error> {
|
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendLoginPacket>, anyhow::Error> {
|
||||||
match get_login_status(&self.entity_gateway, pkt).await.and_then(check_if_already_online) {
|
match get_login_status(&mut self.entity_gateway, pkt).await.and_then(check_if_already_online) {
|
||||||
Ok(mut user) => {
|
Ok(mut user) => {
|
||||||
user.at_login = true;
|
user.at_login = true;
|
||||||
self.entity_gateway.save_user(&user).await.map_err(|_| LoginError::DbError)?;
|
self.entity_gateway.save_user(&user).await.map_err(|_| LoginError::DbError)?;
|
||||||
@ -210,7 +211,7 @@ mod test {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl EntityGateway for TestData {
|
impl EntityGateway for TestData {
|
||||||
async fn get_user_by_name(&self, name: String) -> Result<UserAccountEntity, GatewayError> {
|
async fn get_user_by_name(&mut self, name: String) -> Result<UserAccountEntity, GatewayError> {
|
||||||
assert!(name == "testuser");
|
assert!(name == "testuser");
|
||||||
Ok(UserAccountEntity {
|
Ok(UserAccountEntity {
|
||||||
id: UserAccountId(1),
|
id: UserAccountId(1),
|
||||||
@ -268,7 +269,7 @@ mod test {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl EntityGateway for TestData {
|
impl EntityGateway for TestData {
|
||||||
async fn get_user_by_name(&self, _name: String) -> Result<UserAccountEntity, GatewayError> {
|
async fn get_user_by_name(&mut self, _name: String) -> Result<UserAccountEntity, GatewayError> {
|
||||||
Err(GatewayError::Error)
|
Err(GatewayError::Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,7 +303,7 @@ mod test {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl EntityGateway for TestData {
|
impl EntityGateway for TestData {
|
||||||
async fn get_user_by_name(&self, name: String) -> Result<UserAccountEntity, GatewayError> {
|
async fn get_user_by_name(&mut self, name: String) -> Result<UserAccountEntity, GatewayError> {
|
||||||
assert!(name == "testuser");
|
assert!(name == "testuser");
|
||||||
Ok(UserAccountEntity {
|
Ok(UserAccountEntity {
|
||||||
id: UserAccountId(1),
|
id: UserAccountId(1),
|
||||||
@ -351,7 +352,7 @@ mod test {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl EntityGateway for TestData {
|
impl EntityGateway for TestData {
|
||||||
async fn get_user_by_name(&self, name: String) -> Result<UserAccountEntity, GatewayError> {
|
async fn get_user_by_name(&mut self, name: String) -> Result<UserAccountEntity, GatewayError> {
|
||||||
assert!(name == "testuser");
|
assert!(name == "testuser");
|
||||||
Ok(UserAccountEntity {
|
Ok(UserAccountEntity {
|
||||||
id: UserAccountId(1),
|
id: UserAccountId(1),
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use libpso::character::character;
|
use libpso::character::character;
|
||||||
use crate::common::leveltable::CharacterStats;
|
use crate::common::leveltable::CharacterStats;
|
||||||
use crate::entity::character::CharacterEntity;
|
use crate::entity::character::CharacterEntity;
|
||||||
use crate::ship::items::{CharacterInventory, CharacterBank};
|
//use crate::ship::items::{CharacterInventory, CharacterBank};
|
||||||
|
use crate::ship::items::bank::BankState;
|
||||||
|
use crate::ship::items::inventory::InventoryState;
|
||||||
use crate::entity::item::Meseta;
|
use crate::entity::item::Meseta;
|
||||||
|
|
||||||
|
|
||||||
@ -88,8 +90,8 @@ pub struct FullCharacterBytesBuilder<'a> {
|
|||||||
stats: Option<&'a CharacterStats>,
|
stats: Option<&'a CharacterStats>,
|
||||||
level: Option<u32>,
|
level: Option<u32>,
|
||||||
meseta: Option<Meseta>,
|
meseta: Option<Meseta>,
|
||||||
inventory: Option<&'a CharacterInventory>,
|
inventory: Option<&'a InventoryState>,
|
||||||
bank: Option<&'a CharacterBank>,
|
bank: Option<&'a BankState>,
|
||||||
keyboard_config: Option<&'a [u8; 0x16C]>,
|
keyboard_config: Option<&'a [u8; 0x16C]>,
|
||||||
gamepad_config: Option<&'a [u8; 0x38]>,
|
gamepad_config: Option<&'a [u8; 0x38]>,
|
||||||
symbol_chat: Option<&'a [u8; 1248]>,
|
symbol_chat: Option<&'a [u8; 1248]>,
|
||||||
@ -131,7 +133,7 @@ impl<'a> FullCharacterBytesBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn inventory(self, inventory: &'a CharacterInventory) -> FullCharacterBytesBuilder<'a> {
|
pub fn inventory(self, inventory: &'a InventoryState) -> FullCharacterBytesBuilder<'a> {
|
||||||
FullCharacterBytesBuilder {
|
FullCharacterBytesBuilder {
|
||||||
inventory: Some(inventory),
|
inventory: Some(inventory),
|
||||||
..self
|
..self
|
||||||
@ -139,7 +141,7 @@ impl<'a> FullCharacterBytesBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn bank(self, bank: &'a CharacterBank) -> FullCharacterBytesBuilder<'a> {
|
pub fn bank(self, bank: &'a BankState) -> FullCharacterBytesBuilder<'a> {
|
||||||
FullCharacterBytesBuilder {
|
FullCharacterBytesBuilder {
|
||||||
bank: Some(bank),
|
bank: Some(bank),
|
||||||
..self
|
..self
|
||||||
|
@ -89,6 +89,7 @@ impl GenericUnitTable {
|
|||||||
ItemDropType::Unit(Unit {
|
ItemDropType::Unit(Unit {
|
||||||
unit: unit_type,
|
unit: unit_type,
|
||||||
modifier: unit_modifier,
|
modifier: unit_modifier,
|
||||||
|
kills: None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -116,6 +117,7 @@ mod test {
|
|||||||
assert!(gut.get_drop(&area, &mut rng) == Some(ItemDropType::Unit(Unit {
|
assert!(gut.get_drop(&area, &mut rng) == Some(ItemDropType::Unit(Unit {
|
||||||
unit: unit,
|
unit: unit,
|
||||||
modifier: umod,
|
modifier: umod,
|
||||||
|
kills: None,
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,6 +497,7 @@ impl GenericWeaponTable {
|
|||||||
grind: weapon_grind as u8,
|
grind: weapon_grind as u8,
|
||||||
attrs: weapon_attributes,
|
attrs: weapon_attributes,
|
||||||
tekked: weapon_special.is_none(),
|
tekked: weapon_special.is_none(),
|
||||||
|
kills: None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -518,6 +519,7 @@ mod test {
|
|||||||
grind: 0,
|
grind: 0,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
})));
|
})));
|
||||||
|
|
||||||
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Hard, SectionID::Skyly);
|
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Hard, SectionID::Skyly);
|
||||||
@ -527,6 +529,7 @@ mod test {
|
|||||||
grind: 2,
|
grind: 2,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
})));
|
})));
|
||||||
|
|
||||||
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::VeryHard, SectionID::Skyly);
|
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::VeryHard, SectionID::Skyly);
|
||||||
@ -536,6 +539,7 @@ mod test {
|
|||||||
grind: 0,
|
grind: 0,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: false,
|
tekked: false,
|
||||||
|
kills: None,
|
||||||
})));
|
})));
|
||||||
|
|
||||||
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly);
|
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly);
|
||||||
@ -545,6 +549,7 @@ mod test {
|
|||||||
grind: 0,
|
grind: 0,
|
||||||
attrs: [Some(WeaponAttribute {attr: Attribute::ABeast, value: 30}), Some(WeaponAttribute {attr: Attribute::Dark, value: 30}), None],
|
attrs: [Some(WeaponAttribute {attr: Attribute::ABeast, value: 30}), Some(WeaponAttribute {attr: Attribute::Dark, value: 30}), None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ pub struct MonsterDropStats {
|
|||||||
pub max_meseta: u32,
|
pub max_meseta: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum ItemDropType {
|
pub enum ItemDropType {
|
||||||
Weapon(weapon::Weapon),
|
Weapon(weapon::Weapon),
|
||||||
Armor(armor::Armor),
|
Armor(armor::Armor),
|
||||||
@ -186,6 +186,10 @@ impl<R: Rng + SeedableRng> DropTable<R> {
|
|||||||
pub fn get_box_drop(&mut self, map_area: &MapArea, object: &MapObject) -> Option<ItemDropType> {
|
pub fn get_box_drop(&mut self, map_area: &MapArea, object: &MapObject) -> Option<ItemDropType> {
|
||||||
self.box_table.get_drop(map_area, object, &mut self.rng)
|
self.box_table.get_drop(map_area, object, &mut self.rng)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_rare_drop(&mut self, map_area: &MapArea, monster: &MonsterType) -> Option<ItemDropType> {
|
||||||
|
self.rare_table.get_rare_drop(map_area, monster, &mut self.rng)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -205,4 +209,14 @@ mod test {
|
|||||||
.into_iter().choose(&mut rng).unwrap();
|
.into_iter().choose(&mut rng).unwrap();
|
||||||
DropTable::<rand_chacha::ChaCha20Rng>::new(episode, difficulty, section_id);
|
DropTable::<rand_chacha::ChaCha20Rng>::new(episode, difficulty, section_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sjs_drop() {
|
||||||
|
let mut drop_table = DropTable::<rand_chacha::ChaCha20Rng>::new(Episode::Two, Difficulty::Ultimate, SectionID::Skyly);
|
||||||
|
let drop = drop_table.get_rare_drop(&MapArea::Seaside, &MonsterType::GiGue).unwrap();
|
||||||
|
if let ItemDropType::Weapon(weapon) = drop {
|
||||||
|
assert!(weapon.weapon == weapon::WeaponType::SealedJSword);
|
||||||
|
assert!(weapon.kills == Some(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,14 +97,19 @@ impl RareDropTable {
|
|||||||
pub fn apply_item_stats<R: Rng>(&self, map_area: &MapArea, item: RareDropItem, rng: &mut R) -> ItemDropType {
|
pub fn apply_item_stats<R: Rng>(&self, map_area: &MapArea, item: RareDropItem, rng: &mut R) -> ItemDropType {
|
||||||
match item {
|
match item {
|
||||||
RareDropItem::Weapon(weapon) => {
|
RareDropItem::Weapon(weapon) => {
|
||||||
ItemDropType::Weapon(Weapon {
|
let mut dropped_weapon = Weapon {
|
||||||
weapon,
|
weapon,
|
||||||
special: None,
|
special: None,
|
||||||
grind: 0,
|
grind: 0,
|
||||||
attrs: self.attribute_table.generate_rare_attributes(map_area, rng),
|
attrs: self.attribute_table.generate_rare_attributes(map_area, rng),
|
||||||
tekked: false,
|
tekked: false,
|
||||||
})
|
kills: None,
|
||||||
|
};
|
||||||
|
if dropped_weapon.weapon.has_counter() {
|
||||||
|
dropped_weapon.attrs[2] = None;
|
||||||
|
dropped_weapon.kills = Some(0);
|
||||||
|
};
|
||||||
|
ItemDropType::Weapon(dropped_weapon)
|
||||||
},
|
},
|
||||||
RareDropItem::Armor(armor) => {
|
RareDropItem::Armor(armor) => {
|
||||||
ItemDropType::Armor(Armor {
|
ItemDropType::Armor(Armor {
|
||||||
@ -125,6 +130,13 @@ impl RareDropTable {
|
|||||||
ItemDropType::Unit(Unit {
|
ItemDropType::Unit(Unit {
|
||||||
unit,
|
unit,
|
||||||
modifier: None,
|
modifier: None,
|
||||||
|
kills: {
|
||||||
|
if unit.has_counter() {
|
||||||
|
Some(0)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
RareDropItem::Tool(tool) => {
|
RareDropItem::Tool(tool) => {
|
||||||
@ -153,4 +165,14 @@ impl RareDropTable {
|
|||||||
}).next()
|
}).next()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_rare_drop<R: Rng>(&self, map_area: &MapArea, monster: &MonsterType, rng: &mut R) -> Option<ItemDropType> {
|
||||||
|
self.rates.get(monster)
|
||||||
|
.and_then(|drop_rates| {
|
||||||
|
drop_rates.iter()
|
||||||
|
.map(|drop_rate| {
|
||||||
|
self.apply_item_stats(map_area, drop_rate.item, rng)
|
||||||
|
}).next()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
852
src/ship/items/actions.rs
Normal file
852
src/ship/items/actions.rs
Normal file
@ -0,0 +1,852 @@
|
|||||||
|
// TODO: replace various u32s and usizes denoting item amounts for ItemAmount(u32) for consistency
|
||||||
|
use crate::ship::items::ClientItemId;
|
||||||
|
use crate::entity::item::{Meseta, ItemNote};
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use crate::ship::map::MapArea;
|
||||||
|
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||||
|
use crate::entity::gateway::EntityGatewayTransaction;
|
||||||
|
use crate::ship::items::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail};
|
||||||
|
use crate::ship::items::bank::{BankItem, BankItemDetail};
|
||||||
|
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
|
||||||
|
use crate::ship::items::floor::{FloorItem, FloorItemDetail};
|
||||||
|
use crate::ship::items::apply_item::apply_item;
|
||||||
|
use crate::entity::item::{ItemDetail, NewItemEntity, TradeId};
|
||||||
|
use crate::entity::item::tool::Tool;
|
||||||
|
use crate::entity::item::ItemModifier;
|
||||||
|
use crate::ship::shops::ShopItem;
|
||||||
|
use crate::ship::drops::{ItemDrop, ItemDropType};
|
||||||
|
|
||||||
|
pub enum TriggerCreateItem {
|
||||||
|
Yes,
|
||||||
|
No
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, transaction): (ItemStateProxy<'_>, Box<dyn EntityGatewayTransaction + '_>) , _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut floor = item_state.floor(&character_id)?;
|
||||||
|
let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?;
|
||||||
|
item_state.set_floor(floor);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_floor_item_to_inventory(character: &CharacterEntity)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), TriggerCreateItem), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
let character = character.clone();
|
||||||
|
move |(mut item_state, transaction), floor_item| {
|
||||||
|
let character = character.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character.id)?;
|
||||||
|
|
||||||
|
let character_id = character.id;
|
||||||
|
let transaction = floor_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||||
|
async move {
|
||||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::Pickup {
|
||||||
|
character_id
|
||||||
|
}).await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}}).await?;
|
||||||
|
|
||||||
|
let mut transaction = floor_item.with_mag(transaction, |mut transaction, entity_id, _mag| {
|
||||||
|
let character = character.clone();
|
||||||
|
async move {
|
||||||
|
transaction.gateway().change_mag_owner(&entity_id, &character).await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}}).await?;
|
||||||
|
|
||||||
|
let add_result = inventory.add_floor_item(floor_item)?;
|
||||||
|
transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||||
|
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction),
|
||||||
|
match add_result {
|
||||||
|
AddItemResult::NewItem => TriggerCreateItem::Yes,
|
||||||
|
AddItemResult::AddToStack => TriggerCreateItem::No,
|
||||||
|
AddItemResult::Meseta => TriggerCreateItem::No,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||||
|
let item = inventory.take_item(&item_id, amount).ok_or (ItemStateError::NoFloorItem(item_id))?;
|
||||||
|
|
||||||
|
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32, f32))
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, transaction), inventory_item| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||||
|
async move {
|
||||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::PlayerDrop {
|
||||||
|
character_id,
|
||||||
|
map_area,
|
||||||
|
x: drop_position.0,
|
||||||
|
y: drop_position.1,
|
||||||
|
z: drop_position.2,
|
||||||
|
}).await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}}).await?;
|
||||||
|
|
||||||
|
let mut floor = item_state.floor(&character_id)?;
|
||||||
|
let floor_item = floor.add_inventory_item(inventory_item, map_area, drop_position).clone();
|
||||||
|
item_state.set_floor(floor);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), floor_item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||||
|
inventory.remove_meseta(amount)?;
|
||||||
|
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), ()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u32)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||||
|
inventory.add_meseta(amount)?;
|
||||||
|
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), ()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_area: MapArea, drop_position: (f32, f32))
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
|
||||||
|
move |(mut item_state, transaction), _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let floor_item = FloorItem {
|
||||||
|
item_id: item_state.new_item_id()?,
|
||||||
|
item: FloorItemDetail::Meseta(Meseta(amount)),
|
||||||
|
map_area,
|
||||||
|
x: drop_position.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: drop_position.1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut floor = item_state.floor(&character_id)?;
|
||||||
|
let floor_item = floor.add_shared_item(floor_item).clone();
|
||||||
|
item_state.set_floor(floor);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), floor_item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn take_meseta_from_bank(character_id: CharacterEntityId, amount: u32)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut bank = item_state.bank(&character_id)?;
|
||||||
|
bank.remove_meseta(amount)?;
|
||||||
|
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), ()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_meseta_from_bank_to_inventory(character_id: CharacterEntityId, amount: u32)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||||
|
inventory.add_meseta_no_overflow(amount)?;
|
||||||
|
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), ()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn add_meseta_to_bank(character_id: CharacterEntityId, amount: u32)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut bank = item_state.bank(&character_id)?;
|
||||||
|
bank.add_meseta(amount)?;
|
||||||
|
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), ()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn take_item_from_bank(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), BankItem), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut bank = item_state.bank(&character_id)?;
|
||||||
|
let item = bank.take_item(&item_id, amount).ok_or(ItemStateError::NoBankItem(item_id))?;
|
||||||
|
transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?;
|
||||||
|
item_state.set_bank(bank);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_bank_item_to_inventory(character: &CharacterEntity)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), BankItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
let character = character.clone();
|
||||||
|
move |(mut item_state, transaction), bank_item| {
|
||||||
|
let character = character.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
let bank_name = item_state.bank(&character.id)?.name;
|
||||||
|
let mut inventory = item_state.inventory(&character.id)?;
|
||||||
|
|
||||||
|
let character_id = character.id;
|
||||||
|
let transaction = bank_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||||
|
let bank_name = bank_name.clone();
|
||||||
|
async move {
|
||||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::Withdraw {
|
||||||
|
character_id,
|
||||||
|
bank: bank_name,
|
||||||
|
}).await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}}).await?;
|
||||||
|
|
||||||
|
let inventory_item = InventoryItem {
|
||||||
|
item_id: bank_item.item_id,
|
||||||
|
item: match bank_item.item {
|
||||||
|
BankItemDetail::Individual(individual_item) => InventoryItemDetail::Individual(individual_item),
|
||||||
|
BankItemDetail::Stacked(stacked_item) => InventoryItemDetail::Stacked(stacked_item),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut transaction = inventory_item.with_mag(transaction, |mut transaction, entity_id, _mag| {
|
||||||
|
let character = character.clone();
|
||||||
|
async move {
|
||||||
|
transaction.gateway().change_mag_owner(&entity_id, &character).await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}}).await?;
|
||||||
|
|
||||||
|
inventory.add_item(inventory_item.clone())?;
|
||||||
|
transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), inventory_item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn add_inventory_item_to_bank(character_id: CharacterEntityId)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, transaction), inventory_item| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut bank = item_state.bank(&character_id)?;
|
||||||
|
let bank_name = bank.name.clone();
|
||||||
|
let mut transaction = inventory_item.with_entity_id(transaction, move |mut transaction, entity_id| {
|
||||||
|
let bank_name = bank_name.clone();
|
||||||
|
async move {
|
||||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::Deposit {
|
||||||
|
character_id,
|
||||||
|
bank: bank_name,
|
||||||
|
}).await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}}).await?;
|
||||||
|
|
||||||
|
bank.add_inventory_item(inventory_item)?;
|
||||||
|
|
||||||
|
transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?;
|
||||||
|
item_state.set_bank(bank);
|
||||||
|
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), ()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn equip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId, equip_slot: u8)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||||
|
inventory.equip(&item_id, equip_slot);
|
||||||
|
transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), ()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn unequip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||||
|
inventory.unequip(&item_id);
|
||||||
|
transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), ()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Vec<ClientItemId>)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
let item_ids = item_ids.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||||
|
inventory.sort(&item_ids);
|
||||||
|
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), ()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn use_consumed_item(character: CharacterEntity)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), CharacterEntity), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, transaction), inventory_item| {
|
||||||
|
let mut character = character.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||||
|
async move {
|
||||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::Consumed).await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}}).await?;
|
||||||
|
|
||||||
|
apply_item(&mut item_state, transaction.gateway(), &mut character, inventory_item).await?;
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), character))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), CharacterEntity), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, transaction), tool| {
|
||||||
|
let character = character.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character.id)?;
|
||||||
|
let mag_entity = inventory.get_by_client_id_mut(&mag_item_id)
|
||||||
|
.ok_or(ItemStateError::InvalidItemId(mag_item_id))?
|
||||||
|
.item
|
||||||
|
.as_individual_mut()
|
||||||
|
.ok_or(ItemStateError::NotAMag(mag_item_id))?;
|
||||||
|
let mag_entity_id = mag_entity.entity_id;
|
||||||
|
|
||||||
|
let mut transaction = tool.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||||
|
async move {
|
||||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::FedToMag {
|
||||||
|
//character_id: character.id,
|
||||||
|
mag: mag_entity_id,
|
||||||
|
}).await?;
|
||||||
|
transaction.gateway().feed_mag(&mag_entity_id, &entity_id).await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}}).await?;
|
||||||
|
|
||||||
|
let food_tool = tool
|
||||||
|
.item
|
||||||
|
.stacked()
|
||||||
|
.ok_or(ItemStateError::NotMagFood(tool.item_id))?
|
||||||
|
.tool
|
||||||
|
.tool;
|
||||||
|
|
||||||
|
let mag_entity = mag_entity
|
||||||
|
.as_mag_mut()
|
||||||
|
.ok_or(ItemStateError::NotAMag(mag_item_id))?;
|
||||||
|
|
||||||
|
mag_entity.feed(food_tool);
|
||||||
|
|
||||||
|
transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), character))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId,
|
||||||
|
shop_item: &'a (dyn ShopItem + Send + Sync),
|
||||||
|
item_id: ClientItemId,
|
||||||
|
amount: u32)
|
||||||
|
-> impl Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||||
|
let bought_item = shop_item.as_item();
|
||||||
|
|
||||||
|
let inventory_item = match bought_item {
|
||||||
|
ItemDetail::Tool(tool) if tool.is_stackable() => {
|
||||||
|
let mut item_entities = Vec::new();
|
||||||
|
for _ in 0..amount {
|
||||||
|
let item_entity = transaction.gateway().create_item(NewItemEntity {
|
||||||
|
item: ItemDetail::Tool(tool),
|
||||||
|
}).await?;
|
||||||
|
transaction.gateway().add_item_note(&item_entity.id, ItemNote::BoughtAtShop {
|
||||||
|
character_id,
|
||||||
|
}).await?;
|
||||||
|
item_entities.push(item_entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
let inventory_item = InventoryItem {
|
||||||
|
item_id,
|
||||||
|
item: InventoryItemDetail::Stacked(StackedItemDetail {
|
||||||
|
entity_ids: item_entities.into_iter().map(|i| i.id).collect(),
|
||||||
|
tool,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
inventory.add_item(inventory_item)?.1
|
||||||
|
},
|
||||||
|
item_detail => {
|
||||||
|
let item_entity = transaction.gateway().create_item(NewItemEntity {
|
||||||
|
item: item_detail.clone(),
|
||||||
|
}).await?;
|
||||||
|
transaction.gateway().add_item_note(&item_entity.id, ItemNote::BoughtAtShop {
|
||||||
|
character_id,
|
||||||
|
}).await?;
|
||||||
|
|
||||||
|
let inventory_item = InventoryItem {
|
||||||
|
item_id,
|
||||||
|
item: InventoryItemDetail::Individual(IndividualItemDetail {
|
||||||
|
entity_id: item_entity.id,
|
||||||
|
item: item_detail,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
inventory.add_item(inventory_item)?.1
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
Ok(((item_state, transaction), inventory_item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn sell_inventory_item<'a>(character_id: CharacterEntityId)
|
||||||
|
-> impl Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, transaction), inventory_item| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character_id)?;
|
||||||
|
let price = inventory_item.item.sell_price()?;
|
||||||
|
inventory.add_meseta_no_overflow(price)?;
|
||||||
|
|
||||||
|
let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||||
|
async move {
|
||||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::SoldToShop).await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}}).await?;
|
||||||
|
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
Ok(((item_state, transaction), inventory_item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[async_recursion::async_recursion]
|
||||||
|
async fn iterate_inner<'a, I, O, T, F, FR>(state: (ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>),
|
||||||
|
mut input: Vec<I>,
|
||||||
|
func: F,
|
||||||
|
arg: T)
|
||||||
|
-> Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>
|
||||||
|
where
|
||||||
|
'a: 'async_recursion,
|
||||||
|
I: Send,
|
||||||
|
O: Send,
|
||||||
|
T: Clone + Send + Sync,
|
||||||
|
F: Fn(I) -> FR + Send + Sync + Clone + 'static,
|
||||||
|
FR: Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync,
|
||||||
|
{
|
||||||
|
let item = match input.pop() {
|
||||||
|
Some(item) => item,
|
||||||
|
None => return Ok((state, Vec::new()))
|
||||||
|
};
|
||||||
|
|
||||||
|
let (state, mut output) = iterate_inner(state, input, func.clone(), arg.clone()).await.unwrap();
|
||||||
|
let rfunc = func(item);
|
||||||
|
let (state, result) = rfunc(state, arg.clone()).await.unwrap();
|
||||||
|
|
||||||
|
output.push(result);
|
||||||
|
|
||||||
|
Ok((state, output))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn iterate<'k, I, O, T, F, FR>(
|
||||||
|
input: Vec<I>,
|
||||||
|
func: F)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>> + Send + 'a>>
|
||||||
|
where
|
||||||
|
O: Send,
|
||||||
|
I: Send + Clone + 'static + std::fmt::Debug,
|
||||||
|
T: Send + Clone + 'static + std::fmt::Debug,
|
||||||
|
F: Fn(I) -> FR + Send + Sync + Clone + 'static,
|
||||||
|
FR: for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync,
|
||||||
|
T: Clone + Send + Sync,
|
||||||
|
{
|
||||||
|
move |(item_state, transaction), arg| {
|
||||||
|
let input = input.clone();
|
||||||
|
let func = func.clone();
|
||||||
|
println!("i {:?} {:?}", input, arg);
|
||||||
|
Box::pin(async move {
|
||||||
|
let (state, result) = iterate_inner((item_state, transaction), input, func, arg.clone()).await?;
|
||||||
|
Ok((state, result))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[async_recursion::async_recursion]
|
||||||
|
async fn foreach_inner<'a, O, T, F>(state: (ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>),
|
||||||
|
mut input: Vec<T>,
|
||||||
|
func: F)
|
||||||
|
-> Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>
|
||||||
|
where
|
||||||
|
'a: 'async_recursion,
|
||||||
|
O: Send,
|
||||||
|
T: Clone + Send,
|
||||||
|
F: Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync,
|
||||||
|
F: Clone,
|
||||||
|
{
|
||||||
|
let item = match input.pop() {
|
||||||
|
Some(item) => item,
|
||||||
|
None => return Ok((state, Vec::new()))
|
||||||
|
};
|
||||||
|
|
||||||
|
let (state, mut output) = foreach_inner(state, input, func.clone()).await?;
|
||||||
|
let (state, result) = func(state, item).await?;
|
||||||
|
|
||||||
|
output.push(result);
|
||||||
|
|
||||||
|
Ok((state, output))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn foreach<'k, O, T, F>(func: F)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<T>)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>> + Send + 'a>>
|
||||||
|
where
|
||||||
|
O: Send,
|
||||||
|
T: Send + Clone + 'static + std::fmt::Debug,
|
||||||
|
F: for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync + 'static,
|
||||||
|
F: Clone,
|
||||||
|
T: Clone + Send + Sync,
|
||||||
|
{
|
||||||
|
move |(item_state, transaction), items| {
|
||||||
|
println!("fe {:?}", items);
|
||||||
|
let func = func.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
let (state, result) = foreach_inner((item_state, transaction), items, func).await?;
|
||||||
|
Ok((state, result))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn insert<'a, T: Send + Clone + 'a>(element: T)
|
||||||
|
-> impl Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |state, _| {
|
||||||
|
let element = element.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
Ok((state, element))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_item_to_inventory(character: CharacterEntity)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> + Clone
|
||||||
|
{
|
||||||
|
move |(mut item_state, transaction), inventory_item| {
|
||||||
|
let character = character.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut inventory = item_state.inventory(&character.id)?;
|
||||||
|
let mut transaction = inventory_item.with_mag(transaction, |mut transaction, entity_id, _mag| {
|
||||||
|
let character = character.clone();
|
||||||
|
async move {
|
||||||
|
transaction.gateway().change_mag_owner(&entity_id, &character).await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}}).await?;
|
||||||
|
|
||||||
|
inventory.add_item(inventory_item.clone())?;
|
||||||
|
transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), inventory_item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, character_from: CharacterEntityId)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<InventoryItem>)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<InventoryItem>), ItemStateError>> + Send + 'a>> + Clone
|
||||||
|
{
|
||||||
|
move |(item_state, mut transaction), traded_items| {
|
||||||
|
Box::pin(async move {
|
||||||
|
for item in &traded_items {
|
||||||
|
transaction = item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||||
|
async move {
|
||||||
|
transaction.gateway().add_item_note(&entity_id, ItemNote::Trade {
|
||||||
|
trade_id,
|
||||||
|
character_to,
|
||||||
|
character_from,
|
||||||
|
}).await?;
|
||||||
|
Ok(transaction)
|
||||||
|
}}).await?;
|
||||||
|
}
|
||||||
|
Ok(((item_state, transaction), traded_items))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn assign_new_item_id()
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> + Clone
|
||||||
|
{
|
||||||
|
move |(mut item_state, transaction), mut inventory_item| {
|
||||||
|
Box::pin(async move {
|
||||||
|
inventory_item.item_id = item_state.new_item_id()?;
|
||||||
|
Ok(((item_state, transaction), inventory_item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(super) fn convert_item_drop_to_floor_item(character_id: CharacterEntityId, item_drop: ItemDrop)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>> + Clone
|
||||||
|
{
|
||||||
|
move |(mut item_state, mut transaction), _| {
|
||||||
|
let item_drop = item_drop.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
enum ItemOrMeseta {
|
||||||
|
Individual(ItemDetail),
|
||||||
|
Stacked(Tool),
|
||||||
|
Meseta(Meseta)
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = match item_drop.item {
|
||||||
|
ItemDropType::Weapon(w) => ItemOrMeseta::Individual(ItemDetail::Weapon(w)),
|
||||||
|
ItemDropType::Armor(w) => ItemOrMeseta::Individual(ItemDetail::Armor(w)),
|
||||||
|
ItemDropType::Shield(w) => ItemOrMeseta::Individual(ItemDetail::Shield(w)),
|
||||||
|
ItemDropType::Unit(w) => ItemOrMeseta::Individual(ItemDetail::Unit(w)),
|
||||||
|
ItemDropType::TechniqueDisk(w) => ItemOrMeseta::Individual(ItemDetail::TechniqueDisk(w)),
|
||||||
|
ItemDropType::Mag(w) => ItemOrMeseta::Individual(ItemDetail::Mag(w)),
|
||||||
|
ItemDropType::Tool(t) => {
|
||||||
|
if t.tool.is_stackable() {
|
||||||
|
ItemOrMeseta::Stacked(t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ItemOrMeseta::Individual(ItemDetail::Tool(t))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ItemDropType::Meseta(m) => ItemOrMeseta::Meseta(Meseta(m)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_id = item_state.new_item_id()?;
|
||||||
|
|
||||||
|
let floor_item = match item {
|
||||||
|
ItemOrMeseta::Individual(item_detail) => {
|
||||||
|
let entity = transaction.gateway().create_item(NewItemEntity {
|
||||||
|
item: item_detail.clone(),
|
||||||
|
}).await?;
|
||||||
|
transaction.gateway().add_item_note(&entity.id, ItemNote::EnemyDrop {
|
||||||
|
character_id,
|
||||||
|
map_area: item_drop.map_area,
|
||||||
|
x: item_drop.x,
|
||||||
|
y: item_drop.y,
|
||||||
|
z: item_drop.z,
|
||||||
|
}).await?;
|
||||||
|
FloorItem {
|
||||||
|
item_id,
|
||||||
|
item: FloorItemDetail::Individual(IndividualItemDetail {
|
||||||
|
entity_id: entity.id,
|
||||||
|
item: item_detail,
|
||||||
|
}),
|
||||||
|
map_area: item_drop.map_area,
|
||||||
|
x: item_drop.x,
|
||||||
|
y: item_drop.y,
|
||||||
|
z: item_drop.z,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ItemOrMeseta::Stacked(tool) => {
|
||||||
|
let entity = transaction.gateway().create_item(NewItemEntity {
|
||||||
|
item: ItemDetail::Tool(tool),
|
||||||
|
}).await?;
|
||||||
|
transaction.gateway().add_item_note(&entity.id, ItemNote::EnemyDrop {
|
||||||
|
character_id,
|
||||||
|
map_area: item_drop.map_area,
|
||||||
|
x: item_drop.x,
|
||||||
|
y: item_drop.y,
|
||||||
|
z: item_drop.z,
|
||||||
|
}).await?;
|
||||||
|
FloorItem {
|
||||||
|
item_id,
|
||||||
|
item: FloorItemDetail::Stacked(StackedItemDetail{
|
||||||
|
entity_ids: vec![entity.id],
|
||||||
|
tool,
|
||||||
|
}),
|
||||||
|
map_area: item_drop.map_area,
|
||||||
|
x: item_drop.x,
|
||||||
|
y: item_drop.y,
|
||||||
|
z: item_drop.z,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ItemOrMeseta::Meseta(meseta) => {
|
||||||
|
FloorItem {
|
||||||
|
item_id,
|
||||||
|
item: FloorItemDetail::Meseta(meseta),
|
||||||
|
map_area: item_drop.map_area,
|
||||||
|
x: item_drop.x,
|
||||||
|
y: item_drop.y,
|
||||||
|
z: item_drop.z,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), floor_item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_item_to_local_floor(character_id: CharacterEntityId)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(mut item_state, transaction) , floor_item| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut floor = item_state.floor(&character_id)?;
|
||||||
|
let item = floor.add_local_item(floor_item).clone();
|
||||||
|
item_state.set_floor(floor);
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn apply_modifier_to_inventory_item(modifier: ItemModifier)
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(item_state, mut transaction), mut inventory_item| {
|
||||||
|
let modifier = modifier.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
match (&mut inventory_item.item, modifier) {
|
||||||
|
(InventoryItemDetail::Individual(IndividualItemDetail{entity_id, item: ItemDetail::Weapon(ref mut weapon), ..}), ItemModifier::WeaponModifier(modifier)) => {
|
||||||
|
weapon.apply_modifier(&modifier);
|
||||||
|
transaction.gateway().add_weapon_modifier(entity_id, modifier).await?;
|
||||||
|
},
|
||||||
|
_ => return Err(ItemStateError::InvalidModifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), inventory_item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn as_individual_item()
|
||||||
|
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
|
||||||
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), IndividualItemDetail), ItemStateError>> + Send + 'a>>
|
||||||
|
{
|
||||||
|
move |(item_state, transaction), inventory_item| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let item = match inventory_item.item {
|
||||||
|
InventoryItemDetail::Individual(individual_item) => individual_item,
|
||||||
|
_ => return Err(ItemStateError::WrongItemType(inventory_item.item_id))
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(((item_state, transaction), item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
293
src/ship/items/apply_item.rs
Normal file
293
src/ship/items/apply_item.rs
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||||
|
use crate::entity::character::CharacterEntity;
|
||||||
|
use crate::entity::item::mag::{MagCell, MagCellError};
|
||||||
|
use crate::entity::item::tool::ToolType;
|
||||||
|
use crate::entity::item::{ItemDetail, ItemEntityId};
|
||||||
|
use crate::ship::items::state::{ItemStateProxy, ItemStateError};
|
||||||
|
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ApplyItemError {
|
||||||
|
#[error("no character")]
|
||||||
|
NoCharacter,
|
||||||
|
#[error("item not equipped")]
|
||||||
|
ItemNotEquipped,
|
||||||
|
#[error("invalid item")]
|
||||||
|
InvalidItem,
|
||||||
|
#[error("gateway error {0}")]
|
||||||
|
GatewayError(#[from] GatewayError),
|
||||||
|
|
||||||
|
#[error("itemstate error {0}")]
|
||||||
|
ItemStateError(Box<ItemStateError>),
|
||||||
|
|
||||||
|
#[error("magcell error {0}")]
|
||||||
|
MagCellError(#[from] MagCellError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ItemStateError> for ApplyItemError {
|
||||||
|
fn from(other: ItemStateError) -> ApplyItemError {
|
||||||
|
ApplyItemError::ItemStateError(Box::new(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make all these functions not-pub
|
||||||
|
pub async fn power_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||||
|
character.materials.power += 1;
|
||||||
|
entity_gateway.save_character(character).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn mind_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||||
|
character.materials.mind += 1;
|
||||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn evade_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||||
|
character.materials.evade += 1;
|
||||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn def_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||||
|
character.materials.def += 1;
|
||||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn luck_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||||
|
character.materials.luck += 1;
|
||||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn hp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||||
|
character.materials.hp += 1;
|
||||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn tp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> {
|
||||||
|
character.materials.tp += 1;
|
||||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
async fn mag_cell<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory, mag_cell_type: MagCell) -> Result<(), ApplyItemError> {
|
||||||
|
let mut mag_handle = inventory.get_equipped_mag_handle().ok_or(ApplyItemError::ItemNotEquipped)?;
|
||||||
|
let mag_item = mag_handle.item_mut()
|
||||||
|
.ok_or(ApplyItemError::InvalidItem)?;
|
||||||
|
let actual_mag = mag_item
|
||||||
|
.individual_mut()
|
||||||
|
.ok_or(ApplyItemError::InvalidItem)?
|
||||||
|
.mag_mut()
|
||||||
|
.ok_or(ApplyItemError::InvalidItem)?;
|
||||||
|
actual_mag.apply_mag_cell(mag_cell_type);
|
||||||
|
for mag_entity_id in mag_item.entity_ids() {
|
||||||
|
for cell_entity_id in used_cell.entity_ids() {
|
||||||
|
entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
async fn mag_cell<'a, EG>(item_state: &mut ItemStateProxy<'a>,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
cell_entity_id: ItemEntityId,
|
||||||
|
mag_cell_type: MagCell)
|
||||||
|
-> Result<(), ApplyItemError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway + ?Sized,
|
||||||
|
{
|
||||||
|
let mut inventory = item_state.inventory(&character.id)?;
|
||||||
|
|
||||||
|
let (mag_entity_id, mag) = inventory.equipped_mag_mut()
|
||||||
|
.ok_or(ApplyItemError::ItemNotEquipped)?;
|
||||||
|
mag.apply_mag_cell(mag_cell_type)?;
|
||||||
|
|
||||||
|
entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await?;
|
||||||
|
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||||
|
item_state.set_inventory(inventory);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub async fn cell_of_mag_502<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, inventory, MagCell::CellOfMag502).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn cell_of_mag_213<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag213).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn parts_of_robochao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::PartsOfRobochao).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn heart_of_opaopa<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfOpaOpa).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn heart_of_pian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfPian).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn heart_of_chao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfChao).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn heart_of_angel<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfAngel).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn kit_of_hamburger<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfHamburger).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn panthers_spirit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::PanthersSpirit).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn kit_of_mark3<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMark3).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn kit_of_master_system<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMasterSystem).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn kit_of_genesis<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfGenesis).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn kit_of_sega_saturn<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfSegaSaturn).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn kit_of_dreamcast<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfDreamcast).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn tablet<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::Tablet).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn dragon_scale<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::DragonScale).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn heaven_striker_coat<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeavenStrikerCoat).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn pioneer_parts<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::PioneerParts).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn amities_memo<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::AmitiesMemo).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn heart_of_morolian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfMorolian).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn rappys_beak<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::RappysBeak).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn yahoos_engine<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::YahoosEngine).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn d_photon_core<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::DPhotonCore).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn liberta_kit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> {
|
||||||
|
mag_cell(entity_gateway, used_cell, inventory, MagCell::LibertaKit).await
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
async fn apply_tool<'a, EG>(item_state: &mut ItemStateProxy<'a>,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &mut CharacterEntity,
|
||||||
|
entity_id: ItemEntityId,
|
||||||
|
tool: ToolType)
|
||||||
|
-> Result<(), ApplyItemError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway + ?Sized,
|
||||||
|
{
|
||||||
|
match tool {
|
||||||
|
ToolType::PowerMaterial => power_material(entity_gateway, character).await,
|
||||||
|
ToolType::MindMaterial => mind_material(entity_gateway, character).await,
|
||||||
|
ToolType::EvadeMaterial => evade_material(entity_gateway, character).await,
|
||||||
|
ToolType::DefMaterial => def_material(entity_gateway, character).await,
|
||||||
|
ToolType::LuckMaterial => luck_material(entity_gateway, character).await,
|
||||||
|
ToolType::HpMaterial => hp_material(entity_gateway, character).await,
|
||||||
|
ToolType::TpMaterial => tp_material(entity_gateway, character).await,
|
||||||
|
ToolType::Monomate => Ok(()),
|
||||||
|
ToolType::Dimate => Ok(()),
|
||||||
|
ToolType::Trimate => Ok(()),
|
||||||
|
ToolType::Monofluid => Ok(()),
|
||||||
|
ToolType::Difluid => Ok(()),
|
||||||
|
ToolType::Trifluid => Ok(()),
|
||||||
|
ToolType::HuntersReport => Ok(()),
|
||||||
|
ToolType::CellOfMag502
|
||||||
|
| ToolType::CellOfMag213
|
||||||
|
| ToolType::PartsOfRobochao
|
||||||
|
| ToolType::HeartOfOpaOpa
|
||||||
|
| ToolType::HeartOfPian
|
||||||
|
| ToolType::HeartOfChao
|
||||||
|
| ToolType::HeartOfAngel
|
||||||
|
| ToolType::KitOfHamburger
|
||||||
|
| ToolType::PanthersSpirit
|
||||||
|
| ToolType::KitOfMark3
|
||||||
|
| ToolType::KitOfMasterSystem
|
||||||
|
| ToolType::KitOfGenesis
|
||||||
|
| ToolType::KitOfSegaSaturn
|
||||||
|
| ToolType::KitOfDreamcast
|
||||||
|
| ToolType::Tablet
|
||||||
|
| ToolType::DragonScale
|
||||||
|
| ToolType::HeavenStrikerCoat
|
||||||
|
| ToolType::PioneerParts
|
||||||
|
| ToolType::AmitiesMemo
|
||||||
|
| ToolType::HeartOfMorolian
|
||||||
|
| ToolType::RappysBeak
|
||||||
|
| ToolType::YahoosEngine
|
||||||
|
| ToolType::DPhotonCore
|
||||||
|
| ToolType::LibertaKit => {
|
||||||
|
mag_cell(item_state, entity_gateway, character, entity_id, tool.try_into()?).await
|
||||||
|
}
|
||||||
|
// TODO: rest of these
|
||||||
|
_ => Err(ApplyItemError::InvalidItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn apply_item<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStateProxy<'a>, entity_gateway: &mut EG, character: &mut CharacterEntity, item: InventoryItem) -> Result<(), ApplyItemError> {
|
||||||
|
match item.item {
|
||||||
|
InventoryItemDetail::Individual(individual_item) => {
|
||||||
|
match individual_item.item {
|
||||||
|
ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, individual_item.entity_id, tool.tool).await,
|
||||||
|
_ => Err(ApplyItemError::InvalidItem)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
InventoryItemDetail::Stacked(stacked_item) => {
|
||||||
|
for entity_id in stacked_item.entity_ids {
|
||||||
|
apply_tool(item_state, entity_gateway, character, entity_id, stacked_item.tool.tool).await?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -1,124 +1,43 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
|
use libpso::character::character;
|
||||||
use crate::ship::items::ClientItemId;
|
use crate::ship::items::ClientItemId;
|
||||||
use libpso::character::character;//::InventoryItem;
|
use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, BankEntity, BankItemEntity, BankName};
|
||||||
use crate::entity::item::{ItemEntityId, ItemEntity, ItemDetail, BankEntity, BankItemEntity, BankName};
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::entity::character::CharacterEntityId;
|
use crate::entity::character::CharacterEntityId;
|
||||||
use crate::entity::item::tool::Tool;
|
use crate::ship::items::state::ItemStateError;
|
||||||
use crate::ship::items::inventory::{InventoryItemHandle, InventoryItem};
|
use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult};
|
||||||
|
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
|
||||||
|
|
||||||
const BANK_CAPACITY: usize = 200;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub struct IndividualBankItem {
|
pub enum BankError {
|
||||||
pub entity_id: ItemEntityId,
|
#[error("bank full")]
|
||||||
pub item_id: ClientItemId,
|
BankFull,
|
||||||
pub item: ItemDetail,
|
#[error("stack full")]
|
||||||
}
|
StackFull,
|
||||||
|
#[error("meseta full")]
|
||||||
#[derive(Debug, Clone)]
|
MesetaFull,
|
||||||
pub struct StackedBankItem {
|
|
||||||
pub entity_ids: Vec<ItemEntityId>,
|
|
||||||
pub item_id: ClientItemId,
|
|
||||||
pub tool: Tool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StackedBankItem {
|
|
||||||
pub fn count(&self) -> usize {
|
|
||||||
self.entity_ids.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn take_entity_ids(&mut self, amount: usize) -> Option<Vec<ItemEntityId>> {
|
|
||||||
if amount <= self.count() {
|
|
||||||
Some(self.entity_ids.drain(..amount).collect())
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum BankItem {
|
|
||||||
Individual(IndividualBankItem),
|
|
||||||
Stacked(StackedBankItem),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl std::cmp::PartialEq for BankItem {
|
#[derive(Clone, Debug)]
|
||||||
fn eq(&self, other: &BankItem) -> bool {
|
pub enum BankItemDetail {
|
||||||
let mut self_bytes = [0u8; 4];
|
Individual(IndividualItemDetail),
|
||||||
let mut other_bytes = [0u8; 4];
|
Stacked(StackedItemDetail),
|
||||||
self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]);
|
|
||||||
other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]);
|
|
||||||
|
|
||||||
let self_value = u32::from_be_bytes(self_bytes);
|
|
||||||
let other_value = u32::from_be_bytes(other_bytes);
|
|
||||||
|
|
||||||
self_value.eq(&other_value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::cmp::Eq for BankItem {}
|
impl BankItemDetail {
|
||||||
|
fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> {
|
||||||
impl std::cmp::PartialOrd for BankItem {
|
|
||||||
fn partial_cmp(&self, other: &BankItem) -> Option<std::cmp::Ordering> {
|
|
||||||
//let self_bytes = self.as_client_bytes();
|
|
||||||
//let other_bytes = other.as_client_bytes();
|
|
||||||
let mut self_bytes = [0u8; 4];
|
|
||||||
let mut other_bytes = [0u8; 4];
|
|
||||||
self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]);
|
|
||||||
other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]);
|
|
||||||
|
|
||||||
|
|
||||||
let self_value = u32::from_be_bytes(self_bytes);
|
|
||||||
let other_value = u32::from_be_bytes(other_bytes);
|
|
||||||
|
|
||||||
self_value.partial_cmp(&other_value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::cmp::Ord for BankItem {
|
|
||||||
fn cmp(&self, other: &BankItem) -> std::cmp::Ordering {
|
|
||||||
//let self_bytes = self.as_client_bytes();
|
|
||||||
//let other_bytes = other.as_client_bytes();
|
|
||||||
let mut self_bytes = [0u8; 4];
|
|
||||||
let mut other_bytes = [0u8; 4];
|
|
||||||
self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]);
|
|
||||||
other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]);
|
|
||||||
|
|
||||||
|
|
||||||
let self_value = u32::from_le_bytes(self_bytes);
|
|
||||||
let other_value = u32::from_le_bytes(other_bytes);
|
|
||||||
|
|
||||||
self_value.cmp(&other_value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BankItem {
|
|
||||||
pub fn set_item_id(&mut self, item_id: ClientItemId) {
|
|
||||||
match self {
|
match self {
|
||||||
BankItem::Individual(individual_bank_item) => {
|
BankItemDetail::Stacked(sitem) => Some(sitem),
|
||||||
individual_bank_item.item_id = item_id
|
_ => None,
|
||||||
},
|
|
||||||
BankItem::Stacked(stacked_bank_item) => {
|
|
||||||
stacked_bank_item.item_id = item_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn item_id(&self) -> ClientItemId {
|
|
||||||
match self {
|
|
||||||
BankItem::Individual(individual_bank_item) => {
|
|
||||||
individual_bank_item.item_id
|
|
||||||
},
|
|
||||||
BankItem::Stacked(stacked_bank_item) => {
|
|
||||||
stacked_bank_item.item_id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_client_bytes(&self) -> [u8; 16] {
|
pub fn as_client_bytes(&self) -> [u8; 16] {
|
||||||
match self {
|
match self {
|
||||||
BankItem::Individual(item) => {
|
BankItemDetail::Individual(item) => {
|
||||||
match &item.item {
|
match &item.item {
|
||||||
ItemDetail::Weapon(w) => w.as_bytes(),
|
ItemDetail::Weapon(w) => w.as_bytes(),
|
||||||
ItemDetail::Armor(a) => a.as_bytes(),
|
ItemDetail::Armor(a) => a.as_bytes(),
|
||||||
@ -130,7 +49,7 @@ impl BankItem {
|
|||||||
ItemDetail::ESWeapon(e) => e.as_bytes(),
|
ItemDetail::ESWeapon(e) => e.as_bytes(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BankItem::Stacked(item) => {
|
BankItemDetail::Stacked(item) => {
|
||||||
item.tool.as_stacked_bytes(item.entity_ids.len())
|
item.tool.as_stacked_bytes(item.entity_ids.len())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -138,92 +57,206 @@ impl BankItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct BankItemHandle<'a> {
|
#[derive(Clone, Debug)]
|
||||||
bank: &'a mut CharacterBank,
|
pub struct BankItem {
|
||||||
index: usize
|
pub item_id: ClientItemId,
|
||||||
|
pub item: BankItemDetail,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BankItemHandle<'a> {
|
impl BankItem {
|
||||||
pub fn item(&'a self) -> Option<&'a BankItem> {
|
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
|
||||||
self.bank.items.get(self.index)
|
where
|
||||||
}
|
F: FnMut(T, ItemEntityId) -> Fut,
|
||||||
|
Fut: Future<Output=Result<T, ItemStateError>>,
|
||||||
pub fn item_mut(&mut self) -> Option<&mut BankItem> {
|
{
|
||||||
self.bank.items.get_mut(self.index)
|
match &self.item {
|
||||||
}
|
BankItemDetail::Individual(individual_item) => {
|
||||||
|
param = func(param, individual_item.entity_id).await?;
|
||||||
pub fn remove_from_bank(self) {
|
},
|
||||||
self.bank.items.remove(self.index);
|
BankItemDetail::Stacked(stacked_item) => {
|
||||||
}
|
for entity_id in &stacked_item.entity_ids {
|
||||||
}
|
param = func(param, *entity_id).await?;
|
||||||
|
}
|
||||||
pub struct CharacterBank {
|
}
|
||||||
item_id_counter: u32,
|
|
||||||
items: Vec<BankItem>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CharacterBank {
|
|
||||||
pub fn new(mut items: Vec<BankItem>) -> CharacterBank {
|
|
||||||
items.sort();
|
|
||||||
CharacterBank {
|
|
||||||
item_id_counter: 0,
|
|
||||||
items,
|
|
||||||
}
|
}
|
||||||
|
Ok(param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Bank(Vec<BankItem>);
|
||||||
|
|
||||||
|
impl Bank {
|
||||||
|
pub fn new(items: Vec<BankItem>) -> Bank {
|
||||||
|
Bank(items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BankState {
|
||||||
|
pub character_id: CharacterEntityId,
|
||||||
|
pub item_id_counter: u32,
|
||||||
|
pub name: BankName,
|
||||||
|
pub bank: Bank,
|
||||||
|
pub meseta: Meseta,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BankState {
|
||||||
|
pub fn new(character_id: CharacterEntityId, name: BankName, mut bank: Bank, meseta: Meseta) -> BankState {
|
||||||
|
bank.0.sort();
|
||||||
|
BankState {
|
||||||
|
character_id,
|
||||||
|
item_id_counter: 0,
|
||||||
|
name,
|
||||||
|
bank,
|
||||||
|
meseta,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
self.bank.0.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize_item_ids(&mut self, base_item_id: u32) {
|
pub fn initialize_item_ids(&mut self, base_item_id: u32) {
|
||||||
for (i, item) in self.items.iter_mut().enumerate() {
|
for (i, item) in self.bank.0.iter_mut().enumerate() {
|
||||||
item.set_item_id(ClientItemId(base_item_id + i as u32));
|
item.item_id = ClientItemId(base_item_id + i as u32);
|
||||||
}
|
}
|
||||||
self.item_id_counter = base_item_id + self.items.len() as u32 + 1;
|
self.item_id_counter = base_item_id + self.bank.0.len() as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option<BankItemHandle> {
|
pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
|
||||||
let (index, _) = self.items.iter()
|
if self.meseta.0 + amount > 999999 {
|
||||||
.enumerate()
|
return Err(ItemStateError::FullOfMeseta)
|
||||||
.find(|(_, item)| {
|
}
|
||||||
item.item_id() == item_id
|
self.meseta.0 += amount;
|
||||||
})?;
|
Ok(())
|
||||||
Some(BankItemHandle {
|
}
|
||||||
bank: self,
|
|
||||||
index,
|
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
|
||||||
})
|
if amount > self.meseta.0 {
|
||||||
|
return Err(ItemStateError::InvalidMesetaRemoval(amount))
|
||||||
|
}
|
||||||
|
self.meseta.0 -= amount;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result<AddItemResult, BankError> {
|
||||||
|
match item.item {
|
||||||
|
InventoryItemDetail::Individual(iitem) => {
|
||||||
|
if self.bank.0.len() >= 30 {
|
||||||
|
Err(BankError::BankFull)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.bank.0.push(BankItem {
|
||||||
|
item_id: item.item_id,
|
||||||
|
item: BankItemDetail::Individual(iitem)
|
||||||
|
});
|
||||||
|
self.bank.0.sort();
|
||||||
|
Ok(AddItemResult::NewItem)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
InventoryItemDetail::Stacked(sitem) => {
|
||||||
|
let existing_stack = self.bank.0
|
||||||
|
.iter_mut()
|
||||||
|
.filter_map(|item| item.item.stacked_mut())
|
||||||
|
.find(|item| {
|
||||||
|
item.tool == sitem.tool
|
||||||
|
});
|
||||||
|
match existing_stack {
|
||||||
|
Some(existing_stack) => {
|
||||||
|
if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
|
||||||
|
Err(BankError::StackFull)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
|
||||||
|
Ok(AddItemResult::AddToStack)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
if self.bank.0.len() >= 30 {
|
||||||
|
Err(BankError::BankFull)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.bank.0.push(BankItem {
|
||||||
|
item_id: item.item_id,
|
||||||
|
item: BankItemDetail::Stacked(sitem)
|
||||||
|
});
|
||||||
|
self.bank.0.sort();
|
||||||
|
Ok(AddItemResult::NewItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option<BankItem> {
|
||||||
|
let idx = self.bank.0
|
||||||
|
.iter()
|
||||||
|
.position(|i| i.item_id == *item_id)?;
|
||||||
|
match &mut self.bank.0[idx].item {
|
||||||
|
BankItemDetail::Individual(_individual_item) => {
|
||||||
|
Some(self.bank.0.remove(idx))
|
||||||
|
},
|
||||||
|
BankItemDetail::Stacked(stacked_item) => {
|
||||||
|
let remove_all = match stacked_item.entity_ids.len().cmp(&(amount as usize)) {
|
||||||
|
Ordering::Equal => true,
|
||||||
|
Ordering::Greater => false,
|
||||||
|
Ordering::Less => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if remove_all {
|
||||||
|
Some(self.bank.0.remove(idx))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect();
|
||||||
|
self.item_id_counter += 1;
|
||||||
|
Some(BankItem {
|
||||||
|
item_id: ClientItemId(self.item_id_counter),
|
||||||
|
item: BankItemDetail::Stacked(StackedItemDetail {
|
||||||
|
entity_ids,
|
||||||
|
tool: stacked_item.tool,
|
||||||
|
})})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_client_bank_items(&self) -> character::Bank {
|
pub fn as_client_bank_items(&self) -> character::Bank {
|
||||||
self.items.iter()
|
self.bank.0.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.fold(character::Bank::default(), |mut bank, (slot, item)| {
|
.fold(character::Bank::default(), |mut bank, (slot, item)| {
|
||||||
bank.item_count = (slot + 1) as u32;
|
bank.item_count = (slot + 1) as u32;
|
||||||
let bytes = item.as_client_bytes();
|
let bytes = item.item.as_client_bytes();
|
||||||
bank.items[slot].data1.copy_from_slice(&bytes[0..12]);
|
bank.items[slot].data1.copy_from_slice(&bytes[0..12]);
|
||||||
bank.items[slot].data2.copy_from_slice(&bytes[12..16]);
|
bank.items[slot].data2.copy_from_slice(&bytes[12..16]);
|
||||||
bank.items[slot].item_id = item.item_id().0;
|
bank.items[slot].item_id = item.item_id.0;
|
||||||
|
|
||||||
bank
|
bank
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_client_bank_request(&self) -> Vec<character::BankItem> {
|
pub fn as_client_bank_request(&self) -> Vec<character::BankItem> {
|
||||||
self.items.iter()
|
self.bank.0.iter()
|
||||||
.map(|item| {
|
.map(|item| {
|
||||||
let bytes = item.as_client_bytes();
|
let bytes = item.item.as_client_bytes();
|
||||||
let mut data1 = [0; 12];
|
let mut data1 = [0; 12];
|
||||||
let mut data2 = [0; 4];
|
let mut data2 = [0; 4];
|
||||||
data1.copy_from_slice(&bytes[0..12]);
|
data1.copy_from_slice(&bytes[0..12]);
|
||||||
data2.copy_from_slice(&bytes[12..16]);
|
data2.copy_from_slice(&bytes[12..16]);
|
||||||
let amount = match item {
|
let amount = match &item.item {
|
||||||
BankItem::Individual(_individual_bank_item) => {
|
BankItemDetail::Individual(_individual_bank_item) => {
|
||||||
1
|
1
|
||||||
},
|
},
|
||||||
BankItem::Stacked(stacked_bank_item) => {
|
BankItemDetail::Stacked(stacked_bank_item) => {
|
||||||
stacked_bank_item.count()
|
stacked_bank_item.count()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
character::BankItem {
|
character::BankItem {
|
||||||
data1,
|
data1,
|
||||||
data2,
|
data2,
|
||||||
item_id: item.item_id().0,
|
item_id: item.item_id.0,
|
||||||
amount: amount as u16,
|
amount: amount as u16,
|
||||||
flags: 1,
|
flags: 1,
|
||||||
}
|
}
|
||||||
@ -231,80 +264,18 @@ impl CharacterBank {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count(&self) -> usize {
|
pub fn as_bank_entity(&self) -> BankEntity {
|
||||||
self.items.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deposit_item(&mut self, mut inventory_item: InventoryItemHandle, amount: usize) -> Option<&BankItem> {
|
|
||||||
let remove = match inventory_item.item_mut()? {
|
|
||||||
InventoryItem::Individual(individual_inventory_item) => {
|
|
||||||
if self.items.len() >= BANK_CAPACITY {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
self.items.push(BankItem::Individual(IndividualBankItem {
|
|
||||||
entity_id: individual_inventory_item.entity_id,
|
|
||||||
item_id: individual_inventory_item.item_id,
|
|
||||||
item: individual_inventory_item.item.clone(),
|
|
||||||
}));
|
|
||||||
true
|
|
||||||
},
|
|
||||||
InventoryItem::Stacked(stacked_inventory_item) => {
|
|
||||||
let existing_bank_item = self.items.iter_mut()
|
|
||||||
.find_map(|item| {
|
|
||||||
if let BankItem::Stacked(stacked_bank_item) = item {
|
|
||||||
if stacked_bank_item.tool == stacked_inventory_item.tool {
|
|
||||||
return Some(stacked_bank_item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
});
|
|
||||||
|
|
||||||
match existing_bank_item {
|
|
||||||
Some(stacked_bank_item) => {
|
|
||||||
if stacked_bank_item.count() + stacked_inventory_item.count() > stacked_inventory_item.tool.max_stack() {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut deposited_entity_ids = stacked_inventory_item.take_entity_ids(amount)?;
|
|
||||||
stacked_bank_item.entity_ids.append(&mut deposited_entity_ids);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
if self.items.len() >= BANK_CAPACITY {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
let deposited_entity_ids = stacked_inventory_item.take_entity_ids(amount)?;
|
|
||||||
|
|
||||||
self.item_id_counter += 1;
|
|
||||||
self.items.push(BankItem::Stacked(StackedBankItem {
|
|
||||||
entity_ids: deposited_entity_ids,
|
|
||||||
item_id: ClientItemId(self.item_id_counter),
|
|
||||||
tool: stacked_inventory_item.tool,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stacked_inventory_item.count() == 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if remove {
|
|
||||||
inventory_item.remove_from_inventory();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.items.last()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_bank_entity(&self, _character_id: &CharacterEntityId, _bank_name: &BankName) -> BankEntity {
|
|
||||||
BankEntity {
|
BankEntity {
|
||||||
items: self.items.iter()
|
items: self.bank.0.iter()
|
||||||
.map(|item| {
|
.map(|item| {
|
||||||
match item {
|
match &item.item {
|
||||||
BankItem::Individual(item) => {
|
BankItemDetail::Individual(item) => {
|
||||||
BankItemEntity::Individual(ItemEntity {
|
BankItemEntity::Individual(ItemEntity {
|
||||||
id: item.entity_id,
|
id: item.entity_id,
|
||||||
item: item.item.clone(),
|
item: item.item.clone(),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
BankItem::Stacked(items) => {
|
BankItemDetail::Stacked(items) => {
|
||||||
BankItemEntity::Stacked(items.entity_ids.iter()
|
BankItemEntity::Stacked(items.entity_ids.iter()
|
||||||
.map(|id| {
|
.map(|id| {
|
||||||
ItemEntity {
|
ItemEntity {
|
||||||
@ -321,4 +292,49 @@ impl CharacterBank {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::cmp::PartialEq for BankItem {
|
||||||
|
fn eq(&self, other: &BankItem) -> bool {
|
||||||
|
let mut self_bytes = [0u8; 4];
|
||||||
|
let mut other_bytes = [0u8; 4];
|
||||||
|
self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
|
||||||
|
other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
|
||||||
|
|
||||||
|
let self_value = u32::from_be_bytes(self_bytes);
|
||||||
|
let other_value = u32::from_be_bytes(other_bytes);
|
||||||
|
|
||||||
|
self_value.eq(&other_value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::Eq for BankItem {}
|
||||||
|
|
||||||
|
impl std::cmp::PartialOrd for BankItem {
|
||||||
|
fn partial_cmp(&self, other: &BankItem) -> Option<std::cmp::Ordering> {
|
||||||
|
let mut self_bytes = [0u8; 4];
|
||||||
|
let mut other_bytes = [0u8; 4];
|
||||||
|
self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
|
||||||
|
other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
|
||||||
|
|
||||||
|
|
||||||
|
let self_value = u32::from_be_bytes(self_bytes);
|
||||||
|
let other_value = u32::from_be_bytes(other_bytes);
|
||||||
|
|
||||||
|
self_value.partial_cmp(&other_value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::Ord for BankItem {
|
||||||
|
fn cmp(&self, other: &BankItem) -> std::cmp::Ordering {
|
||||||
|
let mut self_bytes = [0u8; 4];
|
||||||
|
let mut other_bytes = [0u8; 4];
|
||||||
|
self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]);
|
||||||
|
other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]);
|
||||||
|
|
||||||
|
|
||||||
|
let self_value = u32::from_le_bytes(self_bytes);
|
||||||
|
let other_value = u32::from_le_bytes(other_bytes);
|
||||||
|
|
||||||
|
self_value.cmp(&other_value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,253 +1,139 @@
|
|||||||
use crate::ship::items::ClientItemId;
|
use crate::ship::items::ClientItemId;
|
||||||
use crate::entity::item::{ItemEntityId, ItemDetail};
|
use crate::entity::item::{Meseta, ItemEntityId, ItemDetail};
|
||||||
use crate::entity::item::Meseta;
|
use std::future::Future;
|
||||||
use crate::entity::item::tool::Tool;
|
|
||||||
use crate::ship::map::MapArea;
|
use crate::ship::map::MapArea;
|
||||||
use crate::ship::items::inventory::{IndividualInventoryItem, StackedInventoryItem, InventoryItemHandle};
|
use crate::entity::character::CharacterEntityId;
|
||||||
|
use crate::entity::item::mag::Mag;
|
||||||
|
|
||||||
|
use crate::ship::items::state::ItemStateError;
|
||||||
|
use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail};
|
||||||
|
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
|
||||||
|
|
||||||
|
pub enum FloorType {
|
||||||
|
Local,
|
||||||
|
Shared,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct IndividualFloorItem {
|
pub enum FloorItemDetail {
|
||||||
pub entity_id: ItemEntityId,
|
Individual(IndividualItemDetail),
|
||||||
|
Stacked(StackedItemDetail),
|
||||||
|
Meseta(Meseta),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FloorItem {
|
||||||
pub item_id: ClientItemId,
|
pub item_id: ClientItemId,
|
||||||
pub item: ItemDetail,
|
pub item: FloorItemDetail,
|
||||||
pub map_area: MapArea,
|
pub map_area: MapArea,
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
pub z: f32,
|
pub z: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct StackedFloorItem {
|
|
||||||
pub entity_ids: Vec<ItemEntityId>,
|
|
||||||
pub item_id: ClientItemId,
|
|
||||||
pub tool: Tool,
|
|
||||||
pub map_area: MapArea,
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
pub z: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StackedFloorItem {
|
|
||||||
pub fn count(&self) -> usize {
|
|
||||||
self.entity_ids.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_client_bytes(&self) -> [u8; 16] {
|
|
||||||
self.tool.as_stacked_bytes(self.count())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct MesetaFloorItem {
|
|
||||||
pub item_id: ClientItemId,
|
|
||||||
pub meseta: Meseta,
|
|
||||||
pub map_area: MapArea,
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
pub z: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum FloorItem {
|
|
||||||
Individual(IndividualFloorItem),
|
|
||||||
Stacked(StackedFloorItem),
|
|
||||||
Meseta(MesetaFloorItem),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloorItem {
|
impl FloorItem {
|
||||||
pub fn item_id(&self) -> ClientItemId {
|
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
|
||||||
match self {
|
where
|
||||||
FloorItem::Individual(individual_floor_item) => {
|
F: FnMut(T, ItemEntityId) -> Fut,
|
||||||
individual_floor_item.item_id
|
Fut: Future<Output=Result<T, ItemStateError>>,
|
||||||
|
{
|
||||||
|
match &self.item {
|
||||||
|
FloorItemDetail::Individual(individual_item) => {
|
||||||
|
param = func(param, individual_item.entity_id).await?;
|
||||||
},
|
},
|
||||||
FloorItem::Stacked(stacked_floor_item) => {
|
FloorItemDetail::Stacked(stacked_item) => {
|
||||||
stacked_floor_item.item_id
|
for entity_id in &stacked_item.entity_ids {
|
||||||
|
param = func(param, *entity_id).await?;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
FloorItem::Meseta(meseta_floor_item) => {
|
FloorItemDetail::Meseta(_meseta) => {},
|
||||||
meseta_floor_item.item_id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(param)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn x(&self) -> f32 {
|
pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
|
||||||
match self {
|
where
|
||||||
FloorItem::Individual(individual_floor_item) => {
|
F: FnMut(T, ItemEntityId, Mag) -> Fut,
|
||||||
individual_floor_item.x
|
Fut: Future<Output=Result<T, ItemStateError>>,
|
||||||
},
|
{
|
||||||
FloorItem::Stacked(stacked_floor_item) => {
|
if let FloorItemDetail::Individual(individual_item) = &self.item {
|
||||||
stacked_floor_item.x
|
if let ItemDetail::Mag(mag) = &individual_item.item {
|
||||||
},
|
param = func(param, individual_item.entity_id, mag.clone()).await?;
|
||||||
FloorItem::Meseta(meseta_floor_item) => {
|
|
||||||
meseta_floor_item.x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn y(&self) -> f32 {
|
|
||||||
match self {
|
|
||||||
FloorItem::Individual(individual_floor_item) => {
|
|
||||||
individual_floor_item.y
|
|
||||||
},
|
|
||||||
FloorItem::Stacked(stacked_floor_item) => {
|
|
||||||
stacked_floor_item.y
|
|
||||||
},
|
|
||||||
FloorItem::Meseta(meseta_floor_item) => {
|
|
||||||
meseta_floor_item.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn z(&self) -> f32 {
|
|
||||||
match self {
|
|
||||||
FloorItem::Individual(individual_floor_item) => {
|
|
||||||
individual_floor_item.z
|
|
||||||
},
|
|
||||||
FloorItem::Stacked(stacked_floor_item) => {
|
|
||||||
stacked_floor_item.z
|
|
||||||
},
|
|
||||||
FloorItem::Meseta(meseta_floor_item) => {
|
|
||||||
meseta_floor_item.z
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map_area(&self) -> MapArea {
|
|
||||||
match self {
|
|
||||||
FloorItem::Individual(individual_floor_item) => {
|
|
||||||
individual_floor_item.map_area
|
|
||||||
},
|
|
||||||
FloorItem::Stacked(stacked_floor_item) => {
|
|
||||||
stacked_floor_item.map_area
|
|
||||||
},
|
|
||||||
FloorItem::Meseta(meseta_floor_item) => {
|
|
||||||
meseta_floor_item.map_area
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(param)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_client_bytes(&self) -> [u8; 16] {
|
pub fn as_client_bytes(&self) -> [u8; 16] {
|
||||||
match self {
|
match &self.item {
|
||||||
FloorItem::Individual(individual_floor_item) => {
|
FloorItemDetail::Individual(individual_floor_item) => {
|
||||||
individual_floor_item.item.as_client_bytes()
|
individual_floor_item.item.as_client_bytes()
|
||||||
},
|
},
|
||||||
FloorItem::Stacked(stacked_floor_item) => {
|
FloorItemDetail::Stacked(stacked_floor_item) => {
|
||||||
stacked_floor_item.as_client_bytes()
|
stacked_floor_item.tool.as_stacked_bytes(stacked_floor_item.entity_ids.len())
|
||||||
},
|
},
|
||||||
FloorItem::Meseta(meseta_floor_item) => {
|
FloorItemDetail::Meseta(meseta_floor_item) => {
|
||||||
meseta_floor_item.meseta.as_bytes()
|
meseta_floor_item.as_bytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct LocalFloor(pub Vec<FloorItem>);
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct SharedFloor(pub Vec<FloorItem>);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct FloorItemHandle<'a> {
|
pub struct FloorState {
|
||||||
floor: &'a mut RoomFloorItems,
|
pub character_id: CharacterEntityId,
|
||||||
index: usize,
|
pub local: LocalFloor,
|
||||||
|
pub shared: SharedFloor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FloorItemHandle<'a> {
|
impl FloorState {
|
||||||
pub fn item(&'a self) -> Option<&'a FloorItem> {
|
pub fn take_item(&mut self, item_id: &ClientItemId) -> Option<FloorItem> {
|
||||||
self.floor.0.get(self.index)
|
let item = self.local.0
|
||||||
}
|
.drain_filter(|item| {
|
||||||
|
item.item_id == *item_id
|
||||||
pub fn remove_from_floor(self) {
|
})
|
||||||
self.floor.0.remove(self.index);
|
.next();
|
||||||
}
|
item.or_else(|| {
|
||||||
}
|
self.shared.0
|
||||||
|
.drain_filter(|item| {
|
||||||
// TODO: floors should keep track of their own item_ids
|
item.item_id == *item_id
|
||||||
#[derive(Debug, Default)]
|
})
|
||||||
pub struct RoomFloorItems(Vec<FloorItem>);
|
.next()
|
||||||
|
|
||||||
impl RoomFloorItems {
|
|
||||||
pub fn add_item(&mut self, item: FloorItem) {
|
|
||||||
self.0.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_item(&mut self, item_id: &ClientItemId) {
|
|
||||||
self.0.retain(|item| item.item_id() != *item_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: &ClientItemId
|
|
||||||
pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&FloorItem> {
|
|
||||||
self.0.iter().find(|item| item.item_id() == item_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: &ClientItemId
|
|
||||||
pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option<FloorItemHandle> {
|
|
||||||
let index = self.0.iter().position(|item| item.item_id() == item_id)?;
|
|
||||||
Some(FloorItemHandle {
|
|
||||||
floor: self,
|
|
||||||
index,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_item_by_id(&mut self, item_id: ClientItemId) -> Option<FloorItem> {
|
pub fn add_inventory_item(&mut self, inventory_item: InventoryItem, map_area: MapArea, position: (f32, f32, f32)) -> &FloorItem {
|
||||||
self.0
|
let floor_item = FloorItem {
|
||||||
.drain_filter(|i| i.item_id() == item_id)
|
item_id: inventory_item.item_id,
|
||||||
.next()
|
item: match inventory_item.item {
|
||||||
|
InventoryItemDetail::Individual(individual_item) => FloorItemDetail::Individual(individual_item),
|
||||||
|
InventoryItemDetail::Stacked(stacked_item) => FloorItemDetail::Stacked(stacked_item),
|
||||||
|
},
|
||||||
|
map_area,
|
||||||
|
x: position.0,
|
||||||
|
y: position.1,
|
||||||
|
z: position.2,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.shared.0.push(floor_item);
|
||||||
|
&self.shared.0[self.shared.0.len()-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drop_individual_inventory_item(&mut self, individual_inventory_item: IndividualInventoryItem, item_drop_location: (MapArea, f32, f32, f32)) -> &IndividualFloorItem {
|
pub fn add_shared_item(&mut self, floor_item: FloorItem) -> &FloorItem {
|
||||||
self.0.push(FloorItem::Individual(IndividualFloorItem {
|
self.shared.0.push(floor_item);
|
||||||
entity_id: individual_inventory_item.entity_id,
|
&self.shared.0[self.shared.0.len()-1]
|
||||||
item_id: individual_inventory_item.item_id,
|
|
||||||
item: individual_inventory_item.item,
|
|
||||||
map_area: item_drop_location.0,
|
|
||||||
x: item_drop_location.1,
|
|
||||||
y: item_drop_location.2,
|
|
||||||
z: item_drop_location.3,
|
|
||||||
}));
|
|
||||||
|
|
||||||
match self.0.last().unwrap() {
|
|
||||||
FloorItem::Individual(item) => item,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drop_stacked_inventory_item(&mut self, stacked_inventory_item: StackedInventoryItem, item_drop_location: (MapArea, f32, f32, f32)) -> &StackedFloorItem {
|
pub fn add_local_item(&mut self, floor_item: FloorItem) -> &FloorItem {
|
||||||
self.0.push(FloorItem::Stacked(StackedFloorItem {
|
self.local.0.push(floor_item);
|
||||||
entity_ids: stacked_inventory_item.entity_ids,
|
&self.local.0[self.local.0.len()-1]
|
||||||
item_id: stacked_inventory_item.item_id,
|
|
||||||
tool: stacked_inventory_item.tool,
|
|
||||||
map_area: item_drop_location.0,
|
|
||||||
x: item_drop_location.1,
|
|
||||||
y: item_drop_location.2,
|
|
||||||
z: item_drop_location.3,
|
|
||||||
}));
|
|
||||||
|
|
||||||
match self.0.last().unwrap() {
|
|
||||||
FloorItem::Stacked(item) => item,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Result
|
|
||||||
// TODO: if consumed_item is not a tool items do not get placed back into inventory (should I care?)
|
|
||||||
pub fn drop_partial_stacked_inventory_item(&mut self, inventory_item: InventoryItemHandle, amount: usize, new_item_id: ClientItemId, item_drop_location: (MapArea, f32, f32, f32)) -> Option<&StackedFloorItem> {
|
|
||||||
let consumed_item = inventory_item.consume(amount).ok()?;
|
|
||||||
|
|
||||||
if let ItemDetail::Tool(tool) = consumed_item.item() {
|
|
||||||
self.0.push(FloorItem::Stacked(StackedFloorItem {
|
|
||||||
entity_ids: consumed_item.entity_ids(),
|
|
||||||
item_id: new_item_id,
|
|
||||||
tool,
|
|
||||||
map_area: item_drop_location.0,
|
|
||||||
x: item_drop_location.1,
|
|
||||||
y: item_drop_location.2,
|
|
||||||
z: item_drop_location.3,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.0.last().unwrap() {
|
|
||||||
FloorItem::Stacked(item) => Some(item),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
137
src/ship/items/itemstateaction.rs
Normal file
137
src/ship/items/itemstateaction.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait ItemAction {
|
||||||
|
type Input;
|
||||||
|
type Output;
|
||||||
|
type Start;
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error>;
|
||||||
|
async fn commit(&self, v: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct ItemStateAction<T, S, E> {
|
||||||
|
_t: std::marker::PhantomData<T>,
|
||||||
|
_s: std::marker::PhantomData<S>,
|
||||||
|
_e: std::marker::PhantomData<E>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S, E> Default for ItemStateAction<T, S, E> {
|
||||||
|
fn default() -> ItemStateAction<T, S, E> {
|
||||||
|
ItemStateAction {
|
||||||
|
_t: std::marker::PhantomData,
|
||||||
|
_s: std::marker::PhantomData,
|
||||||
|
_e: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S, E> ItemStateAction<T, S, E>
|
||||||
|
where
|
||||||
|
T: Send + Sync,
|
||||||
|
S: Send + Sync,
|
||||||
|
E: Send + Sync,
|
||||||
|
{
|
||||||
|
pub fn act<O, F, Fut>(self, f: F) -> ItemActionStage<O, ItemStateAction<T, S, E>, F, Fut, S, E>
|
||||||
|
where
|
||||||
|
F: Fn(S, ()) -> Fut + Send + Sync,
|
||||||
|
Fut: Future<Output=Result<(S, O), E>> + Send
|
||||||
|
{
|
||||||
|
ItemActionStage {
|
||||||
|
_s: Default::default(),
|
||||||
|
_e: std::marker::PhantomData,
|
||||||
|
prev: self,
|
||||||
|
actionf: f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ItemActionStage<O, P, F, Fut, S, E>
|
||||||
|
where
|
||||||
|
P: ItemAction,
|
||||||
|
F: Fn(S, P::Output) -> Fut + Send + Sync,
|
||||||
|
Fut: Future<Output=Result<(S, O) , E>> + Send,
|
||||||
|
{
|
||||||
|
_s: std::marker::PhantomData<S>,
|
||||||
|
_e: std::marker::PhantomData<E>,
|
||||||
|
prev: P,
|
||||||
|
actionf: F,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<O, P: ItemAction, F, Fut, S, E> ItemAction for ItemActionStage<O, P, F, Fut, S, E>
|
||||||
|
where
|
||||||
|
P: ItemAction + ItemAction<Start = S, Error = E> + Send + Sync,
|
||||||
|
F: Fn(S, P::Output) -> Fut + Send + Sync,
|
||||||
|
Fut: Future<Output=Result<(S, O), E>> + Send,
|
||||||
|
S: Send + Sync,
|
||||||
|
P::Output: Send + Sync,
|
||||||
|
E: Send + Sync,
|
||||||
|
O: Send + Sync,
|
||||||
|
P::Error: Send + Sync,
|
||||||
|
{
|
||||||
|
type Input = P::Output;
|
||||||
|
type Output = O;
|
||||||
|
type Start = S;
|
||||||
|
type Error = P::Error;
|
||||||
|
|
||||||
|
async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> {
|
||||||
|
(self.actionf)(s, i).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn commit(&self, i: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error> {
|
||||||
|
let (i, prev) = self.prev.commit(i).await?;
|
||||||
|
self.action(i, prev).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O, P: ItemAction, F, Fut, S, E> ItemActionStage<O, P, F, Fut, S, E>
|
||||||
|
where
|
||||||
|
P: ItemAction<Start = S, Error = E> + Send + Sync,
|
||||||
|
F: Fn(S, P::Output) -> Fut + Send + Sync,
|
||||||
|
Fut: Future<Output=Result<(S, O), E>> + Send,
|
||||||
|
S: Send + Sync,
|
||||||
|
P::Output: Send + Sync,
|
||||||
|
E: Send + Sync,
|
||||||
|
O: Send + Sync,
|
||||||
|
P::Error: Send + Sync,
|
||||||
|
{
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn act<O2, G, GFut>(self, g: G) -> ItemActionStage<O2, ItemActionStage<O, P, F, Fut, S, E>, G, GFut, S, E>
|
||||||
|
where
|
||||||
|
S: Send + Sync,
|
||||||
|
G: Fn(S, <ItemActionStage<O, P, F, Fut, S, E> as ItemAction>::Output) -> GFut + Send + Sync,
|
||||||
|
GFut: Future<Output=Result<(S, O2), E>> + Send,
|
||||||
|
O2: Send + Sync,
|
||||||
|
{
|
||||||
|
ItemActionStage {
|
||||||
|
_s: Default::default(),
|
||||||
|
_e: Default::default(),
|
||||||
|
prev: self,
|
||||||
|
actionf: g,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<T, S, E> ItemAction for ItemStateAction<T, S, E>
|
||||||
|
where
|
||||||
|
T: Send + Sync,
|
||||||
|
S: Send + Sync,
|
||||||
|
E: Send + Sync,
|
||||||
|
{
|
||||||
|
type Input = T;
|
||||||
|
type Output = ();
|
||||||
|
type Start = T;
|
||||||
|
type Error = E;
|
||||||
|
|
||||||
|
async fn action(&self, s: Self::Start, _i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> {
|
||||||
|
Ok((s, ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn commit(&self, i: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error> {
|
||||||
|
Ok((i, ()))
|
||||||
|
}
|
||||||
|
}
|
@ -6,9 +6,10 @@ use thiserror::Error;
|
|||||||
use crate::entity::gateway::{EntityGateway, GatewayError};
|
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||||
use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel};
|
use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel};
|
||||||
use crate::entity::item::{ItemDetail, ItemNote, BankName};
|
use crate::entity::item::{ItemDetail, ItemNote, BankName};
|
||||||
use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, BankItemEntity};
|
use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, BankItemEntity, EquippedEntity, ItemEntityId};
|
||||||
use crate::entity::item::tool::{Tool, ToolType};
|
use crate::entity::item::tool::{Tool, ToolType};
|
||||||
use crate::entity::item::weapon;
|
use crate::entity::item::weapon::{Weapon, WeaponModifier};
|
||||||
|
use crate::entity::item::unit::UnitModifier;
|
||||||
use crate::ship::map::MapArea;
|
use crate::ship::map::MapArea;
|
||||||
use crate::ship::ship::ItemDropLocation;
|
use crate::ship::ship::ItemDropLocation;
|
||||||
use crate::ship::trade::TradeItem;
|
use crate::ship::trade::TradeItem;
|
||||||
@ -20,8 +21,8 @@ use crate::ship::packet::handler::trade::{TradeError, OTHER_MESETA_ITEM_ID};
|
|||||||
use crate::ship::items::bank::*;
|
use crate::ship::items::bank::*;
|
||||||
use crate::ship::items::floor::*;
|
use crate::ship::items::floor::*;
|
||||||
use crate::ship::items::inventory::*;
|
use crate::ship::items::inventory::*;
|
||||||
use crate::ship::items::use_tool;
|
|
||||||
use crate::ship::items::transaction::{ItemTransaction, ItemAction, TransactionError, TransactionCommitError};
|
use crate::ship::items::transaction::{ItemTransaction, ItemAction, TransactionError, TransactionCommitError};
|
||||||
|
use crate::ship::monster::MonsterType;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub enum FloorType {
|
pub enum FloorType {
|
||||||
@ -82,6 +83,9 @@ pub enum ItemManagerError {
|
|||||||
ItemTransactionAction(Box<dyn std::error::Error + Send + Sync>),
|
ItemTransactionAction(Box<dyn std::error::Error + Send + Sync>),
|
||||||
#[error("invalid trade")]
|
#[error("invalid trade")]
|
||||||
InvalidTrade,
|
InvalidTrade,
|
||||||
|
EntityIdNotInInventory(ItemEntityId),
|
||||||
|
WeaponCannotCombine,
|
||||||
|
NotEnoughKills(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> std::convert::From<TransactionError<E>> for ItemManagerError
|
impl<E> std::convert::From<TransactionError<E>> for ItemManagerError
|
||||||
@ -147,7 +151,7 @@ impl ItemManager {
|
|||||||
|
|
||||||
pub async fn load_character<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), anyhow::Error> {
|
pub async fn load_character<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), anyhow::Error> {
|
||||||
let inventory = entity_gateway.get_character_inventory(&character.id).await?;
|
let inventory = entity_gateway.get_character_inventory(&character.id).await?;
|
||||||
let bank = entity_gateway.get_character_bank(&character.id, BankName("".into())).await?;
|
let bank = entity_gateway.get_character_bank(&character.id, &BankName("".into())).await?;
|
||||||
let equipped = entity_gateway.get_character_equips(&character.id).await?;
|
let equipped = entity_gateway.get_character_equips(&character.id).await?;
|
||||||
|
|
||||||
let inventory_items = inventory.items.into_iter()
|
let inventory_items = inventory.items.into_iter()
|
||||||
@ -205,7 +209,7 @@ impl ItemManager {
|
|||||||
let character_bank = CharacterBank::new(bank_items);
|
let character_bank = CharacterBank::new(bank_items);
|
||||||
|
|
||||||
let character_meseta = entity_gateway.get_character_meseta(&character.id).await?;
|
let character_meseta = entity_gateway.get_character_meseta(&character.id).await?;
|
||||||
let bank_meseta = entity_gateway.get_bank_meseta(&character.id, BankName("".into())).await?;
|
let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &BankName("".into())).await?;
|
||||||
|
|
||||||
self.character_inventory.insert(character.id, character_inventory);
|
self.character_inventory.insert(character.id, character_inventory);
|
||||||
self.character_bank.insert(character.id, character_bank);
|
self.character_bank.insert(character.id, character_bank);
|
||||||
@ -388,7 +392,7 @@ impl ItemManager {
|
|||||||
.map_err(|err| err.into())
|
.map_err(|err| err.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn enemy_drop_item_on_local_floor<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity, item_drop: ItemDrop) -> Result<&FloorItem, anyhow::Error> {
|
pub async fn enemy_drop_item_on_local_floor<'a, EG: EntityGateway>(&'a mut self, entity_gateway: &'a mut EG, character: &'a CharacterEntity, item_drop: ItemDrop) -> Result<&'a FloorItem, anyhow::Error> {
|
||||||
let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
|
|
||||||
enum ItemOrMeseta {
|
enum ItemOrMeseta {
|
||||||
@ -492,6 +496,7 @@ impl ItemManager {
|
|||||||
entity_gateway.add_item_note(
|
entity_gateway.add_item_note(
|
||||||
&individual_floor_item.entity_id,
|
&individual_floor_item.entity_id,
|
||||||
ItemNote::PlayerDrop {
|
ItemNote::PlayerDrop {
|
||||||
|
character_id: character.id,
|
||||||
map_area: item_drop_location.0,
|
map_area: item_drop_location.0,
|
||||||
x: item_drop_location.1,
|
x: item_drop_location.1,
|
||||||
y: item_drop_location.2,
|
y: item_drop_location.2,
|
||||||
@ -505,6 +510,7 @@ impl ItemManager {
|
|||||||
entity_gateway.add_item_note(
|
entity_gateway.add_item_note(
|
||||||
entity_id,
|
entity_id,
|
||||||
ItemNote::PlayerDrop {
|
ItemNote::PlayerDrop {
|
||||||
|
character_id: character.id,
|
||||||
map_area: item_drop_location.0,
|
map_area: item_drop_location.0,
|
||||||
x: item_drop_location.1,
|
x: item_drop_location.1,
|
||||||
y: item_drop_location.2,
|
y: item_drop_location.2,
|
||||||
@ -548,14 +554,14 @@ impl ItemManager {
|
|||||||
Ok(floor_item)
|
Ok(floor_item)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn player_drops_partial_stack_on_shared_floor<EG: EntityGateway>(&mut self,
|
pub async fn player_drops_partial_stack_on_shared_floor<'a, EG: EntityGateway>(&'a mut self,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &'a mut EG,
|
||||||
character: &CharacterEntity,
|
character: &'a CharacterEntity,
|
||||||
//inventory_item: InventoryItem,
|
//inventory_item: InventoryItem,
|
||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
drop_location: ItemDropLocation,
|
drop_location: ItemDropLocation,
|
||||||
amount: usize)
|
amount: usize)
|
||||||
-> Result<&StackedFloorItem, anyhow::Error> {
|
-> Result<&'a StackedFloorItem, anyhow::Error> {
|
||||||
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
let shared_floor = self.room_floor.get_mut(room_id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
let shared_floor = self.room_floor.get_mut(room_id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
@ -570,6 +576,7 @@ impl ItemManager {
|
|||||||
entity_gateway.add_item_note(
|
entity_gateway.add_item_note(
|
||||||
entity_id,
|
entity_id,
|
||||||
ItemNote::PlayerDrop {
|
ItemNote::PlayerDrop {
|
||||||
|
character_id: character.id,
|
||||||
map_area: drop_location.map_area,
|
map_area: drop_location.map_area,
|
||||||
x: drop_location.x,
|
x: drop_location.x,
|
||||||
y: 0.0,
|
y: 0.0,
|
||||||
@ -622,16 +629,16 @@ impl ItemManager {
|
|||||||
let _bank_item = bank.deposit_item(item_to_deposit, amount).ok_or(ItemManagerError::Idunnoman)?;
|
let _bank_item = bank.deposit_item(item_to_deposit, amount).ok_or(ItemManagerError::Idunnoman)?;
|
||||||
|
|
||||||
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||||
entity_gateway.set_character_bank(&character.id, &bank.as_bank_entity(&character.id, &BankName("".into())), BankName("".into())).await?;
|
entity_gateway.set_character_bank(&character.id, &bank.as_bank_entity(&character.id, &BankName("".into())), &BankName("".into())).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn player_withdraws_item<EG: EntityGateway>(&mut self,
|
pub async fn player_withdraws_item<'a, EG: EntityGateway>(&'a mut self,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &'a mut EG,
|
||||||
character: &CharacterEntity,
|
character: &'a CharacterEntity,
|
||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
amount: usize)
|
amount: usize)
|
||||||
-> Result<&InventoryItem, anyhow::Error> {
|
-> Result<&'a InventoryItem, anyhow::Error> {
|
||||||
|
|
||||||
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
let bank = self.character_bank
|
let bank = self.character_bank
|
||||||
@ -645,7 +652,7 @@ impl ItemManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||||
entity_gateway.set_character_bank(&character.id, &bank.as_bank_entity(&character.id, &BankName("".into())), BankName("".into())).await?;
|
entity_gateway.set_character_bank(&character.id, &bank.as_bank_entity(&character.id, &BankName("".into())), &BankName("".into())).await?;
|
||||||
inventory.slot(inventory_item_slot).ok_or_else(|| ItemManagerError::Idunnoman.into())
|
inventory.slot(inventory_item_slot).ok_or_else(|| ItemManagerError::Idunnoman.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -695,7 +702,6 @@ impl ItemManager {
|
|||||||
match &used_item.item() {
|
match &used_item.item() {
|
||||||
ItemDetail::Weapon(_w) => {
|
ItemDetail::Weapon(_w) => {
|
||||||
// something like when items are used to combine/transform them?
|
// something like when items are used to combine/transform them?
|
||||||
//_ => {}
|
|
||||||
},
|
},
|
||||||
ItemDetail::Tool(t) => {
|
ItemDetail::Tool(t) => {
|
||||||
match t.tool {
|
match t.tool {
|
||||||
@ -792,7 +798,7 @@ impl ItemManager {
|
|||||||
ToolType::LibertaKit => {
|
ToolType::LibertaKit => {
|
||||||
use_tool::liberta_kit(entity_gateway, &used_item, inventory).await?;
|
use_tool::liberta_kit(entity_gateway, &used_item, inventory).await?;
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -801,13 +807,13 @@ impl ItemManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn player_buys_item<EG: EntityGateway>(&mut self,
|
pub async fn player_buys_item<'a, EG: EntityGateway>(&'a mut self,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &'a mut EG,
|
||||||
character: &CharacterEntity,
|
character: &'a CharacterEntity,
|
||||||
shop_item: &(dyn ShopItem + Send + Sync),
|
shop_item: &'a (dyn ShopItem + Send + Sync),
|
||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
amount: usize)
|
amount: usize)
|
||||||
-> Result<&InventoryItem, anyhow::Error> {
|
-> Result<&'a InventoryItem, anyhow::Error> {
|
||||||
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
|
|
||||||
let item_detail = shop_item.as_item();
|
let item_detail = shop_item.as_item();
|
||||||
@ -982,8 +988,8 @@ impl ItemManager {
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
tek: weapon::WeaponModifier)
|
tek: WeaponModifier)
|
||||||
-> Result<weapon::Weapon, anyhow::Error> {
|
-> Result<Weapon, anyhow::Error> {
|
||||||
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
|
|
||||||
let item = inventory.remove_by_id(item_id)
|
let item = inventory.remove_by_id(item_id)
|
||||||
@ -1191,6 +1197,42 @@ impl ItemManager {
|
|||||||
.await
|
.await
|
||||||
.map_err(|err| err.into())
|
.map_err(|err| err.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn increase_kill_counters<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity, equipped_items: &EquippedEntity, monstertype: MonsterType) -> Result<(), anyhow::Error> {
|
||||||
|
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
|
if let Some(weapon_entity) = equipped_items.weapon {
|
||||||
|
let weapon_id = inventory.get_item_by_entity_id(weapon_entity).ok_or(ItemManagerError::EntityIdNotInInventory(weapon_entity))?.item_id();
|
||||||
|
let mut weapon_handle = inventory.get_item_handle_by_id(weapon_id).ok_or(ItemManagerError::NoSuchItemId(weapon_id))?;
|
||||||
|
let individual_item_w = weapon_handle.item_mut()
|
||||||
|
.ok_or(ItemManagerError::NoSuchItemId(weapon_id))?
|
||||||
|
.individual_mut()
|
||||||
|
.ok_or(ItemManagerError::WrongItemType(weapon_id))?;
|
||||||
|
let weapon = individual_item_w
|
||||||
|
.weapon_mut()
|
||||||
|
.ok_or(ItemManagerError::WrongItemType(weapon_id))?;
|
||||||
|
|
||||||
|
let wmodifier = WeaponModifier::AddKill { enemy: monstertype };
|
||||||
|
weapon.apply_modifier(&wmodifier);
|
||||||
|
entity_gateway.add_weapon_modifier(&weapon_entity, wmodifier).await?;
|
||||||
|
}
|
||||||
|
for units in equipped_items.unit.iter().flatten() {
|
||||||
|
let unit_id = inventory.get_item_by_entity_id(*units).ok_or(ItemManagerError::EntityIdNotInInventory(*units))?.item_id();
|
||||||
|
let mut unit_handle = inventory.get_item_handle_by_id(unit_id).ok_or(ItemManagerError::NoSuchItemId(unit_id))?;
|
||||||
|
let individual_item_u = unit_handle.item_mut()
|
||||||
|
.ok_or(ItemManagerError::NoSuchItemId(unit_id))?
|
||||||
|
.individual_mut()
|
||||||
|
.ok_or(ItemManagerError::WrongItemType(unit_id))?;
|
||||||
|
let unit = individual_item_u
|
||||||
|
.unit_mut()
|
||||||
|
.ok_or(ItemManagerError::WrongItemType(unit_id))?;
|
||||||
|
|
||||||
|
let umodifier = UnitModifier::AddKill { enemy: monstertype };
|
||||||
|
unit.apply_modifier(&umodifier);
|
||||||
|
entity_gateway.add_unit_modifier(units, umodifier).await?;
|
||||||
|
}
|
||||||
|
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -1373,6 +1415,43 @@ impl<EG: EntityGateway> ItemAction<EG> for TradeMeseta {
|
|||||||
dest_meseta.0 += self.amount as u32;
|
dest_meseta.0 += self.amount as u32;
|
||||||
entity_gateway.set_character_meseta(&self.dest_character_id, *dest_meseta).await?;
|
entity_gateway.set_character_meseta(&self.dest_character_id, *dest_meseta).await?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn increase_kill_counters<EG: EntityGateway>( &mut self,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
equipped_items: &EquippedEntity)
|
||||||
|
-> Result<(), anyhow::Error> {
|
||||||
|
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
|
||||||
|
// weapon
|
||||||
|
if let Some(weapon_entity) = equipped_items.weapon {
|
||||||
|
let weapon_id = inventory.get_item_by_entity_id(weapon_entity).ok_or(ItemManagerError::EntityIdNotInInventory(weapon_entity))?.item_id();
|
||||||
|
let mut weapon_handle = inventory.get_item_handle_by_id(weapon_id).ok_or(ItemManagerError::NoSuchItemId(weapon_id))?;
|
||||||
|
let individual_item_w = weapon_handle.item_mut()
|
||||||
|
.ok_or(ItemManagerError::NoSuchItemId(weapon_id))?
|
||||||
|
.individual_mut()
|
||||||
|
.ok_or(ItemManagerError::WrongItemType(weapon_id))?;
|
||||||
|
let weapon = individual_item_w
|
||||||
|
.weapon_mut()
|
||||||
|
.ok_or(ItemManagerError::WrongItemType(weapon_id))?;
|
||||||
|
|
||||||
|
weapon.increment_kill_counter();
|
||||||
|
entity_gateway.increment_kill_counter(&weapon_entity).await?;
|
||||||
|
}
|
||||||
|
for units in equipped_items.unit.iter().flatten() {
|
||||||
|
let unit_id = inventory.get_item_by_entity_id(*units).ok_or(ItemManagerError::EntityIdNotInInventory(*units))?.item_id();
|
||||||
|
let mut unit_handle = inventory.get_item_handle_by_id(unit_id).ok_or(ItemManagerError::NoSuchItemId(unit_id))?;
|
||||||
|
let individual_item_u = unit_handle.item_mut()
|
||||||
|
.ok_or(ItemManagerError::NoSuchItemId(unit_id))?
|
||||||
|
.individual_mut()
|
||||||
|
.ok_or(ItemManagerError::WrongItemType(unit_id))?;
|
||||||
|
let unit = individual_item_u
|
||||||
|
.unit_mut()
|
||||||
|
.ok_or(ItemManagerError::WrongItemType(unit_id))?;
|
||||||
|
|
||||||
|
unit.increment_kill_counter();
|
||||||
|
}
|
||||||
|
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,11 @@
|
|||||||
mod bank;
|
pub mod state;
|
||||||
mod floor;
|
pub mod actions;
|
||||||
|
pub mod apply_item;
|
||||||
|
pub mod itemstateaction;
|
||||||
pub mod inventory;
|
pub mod inventory;
|
||||||
pub mod manager;
|
pub mod floor;
|
||||||
pub mod transaction;
|
pub mod bank;
|
||||||
pub mod use_tool;
|
pub mod tasks;
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, derive_more::Display)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)]
|
||||||
pub struct ClientItemId(pub u32);
|
pub struct ClientItemId(pub u32);
|
||||||
|
|
||||||
// TODO: remove these and fix use statements in the rest of the codebase
|
|
||||||
pub use inventory::*;
|
|
||||||
pub use floor::*;
|
|
||||||
pub use bank::*;
|
|
||||||
pub use manager::*;
|
|
||||||
|
|
||||||
|
|
||||||
|
391
src/ship/items/state.rs
Normal file
391
src/ship/items/state.rs
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::ship::items::ClientItemId;
|
||||||
|
use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankName};
|
||||||
|
|
||||||
|
use crate::ship::location::{AreaClient, RoomId};
|
||||||
|
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||||
|
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||||
|
use crate::entity::item::tool::Tool;
|
||||||
|
use crate::entity::item::weapon::Weapon;
|
||||||
|
use crate::entity::item::mag::Mag;
|
||||||
|
use crate::ship::drops::ItemDrop;
|
||||||
|
|
||||||
|
use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState};
|
||||||
|
use crate::ship::items::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType};
|
||||||
|
use crate::ship::items::bank::{Bank, BankState, BankItem, BankItemDetail, BankError};
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum ItemStateError {
|
||||||
|
#[error("character {0} not found")]
|
||||||
|
NoCharacter(CharacterEntityId),
|
||||||
|
#[error("room {0} not found")]
|
||||||
|
NoRoom(RoomId),
|
||||||
|
#[error("floor item {0} not found")]
|
||||||
|
NoFloorItem(ClientItemId),
|
||||||
|
|
||||||
|
#[error("expected {0} to be a tool")]
|
||||||
|
NotATool(ClientItemId),
|
||||||
|
|
||||||
|
#[error("bank item {0} not found")]
|
||||||
|
NoBankItem(ClientItemId),
|
||||||
|
|
||||||
|
#[error("inventory error {0}")]
|
||||||
|
InventoryError(#[from] InventoryError),
|
||||||
|
|
||||||
|
#[error("bank error {0}")]
|
||||||
|
BankError(#[from] BankError),
|
||||||
|
|
||||||
|
#[error("invalid item id {0}")]
|
||||||
|
InvalidItemId(ClientItemId),
|
||||||
|
|
||||||
|
#[error("invalid drop? {0:?} (this shouldn't occur)")]
|
||||||
|
BadItemDrop(ItemDrop),
|
||||||
|
|
||||||
|
#[error("idk")]
|
||||||
|
Dummy,
|
||||||
|
|
||||||
|
#[error("gateway")]
|
||||||
|
GatewayError(#[from] GatewayError),
|
||||||
|
|
||||||
|
#[error("tried to remove more meseta than exists: {0}")]
|
||||||
|
InvalidMesetaRemoval(u32),
|
||||||
|
|
||||||
|
#[error("tried to add meseta when there is no more room")]
|
||||||
|
FullOfMeseta,
|
||||||
|
|
||||||
|
#[error("stacked item")]
|
||||||
|
StackedItemError(Vec<ItemEntity>),
|
||||||
|
|
||||||
|
#[error("apply item {0}")]
|
||||||
|
ApplyItemError(#[from] crate::ship::items::apply_item::ApplyItemError),
|
||||||
|
|
||||||
|
#[error("item is not a mag {0}")]
|
||||||
|
NotAMag(ClientItemId),
|
||||||
|
|
||||||
|
#[error("item is not mag food {0}")]
|
||||||
|
NotMagFood(ClientItemId),
|
||||||
|
|
||||||
|
#[error("item is not sellable")]
|
||||||
|
ItemNotSellable,
|
||||||
|
|
||||||
|
#[error("could not modify item")]
|
||||||
|
InvalidModifier,
|
||||||
|
|
||||||
|
#[error("wrong item type ")]
|
||||||
|
WrongItemType(ClientItemId),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct IndividualItemDetail {
|
||||||
|
pub entity_id: ItemEntityId,
|
||||||
|
pub item: ItemDetail,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndividualItemDetail {
|
||||||
|
pub fn as_weapon(&self) -> Option<&Weapon> {
|
||||||
|
match &self.item {
|
||||||
|
ItemDetail::Weapon(weapon) => Some(weapon),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_mag(&self) -> Option<&Mag> {
|
||||||
|
match &self.item {
|
||||||
|
ItemDetail::Mag(mag) => Some(mag),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_mag_mut(&mut self) -> Option<&mut Mag> {
|
||||||
|
match &mut self.item {
|
||||||
|
ItemDetail::Mag(mag) => Some(mag),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_client_bytes(&self) -> [u8; 16] {
|
||||||
|
match &self.item {
|
||||||
|
ItemDetail::Weapon(w) => w.as_bytes(),
|
||||||
|
ItemDetail::Armor(a) => a.as_bytes(),
|
||||||
|
ItemDetail::Shield(s) => s.as_bytes(),
|
||||||
|
ItemDetail::Unit(u) => u.as_bytes(),
|
||||||
|
ItemDetail::Tool(t) => t.as_individual_bytes(),
|
||||||
|
ItemDetail::TechniqueDisk(d) => d.as_bytes(),
|
||||||
|
ItemDetail::Mag(m) => m.as_bytes(),
|
||||||
|
ItemDetail::ESWeapon(e) => e.as_bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct StackedItemDetail {
|
||||||
|
pub entity_ids: Vec<ItemEntityId>,
|
||||||
|
pub tool: Tool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackedItemDetail {
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
self.entity_ids.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum AddItemResult {
|
||||||
|
NewItem,
|
||||||
|
AddToStack,
|
||||||
|
Meseta,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct ItemState {
|
||||||
|
character_inventory: HashMap<CharacterEntityId, InventoryState>,
|
||||||
|
character_bank: HashMap<CharacterEntityId, BankState>,
|
||||||
|
|
||||||
|
character_room: HashMap<CharacterEntityId, RoomId>,
|
||||||
|
character_floor: HashMap<CharacterEntityId, LocalFloor>,
|
||||||
|
room_floor: HashMap<RoomId, SharedFloor>,
|
||||||
|
|
||||||
|
room_item_id_counter: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ItemState {
|
||||||
|
fn default() -> ItemState {
|
||||||
|
ItemState {
|
||||||
|
character_inventory: HashMap::new(),
|
||||||
|
character_bank: HashMap::new(),
|
||||||
|
character_room: HashMap::new(),
|
||||||
|
character_floor: HashMap::new(),
|
||||||
|
room_floor: HashMap::new(),
|
||||||
|
room_item_id_counter: 0x00810000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItemState {
|
||||||
|
pub fn get_character_inventory(&self, character: &CharacterEntity) -> Result<&InventoryState, ItemStateError> {
|
||||||
|
self.character_inventory.get(&character.id)
|
||||||
|
.ok_or(ItemStateError::NoCharacter(character.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_character_bank(&self, character: &CharacterEntity) -> Result<&BankState, ItemStateError> {
|
||||||
|
self.character_bank.get(&character.id)
|
||||||
|
.ok_or(ItemStateError::NoCharacter(character.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItemState {
|
||||||
|
fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> {
|
||||||
|
self.room_item_id_counter += 1;
|
||||||
|
Ok(ClientItemId(self.room_item_id_counter))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn load_character<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), ItemStateError> {
|
||||||
|
let inventory = entity_gateway.get_character_inventory(&character.id).await?;
|
||||||
|
let bank = entity_gateway.get_character_bank(&character.id, &BankName("".into())).await?;
|
||||||
|
let equipped = entity_gateway.get_character_equips(&character.id).await?;
|
||||||
|
|
||||||
|
let inventory_items = inventory.items.into_iter()
|
||||||
|
.map(|item| -> Result<InventoryItem, ItemStateError> {
|
||||||
|
Ok(match item {
|
||||||
|
InventoryItemEntity::Individual(item) => {
|
||||||
|
InventoryItem {
|
||||||
|
item_id: ClientItemId(0),
|
||||||
|
item: InventoryItemDetail::Individual(IndividualItemDetail {
|
||||||
|
entity_id: item.id,
|
||||||
|
item: item.item,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
InventoryItemEntity::Stacked(items) => {
|
||||||
|
InventoryItem {
|
||||||
|
item_id: ClientItemId(0),
|
||||||
|
item: InventoryItemDetail::Stacked(StackedItemDetail {
|
||||||
|
entity_ids: items.iter().map(|i| i.id).collect(),
|
||||||
|
tool: items.get(0)
|
||||||
|
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||||
|
.item
|
||||||
|
.clone()
|
||||||
|
.as_tool()
|
||||||
|
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
let character_meseta = entity_gateway.get_character_meseta(&character.id).await?;
|
||||||
|
let inventory_state = InventoryState {
|
||||||
|
character_id: character.id,
|
||||||
|
item_id_counter: 0,
|
||||||
|
inventory: Inventory::new(inventory_items),
|
||||||
|
equipped,
|
||||||
|
meseta: character_meseta,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bank_items = bank.items.into_iter()
|
||||||
|
.map(|item| -> Result<BankItem, ItemStateError> {
|
||||||
|
Ok(match item {
|
||||||
|
BankItemEntity::Individual(item) => {
|
||||||
|
BankItem {
|
||||||
|
item_id: self.new_item_id()?,
|
||||||
|
item: BankItemDetail::Individual(IndividualItemDetail {
|
||||||
|
entity_id: item.id,
|
||||||
|
item: item.item,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BankItemEntity::Stacked(items) => {
|
||||||
|
BankItem {
|
||||||
|
item_id: self.new_item_id()?,
|
||||||
|
item: BankItemDetail::Stacked(StackedItemDetail {
|
||||||
|
entity_ids: items.iter().map(|i| i.id).collect(),
|
||||||
|
tool: items.get(0)
|
||||||
|
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||||
|
.item
|
||||||
|
.clone()
|
||||||
|
.as_tool()
|
||||||
|
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &BankName("".into())).await?;
|
||||||
|
let bank_state = BankState::new(character.id, BankName("".into()), Bank::new(bank_items), bank_meseta);
|
||||||
|
|
||||||
|
self.character_inventory.insert(character.id, inventory_state);
|
||||||
|
self.character_bank.insert(character.id, bank_state);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) {
|
||||||
|
let base_inventory_id = ((area_client.local_client.id() as u32) << 21) | 0x10000;
|
||||||
|
let inventory = self.character_inventory.get_mut(&character.id).unwrap();
|
||||||
|
inventory.initialize_item_ids(base_inventory_id);
|
||||||
|
let base_bank_id = ((area_client.local_client.id() as u32) << 21) | 0x20000;
|
||||||
|
let default_bank = self.character_bank.get_mut(&character.id);
|
||||||
|
if let Some(default_bank ) = default_bank {
|
||||||
|
default_bank.initialize_item_ids(base_bank_id);
|
||||||
|
}
|
||||||
|
self.character_room.insert(character.id, room_id);
|
||||||
|
self.character_floor.insert(character.id, LocalFloor::default());
|
||||||
|
self.room_floor.entry(room_id).or_insert_with(SharedFloor::default);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_character_from_room(&mut self, character: &CharacterEntity) {
|
||||||
|
self.character_inventory.remove(&character.id);
|
||||||
|
self.character_floor.remove(&character.id);
|
||||||
|
if let Some(room) = self.character_room.remove(&character.id).as_ref() {
|
||||||
|
if self.character_room.iter().any(|(_, r)| r == room) {
|
||||||
|
self.room_floor.remove(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_floor_item(&self, character_id: &CharacterEntityId, item_id: &ClientItemId) -> Result<(&FloorItem, FloorType), ItemStateError> {
|
||||||
|
let local_floor = self.character_floor.get(character_id).ok_or(ItemStateError::NoCharacter(*character_id))?;
|
||||||
|
let room = self.character_room.get(character_id).ok_or(ItemStateError::NoCharacter(*character_id))?;
|
||||||
|
let shared_floor = self.room_floor.get(room).ok_or(ItemStateError::NoCharacter(*character_id))?;
|
||||||
|
|
||||||
|
local_floor.0
|
||||||
|
.iter()
|
||||||
|
.find(|item| item.item_id == *item_id)
|
||||||
|
.map(|item| (item, FloorType::Local))
|
||||||
|
.or_else(|| {
|
||||||
|
shared_floor.0
|
||||||
|
.iter()
|
||||||
|
.find(|item| item.item_id == *item_id)
|
||||||
|
.map(|item| (item, FloorType::Shared))
|
||||||
|
})
|
||||||
|
.ok_or(ItemStateError::NoFloorItem(*item_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ProxiedItemState {
|
||||||
|
character_inventory: HashMap<CharacterEntityId, InventoryState>,
|
||||||
|
character_bank: HashMap<CharacterEntityId, BankState>,
|
||||||
|
|
||||||
|
character_room: HashMap<CharacterEntityId, RoomId>,
|
||||||
|
character_floor: HashMap<CharacterEntityId, LocalFloor>,
|
||||||
|
room_floor: HashMap<RoomId, SharedFloor>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ItemStateProxy<'a> {
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
proxied_state: ProxiedItemState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ItemStateProxy<'a> {
|
||||||
|
pub fn commit(self) {
|
||||||
|
self.item_state.character_inventory.extend(self.proxied_state.character_inventory.clone());
|
||||||
|
self.item_state.character_bank.extend(self.proxied_state.character_bank.clone());
|
||||||
|
self.item_state.character_room.extend(self.proxied_state.character_room.clone());
|
||||||
|
self.item_state.character_floor.extend(self.proxied_state.character_floor.clone());
|
||||||
|
self.item_state.room_floor.extend(self.proxied_state.room_floor.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_or_clone<K, V>(master: &HashMap<K, V>, proxy: &mut HashMap<K, V>, key: K, err: fn(K) -> ItemStateError) -> Result<V, ItemStateError>
|
||||||
|
where
|
||||||
|
K: Eq + std::hash::Hash + Copy,
|
||||||
|
V: Clone
|
||||||
|
{
|
||||||
|
let existing_element = master.get(&key).ok_or_else(|| err(key))?;
|
||||||
|
Ok(proxy.entry(key)
|
||||||
|
.or_insert_with(|| existing_element.clone()).clone())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ItemStateProxy<'a> {
|
||||||
|
pub fn new(item_state: &'a mut ItemState) -> Self {
|
||||||
|
ItemStateProxy {
|
||||||
|
item_state,
|
||||||
|
proxied_state: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inventory(&mut self, character_id: &CharacterEntityId) -> Result<InventoryState, ItemStateError> {
|
||||||
|
get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, ItemStateError::NoCharacter)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_inventory(&mut self, inventory: InventoryState) {
|
||||||
|
self.proxied_state.character_inventory.insert(inventory.character_id, inventory);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bank(&mut self, character_id: &CharacterEntityId) -> Result<BankState, ItemStateError> {
|
||||||
|
get_or_clone(&self.item_state.character_bank, &mut self.proxied_state.character_bank, *character_id, ItemStateError::NoCharacter)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_bank(&mut self, bank: BankState) {
|
||||||
|
self.proxied_state.character_bank.insert(bank.character_id, bank);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn floor(&mut self, character_id: &CharacterEntityId) -> Result<FloorState, ItemStateError> {
|
||||||
|
let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, ItemStateError::NoCharacter)?;
|
||||||
|
Ok(FloorState {
|
||||||
|
character_id: *character_id,
|
||||||
|
local: get_or_clone(&self.item_state.character_floor, &mut self.proxied_state.character_floor, *character_id, ItemStateError::NoCharacter)?,
|
||||||
|
shared: get_or_clone(&self.item_state.room_floor, &mut self.proxied_state.room_floor, room_id, ItemStateError::NoRoom)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_floor(&mut self, floor: FloorState) {
|
||||||
|
let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, floor.character_id, ItemStateError::NoCharacter).unwrap();
|
||||||
|
self.proxied_state.character_floor.insert(floor.character_id, floor.local);
|
||||||
|
self.proxied_state.room_floor.insert(room_id, floor.shared);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> {
|
||||||
|
self.item_state.new_item_id()
|
||||||
|
}
|
||||||
|
}
|
500
src/ship/items/tasks.rs
Normal file
500
src/ship/items/tasks.rs
Normal file
@ -0,0 +1,500 @@
|
|||||||
|
use crate::ship::items::ClientItemId;
|
||||||
|
use crate::entity::item::Meseta;
|
||||||
|
|
||||||
|
use crate::ship::map::MapArea;
|
||||||
|
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||||
|
use crate::entity::gateway::EntityGateway;
|
||||||
|
use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, IndividualItemDetail};
|
||||||
|
use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction};
|
||||||
|
use crate::ship::items::inventory::InventoryItem;
|
||||||
|
use crate::ship::items::floor::FloorItem;
|
||||||
|
use crate::entity::item::ItemModifier;
|
||||||
|
use crate::ship::shops::ShopItem;
|
||||||
|
use crate::ship::trade::TradeItem;
|
||||||
|
use crate::ship::location::AreaClient;
|
||||||
|
use crate::ship::drops::ItemDrop;
|
||||||
|
|
||||||
|
use crate::ship::items::actions;
|
||||||
|
|
||||||
|
pub async fn pick_up_item<EG>(
|
||||||
|
item_state: &mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
item_id: &ClientItemId)
|
||||||
|
-> Result<actions::TriggerCreateItem, ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::take_item_from_floor(character.id, *item_id))
|
||||||
|
.act(actions::add_floor_item_to_inventory(character))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn drop_item<EG>(
|
||||||
|
item_state: &mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
item_id: &ClientItemId,
|
||||||
|
map_area: MapArea,
|
||||||
|
drop_position: (f32, f32, f32))
|
||||||
|
-> Result<FloorItem, ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::take_item_from_inventory(character.id, *item_id, 0))
|
||||||
|
.act(actions::add_inventory_item_to_shared_floor(character.id, map_area, drop_position))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn drop_partial_item<'a, EG>(
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
item_id: &ClientItemId,
|
||||||
|
map_area: MapArea,
|
||||||
|
drop_position: (f32, f32),
|
||||||
|
amount: u32)
|
||||||
|
-> Result<FloorItem, ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::take_item_from_inventory(character.id, *item_id, amount))
|
||||||
|
.act(actions::add_inventory_item_to_shared_floor(character.id, map_area, (drop_position.0, 0.0, drop_position.1)))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn drop_meseta<'a, EG>(
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
map_area: MapArea,
|
||||||
|
drop_position: (f32, f32),
|
||||||
|
amount: u32)
|
||||||
|
-> Result<FloorItem, ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::take_meseta_from_inventory(character.id, amount))
|
||||||
|
.act(actions::add_meseta_to_shared_floor(character.id, amount, map_area, drop_position))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn withdraw_meseta<'a, EG>(
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
amount: u32)
|
||||||
|
-> Result<(), ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::take_meseta_from_bank(character.id, amount))
|
||||||
|
.act(actions::add_meseta_from_bank_to_inventory(character.id, amount))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn deposit_meseta<'a, EG>(
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
amount: u32)
|
||||||
|
-> Result<(), ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
|
||||||
|
.act(actions::take_meseta_from_inventory(character.id, amount))
|
||||||
|
.act(actions::add_meseta_to_bank(character.id, amount))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, ()))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn withdraw_item<'a, EG>(
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
item_id: &ClientItemId,
|
||||||
|
amount: u32)
|
||||||
|
-> Result<InventoryItem, ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::take_item_from_bank(character.id, *item_id, amount))
|
||||||
|
//.act(bank_item_to_inventory_item)
|
||||||
|
//.act(add_item_to_inventory)
|
||||||
|
.act(actions::add_bank_item_to_inventory(character))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn deposit_item<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
item_id: &ClientItemId,
|
||||||
|
amount: u32)
|
||||||
|
-> Result<(), ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::take_item_from_inventory(character.id, *item_id, amount))
|
||||||
|
.act(actions::add_inventory_item_to_bank(character.id))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn equip_item<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
item_id: &ClientItemId,
|
||||||
|
equip_slot: u8,
|
||||||
|
) -> Result<(), ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::equip_inventory_item(character.id, *item_id, equip_slot))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn unequip_item<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
item_id: &ClientItemId,
|
||||||
|
) -> Result<(), ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::unequip_inventory_item(character.id, *item_id))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn sort_inventory<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
item_ids: Vec<ClientItemId>,
|
||||||
|
) -> Result<(), ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::sort_inventory_items(character.id, item_ids))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn use_item<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &mut CharacterEntity,
|
||||||
|
item_id: &ClientItemId,
|
||||||
|
amount: u32,
|
||||||
|
) -> Result<(), ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), new_character) = ItemStateAction::default()
|
||||||
|
.act(actions::take_item_from_inventory(character.id, *item_id, amount))
|
||||||
|
.act(actions::use_consumed_item(character.clone()))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
*character = new_character;
|
||||||
|
Ok((transaction, ()))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn feed_mag<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
mag_item_id: &ClientItemId,
|
||||||
|
tool_item_id: &ClientItemId,
|
||||||
|
) -> Result<(), ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
|
||||||
|
.act(actions::take_item_from_inventory(character.id, *tool_item_id, 1))
|
||||||
|
.act(actions::feed_mag_item(character.clone(), *mag_item_id))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, ()))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn buy_shop_item<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
shop_item: &'a (dyn ShopItem + Send + Sync),
|
||||||
|
item_id: ClientItemId,
|
||||||
|
amount: u32,
|
||||||
|
) -> Result<InventoryItem, ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
let item_price = shop_item.price() as u32 * amount;
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::take_meseta_from_inventory(character.id, item_price))
|
||||||
|
//.act(bought_item_to_inventory_item)
|
||||||
|
//.act(add_item_to_inventory)
|
||||||
|
.act(actions::add_bought_item_to_inventory(character.id, shop_item, item_id, amount))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn sell_item<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
item_id: ClientItemId,
|
||||||
|
amount: u32,
|
||||||
|
) -> Result<InventoryItem, ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), result) = ItemStateAction::default()
|
||||||
|
.act(actions::take_item_from_inventory(character.id, item_id, amount))
|
||||||
|
.act(actions::sell_inventory_item(character.id))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, result))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
pub async fn trade_items<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
p1: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta),
|
||||||
|
p2: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta))
|
||||||
|
-> Result<(Vec<InventoryItem>, Vec<InventoryItem>), ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
let p1_trade_items = p1.2
|
||||||
|
.iter()
|
||||||
|
.map(|item| {
|
||||||
|
match item {
|
||||||
|
TradeItem::Individual(item_id) => (*item_id, 1),
|
||||||
|
TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let p2_trade_items = p2.2
|
||||||
|
.iter()
|
||||||
|
.map(|item| {
|
||||||
|
match item {
|
||||||
|
TradeItem::Individual(item_id) => (*item_id, 1),
|
||||||
|
TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
entity_gateway.with_transaction(|mut transaction| async move {
|
||||||
|
let p1_id = p1.1.id;
|
||||||
|
let p2_id = p2.1.id;
|
||||||
|
let trade = transaction.gateway().create_trade(&p1_id, &p2_id).await?;
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), p1_removed_items) = ItemStateAction::default()
|
||||||
|
.act(actions::iterate(p1_trade_items, move |p1_trade_item| actions::take_item_from_inventory(p1_id, p1_trade_item.0, p1_trade_item.1) ))
|
||||||
|
.act(actions::foreach(actions::assign_new_item_id()))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
let ((item_state_proxy, transaction), p2_removed_items) = ItemStateAction::default()
|
||||||
|
.act(actions::iterate(p2_trade_items, move |p2_trade_item| actions::take_item_from_inventory(p2_id, p2_trade_item.0, p2_trade_item.1) ))
|
||||||
|
.act(actions::foreach(actions::assign_new_item_id()))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let ((item_state_proxy, transaction), p2_new_items) = ItemStateAction::default()
|
||||||
|
.act(actions::insert(p1_removed_items))
|
||||||
|
.act(actions::foreach(actions::add_item_to_inventory(p2.1.clone())))
|
||||||
|
.act(actions::record_trade(trade.id, p1_id, p2_id))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
let ((item_state_proxy, transaction), p1_new_items) = ItemStateAction::default()
|
||||||
|
.act(actions::insert(p2_removed_items))
|
||||||
|
.act(actions::foreach(actions::add_item_to_inventory(p1.1.clone())))
|
||||||
|
.act(actions::record_trade(trade.id, p2_id, p1_id))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
|
||||||
|
.act(actions::take_meseta_from_inventory(p1_id, p1.3.0))
|
||||||
|
.act(actions::take_meseta_from_inventory(p2_id, p2.3.0))
|
||||||
|
.act(actions::add_meseta_to_inventory(p1_id, p2.3.0))
|
||||||
|
.act(actions::add_meseta_to_inventory(p2_id, p1.3.0))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, (p1_new_items, p2_new_items)))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn take_meseta<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character_id: &CharacterEntityId,
|
||||||
|
meseta: Meseta)
|
||||||
|
-> Result<(), ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
|
||||||
|
.act(actions::take_meseta_from_inventory(*character_id, meseta.0))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, ()))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn enemy_drops_item<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character_id: CharacterEntityId,
|
||||||
|
item_drop: ItemDrop)
|
||||||
|
-> Result<FloorItem, ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), floor_item) = ItemStateAction::default()
|
||||||
|
.act(actions::convert_item_drop_to_floor_item(character_id, item_drop))
|
||||||
|
.act(actions::add_item_to_local_floor(character_id))
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, floor_item))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn apply_modifier<'a, EG> (
|
||||||
|
item_state: &'a mut ItemState,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &CharacterEntity,
|
||||||
|
item_id: ClientItemId,
|
||||||
|
modifier: ItemModifier)
|
||||||
|
-> Result<IndividualItemDetail, ItemStateError>
|
||||||
|
where
|
||||||
|
EG: EntityGateway,
|
||||||
|
{
|
||||||
|
entity_gateway.with_transaction(|transaction| async move {
|
||||||
|
let item_state_proxy = ItemStateProxy::new(item_state);
|
||||||
|
let ((item_state_proxy, transaction), item) = ItemStateAction::default()
|
||||||
|
.act(actions::take_item_from_inventory(character.id, item_id, 1))
|
||||||
|
.act(actions::apply_modifier_to_inventory_item(modifier))
|
||||||
|
.act(actions::add_item_to_inventory(character.clone()))
|
||||||
|
.act(actions::as_individual_item())
|
||||||
|
.commit((item_state_proxy, transaction))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
item_state_proxy.commit();
|
||||||
|
Ok((transaction, item))
|
||||||
|
}).await
|
||||||
|
}
|
@ -1,337 +0,0 @@
|
|||||||
use crate::entity::gateway::EntityGateway;
|
|
||||||
use thiserror::Error;
|
|
||||||
use crate::ship::items::manager::{ItemManager, ItemManagerError};
|
|
||||||
use crate::entity::gateway::GatewayError;
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum TransactionCommitError {
|
|
||||||
#[error("transaction commit gateway error {0}")]
|
|
||||||
Gateway(#[from] GatewayError),
|
|
||||||
#[error("transaction commit itemmanager error {0}")]
|
|
||||||
ItemManager(#[from] ItemManagerError),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
pub trait ItemAction<EG: EntityGateway>: std::marker::Send + std::marker::Sync + std::fmt::Debug {
|
|
||||||
async fn commit(&self, manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ItemTransactionActions<'a, EG: EntityGateway> {
|
|
||||||
action_queue: Vec<Box<dyn ItemAction<EG>>>,
|
|
||||||
pub manager: &'a ItemManager,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl<'a, EG: EntityGateway> ItemTransactionActions<'a, EG> {
|
|
||||||
fn new(manager: &'a ItemManager) -> ItemTransactionActions<'a, EG> {
|
|
||||||
ItemTransactionActions {
|
|
||||||
action_queue: Vec::new(),
|
|
||||||
manager
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn action(&mut self, action: Box<dyn ItemAction<EG>>) {
|
|
||||||
self.action_queue.push(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub struct ItemTransaction<'a, T, EG: EntityGateway> {
|
|
||||||
data: T,
|
|
||||||
actions: ItemTransactionActions<'a, EG>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T, EG: EntityGateway> ItemTransaction<'a, T, EG> {
|
|
||||||
pub fn new(manager: &'a ItemManager, arg: T) -> ItemTransaction<'a, T, EG> {
|
|
||||||
ItemTransaction {
|
|
||||||
data: arg,
|
|
||||||
actions: ItemTransactionActions::new(manager),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn act<E: std::fmt::Debug, U>(mut self, action: fn(&mut ItemTransactionActions<EG>, &T) -> Result<U, E>) -> FinalizedItemTransaction<U, E, EG> {
|
|
||||||
match action(&mut self.actions, &self.data) {
|
|
||||||
Ok(k) => {
|
|
||||||
FinalizedItemTransaction {
|
|
||||||
value: Ok(k),
|
|
||||||
action_queue: self.actions.action_queue,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
FinalizedItemTransaction {
|
|
||||||
value: Err(err),
|
|
||||||
action_queue: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum TransactionError<E: std::fmt::Debug> {
|
|
||||||
#[error("transaction action error {0:?}")]
|
|
||||||
Action(E),
|
|
||||||
#[error("transaction commit error {0}")]
|
|
||||||
Commit(#[from] TransactionCommitError),
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// this only exists to drop the ItemManager borrow of ItemTransaction so a mutable ItemTransaction can be passed in later
|
|
||||||
pub struct FinalizedItemTransaction<T, E: std::fmt::Debug, EG: EntityGateway> {
|
|
||||||
value: Result<T, E>,
|
|
||||||
action_queue: Vec<Box<dyn ItemAction<EG>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, E: std::fmt::Debug, EG: EntityGateway> FinalizedItemTransaction<T, E, EG> {
|
|
||||||
pub async fn commit(self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<T, TransactionError<E>> {
|
|
||||||
match self.value {
|
|
||||||
Ok(value) => {
|
|
||||||
for action in self.action_queue.into_iter() {
|
|
||||||
// TODO: better handle rolling back if this ever errors out
|
|
||||||
action.commit(item_manager, entity_gateway).await.map_err(|err| TransactionError::Commit(err))?;
|
|
||||||
}
|
|
||||||
Ok(value)
|
|
||||||
},
|
|
||||||
Err(err) => Err(TransactionError::Action(err)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use crate::entity::account::{UserAccountId, NewUserAccountEntity, UserAccountEntity};
|
|
||||||
use crate::entity::character::{NewCharacterEntity, CharacterEntity};
|
|
||||||
use crate::entity::gateway::GatewayError;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
async fn test_item_transaction() {
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct DummyAction1 {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct DummyAction2 {
|
|
||||||
value: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
#[error("")]
|
|
||||||
enum DummyError {
|
|
||||||
Error
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
struct DummyGateway {
|
|
||||||
d1_set: String,
|
|
||||||
d2_inc: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl EntityGateway for DummyGateway {
|
|
||||||
async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
|
|
||||||
self.d1_set = user.username;
|
|
||||||
Ok(UserAccountEntity::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
|
||||||
self.d2_inc += char.slot;
|
|
||||||
Ok(CharacterEntity::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 {
|
|
||||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||||
item_manager.id_counter = 55555;
|
|
||||||
entity_gateway.create_user(NewUserAccountEntity {
|
|
||||||
username: self.name.clone(),
|
|
||||||
..NewUserAccountEntity::default()
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 {
|
|
||||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||||
item_manager.id_counter += self.value;
|
|
||||||
entity_gateway.create_character(NewCharacterEntity {
|
|
||||||
slot: self.value,
|
|
||||||
..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut item_manager = ItemManager::default();
|
|
||||||
let mut entity_gateway = DummyGateway::default();
|
|
||||||
|
|
||||||
let result = ItemTransaction::new(&item_manager, 12)
|
|
||||||
.act(|it, k| {
|
|
||||||
it.action(Box::new(DummyAction1 {name: "asdf".into()}));
|
|
||||||
it.action(Box::new(DummyAction2 {value: 11}));
|
|
||||||
it.action(Box::new(DummyAction2 {value: *k}));
|
|
||||||
if *k == 99 {
|
|
||||||
return Err(DummyError::Error)
|
|
||||||
}
|
|
||||||
Ok(String::from("hello"))
|
|
||||||
})
|
|
||||||
.commit(&mut item_manager, &mut entity_gateway)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
assert!(entity_gateway.d1_set == "asdf");
|
|
||||||
assert!(entity_gateway.d2_inc == 23);
|
|
||||||
assert!(item_manager.id_counter == 55578);
|
|
||||||
assert!(result.unwrap() == "hello");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
async fn test_item_transaction_with_action_error() {
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct DummyAction1 {
|
|
||||||
}
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct DummyAction2 {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq, Eq)]
|
|
||||||
#[error("")]
|
|
||||||
enum DummyError {
|
|
||||||
Error
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
struct DummyGateway {
|
|
||||||
_d1_set: String,
|
|
||||||
d2_inc: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl EntityGateway for DummyGateway {
|
|
||||||
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
|
||||||
self.d2_inc += char.slot;
|
|
||||||
Ok(CharacterEntity::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 {
|
|
||||||
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||||
entity_gateway.create_character(NewCharacterEntity {
|
|
||||||
slot: 1,
|
|
||||||
..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 {
|
|
||||||
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||||
entity_gateway.create_character(NewCharacterEntity {
|
|
||||||
slot: 1,
|
|
||||||
..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut item_manager = ItemManager::default();
|
|
||||||
let mut entity_gateway = DummyGateway::default();
|
|
||||||
|
|
||||||
let result = ItemTransaction::new(&item_manager, 12)
|
|
||||||
.act(|it, _| -> Result<(), _> {
|
|
||||||
it.action(Box::new(DummyAction1 {}));
|
|
||||||
it.action(Box::new(DummyAction2 {}));
|
|
||||||
it.action(Box::new(DummyAction2 {}));
|
|
||||||
Err(DummyError::Error)
|
|
||||||
})
|
|
||||||
.commit(&mut item_manager, &mut entity_gateway)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
assert!(entity_gateway.d2_inc == 0);
|
|
||||||
assert!(matches!(result, Err(TransactionError::Action(DummyError::Error))));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
async fn test_item_transaction_with_commit_error() {
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct DummyAction1 {
|
|
||||||
}
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct DummyAction2 {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq, Eq)]
|
|
||||||
#[error("")]
|
|
||||||
enum DummyError {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
struct DummyGateway {
|
|
||||||
_d1_set: String,
|
|
||||||
d2_inc: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl EntityGateway for DummyGateway {
|
|
||||||
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
|
||||||
self.d2_inc += char.slot;
|
|
||||||
Ok(CharacterEntity::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 {
|
|
||||||
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||||
entity_gateway.create_character(NewCharacterEntity {
|
|
||||||
slot: 1,
|
|
||||||
..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
Err(GatewayError::Error.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 {
|
|
||||||
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
|
||||||
entity_gateway.create_character(NewCharacterEntity {
|
|
||||||
slot: 1,
|
|
||||||
..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut item_manager = ItemManager::default();
|
|
||||||
let mut entity_gateway = DummyGateway::default();
|
|
||||||
|
|
||||||
let result = ItemTransaction::new(&item_manager, 12)
|
|
||||||
.act(|it, _| -> Result<_, DummyError> {
|
|
||||||
it.action(Box::new(DummyAction1 {}));
|
|
||||||
it.action(Box::new(DummyAction2 {}));
|
|
||||||
it.action(Box::new(DummyAction2 {}));
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.commit(&mut item_manager, &mut entity_gateway)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// in an ideal world this would be 0 as rollbacks would occur
|
|
||||||
assert!(entity_gateway.d2_inc == 1);
|
|
||||||
assert!(matches!(result, Err(TransactionError::Commit(TransactionCommitError::Gateway(GatewayError::Error)))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,163 +0,0 @@
|
|||||||
use thiserror::Error;
|
|
||||||
use crate::entity::gateway::EntityGateway;
|
|
||||||
use crate::entity::character::CharacterEntity;
|
|
||||||
use crate::entity::item::mag::MagCell;
|
|
||||||
use crate::ship::items::{CharacterInventory, ConsumedItem};
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
#[error("")]
|
|
||||||
pub enum UseItemError {
|
|
||||||
NoCharacter,
|
|
||||||
ItemNotEquipped,
|
|
||||||
InvalidItem,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn power_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||||
character.materials.power += 1;
|
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn mind_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||||
character.materials.mind += 1;
|
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn evade_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||||
character.materials.evade += 1;
|
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn def_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||||
character.materials.def += 1;
|
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn luck_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||||
character.materials.luck += 1;
|
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn hp_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||||
character.materials.hp += 1;
|
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn tp_material<EG: EntityGateway>(entity_gateway: &mut EG, character: &mut CharacterEntity) {
|
|
||||||
character.materials.tp += 1;
|
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn mag_cell<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory, mag_cell_type: MagCell) -> Result<(), UseItemError> {
|
|
||||||
let mut mag_handle = inventory.get_equipped_mag_handle().ok_or(UseItemError::ItemNotEquipped)?;
|
|
||||||
let mag_item = mag_handle.item_mut()
|
|
||||||
.ok_or(UseItemError::InvalidItem)?;
|
|
||||||
let actual_mag = mag_item
|
|
||||||
.individual_mut()
|
|
||||||
.ok_or(UseItemError::InvalidItem)?
|
|
||||||
.mag_mut()
|
|
||||||
.ok_or(UseItemError::InvalidItem)?;
|
|
||||||
actual_mag.apply_mag_cell(mag_cell_type);
|
|
||||||
for mag_entity_id in mag_item.entity_ids() {
|
|
||||||
for cell_entity_id in used_cell.entity_ids() {
|
|
||||||
entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn cell_of_mag_502<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag502).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn cell_of_mag_213<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag213).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn parts_of_robochao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::PartsOfRobochao).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn heart_of_opaopa<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfOpaOpa).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn heart_of_pian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfPian).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn heart_of_chao<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfChao).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn heart_of_angel<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfAngel).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn kit_of_hamburger<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfHamburger).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn panthers_spirit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::PanthersSpirit).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn kit_of_mark3<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMark3).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn kit_of_master_system<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMasterSystem).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn kit_of_genesis<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfGenesis).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn kit_of_sega_saturn<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfSegaSaturn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn kit_of_dreamcast<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfDreamcast).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn tablet<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::Tablet).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn dragon_scale<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::DragonScale).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn heaven_striker_coat<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeavenStrikerCoat).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn pioneer_parts<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::PioneerParts).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn amities_memo<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::AmitiesMemo).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn heart_of_morolian<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfMorolian).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn rappys_beak<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::RappysBeak).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn yahoos_engine<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::YahoosEngine).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn d_photon_core<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::DPhotonCore).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn liberta_kit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> {
|
|
||||||
mag_cell(entity_gateway, used_cell, inventory, MagCell::LibertaKit).await
|
|
||||||
}
|
|
@ -12,7 +12,7 @@ pub enum AreaType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct LobbyId(pub usize);
|
pub struct LobbyId(pub usize);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)]
|
||||||
@ -25,7 +25,7 @@ impl LobbyId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("create room")]
|
#[error("create room")]
|
||||||
pub enum CreateRoomError {
|
pub enum CreateRoomError {
|
||||||
NoOpenSlots,
|
NoOpenSlots,
|
||||||
@ -33,7 +33,7 @@ pub enum CreateRoomError {
|
|||||||
JoinError,
|
JoinError,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("join room")]
|
#[error("join room")]
|
||||||
pub enum JoinRoomError {
|
pub enum JoinRoomError {
|
||||||
RoomDoesNotExist,
|
RoomDoesNotExist,
|
||||||
@ -41,7 +41,7 @@ pub enum JoinRoomError {
|
|||||||
ClientInAreaAlready,
|
ClientInAreaAlready,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("join lobby")]
|
#[error("join lobby")]
|
||||||
pub enum JoinLobbyError {
|
pub enum JoinLobbyError {
|
||||||
LobbyDoesNotExist,
|
LobbyDoesNotExist,
|
||||||
@ -49,7 +49,7 @@ pub enum JoinLobbyError {
|
|||||||
ClientInAreaAlready,
|
ClientInAreaAlready,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("get area")]
|
#[error("get area")]
|
||||||
pub enum GetAreaError {
|
pub enum GetAreaError {
|
||||||
NotInRoom,
|
NotInRoom,
|
||||||
@ -57,28 +57,28 @@ pub enum GetAreaError {
|
|||||||
InvalidClient,
|
InvalidClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("client removal")]
|
#[error("client removal")]
|
||||||
pub enum ClientRemovalError {
|
pub enum ClientRemovalError {
|
||||||
ClientNotInArea,
|
ClientNotInArea,
|
||||||
InvalidArea,
|
InvalidArea,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("get clients")]
|
#[error("get clients")]
|
||||||
pub enum GetClientsError {
|
pub enum GetClientsError {
|
||||||
InvalidClient,
|
InvalidClient,
|
||||||
InvalidArea,
|
InvalidArea,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("get neighbor")]
|
#[error("get neighbor")]
|
||||||
pub enum GetNeighborError {
|
pub enum GetNeighborError {
|
||||||
InvalidClient,
|
InvalidClient,
|
||||||
InvalidArea,
|
InvalidArea,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("get leader")]
|
#[error("get leader")]
|
||||||
pub enum GetLeaderError {
|
pub enum GetLeaderError {
|
||||||
InvalidClient,
|
InvalidClient,
|
||||||
@ -86,7 +86,7 @@ pub enum GetLeaderError {
|
|||||||
NoClientInArea,
|
NoClientInArea,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("clientlocation")]
|
#[error("clientlocation")]
|
||||||
pub enum ClientLocationError {
|
pub enum ClientLocationError {
|
||||||
CreateRoomError(#[from] CreateRoomError),
|
CreateRoomError(#[from] CreateRoomError),
|
||||||
@ -100,7 +100,7 @@ pub enum ClientLocationError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct LocalClientId(usize);
|
pub struct LocalClientId(usize);
|
||||||
|
|
||||||
impl LocalClientId {
|
impl LocalClientId {
|
||||||
@ -115,19 +115,19 @@ impl PartialEq<u8> for LocalClientId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct AreaClient {
|
pub struct AreaClient {
|
||||||
pub client: ClientId,
|
pub client: ClientId,
|
||||||
pub local_client: LocalClientId,
|
pub local_client: LocalClientId,
|
||||||
time_join: SystemTime,
|
time_join: SystemTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
struct Lobby([Option<AreaClient>; 12]);
|
struct Lobby([Option<AreaClient>; 12]);
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
struct Room([Option<AreaClient>; 4]);
|
struct Room([Option<AreaClient>; 4]);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum RoomLobby {
|
pub enum RoomLobby {
|
||||||
Room(RoomId),
|
Room(RoomId),
|
||||||
Lobby(LobbyId),
|
Lobby(LobbyId),
|
||||||
|
@ -4,7 +4,7 @@ use std::collections::HashMap;
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use crate::ship::room::Episode;
|
use crate::ship::room::Episode;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum MapArea {
|
pub enum MapArea {
|
||||||
Pioneer2Ep1,
|
Pioneer2Ep1,
|
||||||
Forest1,
|
Forest1,
|
||||||
|
@ -320,6 +320,16 @@ impl Maps {
|
|||||||
self.object_data = objects;
|
self.object_data = objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_enemy_id_by_monster_type(&self, monster: MonsterType) -> Option<u16> {
|
||||||
|
let (id, _) = self.enemy_data
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_i, &m)| m.is_some())
|
||||||
|
.find(|(_i, &m)| m.unwrap().monster == monster)?;
|
||||||
|
|
||||||
|
Some(id as u16)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_rare_monster_list(&self) -> Vec<u16> {
|
pub fn get_rare_monster_list(&self) -> Vec<u16> {
|
||||||
let mut rare_monsters = vec![0xFFFF; 16];
|
let mut rare_monsters = vec![0xFFFF; 16];
|
||||||
let shiny: Vec<(usize, &Option<MapEnemy>)> = self.enemy_data.iter()
|
let shiny: Vec<(usize, &Option<MapEnemy>)> = self.enemy_data.iter()
|
||||||
|
@ -5,7 +5,7 @@ use rand::Rng;
|
|||||||
// TODO: don't use *
|
// TODO: don't use *
|
||||||
use crate::ship::map::*;
|
use crate::ship::map::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum MapVariantMode {
|
pub enum MapVariantMode {
|
||||||
Online,
|
Online,
|
||||||
Offline,
|
Offline,
|
||||||
|
@ -4,21 +4,21 @@ use crate::common::leveltable::CharacterLevelTable;
|
|||||||
use crate::ship::ship::{ShipError, Clients};
|
use crate::ship::ship::{ShipError, Clients};
|
||||||
use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError};
|
use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError};
|
||||||
use crate::ship::packet::builder::{player_info};
|
use crate::ship::packet::builder::{player_info};
|
||||||
use crate::ship::items::ItemManager;
|
use crate::ship::items::state::ItemState;
|
||||||
|
|
||||||
|
|
||||||
pub fn join_lobby(id: ClientId,
|
pub fn join_lobby(id: ClientId,
|
||||||
lobby: LobbyId,
|
lobby: LobbyId,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_manager: &ItemManager,
|
item_state: &ItemState,
|
||||||
level_table: &CharacterLevelTable)
|
level_table: &CharacterLevelTable)
|
||||||
-> Result<JoinLobby, anyhow::Error> {
|
-> Result<JoinLobby, anyhow::Error> {
|
||||||
let lobby_clients = client_location.get_clients_in_lobby(lobby).map_err(|err| -> ClientLocationError { err.into() })?;
|
let lobby_clients = client_location.get_clients_in_lobby(lobby).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
let playerinfo = lobby_clients.iter()
|
let playerinfo = lobby_clients.iter()
|
||||||
.map(|area_client| {
|
.map(|area_client| {
|
||||||
let client = clients.get(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client)).unwrap();
|
let client = clients.get(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client)).unwrap();
|
||||||
player_info(0x100, client, area_client, item_manager, level_table)
|
player_info(0x100, client, area_client, item_state, level_table)
|
||||||
});
|
});
|
||||||
|
|
||||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||||
@ -40,7 +40,7 @@ pub fn add_to_lobby(id: ClientId,
|
|||||||
lobby: LobbyId,
|
lobby: LobbyId,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_manager: &ItemManager,
|
item_state: &ItemState,
|
||||||
level_table: &CharacterLevelTable)
|
level_table: &CharacterLevelTable)
|
||||||
-> Result<AddToLobby, ShipError> {
|
-> Result<AddToLobby, ShipError> {
|
||||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||||
@ -55,7 +55,7 @@ pub fn add_to_lobby(id: ClientId,
|
|||||||
block: client.block as u16,
|
block: client.block as u16,
|
||||||
event: 0,
|
event: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
playerinfo: player_info(0x100, client, &area_client, item_manager, level_table),
|
playerinfo: player_info(0x100, client, &area_client, item_state, level_table),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,11 @@ use libpso::packet::ship::*;
|
|||||||
use crate::entity::item;
|
use crate::entity::item;
|
||||||
use crate::common::leveltable::CharacterStats;
|
use crate::common::leveltable::CharacterStats;
|
||||||
use crate::ship::ship::{ShipError};
|
use crate::ship::ship::{ShipError};
|
||||||
use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank};
|
use crate::ship::items::ClientItemId;
|
||||||
|
use crate::ship::items::inventory::InventoryItem;
|
||||||
|
use crate::ship::items::state::IndividualItemDetail;
|
||||||
|
use crate::ship::items::bank::BankState;
|
||||||
|
use crate::ship::items::floor::FloorItem;
|
||||||
use crate::ship::location::AreaClient;
|
use crate::ship::location::AreaClient;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use crate::ship::shops::ShopItem;
|
use crate::ship::shops::ShopItem;
|
||||||
@ -14,21 +18,21 @@ pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result<ItemDr
|
|||||||
Ok(ItemDrop {
|
Ok(ItemDrop {
|
||||||
client,
|
client,
|
||||||
target,
|
target,
|
||||||
map_area: item_drop.map_area().area_value(),
|
map_area: item_drop.map_area.area_value(),
|
||||||
variety: 0,
|
variety: 0,
|
||||||
unknown: 0,
|
unknown: 0,
|
||||||
x: item_drop.x(),
|
x: item_drop.x,
|
||||||
z: item_drop.z(),
|
z: item_drop.z,
|
||||||
y: item_drop.y(),
|
y: item_drop.y,
|
||||||
item_bytes: item_bytes[0..12].try_into()?,
|
item_bytes: item_bytes[0..12].try_into()?,
|
||||||
item_id: item_drop.item_id().0,
|
item_id: item_drop.item_id.0,
|
||||||
item_bytes2: item_bytes[12..16].try_into()?,
|
item_bytes2: item_bytes[12..16].try_into()?,
|
||||||
unknown2: 0,
|
unknown2: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this doesn't need to be a Result, just unwrap try_intos they are guaranteed to succeed
|
// TODO: this doesn't need to be a Result, just unwrap try_intos they are guaranteed to succeed
|
||||||
pub fn create_individual_item(area_client: AreaClient, item_id: ClientItemId, item: &item::ItemDetail) -> Result<CreateItem, ShipError> {
|
pub fn create_individual_item(area_client: AreaClient, item_id: ClientItemId, item: &IndividualItemDetail) -> Result<CreateItem, ShipError> {
|
||||||
let bytes = item.as_client_bytes();
|
let bytes = item.as_client_bytes();
|
||||||
Ok(CreateItem {
|
Ok(CreateItem {
|
||||||
client: area_client.local_client.id(),
|
client: area_client.local_client.id(),
|
||||||
@ -66,12 +70,24 @@ pub fn create_meseta(area_client: AreaClient, amount: usize) -> CreateItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result<CreateItem, ShipError> {
|
pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result<CreateItem, ShipError> {
|
||||||
let bytes = item.as_client_bytes();
|
let bytes = item.item.as_client_bytes();
|
||||||
Ok(CreateItem {
|
Ok(CreateItem {
|
||||||
client: area_client.local_client.id(),
|
client: area_client.local_client.id(),
|
||||||
target: 0,
|
target: 0,
|
||||||
item_data: bytes[0..12].try_into()?,
|
item_data: bytes[0..12].try_into()?,
|
||||||
item_id: item.item_id().0,
|
item_id: item.item_id.0,
|
||||||
|
item_data2: bytes[12..16].try_into()?,
|
||||||
|
unknown: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &InventoryItem) -> Result<CreateItem, ShipError> {
|
||||||
|
let bytes = item.item.as_client_bytes();
|
||||||
|
Ok(CreateItem {
|
||||||
|
client: area_client.local_client.id(),
|
||||||
|
target: 0,
|
||||||
|
item_data: bytes[0..12].try_into()?,
|
||||||
|
item_id: item.item_id.0,
|
||||||
item_data2: bytes[12..16].try_into()?,
|
item_data2: bytes[12..16].try_into()?,
|
||||||
unknown: 0,
|
unknown: 0,
|
||||||
})
|
})
|
||||||
@ -83,13 +99,13 @@ pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Resu
|
|||||||
target: 0,
|
target: 0,
|
||||||
client_id: area_client.local_client.id(),
|
client_id: area_client.local_client.id(),
|
||||||
unknown: 0,
|
unknown: 0,
|
||||||
map_area: item.map_area().area_value(),
|
map_area: item.map_area.area_value(),
|
||||||
unknown2: 0,
|
unknown2: 0,
|
||||||
item_id: item.item_id().0,
|
item_id: item.item_id.0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drop_split_stack(area_client: AreaClient, item: &StackedFloorItem) -> Result<DropSplitStack, ShipError> {
|
pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem) -> Result<DropSplitStack, ShipError> {
|
||||||
let item_bytes = item.as_client_bytes();
|
let item_bytes = item.as_client_bytes();
|
||||||
Ok(DropSplitStack {
|
Ok(DropSplitStack {
|
||||||
client: area_client.local_client.id(),
|
client: area_client.local_client.id(),
|
||||||
@ -113,11 +129,11 @@ pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem) -> Res
|
|||||||
target: 0,
|
target: 0,
|
||||||
variety: 0,
|
variety: 0,
|
||||||
unknown1: 0,
|
unknown1: 0,
|
||||||
map_area: item.map_area().area_value(),
|
map_area: item.map_area.area_value(),
|
||||||
x: item.x(),
|
x: item.x,
|
||||||
z: item.z(),
|
z: item.z,
|
||||||
item_bytes: item_bytes[0..12].try_into()?,
|
item_bytes: item_bytes[0..12].try_into()?,
|
||||||
item_id: item.item_id().0,
|
item_id: item.item_id.0,
|
||||||
item_bytes2: item_bytes[12..16].try_into()?,
|
item_bytes2: item_bytes[12..16].try_into()?,
|
||||||
unknown2: 0,
|
unknown2: 0,
|
||||||
})
|
})
|
||||||
@ -146,7 +162,7 @@ pub fn character_leveled_up(area_client: AreaClient, level: u32, before_stats: C
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TOOD: checksum?
|
// TOOD: checksum?
|
||||||
pub fn bank_item_list(bank: &CharacterBank, char_bank_meseta: u32) -> BankItemList {
|
pub fn bank_item_list(bank: &BankState) -> BankItemList {
|
||||||
BankItemList {
|
BankItemList {
|
||||||
aflag: 0,
|
aflag: 0,
|
||||||
cmd: 0xBC,
|
cmd: 0xBC,
|
||||||
@ -154,7 +170,7 @@ pub fn bank_item_list(bank: &CharacterBank, char_bank_meseta: u32) -> BankItemLi
|
|||||||
size: bank.count() as u32 * 0x18 + 0x14,
|
size: bank.count() as u32 * 0x18 + 0x14,
|
||||||
checksum: 0x123434,
|
checksum: 0x123434,
|
||||||
item_count: bank.count() as u32,
|
item_count: bank.count() as u32,
|
||||||
meseta: char_bank_meseta,
|
meseta: bank.meseta.0,
|
||||||
items: bank.as_client_bank_request()
|
items: bank.as_client_bank_request()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ use crate::common::leveltable::CharacterLevelTable;
|
|||||||
use crate::ship::character::CharacterBytesBuilder;
|
use crate::ship::character::CharacterBytesBuilder;
|
||||||
use crate::ship::ship::ClientState;
|
use crate::ship::ship::ClientState;
|
||||||
use crate::ship::location::AreaClient;
|
use crate::ship::location::AreaClient;
|
||||||
use crate::ship::items::ItemManager;
|
use crate::ship::items::state::ItemState;
|
||||||
|
|
||||||
pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) -> PlayerHeader {
|
pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) -> PlayerHeader {
|
||||||
PlayerHeader {
|
PlayerHeader {
|
||||||
@ -23,15 +23,14 @@ pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, item_manager: &ItemManager, level_table: &CharacterLevelTable) -> PlayerInfo {
|
pub fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, item_state: &ItemState, level_table: &CharacterLevelTable) -> PlayerInfo {
|
||||||
let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp);
|
let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp);
|
||||||
let inventory = item_manager.get_character_inventory(&client.character).unwrap();
|
let inventory = item_state.get_character_inventory(&client.character).unwrap();
|
||||||
let meseta = item_manager.get_character_meseta(&client.character.id).unwrap();
|
|
||||||
let character = CharacterBytesBuilder::default()
|
let character = CharacterBytesBuilder::default()
|
||||||
.character(&client.character)
|
.character(&client.character)
|
||||||
.stats(&stats)
|
.stats(&stats)
|
||||||
.level(level - 1)
|
.level(level - 1)
|
||||||
.meseta(*meseta)
|
.meseta(inventory.meseta)
|
||||||
.build();
|
.build();
|
||||||
PlayerInfo {
|
PlayerInfo {
|
||||||
header: player_header(tag, client, area_client),
|
header: player_header(tag, client, area_client),
|
||||||
|
@ -4,7 +4,7 @@ use crate::common::leveltable::CharacterLevelTable;
|
|||||||
use crate::ship::ship::{ShipError, ClientState, Clients};
|
use crate::ship::ship::{ShipError, ClientState, Clients};
|
||||||
use crate::ship::location::{ClientLocation, RoomId, AreaClient, ClientLocationError};
|
use crate::ship::location::{ClientLocation, RoomId, AreaClient, ClientLocationError};
|
||||||
use crate::ship::room::RoomState;
|
use crate::ship::room::RoomState;
|
||||||
use crate::ship::items::ItemManager;
|
use crate::ship::items::state::ItemState;
|
||||||
use crate::ship::packet::builder::{player_header, player_info};
|
use crate::ship::packet::builder::{player_header, player_info};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ pub fn add_to_room(_id: ClientId,
|
|||||||
client: &ClientState,
|
client: &ClientState,
|
||||||
area_client: &AreaClient,
|
area_client: &AreaClient,
|
||||||
leader: &AreaClient,
|
leader: &AreaClient,
|
||||||
item_manager: &ItemManager,
|
item_state: &ItemState,
|
||||||
level_table: &CharacterLevelTable,
|
level_table: &CharacterLevelTable,
|
||||||
_room_id: RoomId,
|
_room_id: RoomId,
|
||||||
)
|
)
|
||||||
@ -68,7 +68,7 @@ pub fn add_to_room(_id: ClientId,
|
|||||||
block: 0,
|
block: 0,
|
||||||
event: 0,
|
event: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
playerinfo: player_info(0x10000, client, area_client, item_manager, level_table),
|
playerinfo: player_info(0x10000, client, area_client, item_state, level_table),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use crate::common::serverstate::ClientId;
|
|||||||
use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients};
|
use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients};
|
||||||
use crate::login::login::get_login_status;
|
use crate::login::login::get_login_status;
|
||||||
use crate::entity::gateway::EntityGateway;
|
use crate::entity::gateway::EntityGateway;
|
||||||
use crate::ship::items::ItemManager;
|
use crate::ship::items::state::ItemState;
|
||||||
use crate::common::interserver::ShipMessage;
|
use crate::common::interserver::ShipMessage;
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
@ -12,7 +12,7 @@ pub async fn validate_login<EG: EntityGateway>(id: ClientId,
|
|||||||
pkt: &Login,
|
pkt: &Login,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager,
|
item_state: &mut ItemState,
|
||||||
shipgate_sender: &Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
|
shipgate_sender: &Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
|
||||||
ship_name: &str,
|
ship_name: &str,
|
||||||
num_blocks: usize)
|
num_blocks: usize)
|
||||||
@ -30,7 +30,7 @@ pub async fn validate_login<EG: EntityGateway>(id: ClientId,
|
|||||||
.clone();
|
.clone();
|
||||||
let settings = entity_gateway.get_user_settings_by_user(&user).await?;
|
let settings = entity_gateway.get_user_settings_by_user(&user).await?;
|
||||||
|
|
||||||
item_manager.load_character(entity_gateway, &character).await?;
|
item_state.load_character(entity_gateway, &character).await?;
|
||||||
|
|
||||||
if let Some(shipgate_sender) = shipgate_sender.as_ref() {
|
if let Some(shipgate_sender) = shipgate_sender.as_ref() {
|
||||||
shipgate_sender(ShipMessage::AddUser(user.id));
|
shipgate_sender(ShipMessage::AddUser(user.id));
|
||||||
|
@ -8,12 +8,16 @@ use crate::common::serverstate::ClientId;
|
|||||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops};
|
use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops};
|
||||||
use crate::ship::location::{ClientLocation, ClientLocationError};
|
use crate::ship::location::{ClientLocation, ClientLocationError};
|
||||||
use crate::ship::drops::ItemDrop;
|
use crate::ship::drops::ItemDrop;
|
||||||
use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, TriggerCreateItem, FloorItem, FloorType};
|
use crate::ship::items::ClientItemId;
|
||||||
use crate::entity::gateway::EntityGateway;
|
use crate::entity::gateway::EntityGateway;
|
||||||
use crate::entity::item;
|
use crate::entity::item;
|
||||||
use libpso::utf8_to_utf16_array;
|
use libpso::utf8_to_utf16_array;
|
||||||
use crate::ship::packet::builder;
|
use crate::ship::packet::builder;
|
||||||
use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
|
use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
|
||||||
|
use crate::ship::items::state::{ItemState, ItemStateError};
|
||||||
|
use crate::ship::items::floor::{FloorType, FloorItemDetail};
|
||||||
|
use crate::ship::items::actions::TriggerCreateItem;
|
||||||
|
use crate::ship::items::tasks::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier};
|
||||||
|
|
||||||
const BANK_ACTION_DEPOSIT: u8 = 0;
|
const BANK_ACTION_DEPOSIT: u8 = 0;
|
||||||
const BANK_ACTION_WITHDRAW: u8 = 1;
|
const BANK_ACTION_WITHDRAW: u8 = 1;
|
||||||
@ -22,15 +26,11 @@ const SHOP_OPTION_TOOL: u8 = 0;
|
|||||||
const SHOP_OPTION_WEAPON: u8 = 1;
|
const SHOP_OPTION_WEAPON: u8 = 1;
|
||||||
const SHOP_OPTION_ARMOR: u8 = 2;
|
const SHOP_OPTION_ARMOR: u8 = 2;
|
||||||
|
|
||||||
const INVENTORY_MESETA_CAPACITY: u32 = 999999;
|
|
||||||
const BANK_MESETA_CAPACITY: u32 = 999999;
|
|
||||||
|
|
||||||
//const BANK_ACTION_: u8 = 1;
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
#[error("")]
|
|
||||||
pub enum MessageError {
|
pub enum MessageError {
|
||||||
|
#[error("invalid tek {0}")]
|
||||||
InvalidTek(ClientItemId),
|
InvalidTek(ClientItemId),
|
||||||
|
#[error("mismatched tek {0} {1}")]
|
||||||
MismatchedTekIds(ClientItemId, ClientItemId),
|
MismatchedTekIds(ClientItemId, ClientItemId),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ pub async fn request_item<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
rooms: &mut Rooms,
|
rooms: &mut Rooms,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
@ -94,9 +94,15 @@ where
|
|||||||
|
|
||||||
let client_and_drop = clients_in_area.into_iter()
|
let client_and_drop = clients_in_area.into_iter()
|
||||||
.filter_map(|area_client| {
|
.filter_map(|area_client| {
|
||||||
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
|
if room.redbox {
|
||||||
(area_client, item_drop_type)
|
room.drop_table.get_rare_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
|
||||||
})
|
(area_client, item_drop_type)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
|
||||||
|
(area_client, item_drop_type)
|
||||||
|
})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut item_drop_packets = Vec::new();
|
let mut item_drop_packets = Vec::new();
|
||||||
@ -109,8 +115,8 @@ where
|
|||||||
item: item_drop,
|
item: item_drop,
|
||||||
};
|
};
|
||||||
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
||||||
let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await?;
|
let floor_item = enemy_drops_item(item_state, entity_gateway, client.character.id, item_drop).await?;
|
||||||
let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, floor_item)?;
|
let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?;
|
||||||
|
|
||||||
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))));
|
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))));
|
||||||
}
|
}
|
||||||
@ -123,7 +129,7 @@ pub async fn pickup_item<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
@ -134,7 +140,9 @@ where
|
|||||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
|
|
||||||
// TODO: should not need to fetch the item here to construct this packet
|
// TODO: should not need to fetch the item here to construct this packet
|
||||||
let (item, floor_type) = item_manager.get_floor_item_by_id(&client.character, ClientItemId(pickup_item.item_id))?;
|
//let (item, floor_type) = item_manager.get_floor_item_by_id(&client.character, ClientItemId(pickup_item.item_id))?;
|
||||||
|
/*
|
||||||
|
let (item, floor_type) = item_state.get_floor_item(&client.character, &ClientItemId(pickup_item.item_id))?;
|
||||||
let remove_item = builder::message::remove_item_from_floor(area_client, item)?;
|
let remove_item = builder::message::remove_item_from_floor(area_client, item)?;
|
||||||
let create_item = match item {
|
let create_item = match item {
|
||||||
FloorItem::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id(), &individual_floor_item.item)?),
|
FloorItem::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id(), &individual_floor_item.item)?),
|
||||||
@ -142,8 +150,19 @@ where
|
|||||||
FloorItem::Meseta(_) => None,
|
FloorItem::Meseta(_) => None,
|
||||||
//_ => Some(builder::message::create_item(area_client, &item)?),
|
//_ => Some(builder::message::create_item(area_client, &item)?),
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
match item_manager.character_picks_up_item(entity_gateway, &mut client.character, ClientItemId(pickup_item.item_id)).await {
|
let (item, floor_type) = item_state.get_floor_item(&client.character.id, &ClientItemId(pickup_item.item_id))?;
|
||||||
|
let remove_item = builder::message::remove_item_from_floor(area_client, item)?;
|
||||||
|
let create_item = match &item.item {
|
||||||
|
FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, individual_floor_item)?),
|
||||||
|
FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())?),
|
||||||
|
FloorItemDetail::Meseta(_) => None,
|
||||||
|
//_ => Some(builder::message::create_item(area_client, &item)?),
|
||||||
|
};
|
||||||
|
|
||||||
|
//match item_manager.character_picks_up_item(entity_gateway, &mut client.character, ClientItemId(pickup_item.item_id)).await {
|
||||||
|
match pick_up_item(item_state, entity_gateway, &client.character, &ClientItemId(pickup_item.item_id)).await {
|
||||||
Ok(trigger_create_item) => {
|
Ok(trigger_create_item) => {
|
||||||
let remove_packets: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = match floor_type {
|
let remove_packets: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = match floor_type {
|
||||||
FloorType::Local => {
|
FloorType::Local => {
|
||||||
@ -180,7 +199,7 @@ pub async fn request_box_item<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
rooms: &mut Rooms,
|
rooms: &mut Rooms,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
@ -215,8 +234,8 @@ EG: EntityGateway
|
|||||||
item: item_drop,
|
item: item_drop,
|
||||||
};
|
};
|
||||||
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
||||||
let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await?; // TODO: unwrap
|
let floor_item = enemy_drops_item(item_state, entity_gateway, client.character.id, item_drop).await?;
|
||||||
let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, floor_item)?;
|
let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?;
|
||||||
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))))
|
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,13 +246,12 @@ EG: EntityGateway
|
|||||||
// item_manager is not mutable in this, but for reasons I don't quite understand it requires the unique access of it to compile here
|
// item_manager is not mutable in this, but for reasons I don't quite understand it requires the unique access of it to compile here
|
||||||
pub async fn send_bank_list(id: ClientId,
|
pub async fn send_bank_list(id: ClientId,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
{
|
{
|
||||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
let bank_items = item_manager.get_character_bank(&client.character)?;
|
let bank = item_state.get_character_bank(&client.character)?;
|
||||||
let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?;
|
let bank_items_pkt = builder::message::bank_item_list(bank);
|
||||||
let bank_items_pkt = builder::message::bank_item_list(bank_items, bank_meseta.0);
|
|
||||||
Ok(Box::new(vec![(id, SendShipPacket::BankItemList(bank_items_pkt))].into_iter()))
|
Ok(Box::new(vec![(id, SendShipPacket::BankItemList(bank_items_pkt))].into_iter()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +260,7 @@ pub async fn bank_interaction<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
@ -252,42 +270,24 @@ where
|
|||||||
let other_clients_in_area = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
let other_clients_in_area = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
let bank_action_pkts = match bank_interaction.action {
|
let bank_action_pkts = match bank_interaction.action {
|
||||||
BANK_ACTION_DEPOSIT => {
|
BANK_ACTION_DEPOSIT => {
|
||||||
let character_meseta = item_manager.get_character_meseta(&client.character.id)?;
|
|
||||||
let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?;
|
|
||||||
if bank_interaction.item_id == 0xFFFFFFFF {
|
if bank_interaction.item_id == 0xFFFFFFFF {
|
||||||
if character_meseta.0 >= bank_interaction.meseta_amount && (bank_interaction.meseta_amount + bank_meseta.0) <= BANK_MESETA_CAPACITY {
|
deposit_meseta(item_state, entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
|
||||||
let (character_meseta, bank_meseta) = item_manager.get_character_and_bank_meseta_mut(&client.character.id)?;
|
|
||||||
character_meseta.0 -= bank_interaction.meseta_amount;
|
|
||||||
bank_meseta.0 += bank_interaction.meseta_amount;
|
|
||||||
entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?;
|
|
||||||
// TODO: BankName
|
|
||||||
entity_gateway.set_bank_meseta(&client.character.id, item::BankName("".into()), *bank_meseta).await?;
|
|
||||||
}
|
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
item_manager.player_deposits_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?;
|
deposit_item(item_state, entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?;
|
||||||
let player_no_longer_has_item = builder::message::player_no_longer_has_item(area_client, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32);
|
let player_no_longer_has_item = builder::message::player_no_longer_has_item(area_client, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32);
|
||||||
vec![SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)))]
|
vec![SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)))]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BANK_ACTION_WITHDRAW => {
|
BANK_ACTION_WITHDRAW => {
|
||||||
let character_meseta = item_manager.get_character_meseta(&client.character.id)?;
|
|
||||||
let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?;
|
|
||||||
if bank_interaction.item_id == 0xFFFFFFFF {
|
if bank_interaction.item_id == 0xFFFFFFFF {
|
||||||
if (bank_meseta.0 >= bank_interaction.meseta_amount) && (character_meseta.0 + bank_interaction.meseta_amount <= INVENTORY_MESETA_CAPACITY) {
|
withdraw_meseta(item_state, entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
|
||||||
let (character_meseta, bank_meseta) = item_manager.get_character_and_bank_meseta_mut(&client.character.id)?;
|
|
||||||
character_meseta.0 += bank_interaction.meseta_amount;
|
|
||||||
bank_meseta.0 -= bank_interaction.meseta_amount;
|
|
||||||
entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?;
|
|
||||||
// TODO: BankName
|
|
||||||
entity_gateway.set_bank_meseta(&client.character.id, item::BankName("".into()), *bank_meseta).await?;
|
|
||||||
}
|
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let item_added_to_inventory = item_manager.player_withdraws_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?;
|
let item_added_to_inventory = withdraw_item(item_state, entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?;
|
||||||
let item_created = builder::message::create_withdrawn_inventory_item(area_client, item_added_to_inventory)?;
|
let item_created = builder::message::create_withdrawn_inventory_item2(area_client, &item_added_to_inventory)?;
|
||||||
vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(item_created)))]
|
vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(item_created)))]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -351,7 +351,7 @@ pub async fn buy_item<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
@ -359,7 +359,6 @@ where
|
|||||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
|
|
||||||
|
|
||||||
let (item, remove): (&(dyn ShopItem + Send + Sync), bool) = match buy_item.shop_type {
|
let (item, remove): (&(dyn ShopItem + Send + Sync), bool) = match buy_item.shop_type {
|
||||||
SHOP_OPTION_WEAPON => {
|
SHOP_OPTION_WEAPON => {
|
||||||
(client.weapon_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?, false)
|
(client.weapon_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?, false)
|
||||||
@ -379,16 +378,8 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let character_meseta = item_manager.get_character_meseta_mut(&client.character.id)?;
|
let inventory_item = buy_shop_item(item_state, entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as u32).await?;
|
||||||
if character_meseta.0 < (item.price() * buy_item.amount as usize) as u32 {
|
let create = builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)?;
|
||||||
return Err(ShipError::ShopError.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
character_meseta.0 -= (item.price() * buy_item.amount as usize) as u32;
|
|
||||||
entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?;
|
|
||||||
|
|
||||||
let inventory_item = item_manager.player_buys_item(entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as usize).await?;
|
|
||||||
let create = builder::message::create_withdrawn_inventory_item(area_client, inventory_item)?;
|
|
||||||
|
|
||||||
if remove {
|
if remove {
|
||||||
match buy_item.shop_type {
|
match buy_item.shop_type {
|
||||||
@ -424,7 +415,7 @@ pub async fn request_tek_item<EG>(id: ClientId,
|
|||||||
tek_request: &TekRequest,
|
tek_request: &TekRequest,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
@ -442,13 +433,13 @@ where
|
|||||||
|
|
||||||
client.tek = Some((ClientItemId(tek_request.item_id), special_mod, percent_mod, grind_mod));
|
client.tek = Some((ClientItemId(tek_request.item_id), special_mod, percent_mod, grind_mod));
|
||||||
|
|
||||||
let inventory = item_manager.get_character_inventory(&client.character)?;
|
let inventory = item_state.get_character_inventory(&client.character)?;
|
||||||
let item = inventory.get_item_by_id(ClientItemId(tek_request.item_id))
|
let item = inventory.get_by_client_id(&ClientItemId(tek_request.item_id))
|
||||||
.ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?;
|
.ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?;
|
||||||
let mut weapon = *item.individual()
|
let mut weapon = *item.item.as_individual()
|
||||||
.ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?
|
.ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?
|
||||||
.weapon()
|
.as_weapon()
|
||||||
.ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?;
|
.ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?;
|
||||||
|
|
||||||
weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked {
|
weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked {
|
||||||
special: special_mod,
|
special: special_mod,
|
||||||
@ -456,9 +447,7 @@ where
|
|||||||
grind: grind_mod,
|
grind: grind_mod,
|
||||||
});
|
});
|
||||||
|
|
||||||
let character_meseta = item_manager.get_character_meseta_mut(&client.character.id)?;
|
take_meseta(item_state, entity_gateway, &client.character.id, item::Meseta(100)).await?;
|
||||||
character_meseta.0 -= 100;
|
|
||||||
entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?;
|
|
||||||
|
|
||||||
let preview_pkt = builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?;
|
let preview_pkt = builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?;
|
||||||
|
|
||||||
@ -470,7 +459,7 @@ pub async fn accept_tek_item<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
@ -488,9 +477,9 @@ where
|
|||||||
percent: percent_mod,
|
percent: percent_mod,
|
||||||
grind: grind_mod,
|
grind: grind_mod,
|
||||||
};
|
};
|
||||||
let weapon = item_manager.replace_item_with_tekked(entity_gateway, &client.character, item_id, modifier).await?;
|
let weapon = apply_modifier(item_state, entity_gateway, &client.character, item_id, item::ItemModifier::WeaponModifier(modifier)).await?;
|
||||||
|
|
||||||
let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &item::ItemDetail::Weapon(weapon))?;
|
let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon)?;
|
||||||
|
|
||||||
let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
Ok(Box::new(neighbors.into_iter()
|
Ok(Box::new(neighbors.into_iter()
|
||||||
|
@ -6,14 +6,14 @@ use crate::ship::character::{FullCharacterBytesBuilder};
|
|||||||
use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError};
|
use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError};
|
||||||
//use crate::ship::items::;
|
//use crate::ship::items::;
|
||||||
use crate::ship::packet;
|
use crate::ship::packet;
|
||||||
use crate::ship::items::ItemManager;
|
use crate::ship::items::state::ItemState;
|
||||||
use crate::entity::gateway::EntityGateway;
|
use crate::entity::gateway::EntityGateway;
|
||||||
|
|
||||||
// this function needs a better home
|
// this function needs a better home
|
||||||
pub fn block_selected(id: ClientId,
|
pub fn block_selected(id: ClientId,
|
||||||
pkt: &MenuSelect,
|
pkt: &MenuSelect,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &ItemManager,
|
item_state: &ItemState,
|
||||||
level_table: &CharacterLevelTable)
|
level_table: &CharacterLevelTable)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
@ -21,15 +21,14 @@ pub fn block_selected(id: ClientId,
|
|||||||
|
|
||||||
let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp);
|
let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp);
|
||||||
|
|
||||||
let inventory = item_manager.get_character_inventory(&client.character).unwrap();
|
let inventory = item_state.get_character_inventory(&client.character).unwrap();
|
||||||
let meseta = item_manager.get_character_meseta(&client.character.id).unwrap();
|
let bank = item_state.get_character_bank(&client.character).unwrap();
|
||||||
let bank = item_manager.get_character_bank(&client.character).unwrap();
|
|
||||||
|
|
||||||
let fc = FullCharacterBytesBuilder::default()
|
let fc = FullCharacterBytesBuilder::default()
|
||||||
.character(&client.character)
|
.character(&client.character)
|
||||||
.stats(&stats)
|
.stats(&stats)
|
||||||
.level(level)
|
.level(level)
|
||||||
.meseta(*meseta)
|
.meseta(inventory.meseta)
|
||||||
.inventory(inventory)
|
.inventory(inventory)
|
||||||
.bank(bank)
|
.bank(bank)
|
||||||
.keyboard_config(&client.character.keyboard_config.as_bytes())
|
.keyboard_config(&client.character.keyboard_config.as_bytes())
|
||||||
@ -52,12 +51,12 @@ pub fn send_player_to_lobby(id: ClientId,
|
|||||||
_pkt: &CharData,
|
_pkt: &CharData,
|
||||||
client_location: &mut ClientLocation,
|
client_location: &mut ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_manager: &ItemManager,
|
item_state: &ItemState,
|
||||||
level_table: &CharacterLevelTable)
|
level_table: &CharacterLevelTable)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).map_err(|_| ShipError::TooManyClients)?;
|
let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).map_err(|_| ShipError::TooManyClients)?;
|
||||||
let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_manager, level_table)?;
|
let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, level_table)?;
|
||||||
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_manager, level_table)?;
|
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, level_table)?;
|
||||||
let neighbors = client_location.get_client_neighbors(id).unwrap();
|
let neighbors = client_location.get_client_neighbors(id).unwrap();
|
||||||
Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))]
|
Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -70,7 +69,7 @@ pub async fn change_lobby<EG: EntityGateway>(id: ClientId,
|
|||||||
requested_lobby: u32,
|
requested_lobby: u32,
|
||||||
client_location: &mut ClientLocation,
|
client_location: &mut ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_manager: &mut ItemManager,
|
item_state: &mut ItemState,
|
||||||
level_table: &CharacterLevelTable,
|
level_table: &CharacterLevelTable,
|
||||||
ship_rooms: &mut Rooms,
|
ship_rooms: &mut Rooms,
|
||||||
entity_gateway: &mut EG)
|
entity_gateway: &mut EG)
|
||||||
@ -87,7 +86,7 @@ pub async fn change_lobby<EG: EntityGateway>(id: ClientId,
|
|||||||
if client_location.get_client_neighbors(id)?.is_empty() {
|
if client_location.get_client_neighbors(id)?.is_empty() {
|
||||||
ship_rooms[old_room.0] = None;
|
ship_rooms[old_room.0] = None;
|
||||||
}
|
}
|
||||||
item_manager.remove_character_from_room(&client.character);
|
item_state.remove_character_from_room(&client.character);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location)?;
|
let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location)?;
|
||||||
@ -104,9 +103,9 @@ pub async fn change_lobby<EG: EntityGateway>(id: ClientId,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item_manager.load_character(entity_gateway, &client.character).await?;
|
item_state.load_character(entity_gateway, &client.character).await?;
|
||||||
let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_manager, level_table)?;
|
let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, level_table)?;
|
||||||
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_manager, level_table)?;
|
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, level_table)?;
|
||||||
let neighbors = client_location.get_client_neighbors(id).unwrap();
|
let neighbors = client_location.get_client_neighbors(id).unwrap();
|
||||||
Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))]
|
Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
use libpso::packet::ship::*;
|
use libpso::packet::ship::*;
|
||||||
use libpso::packet::messages::*;
|
use libpso::packet::messages::*;
|
||||||
use crate::entity::gateway::EntityGateway;
|
use crate::entity::gateway::EntityGateway;
|
||||||
|
use crate::entity::item::Meseta;
|
||||||
use crate::common::serverstate::ClientId;
|
use crate::common::serverstate::ClientId;
|
||||||
use crate::common::leveltable::CharacterLevelTable;
|
use crate::common::leveltable::CharacterLevelTable;
|
||||||
use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation};
|
use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation};
|
||||||
use crate::ship::location::{ClientLocation, ClientLocationError};
|
use crate::ship::location::{ClientLocation, ClientLocationError};
|
||||||
use crate::ship::items::{ItemManager, ClientItemId};
|
use crate::ship::items::ClientItemId;
|
||||||
use crate::ship::packet::builder;
|
use crate::ship::packet::builder;
|
||||||
|
use crate::ship::items::state::ItemState;
|
||||||
|
use crate::ship::items::tasks::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item, take_meseta};
|
||||||
|
|
||||||
pub async fn request_exp<EG: EntityGateway>(id: ClientId,
|
pub async fn request_exp<EG: EntityGateway>(id: ClientId,
|
||||||
request_exp: &RequestExp,
|
request_exp: &RequestExp,
|
||||||
@ -63,12 +66,12 @@ pub async fn request_exp<EG: EntityGateway>(id: ClientId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn player_drop_item<EG>(id: ClientId,
|
pub async fn player_drop_item<EG>(id: ClientId,
|
||||||
player_drop_item: &PlayerDropItem,
|
player_drop_item: &PlayerDropItem,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
rooms: &mut Rooms,
|
rooms: &mut Rooms,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
@ -80,7 +83,7 @@ where
|
|||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||||
let area = room.map_areas.get_area_map(player_drop_item.map_area)?;
|
let area = room.map_areas.get_area_map(player_drop_item.map_area)?;
|
||||||
item_manager.player_drop_item_on_shared_floor(entity_gateway, &client.character, ClientItemId(player_drop_item.item_id), (*area, player_drop_item.x, player_drop_item.y, player_drop_item.z)).await?;
|
drop_item(item_state, entity_gateway, &client.character, &ClientItemId(player_drop_item.item_id), *area, (player_drop_item.x, player_drop_item.y, player_drop_item.z)).await?;
|
||||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
let pdi = player_drop_item.clone();
|
let pdi = player_drop_item.clone();
|
||||||
Ok(Box::new(clients_in_area.into_iter()
|
Ok(Box::new(clients_in_area.into_iter()
|
||||||
@ -118,7 +121,7 @@ pub async fn no_longer_has_item<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
@ -132,7 +135,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
if no_longer_has_item.item_id == 0xFFFFFFFF {
|
if no_longer_has_item.item_id == 0xFFFFFFFF {
|
||||||
let dropped_meseta = item_manager.player_drops_meseta_on_shared_floor(entity_gateway, &mut client.character, drop_location, no_longer_has_item.amount as u32).await?;
|
let dropped_meseta = drop_meseta(item_state, entity_gateway, &client.character, drop_location.map_area, (drop_location.x, drop_location.z), no_longer_has_item.amount).await?;
|
||||||
|
|
||||||
let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?;
|
let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?;
|
||||||
let no_longer_has_meseta_pkt = builder::message::player_no_longer_has_meseta(area_client, no_longer_has_item.amount as u32);
|
let no_longer_has_meseta_pkt = builder::message::player_no_longer_has_meseta(area_client, no_longer_has_item.amount as u32);
|
||||||
@ -156,9 +159,9 @@ where
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, drop_location.item_id, drop_location, no_longer_has_item.amount as usize).await?;
|
let dropped_item = drop_partial_item(item_state, entity_gateway, &client.character, &drop_location.item_id, drop_location.map_area, (drop_location.x, drop_location.z), no_longer_has_item.amount).await?;
|
||||||
|
|
||||||
let dropped_item_pkt = builder::message::drop_split_stack(area_client, dropped_item)?;
|
let dropped_item_pkt = builder::message::drop_split_stack(area_client, &dropped_item)?;
|
||||||
client.item_drop_location = None;
|
client.item_drop_location = None;
|
||||||
|
|
||||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
@ -255,63 +258,61 @@ pub fn update_player_position(id: ClientId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn charge_attack<EG>(id: ClientId,
|
pub async fn charge_attack<EG>(id: ClientId,
|
||||||
charge: &ChargeAttack,
|
charge: &ChargeAttack,
|
||||||
clients: &mut Clients,
|
entity_gateway: &mut EG,
|
||||||
entity_gateway: &mut EG,
|
client_location: &ClientLocation,
|
||||||
item_manager: &mut ItemManager)
|
clients: &mut Clients,
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
item_state: &mut ItemState)
|
||||||
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
{
|
{
|
||||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
let meseta = item_manager.get_character_meseta_mut(&client.character.id)?;
|
|
||||||
|
|
||||||
if meseta.0 >= charge.meseta {
|
// TODO: should probably validate this to be a legit number, I'd just hardcode 200 but vjaya
|
||||||
meseta.0 -= charge.meseta;
|
take_meseta(item_state, entity_gateway, &client.character.id, Meseta(charge.meseta)).await?;
|
||||||
entity_gateway.set_character_meseta(&client.character.id, *meseta).await?;
|
|
||||||
// TODO: this should probably echo the packet
|
let charge = charge.clone();
|
||||||
Ok(Box::new(None.into_iter()))
|
Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||||
} else {
|
.map(move |client| {
|
||||||
Err(ShipError::NotEnoughMeseta(id, meseta.0).into())
|
(client.client, SendShipPacket::Message(Message::new(GameMessage::ChargeAttack(charge.clone()))))
|
||||||
}
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn use_item<EG>(id: ClientId,
|
pub async fn player_uses_item<EG>(id: ClientId,
|
||||||
player_use_tool: &PlayerUseItem,
|
player_use_tool: &PlayerUseItem,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
_client_location: &ClientLocation,
|
_client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
{
|
{
|
||||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
let item_used_type = item_manager.player_consumes_tool(entity_gateway, &mut client.character, ClientItemId(player_use_tool.item_id), 1).await?;
|
use_item(item_state, entity_gateway, &mut client.character, &ClientItemId(player_use_tool.item_id), 1).await?;
|
||||||
|
|
||||||
item_manager.use_item(item_used_type, entity_gateway, &mut client.character).await?;
|
|
||||||
Ok(Box::new(None.into_iter())) // TODO: should probably tell other players we used an item
|
Ok(Box::new(None.into_iter())) // TODO: should probably tell other players we used an item
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn player_used_medical_center<EG>(id: ClientId,
|
pub async fn player_used_medical_center<EG>(id: ClientId,
|
||||||
_pumc: &PlayerUsedMedicalCenter, // not needed?
|
pumc: &PlayerUsedMedicalCenter,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
{
|
{
|
||||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
let meseta = item_manager.get_character_meseta_mut(&client.character.id)?;
|
|
||||||
if meseta.0 >= 10 {
|
take_meseta(item_state, entity_gateway, &client.character.id, Meseta(10)).await?;
|
||||||
meseta.0 -= 10;
|
|
||||||
entity_gateway.set_character_meseta(&client.character.id, *meseta).await?;
|
let pumc = pumc.clone();
|
||||||
// TODO: this should probably echo the packet
|
Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||||
Ok(Box::new(None.into_iter()))
|
.map(move |client| {
|
||||||
} else {
|
(client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerUsedMedicalCenter(pumc.clone()))))
|
||||||
Err(ShipError::NotEnoughMeseta(id, meseta.0).into())
|
})))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -320,13 +321,13 @@ pub async fn player_feed_mag<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
{
|
{
|
||||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
item_manager.player_feeds_mag_item(entity_gateway, &client.character, ClientItemId(mag_feed.mag_id), ClientItemId(mag_feed.item_id)).await?;
|
feed_mag(item_state, entity_gateway, &client.character, &ClientItemId(mag_feed.mag_id), &ClientItemId(mag_feed.item_id)).await?;
|
||||||
|
|
||||||
let mag_feed = mag_feed.clone();
|
let mag_feed = mag_feed.clone();
|
||||||
Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter()
|
Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||||
@ -339,7 +340,7 @@ pub async fn player_equips_item<EG>(id: ClientId,
|
|||||||
pkt: &PlayerEquipItem,
|
pkt: &PlayerEquipItem,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
@ -351,7 +352,7 @@ where
|
|||||||
else {
|
else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
item_manager.player_equips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id), equip_slot).await?;
|
equip_item(item_state, entity_gateway, &client.character, &ClientItemId(pkt.item_id), equip_slot).await?;
|
||||||
Ok(Box::new(None.into_iter())) // TODO: tell other players you equipped an item
|
Ok(Box::new(None.into_iter())) // TODO: tell other players you equipped an item
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,13 +360,13 @@ pub async fn player_unequips_item<EG>(id: ClientId,
|
|||||||
pkt: &PlayerUnequipItem,
|
pkt: &PlayerUnequipItem,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
{
|
{
|
||||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
item_manager.player_unequips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id)).await?;
|
unequip_item(item_state, entity_gateway, &client.character, &ClientItemId(pkt.item_id)).await?;
|
||||||
Ok(Box::new(None.into_iter())) // TODO: tell other players if you unequip an item
|
Ok(Box::new(None.into_iter())) // TODO: tell other players if you unequip an item
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,28 +374,62 @@ pub async fn player_sorts_items<EG>(id: ClientId,
|
|||||||
pkt: &SortItems,
|
pkt: &SortItems,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_state: &mut ItemState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
{
|
{
|
||||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
item_manager.player_sorts_items(entity_gateway, &client.character, pkt.item_ids).await?;
|
let item_ids = pkt.item_ids
|
||||||
|
.iter()
|
||||||
|
.filter_map(|item_id| {
|
||||||
|
if *item_id != 0 {
|
||||||
|
Some(ClientItemId(*item_id))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
sort_inventory(item_state, entity_gateway, &client.character, item_ids).await?;
|
||||||
Ok(Box::new(None.into_iter())) // TODO: clients probably care about each others item orders
|
Ok(Box::new(None.into_iter())) // TODO: clients probably care about each others item orders
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn player_sells_item<EG> (id: ClientId,
|
pub async fn player_sells_item<EG> (id: ClientId,
|
||||||
sold_item: &PlayerSoldItem,
|
sold_item: &PlayerSoldItem,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
// client_location: &ClientLocation,
|
clients: &mut Clients,
|
||||||
clients: &mut Clients,
|
item_state: &mut ItemState)
|
||||||
item_manager: &mut ItemManager)
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
{
|
{
|
||||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
item_manager.player_sells_item(entity_gateway, &mut client.character, ClientItemId(sold_item.item_id), sold_item.amount as usize).await?;
|
sell_item(item_state, entity_gateway, &client.character, ClientItemId(sold_item.item_id), sold_item.amount as u32).await?;
|
||||||
// TODO: send the packet to other clients
|
// TODO: send the packet to other clients
|
||||||
Ok(Box::new(None.into_iter()))
|
Ok(Box::new(None.into_iter())) // TODO: Do clients care about the order of other clients items?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn player_killed_monster<EG>( id: ClientId,
|
||||||
|
pkt: &KillMonster,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
client_location: &ClientLocation,
|
||||||
|
clients: &Clients,
|
||||||
|
rooms: &mut Rooms,
|
||||||
|
item_manager: &mut ItemManager)
|
||||||
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
|
where
|
||||||
|
EG: EntityGateway
|
||||||
|
{
|
||||||
|
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
|
let equipped_items = entity_gateway.get_character_equips(&client.character.id).await?;
|
||||||
|
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
|
let room = rooms.get_mut(room_id.0)
|
||||||
|
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?
|
||||||
|
.as_mut()
|
||||||
|
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||||
|
let enemy_id = u16::from_le_bytes([pkt.client, pkt.target]) & 0x0FFF;
|
||||||
|
let monstertype = room.maps.enemy_by_id(enemy_id as usize)?.monster;
|
||||||
|
item_manager.increase_kill_counters(entity_gateway, &client.character, &equipped_items, monstertype).await?;
|
||||||
|
Ok(Box::new(None.into_iter())) // TODO: forward to other clients in the room
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,14 @@ use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients};
|
|||||||
use crate::ship::location::{ClientLocation, RoomId, RoomLobby, ClientLocationError};
|
use crate::ship::location::{ClientLocation, RoomId, RoomLobby, ClientLocationError};
|
||||||
use crate::ship::packet::builder;
|
use crate::ship::packet::builder;
|
||||||
use crate::ship::room;
|
use crate::ship::room;
|
||||||
use crate::ship::items::ItemManager;
|
use crate::ship::items::state::ItemState;
|
||||||
use std::convert::{TryFrom};
|
use std::convert::{TryFrom};
|
||||||
|
|
||||||
pub fn create_room(id: ClientId,
|
pub fn create_room(id: ClientId,
|
||||||
create_room: &CreateRoom,
|
create_room: &CreateRoom,
|
||||||
client_location: &mut ClientLocation,
|
client_location: &mut ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager,
|
item_state: &mut ItemState,
|
||||||
level_table: &CharacterLevelTable,
|
level_table: &CharacterLevelTable,
|
||||||
rooms: &mut Rooms)
|
rooms: &mut Rooms)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||||
@ -45,7 +45,7 @@ pub fn create_room(id: ClientId,
|
|||||||
let mut room = room::RoomState::from_create_room(create_room, client.character.section_id).unwrap();
|
let mut room = room::RoomState::from_create_room(create_room, client.character.section_id).unwrap();
|
||||||
room.bursting = true;
|
room.bursting = true;
|
||||||
|
|
||||||
item_manager.add_character_to_room(room_id, &client.character, area_client);
|
item_state.add_character_to_room(room_id, &client.character, area_client);
|
||||||
|
|
||||||
let join_room = builder::room::join_room(id, clients, client_location, room_id, &room)?;
|
let join_room = builder::room::join_room(id, clients, client_location, room_id, &room)?;
|
||||||
rooms[room_id.0] = Some(room);
|
rooms[room_id.0] = Some(room);
|
||||||
@ -80,7 +80,7 @@ pub fn join_room(id: ClientId,
|
|||||||
pkt: &MenuSelect,
|
pkt: &MenuSelect,
|
||||||
client_location: &mut ClientLocation,
|
client_location: &mut ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager,
|
item_state: &mut ItemState,
|
||||||
level_table: &CharacterLevelTable,
|
level_table: &CharacterLevelTable,
|
||||||
rooms: &mut Rooms)
|
rooms: &mut Rooms)
|
||||||
-> Result<Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send>, ShipError> {
|
-> Result<Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||||
@ -119,11 +119,11 @@ pub fn join_room(id: ClientId,
|
|||||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
|
|
||||||
item_manager.add_character_to_room(room_id, &client.character, area_client);
|
item_state.add_character_to_room(room_id, &client.character, area_client);
|
||||||
|
|
||||||
let leader = client_location.get_room_leader(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
let leader = client_location.get_room_leader(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
let join_room = builder::room::join_room(id, clients, client_location, room_id, room)?;
|
let join_room = builder::room::join_room(id, clients, client_location, room_id, room)?;
|
||||||
let add_to = builder::room::add_to_room(id, client, &area_client, &leader, item_manager, level_table, room_id)?;
|
let add_to = builder::room::add_to_room(id, client, &area_client, &leader, item_state, level_table, room_id)?;
|
||||||
|
|
||||||
let room = rooms.get_mut(room_id.0).unwrap().as_mut().unwrap();
|
let room = rooms.get_mut(room_id.0).unwrap().as_mut().unwrap();
|
||||||
room.bursting = true;
|
room.bursting = true;
|
||||||
|
@ -4,16 +4,19 @@ use libpso::packet::messages::*;
|
|||||||
use crate::common::serverstate::ClientId;
|
use crate::common::serverstate::ClientId;
|
||||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
||||||
use crate::ship::location::{ClientLocation, ClientLocationError};
|
use crate::ship::location::{ClientLocation, ClientLocationError};
|
||||||
use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, ItemToTradeDetail};
|
use crate::ship::items::ClientItemId;
|
||||||
use crate::ship::items::inventory::InventoryItem;
|
use crate::ship::items::state::{ItemState, ItemStateError};
|
||||||
|
use crate::ship::items::inventory::InventoryItemDetail;
|
||||||
use crate::ship::trade::{TradeItem, TradeState, TradeStatus};
|
use crate::ship::trade::{TradeItem, TradeState, TradeStatus};
|
||||||
use crate::entity::gateway::EntityGateway;
|
use crate::entity::gateway::EntityGateway;
|
||||||
use crate::ship::packet::builder;
|
use crate::ship::packet::builder;
|
||||||
|
use crate::ship::items::tasks::trade_items;
|
||||||
|
use crate::ship::location::{AreaClient, RoomId};
|
||||||
|
use crate::entity::item::Meseta;
|
||||||
|
|
||||||
pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01);
|
pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01);
|
||||||
pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF);
|
pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF);
|
||||||
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
|
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
|
||||||
pub enum TradeError {
|
pub enum TradeError {
|
||||||
#[error("no partner")]
|
#[error("no partner")]
|
||||||
@ -51,9 +54,9 @@ pub async fn trade_request(id: ClientId,
|
|||||||
target: u32,
|
target: u32,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager,
|
item_state: &mut ItemState,
|
||||||
trades: &mut TradeState)
|
trades: &mut TradeState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
||||||
{
|
{
|
||||||
let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet
|
let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet
|
||||||
match trade_request.trade {
|
match trade_request.trade {
|
||||||
@ -114,18 +117,18 @@ pub async fn trade_request(id: ClientId,
|
|||||||
.with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
.with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||||
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
||||||
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
||||||
let inventory = item_manager.get_character_inventory(&client.character)?;
|
let inventory = item_state.get_character_inventory(&client.character)?;
|
||||||
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
||||||
this.meseta += amount as usize;
|
this.meseta += amount as usize;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let item = inventory.get_item_by_id(ClientItemId(item_id)).ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item_id)))?;
|
let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?;
|
||||||
|
|
||||||
match item {
|
match &item.item {
|
||||||
InventoryItem::Individual(_) => {
|
InventoryItemDetail::Individual(_) => {
|
||||||
this.items.push(TradeItem::Individual(ClientItemId(item_id)));
|
this.items.push(TradeItem::Individual(ClientItemId(item_id)));
|
||||||
},
|
},
|
||||||
InventoryItem::Stacked(stacked_item) => {
|
InventoryItemDetail::Stacked(stacked_item) => {
|
||||||
if stacked_item.count() < amount as usize {
|
if stacked_item.count() < amount as usize {
|
||||||
return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into());
|
return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into());
|
||||||
}
|
}
|
||||||
@ -160,20 +163,20 @@ pub async fn trade_request(id: ClientId,
|
|||||||
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
||||||
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
||||||
let client = clients.get(&this.client())?; //.ok_or(ShipError::ClientNotFound(id)).ok()?;
|
let client = clients.get(&this.client())?; //.ok_or(ShipError::ClientNotFound(id)).ok()?;
|
||||||
let inventory = item_manager.get_character_inventory(&client.character).ok()?;
|
let inventory = item_state.get_character_inventory(&client.character).ok()?;
|
||||||
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
||||||
this.meseta -= amount as usize;
|
this.meseta -= amount as usize;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let item = inventory.get_item_by_id(ClientItemId(item_id))?;
|
let item = inventory.get_by_client_id(&ClientItemId(item_id))?;
|
||||||
|
|
||||||
match item {
|
match &item.item {
|
||||||
InventoryItem::Individual(_) => {
|
InventoryItemDetail::Individual(_) => {
|
||||||
this.items.retain(|item| {
|
this.items.retain(|item| {
|
||||||
item.item_id() != ClientItemId(item_id)
|
item.item_id() != ClientItemId(item_id)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
InventoryItem::Stacked(_stacked_item) => {
|
InventoryItemDetail::Stacked(_stacked_item) => {
|
||||||
let trade_item_index = this.items.iter()
|
let trade_item_index = this.items.iter()
|
||||||
.position(|item| {
|
.position(|item| {
|
||||||
item.item_id() == ClientItemId(item_id)
|
item.item_id() == ClientItemId(item_id)
|
||||||
@ -293,7 +296,7 @@ pub async fn inner_items_to_trade(id: ClientId,
|
|||||||
items_to_trade: &ItemsToTrade,
|
items_to_trade: &ItemsToTrade,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager,
|
item_state: &mut ItemState,
|
||||||
trades: &mut TradeState)
|
trades: &mut TradeState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
{
|
{
|
||||||
@ -305,7 +308,7 @@ pub async fn inner_items_to_trade(id: ClientId,
|
|||||||
|
|
||||||
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
||||||
let other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?;
|
let other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?;
|
||||||
let inventory = item_manager.get_character_inventory(&client.character)?;
|
let inventory = item_state.get_character_inventory(&client.character)?;
|
||||||
|
|
||||||
if items_to_trade.count as usize != (this.items.len() + (if this.meseta != 0 { 1 } else { 0 })) {
|
if items_to_trade.count as usize != (this.items.len() + (if this.meseta != 0 { 1 } else { 0 })) {
|
||||||
return Err(TradeError::MismatchedTradeItems.into())
|
return Err(TradeError::MismatchedTradeItems.into())
|
||||||
@ -320,8 +323,8 @@ pub async fn inner_items_to_trade(id: ClientId,
|
|||||||
return Err(TradeError::InvalidItemId(ClientItemId(item.item_id)).into())
|
return Err(TradeError::InvalidItemId(ClientItemId(item.item_id)).into())
|
||||||
}
|
}
|
||||||
let amount = u32::from_le_bytes(item.item_data2);
|
let amount = u32::from_le_bytes(item.item_data2);
|
||||||
let character_meseta = item_manager.get_character_meseta(&client.character.id).map_err(|_| TradeError::InvalidMeseta)?;
|
let character_meseta = item_state.get_character_inventory(&client.character).map_err(|_| TradeError::InvalidMeseta)?.meseta;
|
||||||
let other_character_meseta = item_manager.get_character_meseta(&other_client.character.id).map_err(|_| TradeError::InvalidMeseta)?;
|
let other_character_meseta = item_state.get_character_inventory(&other_client.character).map_err(|_| TradeError::InvalidMeseta)?.meseta;
|
||||||
if amount > character_meseta.0 {
|
if amount > character_meseta.0 {
|
||||||
return Err(TradeError::InvalidMeseta.into())
|
return Err(TradeError::InvalidMeseta.into())
|
||||||
}
|
}
|
||||||
@ -334,8 +337,8 @@ pub async fn inner_items_to_trade(id: ClientId,
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let real_item = inventory.get_item_by_id(ClientItemId(item.item_id))
|
let real_item = inventory.get_by_client_id(&ClientItemId(item.item_id))
|
||||||
.ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item.item_id)))?;
|
.ok_or(ItemStateError::InvalidItemId(ClientItemId(item.item_id)))?;
|
||||||
let real_trade_item = this.items
|
let real_trade_item = this.items
|
||||||
.iter()
|
.iter()
|
||||||
.find(|i| i.item_id() == ClientItemId(item.item_id))
|
.find(|i| i.item_id() == ClientItemId(item.item_id))
|
||||||
@ -345,28 +348,28 @@ pub async fn inner_items_to_trade(id: ClientId,
|
|||||||
.cloned().collect::<Vec<u8>>()
|
.cloned().collect::<Vec<u8>>()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match real_item {
|
match &real_item.item {
|
||||||
InventoryItem::Individual(_individual_inventory_item) => {
|
InventoryItemDetail::Individual(_individual_inventory_item) => {
|
||||||
if real_item.as_client_bytes() == trade_item_bytes {
|
if real_item.item.as_client_bytes() == trade_item_bytes {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into())
|
Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
InventoryItem::Stacked(stacked_inventory_item) => {
|
InventoryItemDetail::Stacked(stacked_inventory_item) => {
|
||||||
if real_item.as_client_bytes()[0..4] == trade_item_bytes[0..4] {
|
if real_item.item.as_client_bytes()[0..4] == trade_item_bytes[0..4] {
|
||||||
let amount = trade_item_bytes[5] as usize;
|
let amount = trade_item_bytes[5] as usize;
|
||||||
if amount <= stacked_inventory_item.entity_ids.len() {
|
if amount <= stacked_inventory_item.entity_ids.len() {
|
||||||
if real_trade_item.stacked().ok_or(TradeError::SketchyTrade)?.1 == amount {
|
if real_trade_item.stacked().ok_or(TradeError::SketchyTrade)?.1 == amount {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into())
|
Err(TradeError::InvalidStackAmount(real_item.item_id, amount).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into())
|
Err(TradeError::InvalidStackAmount(real_item.item_id, amount).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -405,11 +408,11 @@ pub async fn items_to_trade(id: ClientId,
|
|||||||
items_to_trade_pkt: &ItemsToTrade,
|
items_to_trade_pkt: &ItemsToTrade,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager,
|
item_state: &mut ItemState,
|
||||||
trades: &mut TradeState)
|
trades: &mut TradeState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
{
|
{
|
||||||
let t = inner_items_to_trade(id, items_to_trade_pkt, client_location, clients, item_manager, trades).await;
|
let t = inner_items_to_trade(id, items_to_trade_pkt, client_location, clients, item_state, trades).await;
|
||||||
match t {
|
match t {
|
||||||
Ok(p) => Ok(p),
|
Ok(p) => Ok(p),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@ -429,7 +432,7 @@ pub async fn trade_confirmed<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager,
|
item_state: &mut ItemState,
|
||||||
trades: &mut TradeState)
|
trades: &mut TradeState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
@ -437,9 +440,9 @@ where
|
|||||||
{
|
{
|
||||||
enum TradeReady<'a> {
|
enum TradeReady<'a> {
|
||||||
OnePlayer,
|
OnePlayer,
|
||||||
BothPlayers(crate::ship::location::RoomId,
|
BothPlayers(RoomId,
|
||||||
(crate::ship::location::AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState),
|
(AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState),
|
||||||
(crate::ship::location::AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)),
|
(AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)),
|
||||||
}
|
}
|
||||||
|
|
||||||
let trade_instructions = trades
|
let trade_instructions = trades
|
||||||
@ -472,37 +475,63 @@ where
|
|||||||
TradeReady::OnePlayer => {
|
TradeReady::OnePlayer => {
|
||||||
Ok(Box::new(None.into_iter()) as Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>)
|
Ok(Box::new(None.into_iter()) as Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>)
|
||||||
},
|
},
|
||||||
TradeReady::BothPlayers(room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => {
|
TradeReady::BothPlayers(_room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => {
|
||||||
let traded_items = item_manager.trade_items(entity_gateway,
|
let remove_item_packets = this.items
|
||||||
room_id,
|
.clone()
|
||||||
(&this_local_client, &this_client.character, &this.items, this.meseta),
|
.into_iter()
|
||||||
(&other_local_client, &other_client.character, &other.items, other.meseta)).await?;
|
.map(move |item| {
|
||||||
|
(this_local_client, item)
|
||||||
|
})
|
||||||
|
.chain(other.items
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(move |item| {
|
||||||
|
(other_local_client, item)
|
||||||
|
}))
|
||||||
|
.map(|(client, item)| {
|
||||||
|
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(client, item.item_id(), item.amount() as u32))
|
||||||
|
});
|
||||||
|
|
||||||
|
let (this_new_items, other_new_items) = trade_items(item_state,
|
||||||
|
entity_gateway,
|
||||||
|
(&this_local_client, &this_client.character, &this.items, Meseta(this.meseta as u32)),
|
||||||
|
(&other_local_client, &other_client.character, &other.items, Meseta(other.meseta as u32))).await?;
|
||||||
|
|
||||||
|
let create_item_packets = this_new_items
|
||||||
|
.into_iter()
|
||||||
|
.map(move |item| {
|
||||||
|
(this_local_client, item)
|
||||||
|
})
|
||||||
|
.chain(other_new_items
|
||||||
|
.into_iter()
|
||||||
|
.map(move |item| {
|
||||||
|
(other_local_client, item)
|
||||||
|
}))
|
||||||
|
.map(|(client, item)| {
|
||||||
|
match item.item {
|
||||||
|
InventoryItemDetail::Individual(individual_item) => {
|
||||||
|
GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item).unwrap())
|
||||||
|
},
|
||||||
|
InventoryItemDetail::Stacked(stacked_item) => {
|
||||||
|
GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let meseta_packets = vec![(this_local_client, other_local_client, this.meseta), (other_local_client, this_local_client, other.meseta)]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, _, meseta)| *meseta != 0)
|
||||||
|
.flat_map(|(this, other, meseta)| {
|
||||||
|
[
|
||||||
|
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(this, MESETA_ITEM_ID, meseta as u32)),
|
||||||
|
GameMessage::CreateItem(builder::message::create_meseta(other, meseta)),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
let clients_in_room = client_location.get_all_clients_by_client(id)?;
|
let clients_in_room = client_location.get_all_clients_by_client(id)?;
|
||||||
let traded_item_packets = traded_items
|
let traded_item_packets = remove_item_packets
|
||||||
.into_iter()
|
.chain(create_item_packets)
|
||||||
.flat_map(|item| {
|
.chain(meseta_packets)
|
||||||
match item.item_detail {
|
|
||||||
ItemToTradeDetail::Individual(item_detail) => {
|
|
||||||
[
|
|
||||||
GameMessage::CreateItem(builder::message::create_individual_item(item.add_to, item.new_item_id, &item_detail).unwrap()),
|
|
||||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, 1)) // TODO: amount = ?
|
|
||||||
]
|
|
||||||
},
|
|
||||||
ItemToTradeDetail::Stacked(tool, amount) => {
|
|
||||||
[
|
|
||||||
GameMessage::CreateItem(builder::message::create_stacked_item(item.add_to, item.new_item_id, &tool, amount).unwrap()),
|
|
||||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, amount as u32))
|
|
||||||
]
|
|
||||||
},
|
|
||||||
ItemToTradeDetail::Meseta(amount) => {
|
|
||||||
[
|
|
||||||
GameMessage::CreateItem(builder::message::create_meseta(item.add_to, amount)),
|
|
||||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, amount as u32))
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flat_map(move |packet| {
|
.flat_map(move |packet| {
|
||||||
clients_in_room
|
clients_in_room
|
||||||
.clone()
|
.clone()
|
||||||
@ -521,6 +550,7 @@ where
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let close_trade = vec![
|
let close_trade = vec![
|
||||||
(this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())),
|
(this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())),
|
||||||
(other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default()))
|
(other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default()))
|
||||||
|
@ -203,6 +203,7 @@ pub struct RoomState {
|
|||||||
pub rare_monster_table: Box<RareMonsterAppearTable>,
|
pub rare_monster_table: Box<RareMonsterAppearTable>,
|
||||||
pub quest_group: QuestCategoryType,
|
pub quest_group: QuestCategoryType,
|
||||||
pub quests: Vec<quests::QuestList>,
|
pub quests: Vec<quests::QuestList>,
|
||||||
|
pub redbox: bool,
|
||||||
// items on ground
|
// items on ground
|
||||||
// enemy info
|
// enemy info
|
||||||
}
|
}
|
||||||
@ -316,6 +317,11 @@ impl RoomState {
|
|||||||
map_areas: MapAreaLookup::new(&room_mode.episode()),
|
map_areas: MapAreaLookup::new(&room_mode.episode()),
|
||||||
quest_group: QuestCategoryType::Standard,
|
quest_group: QuestCategoryType::Standard,
|
||||||
quests: room_quests,
|
quests: room_quests,
|
||||||
|
redbox: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn toggle_redbox_mode(&mut self) {
|
||||||
|
self.redbox = !self.redbox;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
109
src/ship/ship.rs
109
src/ship/ship.rs
@ -41,44 +41,71 @@ pub type Rooms = [Option<room::RoomState>; MAX_ROOMS];
|
|||||||
pub type Clients = HashMap<ClientId, ClientState>;
|
pub type Clients = HashMap<ClientId, ClientState>;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("shiperror {0:?}")]
|
|
||||||
pub enum ShipError {
|
pub enum ShipError {
|
||||||
|
#[error("client not found {0}")]
|
||||||
ClientNotFound(ClientId),
|
ClientNotFound(ClientId),
|
||||||
|
#[error("no character in slot {0} {1}")]
|
||||||
NoCharacterInSlot(ClientId, u32),
|
NoCharacterInSlot(ClientId, u32),
|
||||||
|
#[error("invalid slot {0} {1}")]
|
||||||
InvalidSlot(ClientId, u32),
|
InvalidSlot(ClientId, u32),
|
||||||
#[error("")]
|
#[error("too many clients")]
|
||||||
TooManyClients,
|
TooManyClients,
|
||||||
|
#[error("client error location {0}")]
|
||||||
ClientLocationError(#[from] ClientLocationError),
|
ClientLocationError(#[from] ClientLocationError),
|
||||||
|
#[error("get neighbor error {0}")]
|
||||||
GetNeighborError(#[from] GetNeighborError),
|
GetNeighborError(#[from] GetNeighborError),
|
||||||
|
#[error("get clients error {0}")]
|
||||||
GetClientsError(#[from] GetClientsError),
|
GetClientsError(#[from] GetClientsError),
|
||||||
|
#[error("get area error {0}")]
|
||||||
GetAreaError(#[from] GetAreaError),
|
GetAreaError(#[from] GetAreaError),
|
||||||
|
#[error("maps error {0}")]
|
||||||
MapsError(#[from] MapsError),
|
MapsError(#[from] MapsError),
|
||||||
|
#[error("map area error {0}")]
|
||||||
MapAreaError(#[from] MapAreaError),
|
MapAreaError(#[from] MapAreaError),
|
||||||
|
#[error("invalid room {0}")]
|
||||||
InvalidRoom(u32),
|
InvalidRoom(u32),
|
||||||
|
#[error("monster already droppped item {0} {1}")]
|
||||||
MonsterAlreadyDroppedItem(ClientId, u16),
|
MonsterAlreadyDroppedItem(ClientId, u16),
|
||||||
|
#[error("slice error {0}")]
|
||||||
SliceError(#[from] std::array::TryFromSliceError),
|
SliceError(#[from] std::array::TryFromSliceError),
|
||||||
#[error("")]
|
#[error("item error")]
|
||||||
ItemError, // TODO: refine this
|
ItemError, // TODO: refine this
|
||||||
|
#[error("pick up invalid item id {0}")]
|
||||||
PickUpInvalidItemId(u32),
|
PickUpInvalidItemId(u32),
|
||||||
|
#[error("drop invalid item id {0}")]
|
||||||
DropInvalidItemId(u32),
|
DropInvalidItemId(u32),
|
||||||
ItemManagerError(#[from] items::ItemManagerError),
|
#[error("item state error {0}")]
|
||||||
#[error("")]
|
ItemStateError(#[from] items::state::ItemStateError),
|
||||||
|
#[error("item drop location not set")]
|
||||||
ItemDropLocationNotSet,
|
ItemDropLocationNotSet,
|
||||||
|
#[error("box already dropped item {0} {1}")]
|
||||||
BoxAlreadyDroppedItem(ClientId, u16),
|
BoxAlreadyDroppedItem(ClientId, u16),
|
||||||
|
#[error("invalid quest category {0}")]
|
||||||
InvalidQuestCategory(u32),
|
InvalidQuestCategory(u32),
|
||||||
|
#[error("invalid quest {0}")]
|
||||||
InvalidQuest(u32),
|
InvalidQuest(u32),
|
||||||
|
#[error("invalid quest filename {0}")]
|
||||||
InvalidQuestFilename(String),
|
InvalidQuestFilename(String),
|
||||||
|
#[error("io error {0}")]
|
||||||
IoError(#[from] std::io::Error),
|
IoError(#[from] std::io::Error),
|
||||||
|
#[error("not enough meseta {0} {1}")]
|
||||||
NotEnoughMeseta(ClientId, u32),
|
NotEnoughMeseta(ClientId, u32),
|
||||||
#[error("")]
|
#[error("shop error")]
|
||||||
ShopError,
|
ShopError,
|
||||||
|
#[error("gateway error {0}")]
|
||||||
GatewayError(#[from] GatewayError),
|
GatewayError(#[from] GatewayError),
|
||||||
|
#[error("unknown monster {0}")]
|
||||||
UnknownMonster(crate::ship::monster::MonsterType),
|
UnknownMonster(crate::ship::monster::MonsterType),
|
||||||
|
#[error("invalid ship {0}")]
|
||||||
InvalidShip(usize),
|
InvalidShip(usize),
|
||||||
|
#[error("invalid block {0}")]
|
||||||
InvalidBlock(usize),
|
InvalidBlock(usize),
|
||||||
|
#[error("invalid item {0}")]
|
||||||
InvalidItem(items::ClientItemId),
|
InvalidItem(items::ClientItemId),
|
||||||
#[error("tradeerror {0}")]
|
#[error("trade error {0}")]
|
||||||
TradeError(#[from] crate::ship::packet::handler::trade::TradeError),
|
TradeError(#[from] crate::ship::packet::handler::trade::TradeError),
|
||||||
|
#[error("trade state error {0}")]
|
||||||
|
TradeStateError(#[from] crate::ship::trade::TradeStateError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -403,7 +430,7 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
|
|||||||
clients: HashMap::new(),
|
clients: HashMap::new(),
|
||||||
level_table: CharacterLevelTable::default(),
|
level_table: CharacterLevelTable::default(),
|
||||||
name: self.name.unwrap_or_else(|| "NAMENOTSET".into()),
|
name: self.name.unwrap_or_else(|| "NAMENOTSET".into()),
|
||||||
item_manager: items::ItemManager::default(),
|
item_state: items::state::ItemState::default(),
|
||||||
ip: self.ip.unwrap_or_else(|| Ipv4Addr::new(127,0,0,1)),
|
ip: self.ip.unwrap_or_else(|| Ipv4Addr::new(127,0,0,1)),
|
||||||
port: self.port.unwrap_or(SHIP_PORT),
|
port: self.port.unwrap_or(SHIP_PORT),
|
||||||
shops: Box::new(ItemShops::default()),
|
shops: Box::new(ItemShops::default()),
|
||||||
@ -447,7 +474,7 @@ pub struct ShipServerState<EG: EntityGateway> {
|
|||||||
pub clients: Clients,
|
pub clients: Clients,
|
||||||
level_table: CharacterLevelTable,
|
level_table: CharacterLevelTable,
|
||||||
name: String,
|
name: String,
|
||||||
item_manager: items::ItemManager,
|
item_state: items::state::ItemState,
|
||||||
shops: Box<ItemShops>,
|
shops: Box<ItemShops>,
|
||||||
pub blocks: Blocks,
|
pub blocks: Blocks,
|
||||||
|
|
||||||
@ -477,7 +504,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
},
|
},
|
||||||
GameMessage::PlayerDropItem(player_drop_item) => {
|
GameMessage::PlayerDropItem(player_drop_item) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await?
|
handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::DropCoordinates(drop_coordinates) => {
|
GameMessage::DropCoordinates(drop_coordinates) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
@ -485,7 +512,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
},
|
},
|
||||||
GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => {
|
GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
handler::message::no_longer_has_item(id, no_longer_has_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
handler::message::no_longer_has_item(id, no_longer_has_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) |
|
GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) |
|
||||||
GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) |
|
GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) |
|
||||||
@ -495,30 +522,36 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
handler::message::update_player_position(id, msg, &mut self.clients, &block.client_location, &block.rooms)?
|
handler::message::update_player_position(id, msg, &mut self.clients, &block.client_location, &block.rooms)?
|
||||||
},
|
},
|
||||||
GameMessage::ChargeAttack(charge_attack) => {
|
GameMessage::ChargeAttack(charge_attack) => {
|
||||||
handler::message::charge_attack(id, charge_attack, &mut self.clients, &mut self.entity_gateway, &mut self.item_manager).await?
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
|
handler::message::charge_attack(id, charge_attack, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::PlayerUseItem(player_use_item) => {
|
GameMessage::PlayerUseItem(player_use_item) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
handler::message::use_item(id, player_use_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
handler::message::player_uses_item(id, player_use_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => {
|
GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => {
|
||||||
handler::message::player_used_medical_center(id, player_used_medical_center, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
|
handler::message::player_used_medical_center(id, player_used_medical_center, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::PlayerFeedMag(player_feed_mag) => {
|
GameMessage::PlayerFeedMag(player_feed_mag) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
handler::message::player_feed_mag(id, player_feed_mag, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_manager).await?
|
handler::message::player_feed_mag(id, player_feed_mag, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::PlayerEquipItem(player_equip_item) => {
|
GameMessage::PlayerEquipItem(player_equip_item) => {
|
||||||
handler::message::player_equips_item(id, player_equip_item, &mut self.entity_gateway, &self.clients, &mut self.item_manager).await?
|
handler::message::player_equips_item(id, player_equip_item, &mut self.entity_gateway, &self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::PlayerUnequipItem(player_unequip_item) => {
|
GameMessage::PlayerUnequipItem(player_unequip_item) => {
|
||||||
handler::message::player_unequips_item(id, player_unequip_item, &mut self.entity_gateway, &self.clients, &mut self.item_manager).await?
|
handler::message::player_unequips_item(id, player_unequip_item, &mut self.entity_gateway, &self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::SortItems(sort_items) => {
|
GameMessage::SortItems(sort_items) => {
|
||||||
handler::message::player_sorts_items(id, sort_items, &mut self.entity_gateway, &self.clients, &mut self.item_manager).await?
|
handler::message::player_sorts_items(id, sort_items, &mut self.entity_gateway, &self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::PlayerSoldItem(player_sold_item) => {
|
GameMessage::PlayerSoldItem(player_sold_item) => {
|
||||||
handler::message::player_sells_item(id, player_sold_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?
|
handler::message::player_sells_item(id, player_sold_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_state).await?
|
||||||
|
},
|
||||||
|
GameMessage::KillMonster(kill_monster) => {
|
||||||
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
|
handler::message::player_killed_monster(id, kill_monster, &mut self.entity_gateway, &block.client_location, &self.clients, &mut block.rooms, &mut self.item_manager).await?
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let cmsg = msg.clone();
|
let cmsg = msg.clone();
|
||||||
@ -539,34 +572,34 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients)
|
handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients)
|
||||||
},
|
},
|
||||||
GameMessage::RequestItem(request_item) => {
|
GameMessage::RequestItem(request_item) => {
|
||||||
handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await?
|
handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::PickupItem(pickup_item) => {
|
GameMessage::PickupItem(pickup_item) => {
|
||||||
handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::BoxDropRequest(box_drop_request) => {
|
GameMessage::BoxDropRequest(box_drop_request) => {
|
||||||
handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await?
|
handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::BankRequest(_bank_request) => {
|
GameMessage::BankRequest(_bank_request) => {
|
||||||
handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await?
|
handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::BankInteraction(bank_interaction) => {
|
GameMessage::BankInteraction(bank_interaction) => {
|
||||||
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::ShopRequest(shop_request) => {
|
GameMessage::ShopRequest(shop_request) => {
|
||||||
handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &self.level_table, &mut self.shops).await?
|
handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &self.level_table, &mut self.shops).await?
|
||||||
},
|
},
|
||||||
GameMessage::BuyItem(buy_item) => {
|
GameMessage::BuyItem(buy_item) => {
|
||||||
handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::TekRequest(tek_request) => {
|
GameMessage::TekRequest(tek_request) => {
|
||||||
handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?
|
handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &mut self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::TekAccept(tek_accept) => {
|
GameMessage::TekAccept(tek_accept) => {
|
||||||
handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||||
},
|
},
|
||||||
GameMessage::TradeRequest(trade_request) => {
|
GameMessage::TradeRequest(trade_request) => {
|
||||||
handler::trade::trade_request(id, trade_request, target, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await?
|
handler::trade::trade_request(id, trade_request, target, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await?
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let cmsg = msg.clone();
|
let cmsg = msg.clone();
|
||||||
@ -604,7 +637,7 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
|||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||||
Ok(match pkt {
|
Ok(match pkt {
|
||||||
RecvShipPacket::Login(login) => {
|
RecvShipPacket::Login(login) => {
|
||||||
Box::new(handler::auth::validate_login(id, login, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager, &self.shipgate_sender, &self.name, self.blocks.0.len())
|
Box::new(handler::auth::validate_login(id, login, &mut self.entity_gateway, &mut self.clients, &mut self.item_state, &self.shipgate_sender, &self.name, self.blocks.0.len())
|
||||||
.await?.into_iter().map(move |pkt| (id, pkt)))
|
.await?.into_iter().map(move |pkt| (id, pkt)))
|
||||||
},
|
},
|
||||||
RecvShipPacket::QuestDetailRequest(questdetailrequest) => {
|
RecvShipPacket::QuestDetailRequest(questdetailrequest) => {
|
||||||
@ -624,10 +657,10 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
|||||||
}
|
}
|
||||||
BLOCK_MENU_ID => {
|
BLOCK_MENU_ID => {
|
||||||
let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).into_iter().into_iter().flatten();
|
let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).into_iter().into_iter().flatten();
|
||||||
let select_block = handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_manager, &self.level_table)?.into_iter();
|
let select_block = handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_state, &self.level_table)?.into_iter();
|
||||||
Box::new(leave_lobby.chain(select_block))
|
Box::new(leave_lobby.chain(select_block))
|
||||||
}
|
}
|
||||||
ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)?,
|
ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &self.level_table, &mut block.rooms)?,
|
||||||
QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &block.client_location, &mut block.rooms)?,
|
QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &block.client_location, &mut block.rooms)?,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -649,7 +682,7 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
|||||||
menu: room_password_req.menu,
|
menu: room_password_req.menu,
|
||||||
item: room_password_req.item,
|
item: room_password_req.item,
|
||||||
};
|
};
|
||||||
handler::room::join_room(id, &menuselect, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)?
|
handler::room::join_room(id, &menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &self.level_table, &mut block.rooms)?
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))].into_iter())
|
Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))].into_iter())
|
||||||
@ -657,7 +690,7 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
|||||||
},
|
},
|
||||||
RecvShipPacket::CharData(chardata) => {
|
RecvShipPacket::CharData(chardata) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_manager, &self.level_table)?.into_iter())
|
Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_state, &self.level_table)?.into_iter())
|
||||||
},
|
},
|
||||||
RecvShipPacket::Message(msg) => {
|
RecvShipPacket::Message(msg) => {
|
||||||
self.message(id, msg).await?
|
self.message(id, msg).await?
|
||||||
@ -671,7 +704,7 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
|||||||
},
|
},
|
||||||
RecvShipPacket::CreateRoom(create_room) => {
|
RecvShipPacket::CreateRoom(create_room) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
handler::room::create_room(id, create_room, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)?
|
handler::room::create_room(id, create_room, &mut block.client_location, &mut self.clients, &mut self.item_state, &self.level_table, &mut block.rooms)?
|
||||||
},
|
},
|
||||||
RecvShipPacket::RoomNameRequest(_req) => {
|
RecvShipPacket::RoomNameRequest(_req) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
@ -709,7 +742,7 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
|||||||
},
|
},
|
||||||
RecvShipPacket::LobbySelect(pkt) => {
|
RecvShipPacket::LobbySelect(pkt) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
Box::new(handler::lobby::change_lobby(id, pkt.lobby, &mut block.client_location, &self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms, &mut self.entity_gateway).await?.into_iter())
|
Box::new(handler::lobby::change_lobby(id, pkt.lobby, &mut block.client_location, &self.clients, &mut self.item_state, &self.level_table, &mut block.rooms, &mut self.entity_gateway).await?.into_iter())
|
||||||
},
|
},
|
||||||
RecvShipPacket::RequestQuestList(rql) => {
|
RecvShipPacket::RequestQuestList(rql) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
@ -741,11 +774,11 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
|||||||
},
|
},
|
||||||
RecvShipPacket::ItemsToTrade(items_to_trade) => {
|
RecvShipPacket::ItemsToTrade(items_to_trade) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
handler::trade::items_to_trade(id, items_to_trade, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await?
|
handler::trade::items_to_trade(id, items_to_trade, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await?
|
||||||
},
|
},
|
||||||
RecvShipPacket::TradeConfirmed(_) => {
|
RecvShipPacket::TradeConfirmed(_) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
handler::trade::trade_confirmed(id, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await?
|
handler::trade::trade_confirmed(id, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await?
|
||||||
},
|
},
|
||||||
RecvShipPacket::KeyboardConfig(keyboard_config) => {
|
RecvShipPacket::KeyboardConfig(keyboard_config) => {
|
||||||
handler::settings::keyboard_config(id, keyboard_config, &mut self.clients, &mut self.entity_gateway).await
|
handler::settings::keyboard_config(id, keyboard_config, &mut self.clients, &mut self.entity_gateway).await
|
||||||
@ -781,7 +814,7 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
block.client_location.remove_client_from_area(id);
|
block.client_location.remove_client_from_area(id);
|
||||||
self.item_manager.remove_character_from_room(&client.character);
|
self.item_state.remove_character_from_room(&client.character);
|
||||||
|
|
||||||
if let Some(mut client) = self.clients.remove(&id) {
|
if let Some(mut client) = self.clients.remove(&id) {
|
||||||
client.user.at_ship = false;
|
client.user.at_ship = false;
|
||||||
|
@ -92,6 +92,7 @@ impl ShopItem for ArmorShopItem {
|
|||||||
ItemDetail::Unit(Unit {
|
ItemDetail::Unit(Unit {
|
||||||
unit: unit.unit,
|
unit: unit.unit,
|
||||||
modifier: None,
|
modifier: None,
|
||||||
|
kills: None,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -333,6 +334,7 @@ impl<R: Rng + SeedableRng> ArmorShop<R> {
|
|||||||
ArmorShopItem::Unit(Unit {
|
ArmorShopItem::Unit(Unit {
|
||||||
unit: unit_detail.item,
|
unit: unit_detail.item,
|
||||||
modifier: None,
|
modifier: None,
|
||||||
|
kills: None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -148,6 +148,7 @@ impl ShopItem for WeaponShopItem {
|
|||||||
grind: self.grind as u8,
|
grind: self.grind as u8,
|
||||||
attrs: [self.attributes[0], self.attributes[1], None],
|
attrs: [self.attributes[0], self.attributes[1], None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,13 @@ impl TradeItem {
|
|||||||
TradeItem::Stacked(item_id, _) => *item_id,
|
TradeItem::Stacked(item_id, _) => *item_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn amount(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
TradeItem::Individual(_) => 1,
|
||||||
|
TradeItem::Stacked(_, amount) => *amount,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -67,9 +74,10 @@ impl ClientTradeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
#[error("")]
|
|
||||||
pub enum TradeStateError {
|
pub enum TradeStateError {
|
||||||
|
#[error("client not in trade {0}")]
|
||||||
ClientNotInTrade(ClientId),
|
ClientNotInTrade(ClientId),
|
||||||
|
#[error("mismatched trade {0} {1}")]
|
||||||
MismatchedTrade(ClientId, ClientId),
|
MismatchedTrade(ClientId, ClientId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
use elseware::common::serverstate::{ClientId, ServerState};
|
use elseware::common::serverstate::{ClientId, ServerState};
|
||||||
use elseware::entity::gateway::EntityGateway;
|
use elseware::entity::gateway::EntityGateway;
|
||||||
use elseware::entity::account::{UserAccountEntity, NewUserAccountEntity, NewUserSettingsEntity};
|
use elseware::entity::account::{UserAccountEntity, NewUserAccountEntity, NewUserSettingsEntity};
|
||||||
use elseware::entity::character::{CharacterEntity, NewCharacterEntity};
|
|
||||||
use elseware::entity::item::{Meseta, BankName};
|
use elseware::entity::item::{Meseta, BankName};
|
||||||
|
use elseware::entity::character::{CharacterEntity, NewCharacterEntity, SectionID};
|
||||||
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
|
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
|
||||||
use elseware::ship::room::Difficulty;
|
use elseware::ship::room::Difficulty;
|
||||||
|
|
||||||
@ -29,11 +29,32 @@ pub async fn new_user_character<EG: EntityGateway>(entity_gateway: &mut EG, user
|
|||||||
let new_character = NewCharacterEntity::new(user.id, kb_conf_preset);
|
let new_character = NewCharacterEntity::new(user.id, kb_conf_preset);
|
||||||
let character = entity_gateway.create_character(new_character).await.unwrap();
|
let character = entity_gateway.create_character(new_character).await.unwrap();
|
||||||
entity_gateway.set_character_meseta(&character.id, Meseta(0)).await.unwrap();
|
entity_gateway.set_character_meseta(&character.id, Meseta(0)).await.unwrap();
|
||||||
entity_gateway.set_bank_meseta(&character.id, BankName("".into()), Meseta(0)).await.unwrap();
|
entity_gateway.set_bank_meseta(&character.id, &BankName("".into()), Meseta(0)).await.unwrap();
|
||||||
|
|
||||||
(user, character)
|
(user, character)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn new_user_character_with_sid<EG: EntityGateway>(entity_gateway: &mut EG, username: &str, password: &str, sid: SectionID) -> (UserAccountEntity, CharacterEntity) {
|
||||||
|
let new_user = NewUserAccountEntity {
|
||||||
|
email: format!("{}@pso.com", username),
|
||||||
|
username: username.into(),
|
||||||
|
password: bcrypt::hash(password, 5).unwrap(),
|
||||||
|
guildcard: 1,
|
||||||
|
activated: true,
|
||||||
|
..NewUserAccountEntity::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let user = entity_gateway.create_user(new_user).await.unwrap();
|
||||||
|
let new_settings = NewUserSettingsEntity::new(user.id);
|
||||||
|
let _settings = entity_gateway.create_user_settings(new_settings).await.unwrap();
|
||||||
|
let mut new_character = NewCharacterEntity::new(user.id, 1);
|
||||||
|
new_character.section_id = sid;
|
||||||
|
let character = entity_gateway.create_character(new_character).await.unwrap();
|
||||||
|
entity_gateway.set_character_meseta(&character.id, Meseta(0)).await.unwrap();
|
||||||
|
entity_gateway.set_bank_meseta(&character.id, BankName("".into()), Meseta(0)).await.unwrap();
|
||||||
|
(user, character)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn log_in_char<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, username: &str, password: &str) {
|
pub async fn log_in_char<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, username: &str, password: &str) {
|
||||||
let username = username.to_string();
|
let username = username.to_string();
|
||||||
let password = password.to_string();
|
let password = password.to_string();
|
||||||
@ -84,6 +105,21 @@ pub async fn create_room_with_difficulty<EG: EntityGateway>(ship: &mut ShipServe
|
|||||||
ship.handle(id, &RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap().for_each(drop);
|
ship.handle(id, &RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap().for_each(drop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn create_ep2_room_with_difficulty<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, name: &str, password: &str, difficulty: Difficulty) {
|
||||||
|
ship.handle(id, &RecvShipPacket::CreateRoom(CreateRoom {
|
||||||
|
unknown: [0; 2],
|
||||||
|
name: utf8_to_utf16_array!(name, 16),
|
||||||
|
password: utf8_to_utf16_array!(password, 16),
|
||||||
|
difficulty: difficulty.into(),
|
||||||
|
battle: 0,
|
||||||
|
challenge: 0,
|
||||||
|
episode: 2,
|
||||||
|
single_player: 0,
|
||||||
|
padding: [0; 3],
|
||||||
|
})).await.unwrap().for_each(drop);
|
||||||
|
ship.handle(id, &RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap().for_each(drop);
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn join_room<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, room_id: u32) {
|
pub async fn join_room<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, room_id: u32) {
|
||||||
ship.handle(id, &RecvShipPacket::MenuSelect(MenuSelect {
|
ship.handle(id, &RecvShipPacket::MenuSelect(MenuSelect {
|
||||||
menu: ROOM_MENU_ID,
|
menu: ROOM_MENU_ID,
|
||||||
|
@ -27,11 +27,12 @@ async fn test_bank_items_sent_in_character_login() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
|
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item]), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item]), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -64,12 +65,13 @@ async fn test_request_bank_items() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -113,7 +115,7 @@ async fn test_request_stacked_bank_items() {
|
|||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -153,6 +155,7 @@ async fn test_request_bank_items_sorted() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
@ -173,12 +176,13 @@ async fn test_request_bank_items_sorted() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
|
|
||||||
let bank = vec![item::BankItemEntity::Individual(item1), vec![monomate].into(), item2.into()];
|
let bank = vec![item::BankItemEntity::Individual(item1), vec![monomate].into(), item2.into()];
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -219,6 +223,7 @@ async fn test_deposit_individual_item() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
@ -231,6 +236,7 @@ async fn test_deposit_individual_item() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
@ -269,7 +275,13 @@ async fn test_deposit_individual_item() {
|
|||||||
&& player_no_longer_has_item.amount == 0
|
&& player_no_longer_has_item.amount == 0
|
||||||
));
|
));
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||||
|
assert_eq!(inventory_items.items.len(), 1);
|
||||||
|
inventory_items.items[0].with_individual(|item| {
|
||||||
|
assert_eq!(item.id, item::ItemEntityId(1));
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 1);
|
assert_eq!(bank_items.items.len(), 1);
|
||||||
bank_items.items[0].with_individual(|item| {
|
bank_items.items[0].with_individual(|item| {
|
||||||
assert_eq!(item.id, item::ItemEntityId(2));
|
assert_eq!(item.id, item::ItemEntityId(2));
|
||||||
@ -329,7 +341,7 @@ async fn test_deposit_stacked_item() {
|
|||||||
&& player_no_longer_has_item.amount == 3
|
&& player_no_longer_has_item.amount == 3
|
||||||
));
|
));
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 1);
|
assert_eq!(bank_items.items.len(), 1);
|
||||||
bank_items.items[0].with_stacked(|items| {
|
bank_items.items[0].with_stacked(|items| {
|
||||||
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
|
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
|
||||||
@ -391,7 +403,7 @@ async fn test_deposit_partial_stacked_item() {
|
|||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 1);
|
assert_eq!(bank_items.items.len(), 1);
|
||||||
bank_items.items[0].with_stacked(|items| {
|
bank_items.items[0].with_stacked(|items| {
|
||||||
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
|
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
|
||||||
@ -437,7 +449,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap();
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap();
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -471,7 +483,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
|
|||||||
&& player_no_longer_has_item.amount == 2
|
&& player_no_longer_has_item.amount == 2
|
||||||
));
|
));
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 1);
|
assert_eq!(bank_items.items.len(), 1);
|
||||||
bank_items.items[0].with_stacked(|items| {
|
bank_items.items[0].with_stacked(|items| {
|
||||||
assert_eq!(items.iter().map(|i| i.id).collect::<BTreeSet<_>>(),
|
assert_eq!(items.iter().map(|i| i.id).collect::<BTreeSet<_>>(),
|
||||||
@ -510,7 +522,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap();
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap();
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -537,7 +549,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
|
|||||||
|
|
||||||
assert!(packets.is_err());
|
assert!(packets.is_err());
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 1);
|
assert_eq!(bank_items.items.len(), 1);
|
||||||
bank_items.items[0].with_stacked(|items| {
|
bank_items.items[0].with_stacked(|items| {
|
||||||
assert_eq!(items.len(), 10);
|
assert_eq!(items.len(), 10);
|
||||||
@ -567,6 +579,7 @@ async fn test_deposit_individual_item_in_full_bank() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -582,13 +595,14 @@ async fn test_deposit_individual_item_in_full_bank() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -615,7 +629,7 @@ async fn test_deposit_individual_item_in_full_bank() {
|
|||||||
|
|
||||||
assert!(packets.is_err());
|
assert!(packets.is_err());
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 200);
|
assert_eq!(bank_items.items.len(), 200);
|
||||||
|
|
||||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||||
@ -654,13 +668,14 @@ async fn test_deposit_stacked_item_in_full_bank() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap();
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap();
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(full_bank), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(full_bank), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -687,7 +702,7 @@ async fn test_deposit_stacked_item_in_full_bank() {
|
|||||||
|
|
||||||
assert!(packets.is_err());
|
assert!(packets.is_err());
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 200);
|
assert_eq!(bank_items.items.len(), 200);
|
||||||
|
|
||||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||||
@ -739,6 +754,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap().into());
|
}).await.unwrap().into());
|
||||||
@ -746,7 +762,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
|
|||||||
almost_full_bank.push(bank_monomates.into());
|
almost_full_bank.push(bank_monomates.into());
|
||||||
|
|
||||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap();
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap();
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(almost_full_bank), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(almost_full_bank), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -771,7 +787,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
|
|||||||
unknown: 0,
|
unknown: 0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 200);
|
assert_eq!(bank_items.items.len(), 200);
|
||||||
bank_items.items[199].with_stacked(|items| {
|
bank_items.items[199].with_stacked(|items| {
|
||||||
assert_eq!(items.len(), 4);
|
assert_eq!(items.len(), 4);
|
||||||
@ -812,7 +828,7 @@ async fn test_deposit_meseta() {
|
|||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert!(c1_meseta.0 == 277);
|
assert!(c1_meseta.0 == 277);
|
||||||
assert!(c1_bank_meseta.0 == 23);
|
assert!(c1_bank_meseta.0 == 23);
|
||||||
}
|
}
|
||||||
@ -823,7 +839,7 @@ async fn test_deposit_too_much_meseta() {
|
|||||||
|
|
||||||
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap();
|
entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap();
|
||||||
entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(999980)).await.unwrap();
|
entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(999980)).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -838,7 +854,7 @@ async fn test_deposit_too_much_meseta() {
|
|||||||
unknown: 0,
|
unknown: 0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||||
client: 0,
|
client: 0,
|
||||||
target: 0,
|
target: 0,
|
||||||
item_id: 0xFFFFFFFF,
|
item_id: 0xFFFFFFFF,
|
||||||
@ -846,10 +862,12 @@ async fn test_deposit_too_much_meseta() {
|
|||||||
item_amount: 0,
|
item_amount: 0,
|
||||||
meseta_amount: 23,
|
meseta_amount: 23,
|
||||||
unknown: 0,
|
unknown: 0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await;
|
||||||
|
|
||||||
|
assert!(packets.is_err());
|
||||||
|
|
||||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert!(c1_meseta.0 == 300);
|
assert!(c1_meseta.0 == 300);
|
||||||
assert!(c1_bank_meseta.0 == 999980);
|
assert!(c1_bank_meseta.0 == 999980);
|
||||||
}
|
}
|
||||||
@ -860,7 +878,7 @@ async fn test_deposit_meseta_when_bank_is_maxed() {
|
|||||||
|
|
||||||
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap();
|
entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap();
|
||||||
entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(999999)).await.unwrap();
|
entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(999999)).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -875,7 +893,7 @@ async fn test_deposit_meseta_when_bank_is_maxed() {
|
|||||||
unknown: 0,
|
unknown: 0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||||
client: 0,
|
client: 0,
|
||||||
target: 0,
|
target: 0,
|
||||||
item_id: 0xFFFFFFFF,
|
item_id: 0xFFFFFFFF,
|
||||||
@ -883,10 +901,12 @@ async fn test_deposit_meseta_when_bank_is_maxed() {
|
|||||||
item_amount: 0,
|
item_amount: 0,
|
||||||
meseta_amount: 23,
|
meseta_amount: 23,
|
||||||
unknown: 0,
|
unknown: 0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await;
|
||||||
|
|
||||||
|
assert!(packets.is_err());
|
||||||
|
|
||||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert!(c1_meseta.0 == 300);
|
assert!(c1_meseta.0 == 300);
|
||||||
assert!(c1_bank_meseta.0 == 999999);
|
assert!(c1_bank_meseta.0 == 999999);
|
||||||
}
|
}
|
||||||
@ -909,11 +929,12 @@ async fn test_withdraw_individual_item() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
|
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -973,7 +994,7 @@ async fn test_withdraw_stacked_item() {
|
|||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -1003,7 +1024,7 @@ async fn test_withdraw_stacked_item() {
|
|||||||
|
|
||||||
assert!(packets.len() == 2);
|
assert!(packets.len() == 2);
|
||||||
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
|
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
|
||||||
if create_item.item_id == 0x10002
|
if create_item.item_id == 0x20000
|
||||||
));
|
));
|
||||||
|
|
||||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||||
@ -1032,7 +1053,7 @@ async fn test_withdraw_partial_stacked_item() {
|
|||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
}
|
}
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -1062,10 +1083,10 @@ async fn test_withdraw_partial_stacked_item() {
|
|||||||
|
|
||||||
assert!(packets.len() == 2);
|
assert!(packets.len() == 2);
|
||||||
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
|
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
|
||||||
if create_item.item_id == 0x10002
|
if create_item.item_id == 0x20002
|
||||||
));
|
));
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 1);
|
assert_eq!(bank_items.items.len(), 1);
|
||||||
bank_items.items[0].with_stacked(|items| {
|
bank_items.items[0].with_stacked(|items| {
|
||||||
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
|
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
|
||||||
@ -1110,7 +1131,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap();
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap();
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -1140,10 +1161,10 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
|
|||||||
|
|
||||||
assert!(packets.len() == 2);
|
assert!(packets.len() == 2);
|
||||||
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
|
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
|
||||||
if create_item.item_id == 0x10000
|
if create_item.item_id == 0x20000
|
||||||
));
|
));
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 0);
|
assert_eq!(bank_items.items.len(), 0);
|
||||||
|
|
||||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||||
@ -1185,7 +1206,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap();
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap();
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -1212,7 +1233,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
|
|||||||
|
|
||||||
assert!(packets.is_err());
|
assert!(packets.is_err());
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 1);
|
assert_eq!(bank_items.items.len(), 1);
|
||||||
bank_items.items[0].with_stacked(|items| {
|
bank_items.items[0].with_stacked(|items| {
|
||||||
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
|
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
|
||||||
@ -1242,6 +1263,7 @@ async fn test_withdraw_individual_item_in_full_inventory() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -1257,13 +1279,14 @@ async fn test_withdraw_individual_item_in_full_inventory() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -1289,7 +1312,7 @@ async fn test_withdraw_individual_item_in_full_inventory() {
|
|||||||
})))).await;
|
})))).await;
|
||||||
assert!(packets.is_err());
|
assert!(packets.is_err());
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 1);
|
assert_eq!(bank_items.items.len(), 1);
|
||||||
|
|
||||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||||
@ -1325,13 +1348,14 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -1359,7 +1383,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
|
|||||||
assert!(packets.is_err());
|
assert!(packets.is_err());
|
||||||
|
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert_eq!(bank_items.items.len(), 1);
|
assert_eq!(bank_items.items.len(), 1);
|
||||||
bank_items.items[0].with_stacked(|items| {
|
bank_items.items[0].with_stacked(|items| {
|
||||||
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
|
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
|
||||||
@ -1387,7 +1411,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
|
|||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
}
|
}
|
||||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_item]), item::BankName("".into())).await.unwrap();
|
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_item]), &item::BankName("".into())).await.unwrap();
|
||||||
|
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
for _i in 0..29usize {
|
for _i in 0..29usize {
|
||||||
@ -1400,6 +1424,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap().into());
|
}).await.unwrap().into());
|
||||||
@ -1443,7 +1468,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
|
|||||||
unknown: 0,
|
unknown: 0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap();
|
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert!(bank_items.items.len() == 0);
|
assert!(bank_items.items.len() == 0);
|
||||||
|
|
||||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||||
@ -1461,7 +1486,7 @@ async fn test_withdraw_meseta() {
|
|||||||
let mut entity_gateway = InMemoryGateway::default();
|
let mut entity_gateway = InMemoryGateway::default();
|
||||||
|
|
||||||
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(300)).await.unwrap();
|
entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(300)).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -1487,7 +1512,7 @@ async fn test_withdraw_meseta() {
|
|||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert!(c1_meseta.0 == 23);
|
assert!(c1_meseta.0 == 23);
|
||||||
assert!(c1_bank_meseta.0 == 277);
|
assert!(c1_bank_meseta.0 == 277);
|
||||||
}
|
}
|
||||||
@ -1498,7 +1523,7 @@ async fn test_withdraw_too_much_meseta() {
|
|||||||
|
|
||||||
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999980)).await.unwrap();
|
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999980)).await.unwrap();
|
||||||
entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(300)).await.unwrap();
|
entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(300)).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -1513,7 +1538,7 @@ async fn test_withdraw_too_much_meseta() {
|
|||||||
unknown: 0,
|
unknown: 0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
let packet = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||||
client: 0,
|
client: 0,
|
||||||
target: 0,
|
target: 0,
|
||||||
item_id: 0xFFFFFFFF,
|
item_id: 0xFFFFFFFF,
|
||||||
@ -1521,10 +1546,12 @@ async fn test_withdraw_too_much_meseta() {
|
|||||||
item_amount: 0,
|
item_amount: 0,
|
||||||
meseta_amount: 23,
|
meseta_amount: 23,
|
||||||
unknown: 0,
|
unknown: 0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await;
|
||||||
|
|
||||||
|
assert!(packet.is_err());
|
||||||
|
|
||||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert!(c1_meseta.0 == 999980);
|
assert!(c1_meseta.0 == 999980);
|
||||||
assert!(c1_bank_meseta.0 == 300);
|
assert!(c1_bank_meseta.0 == 300);
|
||||||
}
|
}
|
||||||
@ -1535,7 +1562,7 @@ async fn test_withdraw_meseta_inventory_is_maxed() {
|
|||||||
|
|
||||||
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||||
entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(300)).await.unwrap();
|
entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(300)).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
@ -1550,7 +1577,7 @@ async fn test_withdraw_meseta_inventory_is_maxed() {
|
|||||||
unknown: 0,
|
unknown: 0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
let packet = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||||
client: 0,
|
client: 0,
|
||||||
target: 0,
|
target: 0,
|
||||||
item_id: 0xFFFFFFFF,
|
item_id: 0xFFFFFFFF,
|
||||||
@ -1558,10 +1585,12 @@ async fn test_withdraw_meseta_inventory_is_maxed() {
|
|||||||
item_amount: 0,
|
item_amount: 0,
|
||||||
meseta_amount: 23,
|
meseta_amount: 23,
|
||||||
unknown: 0,
|
unknown: 0,
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await;
|
||||||
|
|
||||||
|
assert!(packet.is_err());
|
||||||
|
|
||||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||||
assert!(c1_meseta.0 == 999999);
|
assert!(c1_meseta.0 == 999999);
|
||||||
assert!(c1_bank_meseta.0 == 300);
|
assert!(c1_bank_meseta.0 == 300);
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ async fn test_equip_unit_from_equip_menu() {
|
|||||||
item::unit::Unit{
|
item::unit::Unit{
|
||||||
unit: item::unit::UnitType::KnightPower,
|
unit: item::unit::UnitType::KnightPower,
|
||||||
modifier: None,
|
modifier: None,
|
||||||
|
kills: None,
|
||||||
}),
|
}),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
|
|
||||||
@ -43,12 +44,13 @@ async fn test_equip_unit_from_equip_menu() {
|
|||||||
item::unit::Unit{
|
item::unit::Unit{
|
||||||
unit: item::unit::UnitType::KnightPower,
|
unit: item::unit::UnitType::KnightPower,
|
||||||
modifier: Some(item::unit::UnitModifier::Plus),
|
modifier: Some(item::unit::UnitModifier::Plus),
|
||||||
|
kills: None,
|
||||||
}),
|
}),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
|
|
||||||
let equipped = item::EquippedEntity {
|
let equipped = item::EquippedEntity {
|
||||||
weapon: Some(p1_inv[0].id),
|
weapon: None,
|
||||||
armor: None,
|
armor: Some(p1_inv[0].id),
|
||||||
shield: None,
|
shield: None,
|
||||||
unit: [None; 4],
|
unit: [None; 4],
|
||||||
mag: None,
|
mag: None,
|
||||||
@ -111,6 +113,7 @@ async fn test_unequip_armor_with_units() {
|
|||||||
item::unit::Unit{
|
item::unit::Unit{
|
||||||
unit: item::unit::UnitType::KnightPower,
|
unit: item::unit::UnitType::KnightPower,
|
||||||
modifier: None,
|
modifier: None,
|
||||||
|
kills: None,
|
||||||
}),
|
}),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
|
|
||||||
@ -120,6 +123,7 @@ async fn test_unequip_armor_with_units() {
|
|||||||
item::unit::Unit{
|
item::unit::Unit{
|
||||||
unit: item::unit::UnitType::KnightPower,
|
unit: item::unit::UnitType::KnightPower,
|
||||||
modifier: Some(item::unit::UnitModifier::Plus),
|
modifier: Some(item::unit::UnitModifier::Plus),
|
||||||
|
kills: None,
|
||||||
}),
|
}),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
|
|
||||||
@ -179,6 +183,7 @@ async fn test_sort_items() {
|
|||||||
item::unit::Unit{
|
item::unit::Unit{
|
||||||
unit: item::unit::UnitType::KnightPower,
|
unit: item::unit::UnitType::KnightPower,
|
||||||
modifier: None,
|
modifier: None,
|
||||||
|
kills: None,
|
||||||
}),
|
}),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
|
|
||||||
@ -188,6 +193,7 @@ async fn test_sort_items() {
|
|||||||
item::unit::Unit{
|
item::unit::Unit{
|
||||||
unit: item::unit::UnitType::KnightPower,
|
unit: item::unit::UnitType::KnightPower,
|
||||||
modifier: Some(item::unit::UnitModifier::Plus),
|
modifier: Some(item::unit::UnitModifier::Plus),
|
||||||
|
kills: None,
|
||||||
}),
|
}),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ async fn test_pick_up_individual_item() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -234,6 +235,7 @@ async fn test_pick_up_meseta_when_inventory_full() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -306,6 +308,7 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap().into());
|
}).await.unwrap().into());
|
||||||
@ -389,6 +392,7 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -404,6 +408,7 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -734,7 +739,7 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
|
|||||||
ship.handle(ClientId(2), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
ship.handle(ClientId(2), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||||
client: 0,
|
client: 0,
|
||||||
target: 0,
|
target: 0,
|
||||||
item_id: 0x00810001,
|
item_id: 0x10003,
|
||||||
map_area: 0,
|
map_area: 0,
|
||||||
unknown: [0; 3]
|
unknown: [0; 3]
|
||||||
})))).await.unwrap().for_each(drop);
|
})))).await.unwrap().for_each(drop);
|
||||||
|
@ -164,7 +164,7 @@ async fn test_use_nonstackable_tool() {
|
|||||||
item::NewItemEntity {
|
item::NewItemEntity {
|
||||||
item: item::ItemDetail::Tool(
|
item: item::ItemDetail::Tool(
|
||||||
item::tool::Tool {
|
item::tool::Tool {
|
||||||
tool: item::tool::ToolType::MagicStoneIritista,
|
tool: item::tool::ToolType::HuntersReport,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -251,6 +251,9 @@ async fn test_use_materials() {
|
|||||||
assert!(char.materials.luck == 2);
|
assert!(char.materials.luck == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: tests for ALL ITEMS WOW
|
||||||
|
|
||||||
|
/*
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
pub async fn test_learn_new_tech() {}
|
pub async fn test_learn_new_tech() {}
|
||||||
|
|
||||||
@ -268,3 +271,4 @@ pub async fn test_char_cannot_learn_high_level_tech() {}
|
|||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
pub async fn test_android_cannot_learn_tech() {}
|
pub async fn test_android_cannot_learn_tech() {}
|
||||||
|
*/
|
||||||
|
@ -29,6 +29,7 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -45,6 +46,7 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
|
|||||||
special: None,
|
special: None,
|
||||||
attrs: [None, None, None],
|
attrs: [None, None, None],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
|
@ -3,7 +3,7 @@ use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
|
|||||||
use elseware::entity::item;
|
use elseware::entity::item;
|
||||||
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
|
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
|
||||||
use elseware::ship::room::Difficulty;
|
use elseware::ship::room::Difficulty;
|
||||||
use elseware::ship::items::manager::ItemManagerError;
|
use elseware::ship::items::state::ItemStateError;
|
||||||
|
|
||||||
use libpso::packet::ship::*;
|
use libpso::packet::ship::*;
|
||||||
use libpso::packet::messages::*;
|
use libpso::packet::messages::*;
|
||||||
@ -274,6 +274,7 @@ async fn test_player_sells_3_attr_weapon_to_shop() {
|
|||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -635,6 +636,7 @@ async fn test_player_sells_untekked_weapon() {
|
|||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
|
||||||
tekked: false,
|
tekked: false,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -679,6 +681,7 @@ async fn test_player_sells_rare_item() {
|
|||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
||||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
|
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
|
||||||
tekked: true,
|
tekked: true,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -923,6 +926,7 @@ async fn test_player_sells_1_star_minusminus_unit() {
|
|||||||
item::unit::Unit {
|
item::unit::Unit {
|
||||||
unit: item::unit::UnitType::PriestMind,
|
unit: item::unit::UnitType::PriestMind,
|
||||||
modifier: Some(item::unit::UnitModifier::MinusMinus),
|
modifier: Some(item::unit::UnitModifier::MinusMinus),
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -962,6 +966,7 @@ async fn test_player_sells_5_star_plusplus_unit() {
|
|||||||
item::unit::Unit {
|
item::unit::Unit {
|
||||||
unit: item::unit::UnitType::GeneralHp,
|
unit: item::unit::UnitType::GeneralHp,
|
||||||
modifier: Some(item::unit::UnitModifier::PlusPlus),
|
modifier: Some(item::unit::UnitModifier::PlusPlus),
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -1082,6 +1087,7 @@ async fn test_player_sells_rare_unit() {
|
|||||||
item::unit::Unit {
|
item::unit::Unit {
|
||||||
unit: item::unit::UnitType::V101,
|
unit: item::unit::UnitType::V101,
|
||||||
modifier: None,
|
modifier: None,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -1122,6 +1128,7 @@ async fn test_player_cant_sell_if_meseta_would_go_over_max() {
|
|||||||
item::unit::Unit {
|
item::unit::Unit {
|
||||||
unit: item::unit::UnitType::V101,
|
unit: item::unit::UnitType::V101,
|
||||||
modifier: None,
|
modifier: None,
|
||||||
|
kills: None,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}).await.unwrap());
|
}).await.unwrap());
|
||||||
@ -1142,7 +1149,7 @@ async fn test_player_cant_sell_if_meseta_would_go_over_max() {
|
|||||||
item_id: 0x10000,
|
item_id: 0x10000,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})))).await.err().unwrap();
|
})))).await.err().unwrap();
|
||||||
assert!(matches!(ack.downcast::<ItemManagerError>().unwrap(), ItemManagerError::WalletFull));
|
assert!(matches!(ack.downcast::<ItemStateError>().unwrap(), ItemStateError::FullOfMeseta));
|
||||||
|
|
||||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||||
assert_eq!(c1_meseta.0, 999995);
|
assert_eq!(c1_meseta.0, 999995);
|
||||||
|
File diff suppressed because it is too large
Load Diff
272
tests/test_unseal_items.rs
Normal file
272
tests/test_unseal_items.rs
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
/* TODO:
|
||||||
|
4. test unsealing item:
|
||||||
|
- client item id does not change
|
||||||
|
- unsealed item no longer has kill counter
|
||||||
|
5. test reject unsealing item if not enough kills (can this even happen?)
|
||||||
|
*/
|
||||||
|
|
||||||
|
use elseware::common::serverstate::{ClientId, ServerState};
|
||||||
|
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
|
||||||
|
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
|
||||||
|
use elseware::entity::character::SectionID;
|
||||||
|
use elseware::ship::room::Difficulty;
|
||||||
|
use elseware::ship::monster::MonsterType;
|
||||||
|
use elseware::entity::item;
|
||||||
|
|
||||||
|
use libpso::packet::ship::*;
|
||||||
|
use libpso::packet::messages::*;
|
||||||
|
|
||||||
|
#[path = "common.rs"]
|
||||||
|
mod common;
|
||||||
|
use common::*;
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_sjs_drops_with_kill_counter() {
|
||||||
|
let mut entity_gateway = InMemoryGateway::default();
|
||||||
|
|
||||||
|
let (_user1, mut char1) = new_user_character_with_sid(&mut entity_gateway, "a1", "a", SectionID::Skyly).await;
|
||||||
|
|
||||||
|
char1.exp = 80000000;
|
||||||
|
entity_gateway.save_character(&char1).await.unwrap();
|
||||||
|
|
||||||
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
|
.gateway(entity_gateway.clone())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||||
|
join_lobby(&mut ship, ClientId(1)).await;
|
||||||
|
create_ep2_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||||
|
|
||||||
|
let room = ship.blocks.0[0].rooms[0].as_mut().unwrap();
|
||||||
|
room.toggle_redbox_mode(); // enable redbox mode
|
||||||
|
|
||||||
|
let gigue_id = room.maps.get_enemy_id_by_monster_type(MonsterType::GiGue).unwrap();
|
||||||
|
|
||||||
|
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::RequestItem(RequestItem {
|
||||||
|
client: 0,
|
||||||
|
target: 0,
|
||||||
|
map_area: 9, // seaside
|
||||||
|
pt_index: 55, // gigue ? (taken from ingame logs)
|
||||||
|
enemy_id: gigue_id,
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
})))).await.unwrap().collect::<Vec<_>>(); // this should return 1 packet (ItemDrop)?
|
||||||
|
|
||||||
|
assert!(packets.len() == 1);
|
||||||
|
match &packets[0].1 {
|
||||||
|
SendShipPacket::Message(Message {msg: GameMessage::ItemDrop(item_drop)}) => {
|
||||||
|
assert_eq!(item_drop.item_bytes[10], 0x80)
|
||||||
|
}
|
||||||
|
_ => panic!("SJS didn't drop with the expected value! attr[2] should be 0x80 (128) for 0 kills")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_other_weapons_drop_without_kill_counter() {
|
||||||
|
let mut entity_gateway = InMemoryGateway::default();
|
||||||
|
|
||||||
|
let (_user1, mut char1) = new_user_character_with_sid(&mut entity_gateway, "a1", "a", SectionID::Skyly).await;
|
||||||
|
|
||||||
|
char1.exp = 80000000;
|
||||||
|
entity_gateway.save_character(&char1).await.unwrap();
|
||||||
|
|
||||||
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
|
.gateway(entity_gateway.clone())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||||
|
join_lobby(&mut ship, ClientId(1)).await;
|
||||||
|
create_ep2_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||||
|
|
||||||
|
let room = ship.blocks.0[0].rooms[0].as_mut().unwrap();
|
||||||
|
room.toggle_redbox_mode(); // enable redbox mode
|
||||||
|
|
||||||
|
let enemy_id = room.maps.get_enemy_id_by_monster_type(MonsterType::Hildebear).unwrap();
|
||||||
|
|
||||||
|
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::RequestItem(RequestItem {
|
||||||
|
client: 0,
|
||||||
|
target: 0,
|
||||||
|
map_area: 1, // temple alpha
|
||||||
|
pt_index: 0, // TODO: this is going to break if pt_index ever gets properly used
|
||||||
|
enemy_id: enemy_id,
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
})))).await.unwrap().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert!(packets.len() == 1);
|
||||||
|
match &packets[0].1 {
|
||||||
|
SendShipPacket::Message(Message {msg: GameMessage::ItemDrop(item_drop)}) => {
|
||||||
|
assert_ne!(item_drop.item_bytes[10], 0x80)
|
||||||
|
}
|
||||||
|
_ => panic!("Weapon didn't drop with the expected value! attr[2] should be less than 0x80 (128) because it shouldn't have a kill counter!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_all_equipped_kill_counters_increase_per_kill() {
|
||||||
|
let mut entity_gateway = InMemoryGateway::default();
|
||||||
|
|
||||||
|
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
|
|
||||||
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
|
.gateway(entity_gateway.clone())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
let mut p1_inv = Vec::new();
|
||||||
|
p1_inv.push(entity_gateway.create_item(
|
||||||
|
item::NewItemEntity {
|
||||||
|
item: item::ItemDetail::Weapon(
|
||||||
|
item::weapon::Weapon {
|
||||||
|
weapon: item::weapon::WeaponType::SealedJSword,
|
||||||
|
grind: 0,
|
||||||
|
special: None,
|
||||||
|
attrs: [None,
|
||||||
|
None,
|
||||||
|
None,],
|
||||||
|
tekked: true,
|
||||||
|
kills: Some(0),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}).await.unwrap());
|
||||||
|
p1_inv.push(entity_gateway.create_item(
|
||||||
|
item::NewItemEntity {
|
||||||
|
item: item::ItemDetail::Unit(
|
||||||
|
item::unit::Unit {
|
||||||
|
unit: item::unit::UnitType::Limiter,
|
||||||
|
modifier: None,
|
||||||
|
kills: Some(0),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}).await.unwrap());
|
||||||
|
|
||||||
|
let equipped = item::EquippedEntity {
|
||||||
|
weapon: Some(p1_inv[0].id),
|
||||||
|
armor: None,
|
||||||
|
shield: None,
|
||||||
|
unit: [Some(p1_inv[1].id), None, None, None],
|
||||||
|
mag: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
entity_gateway.set_character_equips(&char1.id, &equipped).await.unwrap();
|
||||||
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
|
||||||
|
|
||||||
|
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||||
|
join_lobby(&mut ship, ClientId(1)).await;
|
||||||
|
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||||
|
|
||||||
|
let enemy_id = {
|
||||||
|
let room = ship.blocks.0[0].rooms[0].as_ref().unwrap();
|
||||||
|
let enemy_id = (0..).filter_map(|i| {
|
||||||
|
room.maps.enemy_by_id(i).ok().and_then(|enemy| {
|
||||||
|
if enemy.monster == MonsterType::Booma {
|
||||||
|
Some(i)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).next().unwrap();
|
||||||
|
enemy_id
|
||||||
|
};
|
||||||
|
|
||||||
|
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::KillMonster(KillMonster{
|
||||||
|
client: enemy_id as u8,
|
||||||
|
target: 16,
|
||||||
|
map_area: 1,
|
||||||
|
data: [8,0],
|
||||||
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
|
let equipped_items = entity_gateway.get_character_equips(&char1.id).await.unwrap();
|
||||||
|
let inventory = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||||
|
let w = inventory.items.iter().find(|x| x.individual().unwrap().id == equipped_items.weapon.unwrap()).unwrap().individual().unwrap();
|
||||||
|
let u = inventory.items.iter().find(|x| x.individual().unwrap().id == equipped_items.unit[0].unwrap()).unwrap().individual().unwrap();
|
||||||
|
|
||||||
|
assert!(w.item.as_client_bytes()[11] == 1);
|
||||||
|
assert!(u.item.as_client_bytes()[11] == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_non_equipped_kill_counter_does_not_increase() {
|
||||||
|
let mut entity_gateway = InMemoryGateway::default();
|
||||||
|
|
||||||
|
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
|
|
||||||
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
|
.gateway(entity_gateway.clone())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
let mut p1_inv = Vec::new();
|
||||||
|
p1_inv.push(entity_gateway.create_item(
|
||||||
|
item::NewItemEntity {
|
||||||
|
item: item::ItemDetail::Weapon(
|
||||||
|
item::weapon::Weapon {
|
||||||
|
weapon: item::weapon::WeaponType::SealedJSword,
|
||||||
|
grind: 0,
|
||||||
|
special: None,
|
||||||
|
attrs: [None,
|
||||||
|
None,
|
||||||
|
None,],
|
||||||
|
tekked: true,
|
||||||
|
kills: Some(0),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}).await.unwrap());
|
||||||
|
p1_inv.push(entity_gateway.create_item(
|
||||||
|
item::NewItemEntity {
|
||||||
|
item: item::ItemDetail::Unit(
|
||||||
|
item::unit::Unit {
|
||||||
|
unit: item::unit::UnitType::Limiter,
|
||||||
|
modifier: None,
|
||||||
|
kills: Some(0),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}).await.unwrap());
|
||||||
|
|
||||||
|
let equipped = item::EquippedEntity {
|
||||||
|
weapon: Some(p1_inv[0].id),
|
||||||
|
armor: None,
|
||||||
|
shield: None,
|
||||||
|
unit: [None; 4],
|
||||||
|
mag: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
entity_gateway.set_character_equips(&char1.id, &equipped).await.unwrap();
|
||||||
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
|
||||||
|
|
||||||
|
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||||
|
join_lobby(&mut ship, ClientId(1)).await;
|
||||||
|
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||||
|
|
||||||
|
let enemy_id = {
|
||||||
|
let room = ship.blocks.0[0].rooms[0].as_ref().unwrap();
|
||||||
|
let enemy_id = (0..).filter_map(|i| {
|
||||||
|
room.maps.enemy_by_id(i).ok().and_then(|enemy| {
|
||||||
|
if enemy.monster == MonsterType::Booma {
|
||||||
|
Some(i)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).next().unwrap();
|
||||||
|
enemy_id
|
||||||
|
};
|
||||||
|
|
||||||
|
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::KillMonster(KillMonster{
|
||||||
|
client: enemy_id as u8,
|
||||||
|
target: 16,
|
||||||
|
map_area: 1,
|
||||||
|
data: [8,0],
|
||||||
|
})))).await.unwrap().for_each(drop);
|
||||||
|
|
||||||
|
let equipped_items = entity_gateway.get_character_equips(&char1.id).await.unwrap();
|
||||||
|
let inventory = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||||
|
|
||||||
|
let w = inventory.items.iter().find(|x| x.individual().unwrap().id == equipped_items.weapon.unwrap()).unwrap().individual().unwrap();
|
||||||
|
let u = inventory.items.iter().find(|x| x.individual().unwrap().id == item::ItemEntityId(2)).unwrap().individual().unwrap();
|
||||||
|
|
||||||
|
assert!(w.item.as_client_bytes()[11] == 1);
|
||||||
|
assert!(u.item.as_client_bytes()[11] == 0);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user