mirror of
https://github.com/acemod/ACE3.git
synced 2024-08-30 18:23:18 +00:00
Extensions - Rust (#9015)
Co-authored-by: Pepijn Holster <pgaholster@gmail.com> Co-authored-by: PabstMirror <pabstmirror@gmail.com> Co-authored-by: LorenLuke <LukeLLL@aol.com> Co-authored-by: Grim <69561145+LinkIsGrim@users.noreply.github.com> Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com>
This commit is contained in:
parent
1cd48c52c3
commit
043b3907fe
@ -11,5 +11,8 @@ trim_trailing_whitespace = true
|
|||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.yml]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
[Makefile]
|
[Makefile]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
68
.github/workflows/extensions.yml
vendored
68
.github/workflows/extensions.yml
vendored
@ -1,29 +1,65 @@
|
|||||||
name: Extensions
|
name: Extensions
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'extensions/**'
|
- 'extension/**'
|
||||||
|
- 'Cargo.toml'
|
||||||
|
- 'Cargo.lock'
|
||||||
|
- '.github/workflows/extensions.yml'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout the source code
|
||||||
|
uses: actions/checkout@master
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
rustup toolchain update stable --no-self-update
|
||||||
|
rustup default stable
|
||||||
|
rustup component add clippy rustfmt
|
||||||
|
- name: Run rustfmt
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
- name: Run clippy
|
||||||
|
run: cargo clippy --all -- -Dwarnings
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: xd009642/tarpaulin
|
||||||
|
options: --security-opt seccomp=unconfined
|
||||||
|
steps:
|
||||||
|
- name: Checkout the source code
|
||||||
|
uses: actions/checkout@master
|
||||||
|
- name: Test & Coverage
|
||||||
|
run: cargo tarpaulin --verbose --no-default-features --workspace --timeout 240
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [windows-latest]
|
arrays: [
|
||||||
|
os: { tag: "windows-latest", target: "i686-pc-windows-msvc" },
|
||||||
|
os: { tag: "windows-latest", target: "x86_64-pc-windows-msvc" },
|
||||||
|
]
|
||||||
|
runs-on: ${{ matrix.arrays.os.tag }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the source code
|
- name: Checkout the source code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Build
|
- name: Install stable Rust
|
||||||
shell: cmd
|
uses: actions-rs/toolchain@v1
|
||||||
run: |
|
|
||||||
cd extensions
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. && cmake --build .
|
|
||||||
- name: Upload Artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
name: ace3_extensions-${{ matrix.os }}-debug
|
target: ${{ matrix.arrays.os.target }}
|
||||||
path: extensions/build
|
toolchain: stable
|
||||||
|
default: true
|
||||||
|
- name: Rust Cache
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --verbose
|
||||||
|
- name: Upload
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.arrays.os.target }}
|
||||||
|
path: target/debug/ace.dll
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 30
|
||||||
|
2
.github/workflows/hemtt.yml
vendored
2
.github/workflows/hemtt.yml
vendored
@ -33,6 +33,8 @@ jobs:
|
|||||||
xcopy /e /h /q pullrequest\addons addons\
|
xcopy /e /h /q pullrequest\addons addons\
|
||||||
xcopy /e /h /q pullrequest\optionals optionals\
|
xcopy /e /h /q pullrequest\optionals optionals\
|
||||||
xcopy /e /h /q pullrequest\include include\
|
xcopy /e /h /q pullrequest\include include\
|
||||||
|
xcopy /e /h /q pullrequest\ace.dll ace.dll
|
||||||
|
xcopy /e /h /q pullrequest\ace_x64.dll ace_x64.dll
|
||||||
- name: Run HEMTT build
|
- name: Run HEMTT build
|
||||||
run: hemtt build
|
run: hemtt build
|
||||||
- name: Rename build folder
|
- name: Rename build folder
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,8 +2,6 @@
|
|||||||
*.zip
|
*.zip
|
||||||
release/*
|
release/*
|
||||||
releases/*
|
releases/*
|
||||||
extensions/vcproj32/*
|
|
||||||
extensions/vcproj64/*
|
|
||||||
.vscode/*
|
.vscode/*
|
||||||
hemtt
|
hemtt
|
||||||
hemtt.exe
|
hemtt.exe
|
||||||
@ -20,4 +18,5 @@ CHANGELOG.md
|
|||||||
sqfvm.exe
|
sqfvm.exe
|
||||||
ArmaScriptCompiler.exe
|
ArmaScriptCompiler.exe
|
||||||
*.sqfc
|
*.sqfc
|
||||||
|
target/
|
||||||
!extras/**/*.zip
|
!extras/**/*.zip
|
||||||
|
886
Cargo.lock
generated
Normal file
886
Cargo.lock
generated
Normal file
@ -0,0 +1,886 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ace"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"arma-rs",
|
||||||
|
"clipboard",
|
||||||
|
"git2",
|
||||||
|
"rand",
|
||||||
|
"rand_chacha",
|
||||||
|
"rayon",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arma-rs"
|
||||||
|
version = "1.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fdda6c2cf1237405dd57f82875c77b4159d415445471b903a901d8a37a61b30c"
|
||||||
|
dependencies = [
|
||||||
|
"arma-rs-proc",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"libc",
|
||||||
|
"link_args",
|
||||||
|
"log",
|
||||||
|
"seq-macro",
|
||||||
|
"state",
|
||||||
|
"uuid",
|
||||||
|
"windows 0.51.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arma-rs-proc"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "517b1a9cc16bc93896216ffeed0c692df4af022ee55ec45e8fc31c90a201dcb4"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.103"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2755ff20a1d93490d26ba33a6f092a38a508398a5320df5d4b3014fcccce9410"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clipboard"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25a904646c0340239dcf7c51677b33928bf24fdf424b79a57909c0109075b2e7"
|
||||||
|
dependencies = [
|
||||||
|
"clipboard-win",
|
||||||
|
"objc",
|
||||||
|
"objc-foundation",
|
||||||
|
"objc_id",
|
||||||
|
"x11-clipboard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clipboard-win"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3a093d6fed558e5fe24c3dfc85a68bb68f1c824f440d3ba5aca189e2998786b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "form_urlencoded"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generator"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"rustversion",
|
||||||
|
"windows 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "git2"
|
||||||
|
version = "0.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
"libgit2-sys",
|
||||||
|
"log",
|
||||||
|
"openssl-probe",
|
||||||
|
"openssl-sys",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-bidi",
|
||||||
|
"unicode-normalization",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.155"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libgit2-sys"
|
||||||
|
version = "0.17.0+1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"libssh2-sys",
|
||||||
|
"libz-sys",
|
||||||
|
"openssl-sys",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libssh2-sys"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"libz-sys",
|
||||||
|
"openssl-sys",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libz-sys"
|
||||||
|
version = "1.1.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "link_args"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c7721e472624c9aaad27a5eb6b7c9c6045c7a396f2efb6dabaec1b640d5e89b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "loom"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"generator",
|
||||||
|
"scoped-tls",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "malloc_buf"
|
||||||
|
version = "0.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata 0.1.10",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||||
|
dependencies = [
|
||||||
|
"overload",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
|
||||||
|
dependencies = [
|
||||||
|
"malloc_buf",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc-foundation"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
|
||||||
|
dependencies = [
|
||||||
|
"block",
|
||||||
|
"objc",
|
||||||
|
"objc_id",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc_id"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
|
||||||
|
dependencies = [
|
||||||
|
"objc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-probe"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-sys"
|
||||||
|
version = "0.9.102"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "overload"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata 0.4.7",
|
||||||
|
"regex-syntax 0.8.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||||
|
dependencies = [
|
||||||
|
"regex-syntax 0.6.29",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax 0.8.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "seq-macro"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.203"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.203"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.119"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "state"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8"
|
||||||
|
dependencies = [
|
||||||
|
"loom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.68"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82"
|
||||||
|
dependencies = [
|
||||||
|
"tinyvec_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec_macros"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-attributes",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-attributes"
|
||||||
|
version = "0.1.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||||
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-bidi"
|
||||||
|
version = "0.3.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-normalization"
|
||||||
|
version = "0.1.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
|
||||||
|
dependencies = [
|
||||||
|
"tinyvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "url"
|
||||||
|
version = "2.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||||
|
dependencies = [
|
||||||
|
"form_urlencoded",
|
||||||
|
"idna",
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.51.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.51.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x11-clipboard"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89bd49c06c9eb5d98e6ba6536cf64ac9f7ee3a009b2f53996d405b3944f6bcea"
|
||||||
|
dependencies = [
|
||||||
|
"xcb",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xcb"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e917a3f24142e9ff8be2414e36c649d47d6cc2ba81f16201cdef96e533e02de"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
]
|
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = [
|
||||||
|
"extension"
|
||||||
|
]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = "z"
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
strip = true
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ace_fcs.dll
BIN
ace_fcs.dll
Binary file not shown.
BIN
ace_fcs_x64.dll
BIN
ace_fcs_x64.dll
Binary file not shown.
BIN
ace_x64.dll
Normal file
BIN
ace_x64.dll
Normal file
Binary file not shown.
@ -1,10 +1,10 @@
|
|||||||
#include "script_component.hpp"
|
#include "script_component.hpp"
|
||||||
|
|
||||||
GVAR(currentbulletID) = -1;
|
#include "initKeybinds.inc.sqf"
|
||||||
|
|
||||||
GVAR(Protractor) = false;
|
GVAR(Protractor) = false;
|
||||||
GVAR(ProtractorStart) = CBA_missionTime;
|
GVAR(ProtractorStart) = CBA_missionTime;
|
||||||
GVAR(allBullets) = [];
|
GVAR(allBullets) = createHashMap;
|
||||||
GVAR(currentGrid) = 0;
|
GVAR(currentGrid) = 0;
|
||||||
|
|
||||||
if (!hasInterface) exitWith {};
|
if (!hasInterface) exitWith {};
|
||||||
|
@ -18,10 +18,3 @@ class CfgPatches {
|
|||||||
#include "CfgVehicles.hpp"
|
#include "CfgVehicles.hpp"
|
||||||
#include "RscTitles.hpp"
|
#include "RscTitles.hpp"
|
||||||
#include "ACE_Settings.hpp"
|
#include "ACE_Settings.hpp"
|
||||||
|
|
||||||
class ACE_Extensions {
|
|
||||||
class ace_advanced_ballistics {
|
|
||||||
windows = 1;
|
|
||||||
client = 1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "..\script_component.hpp"
|
#include "..\script_component.hpp"
|
||||||
/*
|
/*
|
||||||
* Author: Glowbal, Ruthberg, joko // Jonas
|
* Author: Glowbal, Ruthberg, joko // Jonas, Brett Mayson
|
||||||
* Handle the PFH for Bullets
|
* Handle the PFH for Bullets
|
||||||
*
|
*
|
||||||
* Arguments:
|
* Arguments:
|
||||||
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
private _deleted = false;
|
private _deleted = false;
|
||||||
{
|
{
|
||||||
_x params ["_bullet","_caliber","_bulletTraceVisible","_index"];
|
_y params ["_bullet","_caliber","_bulletTraceVisible"];
|
||||||
|
|
||||||
if (alive _bullet) then {
|
if (alive _bullet) then {
|
||||||
private _bulletVelocity = velocity _bullet;
|
private _bulletVelocity = velocity _bullet;
|
||||||
@ -27,13 +27,21 @@ private _deleted = false;
|
|||||||
drop ["\A3\data_f\ParticleEffects\Universal\Refract","","Billboard",1,0.1,getPos _bullet,[0,0,0],0,1.275,1,0,[0.02*_caliber,0.01*_caliber],[[0,0,0,0.65],[0,0,0,0.2]],[1,0],0,0,"","",""];
|
drop ["\A3\data_f\ParticleEffects\Universal\Refract","","Billboard",1,0.1,getPos _bullet,[0,0,0],0,1.275,1,0,[0.02*_caliber,0.01*_caliber],[[0,0,0,0.65],[0,0,0,0.2]],[1,0],0,0,"","",""];
|
||||||
};
|
};
|
||||||
|
|
||||||
_bullet setVelocity (_bulletVelocity vectorAdd (parseSimpleArray ("ace_advanced_ballistics" callExtension format["simulate:%1:%2:%3:%4:%5:%6", _index, _bulletVelocity, _bulletPosition, wind, ASLToATL(_bulletPosition) select 2, CBA_missionTime toFixed 6])));
|
(
|
||||||
|
"ace" callExtension ["ballistics:bullet:simulate", [
|
||||||
|
_x,
|
||||||
|
_bulletVelocity,
|
||||||
|
_bulletPosition,
|
||||||
|
wind,
|
||||||
|
ASLToATL(_bulletPosition) select 2,
|
||||||
|
CBA_missionTime toFixed 6
|
||||||
|
]]
|
||||||
|
) params ["_data", "_code"];
|
||||||
|
if (_code == 0) then {
|
||||||
|
_bullet setVelocity (_bulletVelocity vectorAdd (parseSimpleArray (_data)));
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
GVAR(allBullets) set [_forEachIndex, objNull];
|
GVAR(allBullets) deleteAt _x;
|
||||||
_deleted = true;
|
"ace" callExtension ["ballistics:bullet:delete", [_x]];
|
||||||
};
|
|
||||||
} forEach GVAR(allBullets);
|
|
||||||
|
|
||||||
if (_deleted) then {
|
|
||||||
GVAR(allBullets) = GVAR(allBullets) - [objNull];
|
|
||||||
};
|
};
|
||||||
|
} forEach GVAR(allBullets)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "..\script_component.hpp"
|
#include "..\script_component.hpp"
|
||||||
/*
|
/*
|
||||||
* Author: Glowbal, Ruthberg
|
* Author: Glowbal, Ruthberg, Brett Mayson
|
||||||
*
|
*
|
||||||
* Handles advanced ballistics for (BulletBase) projectiles. Called from the unified fired EH only for players.
|
* Handles advanced ballistics for (BulletBase) projectiles. Called from the unified fired EH only for players.
|
||||||
*
|
*
|
||||||
@ -120,8 +120,26 @@ if (_caliber * _bulletLength * _bulletMass * _barrelTwist > 0) then {
|
|||||||
_stabilityFactor = [_caliber, _bulletLength, _bulletMass, _barrelTwist, _muzzleVelocity, _temperature, _barometricPressure] call FUNC(calculateStabilityFactor);
|
_stabilityFactor = [_caliber, _bulletLength, _bulletMass, _barrelTwist, _muzzleVelocity, _temperature, _barometricPressure] call FUNC(calculateStabilityFactor);
|
||||||
};
|
};
|
||||||
|
|
||||||
GVAR(currentbulletID) = (GVAR(currentbulletID) + 1) % 10000;
|
("ace" callExtension [
|
||||||
|
"ballistics:bullet:new", [
|
||||||
"ace_advanced_ballistics" callExtension format["new:%1:%2:%3:%4:%5:%6:%7:%8:%9:%10:%11:%12:%13:%14:%15:%16:%17:%18", GVAR(currentbulletID), _ammoCount, _airFriction, _ballisticCoefficients, _velocityBoundaries, _atmosphereModel, _dragModel, _stabilityFactor, _twistDirection, _transonicStabilityCoef, getPosASL _projectile, _bulletVelocity, EGVAR(common,mapLatitude), EGVAR(weather,currentTemperature), EGVAR(common,mapAltitude), EGVAR(weather,currentHumidity), EGVAR(weather,currentOvercast), CBA_missionTime toFixed 6];
|
_ammoCount,
|
||||||
|
_airFriction,
|
||||||
GVAR(allBullets) pushBack [_projectile, _caliber, _bulletTraceVisible, GVAR(currentbulletID)];
|
_ballisticCoefficients,
|
||||||
|
_velocityBoundaries,
|
||||||
|
_atmosphereModel,
|
||||||
|
_dragModel,
|
||||||
|
_stabilityFactor,
|
||||||
|
_twistDirection,
|
||||||
|
_transonicStabilityCoef,
|
||||||
|
_bulletVelocity,
|
||||||
|
EGVAR(common,mapLatitude),
|
||||||
|
EGVAR(weather,currentTemperature),
|
||||||
|
EGVAR(common,mapAltitude),
|
||||||
|
EGVAR(weather,currentHumidity),
|
||||||
|
EGVAR(weather,currentOvercast),
|
||||||
|
CBA_missionTime toFixed 6
|
||||||
|
]
|
||||||
|
]) params ["_id", "_code"];
|
||||||
|
if (_code == 0) then {
|
||||||
|
GVAR(allBullets) set [_id, [_projectile, _caliber, _bulletTraceVisible]];
|
||||||
|
};
|
||||||
|
@ -21,7 +21,14 @@ if (!GVAR(enabled)) exitWith {};
|
|||||||
private _initStartTime = diag_tickTime;
|
private _initStartTime = diag_tickTime;
|
||||||
private _mapSize = worldSize;
|
private _mapSize = worldSize;
|
||||||
|
|
||||||
if (("ace_advanced_ballistics" callExtension format["init:%1:%2", worldName, _mapSize]) == "Terrain already initialized") exitWith {
|
(
|
||||||
|
"ace" callExtension ["ballistics:map:init", [worldName, _mapSize]]
|
||||||
|
) params ["_data", "_code"];
|
||||||
|
if (_code != 0) exitWith {
|
||||||
|
ERROR("Error initializing map")
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_data == "true") exitWith {
|
||||||
INFO_1("Terrain already initialized [world: %1]",worldName);
|
INFO_1("Terrain already initialized [world: %1]",worldName);
|
||||||
#ifdef DEBUG_MODE_FULL
|
#ifdef DEBUG_MODE_FULL
|
||||||
systemChat "AdvancedBallistics: Terrain already initialized";
|
systemChat "AdvancedBallistics: Terrain already initialized";
|
||||||
@ -53,8 +60,7 @@ INFO_2("Starting Terrain Extension [cells: %1] [world: %2]",_gridCells,worldName
|
|||||||
private _gridCenter = [_x + 25, _y + 25];
|
private _gridCenter = [_x + 25, _y + 25];
|
||||||
private _gridHeight = round(getTerrainHeightASL _gridCenter);
|
private _gridHeight = round(getTerrainHeightASL _gridCenter);
|
||||||
private _gridNumObjects = count (_gridCenter nearObjects ["Building", 50]);
|
private _gridNumObjects = count (_gridCenter nearObjects ["Building", 50]);
|
||||||
private _gridSurfaceIsWater = parseNumber (surfaceIsWater _gridCenter);
|
"ace" callExtension ["ballistics:map:set", [GVAR(currentGrid), _gridHeight, _gridNumObjects, surfaceIsWater _gridCenter]];
|
||||||
"ace_advanced_ballistics" callExtension format["set:%1:%2:%3", _gridHeight, _gridNumObjects, _gridSurfaceIsWater];
|
|
||||||
GVAR(currentGrid) = GVAR(currentGrid) + 1;
|
GVAR(currentGrid) = GVAR(currentGrid) + 1;
|
||||||
if (GVAR(currentGrid) >= _gridCells) exitWith {};
|
if (GVAR(currentGrid) >= _gridCells) exitWith {};
|
||||||
};
|
};
|
||||||
|
@ -26,28 +26,27 @@ if (GVAR(shiftState)) then {
|
|||||||
switch (true) do {
|
switch (true) do {
|
||||||
// Beginning
|
// Beginning
|
||||||
case (_index == -1): {
|
case (_index == -1): {
|
||||||
"ace_clipboard" callExtension (format ["[%1", endl]);
|
"ace" callExtension ["clipboard:append", [format ["[%1", endl]]];
|
||||||
};
|
};
|
||||||
// End
|
// End
|
||||||
case (_index == _listLength): {
|
case (_index == _listLength): {
|
||||||
"ace_clipboard" callExtension "];";
|
"ace" callExtension ["clipboard:append", ["];"]];
|
||||||
};
|
};
|
||||||
// Rest
|
// Rest
|
||||||
default {
|
default {
|
||||||
"ace_clipboard" callExtension ([" ", str (GVAR(defaultLoadoutsList) select _index), [",", ""] select (_index == _listLength - 1), endl] joinString "");
|
"ace" callExtension ["clipboard:append", [[" ", str (GVAR(defaultLoadoutsList) select _index), [",", ""] select (_index == _listLength - 1), endl] joinString ""]];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
"ace_clipboard" callExtension "--COMPLETE--";
|
"ace" callExtension ["clipboard:complete", []];
|
||||||
|
|
||||||
[_display, LLSTRING(exportDefault)] call FUNC(message);
|
[_display, LLSTRING(exportDefault)] call FUNC(message);
|
||||||
} else {
|
} else {
|
||||||
// Export singular loadout
|
// Export singular loadout
|
||||||
private _export = str (GVAR(center) call CBA_fnc_getLoadout);
|
private _export = str (GVAR(center) call CBA_fnc_getLoadout);
|
||||||
|
"ace" callExtension ["clipboard:append", [_export]];
|
||||||
"ace_clipboard" callExtension (_export + ";");
|
"ace" callExtension ["clipboard:complete", []];
|
||||||
"ace_clipboard" callExtension "--COMPLETE--";
|
|
||||||
|
|
||||||
[_display, LLSTRING(exportCurrent)] call FUNC(message);
|
[_display, LLSTRING(exportCurrent)] call FUNC(message);
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,13 @@
|
|||||||
params ["_display"];
|
params ["_display"];
|
||||||
|
|
||||||
// Can be either a singular loadout or an array of loadouts
|
// Can be either a singular loadout or an array of loadouts
|
||||||
private _extendedLoadout = call compile copyFromClipboard;
|
private _extendedLoadout = if (isMultiplayer) then {
|
||||||
|
("ace" callExtension ["clipboard:loadout", []]) params ["_loadout", "_code"];
|
||||||
|
if (_code != 0) exitWith {};
|
||||||
|
parseSimpleArray _loadout
|
||||||
|
} else {
|
||||||
|
call compile copyFromClipboard
|
||||||
|
};
|
||||||
|
|
||||||
// If error, exit
|
// If error, exit
|
||||||
if (isNil "_extendedLoadout" || {!(_extendedLoadout isEqualType [])}) exitWith {
|
if (isNil "_extendedLoadout" || {!(_extendedLoadout isEqualType [])}) exitWith {
|
||||||
|
@ -138,14 +138,6 @@ _actionsBoxCtrl ctrlSetPosition [
|
|||||||
];
|
];
|
||||||
_actionsBoxCtrl ctrlCommit 0;
|
_actionsBoxCtrl ctrlCommit 0;
|
||||||
|
|
||||||
// Disable import in MP
|
|
||||||
if (isMultiplayer) then {
|
|
||||||
private _importButtonCtrl = _display displayCtrl IDC_buttonImport;
|
|
||||||
_importButtonCtrl ctrlEnable false;
|
|
||||||
_importButtonCtrl ctrlSetFade 0.6;
|
|
||||||
_importButtonCtrl ctrlCommit 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
//--------------- Camera prep
|
//--------------- Camera prep
|
||||||
cutText ["", "PLAIN"];
|
cutText ["", "PLAIN"];
|
||||||
showCommandingMenu "";
|
showCommandingMenu "";
|
||||||
|
@ -94,8 +94,8 @@ if (!isNull _loadoutsDisplay) then {
|
|||||||
};
|
};
|
||||||
} params ["_className"];
|
} params ["_className"];
|
||||||
|
|
||||||
"ace_clipboard" callExtension (_className + ";");
|
"ace" callExtension ["clipboard:append", [_className]];
|
||||||
"ace_clipboard" callExtension "--COMPLETE--";
|
"ace" callExtension ["clipboard:complete", []];
|
||||||
|
|
||||||
[_display, LLSTRING(exportedClassnameText)] call FUNC(message);
|
[_display, LLSTRING(exportedClassnameText)] call FUNC(message);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
TRACE_1("prep",_this);
|
TRACE_1("prep",_this);
|
||||||
|
|
||||||
|
PREP(adjustFire);
|
||||||
|
PREP(calculateElevation);
|
||||||
|
PREP(calculateMaxAngle);
|
||||||
|
PREP(calculateMuzzleVelocity);
|
||||||
|
PREP(calculateSolution);
|
||||||
PREP(firedEH);
|
PREP(firedEH);
|
||||||
PREP(interactMenuOpened);
|
PREP(interactMenuOpened);
|
||||||
PREP(rangeTableOpen);
|
PREP(rangeTableOpen);
|
||||||
PREP(rangeTableUpdate);
|
PREP(rangeTableUpdate);
|
||||||
|
PREP(simulateShot);
|
||||||
PREP(turretChanged);
|
PREP(turretChanged);
|
||||||
PREP(turretPFEH);
|
PREP(turretPFEH);
|
||||||
|
@ -30,6 +30,33 @@
|
|||||||
};
|
};
|
||||||
}] call CBA_fnc_addEventHandler;
|
}] call CBA_fnc_addEventHandler;
|
||||||
|
|
||||||
|
addMissionEventHandler ["ExtensionCallback", {
|
||||||
|
params ["_name", "_function", "_data"];
|
||||||
|
if (_name == "ace:artillery" && {_function == "calculate_table"}) then {
|
||||||
|
(parseSimpleArray _data) params ["_line", "_data"];
|
||||||
|
if (_data isEqualType []) then {
|
||||||
|
GVAR(tableData) set [_line, _data];
|
||||||
|
};
|
||||||
|
GVAR(tableSizeReceived) = GVAR(tableSizeReceived) + 1;
|
||||||
|
if (GVAR(tableSizeReceived) == GVAR(tableSizeActual)) then {
|
||||||
|
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
|
||||||
|
private _ctrlRangeTable = _dialog displayCtrl IDC_TABLE;
|
||||||
|
if (isNull _dialog) exitWith {true};
|
||||||
|
for "_i" from 0 to GVAR(tableSizeActual) do {
|
||||||
|
private _row = GVAR(tableData) getOrDefault [_i, []];
|
||||||
|
if (count _row == 12) then {
|
||||||
|
_ctrlRangeTable lnbAddRow _row;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
|
||||||
|
private _ctrlRangeTable = _dialog displayCtrl IDC_TABLE;
|
||||||
|
if (isNull _dialog) exitWith {TRACE_1("dialog closed",_this);};
|
||||||
|
_ctrlRangeTable lnbAddRow ["", "", "", "", "", "", "", "", "", "", ""];
|
||||||
|
TRACE_1("table filled",_ctrlRangeTable);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}];
|
||||||
|
|
||||||
#ifdef DEBUG_MODE_FULL
|
#ifdef DEBUG_MODE_FULL
|
||||||
#include "dev\showShotInfo.inc.sqf"
|
#include "dev\showShotInfo.inc.sqf"
|
||||||
#include "dev\checkConfigs.inc.sqf"
|
#include "dev\checkConfigs.inc.sqf"
|
||||||
|
@ -14,13 +14,6 @@ class CfgPatches {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class ACE_Extensions {
|
|
||||||
class ace_artillerytables {
|
|
||||||
windows = 1;
|
|
||||||
client = 1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "CfgEventHandlers.hpp"
|
#include "CfgEventHandlers.hpp"
|
||||||
#include "CfgMagazines.hpp"
|
#include "CfgMagazines.hpp"
|
||||||
#include "CfgVehicles.hpp"
|
#include "CfgVehicles.hpp"
|
||||||
|
41
addons/artillerytables/functions/fnc_adjustFire.sqf
Normal file
41
addons/artillerytables/functions/fnc_adjustFire.sqf
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
/*
|
||||||
|
* Author: LorenLuke
|
||||||
|
* Adjusts a target point north and east, and recalculates a solution in air based on atmospheric conditions
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* 0: Gun Position ASL <ARRAY>
|
||||||
|
* 1: Target Position ASL <ARRAY>
|
||||||
|
* 2: Adjustment to the East (negative is West); meters <NUMBER>
|
||||||
|
* 3: Adjustment to the North (negative is South); meters <NUMBER>
|
||||||
|
* 4: Adjustment vertically (negative is Down); meters <NUMBER>
|
||||||
|
* 5: Muzzle velocity; meters/second <NUMBER>
|
||||||
|
* 6: Air Friction; meters^-1 (m/s^2)/(m^2/s^2) <NUMBER>
|
||||||
|
* 7: High angle boolean (true is high angle) <BOOL>
|
||||||
|
* 8: Temperature; degrees Celsius <NUMBER>
|
||||||
|
* 9: Atmospheric Density; kg/(meters^3) <NUMBER>
|
||||||
|
* 10: Direction of wind; degrees clockwise from north <NUMBER>
|
||||||
|
* 11: Speed of wind; meters/second <NUMBER>
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* Array of returns <ARRAY>
|
||||||
|
* 0: Angle of shot; Milliradians <NUMBER>
|
||||||
|
* 1: Angle adjust left or right; Milliradians <NUMBER>
|
||||||
|
* 2: Time of flight; seconds <NUMBER>
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* [getposASL vehicle player, targetPos, 20, 50, 0, 200, -0.0001, true, 15, 1.225, 225, 5] call ace_artilleryTables_fnc_adjustFire
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
params ["_gunPos", "_targetPos", "_adjustEast", "_adjustNorth", "_adjustUp", "_muzzleVelocity", "_airFriction", ["_highAngle", true], ["_temperature", 15], ["_airDensity", 1.225], ["_windDir", 0], ["_windSpeed", 0]];
|
||||||
|
|
||||||
|
//DEFAULT_AIR_FRICTION == -0.00006
|
||||||
|
//MK6_82mm_AIR_FRICTION == -0.0001
|
||||||
|
|
||||||
|
private _resultPos = [_adjustEast + _targetPos select 0, _adjustNorth + _targetPos select 1, _adjustUp + _targetPos select 2];
|
||||||
|
|
||||||
|
private _returns = ["_gunPos", "_resultPos", "_muzzleVelocity", "_highAngle", "_airFriction", "_temperature", "_airDensity", "_windDir", "_windSpeed"] call FUNC(calculateSolution);
|
||||||
|
|
||||||
|
_returns
|
71
addons/artillerytables/functions/fnc_calculateElevation.sqf
Normal file
71
addons/artillerytables/functions/fnc_calculateElevation.sqf
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
/*
|
||||||
|
* Author: LorenLuke
|
||||||
|
* Adjusts a target point north and east, and recalculates a solution in air based on atmospheric conditions
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* 0: Distance to Target; meters <NUMBER>
|
||||||
|
* 1: Height of target; meters, relative to gun altitude (positive means target higher than gun) <NUMBER>
|
||||||
|
* 2: Muzzle velocity; meters/second <NUMBER>
|
||||||
|
* 3: High angle boolean (true is high angle) <BOOL>
|
||||||
|
* 4: Air Friction; meters^-1 [(m/s^2)/(m^2/s^2)] <NUMBER>
|
||||||
|
* 5: Temperature; degrees Celsius <NUMBER>
|
||||||
|
* 6: Atmospheric Density; kg/(meters^3) <NUMBER>
|
||||||
|
* 7: Cross wind; meters/second (negative is Right to Left) <NUMBER>
|
||||||
|
* 8: Tail wind; meters/second (negative is flying against the wind) <NUMBER>
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* Array of returns <ARRAY>
|
||||||
|
* 0: Angle of shot; Milliradians <NUMBER>
|
||||||
|
* 1: Angle adjust left or right; Milliradians <NUMBER>
|
||||||
|
* 2: Time of flight; seconds <NUMBER>
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* [myPos, 0, 200, true, -0.0001, 15, 1.225, 5, -10] call ace_artilleryTables_fnc_calculateElevation
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
params ["_targetDistance", "_targetHeight", "_muzzleVelocity", ["_highArc", true], ["_airFriction", 0], ["_temperature", 15], ["_airDensity", 1.225], ["_crossWind", 0], ["_tailWind", 0]];
|
||||||
|
|
||||||
|
//DEFAULT_AIR_FRICTION == -0.00006
|
||||||
|
//MK6_82mm_AIR_FRICTION == -0.0001
|
||||||
|
|
||||||
|
if (_airFriction != 0) then {
|
||||||
|
_muzzleVelocity = [_muzzleVelocity, _temperature, _atmosphericDensity] call FUNC(calculateMuzzleVelocity);
|
||||||
|
};
|
||||||
|
private _maxResults = [_muzzleVelocity, _airFriction] call FUNC(calculateMaxAngle);
|
||||||
|
|
||||||
|
private _testShot = [_maxResults select 0, _targetHeight, _muzzleVelocity, _airFriction, _crossWind, _tailWind, _temperature, _airDensity] call FUNC(simulateShot);
|
||||||
|
if (_testShot select 1 < _targetDistance) exitWith {
|
||||||
|
//No way we can hit it so don't bother;
|
||||||
|
[-1, -1, -1]
|
||||||
|
};
|
||||||
|
|
||||||
|
private _useDistance = _targetDistance;
|
||||||
|
private _useAngle = 0;
|
||||||
|
private _resultDistance = 0;
|
||||||
|
private _xDeviation = 0;
|
||||||
|
private _tof = 0;
|
||||||
|
|
||||||
|
while {abs(_resultDistance - _targetDistance) > 0.5} do {
|
||||||
|
TRACE_7("callExtension:artillery:simulate_find_solution",_useDistance,_targetHeight,_muzzleVelocity,_airFriction,_higharc,DEFAULT_MIN_ELEV,DEFAULT_MAX_ELEV);
|
||||||
|
(
|
||||||
|
"ace" callExtension ["artillery:simulate_find_solution", [_useDistance, _targetHeight, _muzzleVelocity, _airFriction, _higharc, DEFAULT_MIN_ELEV, DEFAULT_MAX_ELEV]]
|
||||||
|
) params ["_data", "_code"];
|
||||||
|
TRACE_1("",_code);
|
||||||
|
(parseSimpleArray _data) params ["", "_useAngleRad", ""];
|
||||||
|
_useAngle = deg(_useAngleRad) * DEGTOMILS;
|
||||||
|
|
||||||
|
private _shotResults = [_useAngle, _targetHeight, _muzzleVelocity, _airFriction, _crossWind, _tailWind, _temperature, _airDensity] call FUNC(simulateShot);
|
||||||
|
|
||||||
|
_xDeviation = _shotResults select 0;
|
||||||
|
_resultDistance = _shotResults select 1;
|
||||||
|
_tof = _shotResults select 2;
|
||||||
|
_useDistance = (2 * _targetDistance) - _resultDistance;
|
||||||
|
};
|
||||||
|
|
||||||
|
private _angleOffsetDeg = _xDeviation atan2 _resultDistance;
|
||||||
|
private _angleOffset = _angleOffsetDeg * DEGTOMILS;
|
||||||
|
|
||||||
|
[_useAngle, -_angleOffset, _tof]
|
31
addons/artillerytables/functions/fnc_calculateMaxAngle.sqf
Normal file
31
addons/artillerytables/functions/fnc_calculateMaxAngle.sqf
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
/*
|
||||||
|
* Author: LorenLuke
|
||||||
|
* Calculates the best possible angle to shoot farthest based on muzzle velocity and air friction.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* 0: Initial Muzzle Velocity; meters/second <NUMBER>
|
||||||
|
* 1: Air Friction; meters^-1 (m/s^2)/(m^2/s^2) <NUMBER>
|
||||||
|
*
|
||||||
|
* Return Values: <ARRAY>
|
||||||
|
* 1: Best Angle; Milliradians <NUMBER>
|
||||||
|
* 2: Furthest Distance; Meters <NUMBER>
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* [200, -0.00006] call ace_artilleryTables_fnc_calculateMaxAngle
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
params ["_muzzleVelocity", "_airFriction"];
|
||||||
|
|
||||||
|
TRACE_2("callExtension:artillery:find_max_angle",_muzzleVelocity,_airFriction);
|
||||||
|
(
|
||||||
|
"ace" callExtension ["artillery:find_max_angle", [_muzzleVelocity, _airFriction]]
|
||||||
|
) params ["_data", "_code"];
|
||||||
|
TRACE_1("",_code)
|
||||||
|
(parseSimpleArray _data) params ["_bestAngle", "_bestDistance", ""];
|
||||||
|
_returns = [deg _bestAngle * 6400 / 360, _bestDistance];
|
||||||
|
_returns params ["_bestAngle", "_bestDistance"];
|
||||||
|
|
||||||
|
_returns
|
@ -0,0 +1,28 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
/*
|
||||||
|
* Author: LorenLuke
|
||||||
|
* Calculates the muzzleVelocity change with advanced calculations.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* 0: Initial Muzzle velocity; meters/second <NUMBER>
|
||||||
|
* 1: Temperature; degrees Celsius <NUMBER>
|
||||||
|
* 2: Atmospheric Density; kg/(meters^3) <NUMBER>
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* Adjusted Muzzle Velocity; Meters <NUMBER>
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* [200, 15, 1.225] call ace_artilleryTables_fnc_calculateMuzzleVelocity
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
params ["_muzzleVelocity", "_temperature", "_airDensity"];
|
||||||
|
|
||||||
|
// Calculate air density
|
||||||
|
private _relativeDensity = _airDensity / 1.225;
|
||||||
|
private _newMuzzleVelocityCoefficient = (((_temperature + 273.13) / 288.13 - 1) / 40 + 1);
|
||||||
|
|
||||||
|
private _newMuzzleVelocity = _muzzleVelocity * _newMuzzleVelocityCoefficient;
|
||||||
|
|
||||||
|
_newMuzzleVelocity
|
44
addons/artillerytables/functions/fnc_calculateSolution.sqf
Normal file
44
addons/artillerytables/functions/fnc_calculateSolution.sqf
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
/*
|
||||||
|
* Author: LorenLuke
|
||||||
|
* Provides fire angle and deflection solutions on a target of set distance and height, including accounting for drag and atmospheric wind conditions.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* 0: Gun Position ASL; <ARRAY>
|
||||||
|
* 1: Target Position ASL; <ARRAY>
|
||||||
|
* 2: Muzzle Velocity; meters/second <NUMBER>
|
||||||
|
* 3: High angle boolean (true is high angle) <BOOL>
|
||||||
|
* 4: Air Friction; meters^-1 [(m/s^2)/(m^2/s^2)] <NUMBER>
|
||||||
|
* 5: Temperature; degrees Celsius <NUMBER>
|
||||||
|
* 6: Atmospheric Density; kg/(meters^3) <NUMBER>
|
||||||
|
* 7: Direction of wind; degrees clockwise from north <NUMBER>
|
||||||
|
* 8: Speed of wind; meters/second <NUMBER>
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* array of returns <ARRAY>
|
||||||
|
* 0: Angle of shot; Milliradians <NUMBER>
|
||||||
|
* 1: Angle adjust left or right; Milliradians <NUMBER>
|
||||||
|
* 2: Time of flight; seconds <NUMBER>
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* [myPos, targetPos, 200, true, -0.0001, 15, 1.225, 225, 5] call ace_artilleryTables_fnc_calculateSolution
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
params ["_ownPos", "_targetPos", "_muzzleVelocity", ["_highAngle", true], ["_airFriction", 0], ["_temperature", 15], ["_airDensity", 1.225], ["_windDir", 0], ["_windSpeed", 0]];
|
||||||
|
|
||||||
|
//DEFAULT_AIR_FRICTION == -0.00006
|
||||||
|
//MK6_82mm_AIR_FRICTION == -0.0001
|
||||||
|
|
||||||
|
private _relPos = _targetPos vectorDiff _ownPos;
|
||||||
|
|
||||||
|
private _targetDir = (_relpos select 0) atan2 (_relPos select 1);
|
||||||
|
private _targetDist = sqrt( (_relPos select 0)^2 + (_relpos select 1)^2 );
|
||||||
|
private _heightDif = _relPos select 2;
|
||||||
|
private _crossWind = sin(_targetDir - _windDir) * _windSpeed;
|
||||||
|
private _tailWind = -cos(_targetDir - _windDir) * _windSpeed;
|
||||||
|
|
||||||
|
private _solutionReturns = [_targetDist, _heightDif, _muzzleVelocity, _highAngle, _airFriction, _crossWind, _tailWind, _temperature, _airDensity] call FUNC(calculateElevation);
|
||||||
|
|
||||||
|
_solutionReturns
|
@ -34,31 +34,12 @@ _ctrlElevationLow ctrlSetTextColor ([[1,1,1,1],[0.25,0.25,0.25,1]] select GVAR(l
|
|||||||
|
|
||||||
lnbClear _ctrlRangeTable;
|
lnbClear _ctrlRangeTable;
|
||||||
// Call extension with current data and start workers
|
// Call extension with current data and start workers
|
||||||
TRACE_5("callExtension:start",_muzzleVelocity,_airFriction,_elevMin,_elevMax,GVAR(lastElevationMode));
|
TRACE_5("callExtension:artillery:calculate_table",_muzzleVelocity,_airFriction,_elevMin,_elevMax,GVAR(lastElevationMode));
|
||||||
private _ret = "ace_artillerytables" callExtension ["start", [_muzzleVelocity,_airFriction,_elevMin,_elevMax,GVAR(lastElevationMode)]];
|
(
|
||||||
TRACE_1("",_ret);
|
"ace" callExtension ["artillery:calculate_table", [_muzzleVelocity, _airFriction, _elevMin, _elevMax, GVAR(lastElevationMode)]]
|
||||||
|
) params ["_data", "_code"];
|
||||||
|
TRACE_1("",_code)
|
||||||
|
|
||||||
// Non-blocking read data out of extension as it becomes availiable
|
GVAR(tableData) = createHashMap;
|
||||||
[{
|
GVAR(tableSizeActual) = (parseSimpleArray _data) select 1;
|
||||||
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
|
GVAR(tableSizeReceived) = 0;
|
||||||
private _ctrlRangeTable = _dialog displayCtrl IDC_TABLE;
|
|
||||||
if (isNull _dialog) exitWith {true};
|
|
||||||
|
|
||||||
private _status = 1; // 1 = data on line, 2 - data not ready, 3 - done
|
|
||||||
while {_status == 1} do {
|
|
||||||
private _ret = ("ace_artillerytables" callExtension ["getline", []]);
|
|
||||||
// TRACE_1("callExtension:getline",_ret);
|
|
||||||
_status = _ret select 1;
|
|
||||||
if (_status == 1) then { _ctrlRangeTable lnbAddRow parseSimpleArray (_ret select 0) };
|
|
||||||
};
|
|
||||||
|
|
||||||
(_status == 3) // exit loop when all data read
|
|
||||||
}, {
|
|
||||||
// put dummy line at end because scrolling is problematic and can't see last line
|
|
||||||
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
|
|
||||||
private _ctrlRangeTable = _dialog displayCtrl IDC_TABLE;
|
|
||||||
if (isNull _dialog) exitWith {TRACE_1("dialog closed",_this);};
|
|
||||||
|
|
||||||
_ctrlRangeTable lnbAddRow ["", "", "", "", "", "", "", "", "", "", ""];
|
|
||||||
TRACE_1("table filled",_ctrlRangeTable);
|
|
||||||
}, []] call CBA_fnc_waitUntilAndExecute;
|
|
||||||
|
47
addons/artillerytables/functions/fnc_simulateShot.sqf
Normal file
47
addons/artillerytables/functions/fnc_simulateShot.sqf
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
/*
|
||||||
|
* Author: LorenLuke
|
||||||
|
* Simulates an indirect shot on a target of known height with given drag, wind, and atmospheric conditions
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* 0: Gun Elevation Angle; milliradians <NUMBER>
|
||||||
|
* 1: Relative Target Height; meters, relative to gun altitude (positive means target higher than gun) <NUMBER>
|
||||||
|
* 2: Muzzle Velocity; meters/second <NUMBER>
|
||||||
|
* 3: Air Friction; meters^-1 [(m/s^2)/(m^2/s^2)] <NUMBER>
|
||||||
|
* 4: Cross wind; meters/second (negative is Right to Left) <NUMBER>
|
||||||
|
* 5: Tail wind; meters/second (negative is flying against the wind) <NUMBER>
|
||||||
|
* 6: Temperature; degrees Celsius <NUMBER>
|
||||||
|
* 7: Atmospheric Density; kg/(meters^3) <NUMBER>
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* array of returns <ARRAY>
|
||||||
|
* 0: Deflection Adjustment To Hit; Milliradians (negative is Left) <NUMBER>
|
||||||
|
* 1: Distance of Shot; meters <NUMBER>
|
||||||
|
* 2: Time of Flight; seconds <NUMBER>
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* [900, 10, 200, -0.0001, 4, 0, 15, 1.225] call ace_artilleryTables_fnc_simulateShot
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
params ["_angle", "_targetHeight", "_muzzleVelocity", ["_airFriction", 0], ["_crossWind", 0], ["_tailWind", 0], ["_temperature", 15], ["_atmosphericDensity", 1.225]];
|
||||||
|
|
||||||
|
//DEFAULT_AIR_FRICTION == -0.00006
|
||||||
|
//MK6_82mm_AIR_FRICTION == -0.0001
|
||||||
|
|
||||||
|
if (_airFriction != 0) then {
|
||||||
|
_muzzleVelocity = [_muzzleVelocity, _temperature, _atmosphericDensity] call FUNC(calculateMuzzleVelocity);
|
||||||
|
};
|
||||||
|
|
||||||
|
private _atmosphericDensityRatio = _atmosphericDensity / 1.225;
|
||||||
|
private _radAngle = rad(_angle / DEGTOMILS);
|
||||||
|
|
||||||
|
TRACE_8("callExtension:artillery:simulate_shot",_radAngle,_targetHeight,_muzzleVelocity,_airFriction,_crossWind,_tailWind,_temperature,_atmosphericDensityRatio);
|
||||||
|
(
|
||||||
|
"ace" callExtension ["artillery:simulate_shot", [_radAngle, _targetHeight, _muzzleVelocity, _airFriction, _crossWind, _tailWind, _temperature, _atmosphericDensityRatio]]
|
||||||
|
) params ["_data", "_code"];
|
||||||
|
TRACE_1("",_code)
|
||||||
|
|
||||||
|
//[xDeviation, yDistance, timeOfFlight]
|
||||||
|
parseSimpleArray _data
|
@ -12,6 +12,10 @@
|
|||||||
// This is a good fit for most large artillery, but a little low for lighter mortars
|
// This is a good fit for most large artillery, but a little low for lighter mortars
|
||||||
#define DEFAULT_AIR_FRICTION -0.00006
|
#define DEFAULT_AIR_FRICTION -0.00006
|
||||||
|
|
||||||
|
#define DEFAULT_MIN_ELEV 0
|
||||||
|
// 90 degrees in radians
|
||||||
|
#define DEFAULT_MAX_ELEV 1.5708
|
||||||
|
|
||||||
#define DEGTOMILS 17.7777778
|
#define DEGTOMILS 17.7777778
|
||||||
|
|
||||||
#define IDC_MODECONTROLGROUP 1000
|
#define IDC_MODECONTROLGROUP 1000
|
||||||
|
@ -30,9 +30,16 @@ while {_velocity > _thresholdVelocity} do {
|
|||||||
private _bc = GVAR(targetSolutionInput) select 14;
|
private _bc = GVAR(targetSolutionInput) select 14;
|
||||||
private _dragModel = GVAR(targetSolutionInput) select 15;
|
private _dragModel = GVAR(targetSolutionInput) select 15;
|
||||||
private _temperature = GVAR(targetSolutionInput) select 5;
|
private _temperature = GVAR(targetSolutionInput) select 5;
|
||||||
private _drag = parseNumber(("ace_advanced_ballistics" callExtension format["retard:%1:%2:%3:%4", _dragModel, _bc, _velocity, _temperature]));
|
|
||||||
_distance = _distance + _velocity * __DELTA_T;
|
_distance = _distance + _velocity * __DELTA_T;
|
||||||
_velocity = _velocity - (_drag * __DELTA_T);
|
private _data = (
|
||||||
|
"ace" callExtension ["ballistics:retard", [
|
||||||
|
_dragModel,
|
||||||
|
_bc,
|
||||||
|
_velocity,
|
||||||
|
_temperature
|
||||||
|
]]
|
||||||
|
) select 0;
|
||||||
|
_velocity = _velocity - ((parseNumber _data) * __DELTA_T);
|
||||||
};
|
};
|
||||||
|
|
||||||
_distance
|
_distance
|
||||||
|
@ -90,7 +90,14 @@ private _wind1 = [cos(270 - _windDirection * 30) * _windSpeed1, sin(270 - _windD
|
|||||||
private _wind2 = [cos(270 - _windDirection * 30) * _windSpeed2, sin(270 - _windDirection * 30) * _windSpeed2, 0];
|
private _wind2 = [cos(270 - _windDirection * 30) * _windSpeed2, sin(270 - _windDirection * 30) * _windSpeed2, 0];
|
||||||
private _windDrift = 0;
|
private _windDrift = 0;
|
||||||
if (missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) then {
|
if (missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) then {
|
||||||
_bc = parseNumber(("ace_advanced_ballistics" callExtension format["atmosphericCorrection:%1:%2:%3:%4:%5", _bc, _temperature, _barometricPressure, _relativeHumidity, _atmosphereModel]));
|
_bc = parseNumber (("ace" callExtension ["ballistics:atmospheric_correction", [
|
||||||
|
_bc,
|
||||||
|
_temperature,
|
||||||
|
_barometricPressure,
|
||||||
|
_relativeHumidity,
|
||||||
|
_atmosphereModel
|
||||||
|
]]
|
||||||
|
) select 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _eoetvoesMultiplier = 0;
|
private _eoetvoesMultiplier = 0;
|
||||||
@ -113,8 +120,15 @@ while {_TOF < 15 && (_bulletPos select 1) < _targetRange} do {
|
|||||||
_trueSpeed = vectorMagnitude _trueVelocity;
|
_trueSpeed = vectorMagnitude _trueVelocity;
|
||||||
|
|
||||||
if (missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) then {
|
if (missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) then {
|
||||||
private _drag = parseNumber(("ace_advanced_ballistics" callExtension format["retard:%1:%2:%3:%4", _dragModel, _bc, _trueSpeed, _temperature]));
|
private _data = (
|
||||||
_bulletAccel = (vectorNormalized _trueVelocity) vectorMultiply (-1 * _drag);
|
"ace" callExtension ["ballistics:retard", [
|
||||||
|
_dragModel,
|
||||||
|
_bc,
|
||||||
|
_trueSpeed,
|
||||||
|
_temperature
|
||||||
|
]]
|
||||||
|
) select 0;
|
||||||
|
_bulletAccel = (vectorNormalized _trueVelocity) vectorMultiply (-1 * (parseNumber _data));
|
||||||
} else {
|
} else {
|
||||||
_bulletAccel = _trueVelocity vectorMultiply (_trueSpeed * _airFriction);
|
_bulletAccel = _trueVelocity vectorMultiply (_trueSpeed * _airFriction);
|
||||||
};
|
};
|
||||||
|
@ -36,11 +36,25 @@ if (!GVAR(atmosphereModeTBH)) then {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private _scopeBaseAngle = if !(missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) then {
|
private _scopeBaseAngle = if !(missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) then {
|
||||||
private _zeroAngle = "ace_advanced_ballistics" callExtension format ["calcZero:%1:%2:%3:%4", _zeroRange, _muzzleVelocity, _airFriction, _boreHeight];
|
parseNumber (("ace" callExtension ["ballistics:zero_vanilla", [
|
||||||
(parseNumber _zeroAngle)
|
_zeroRange,
|
||||||
|
_muzzleVelocity,
|
||||||
|
_airFriction,
|
||||||
|
_boreHeight
|
||||||
|
]]) select 0)
|
||||||
} else {
|
} else {
|
||||||
private _zeroAngle = "ace_advanced_ballistics" callExtension format ["calcZeroAB:%1:%2:%3:%4:%5:%6:%7:%8:%9", _zeroRange, _muzzleVelocity, _boreHeight, _temperature, _barometricPressure, _relativeHumidity, _bc, _dragModel, _atmosphereModel];
|
parseNumber (("ace" callExtension ["ballistics:zero_advanced", [
|
||||||
(parseNumber _zeroAngle)
|
_zeroRange,
|
||||||
|
_muzzleVelocity,
|
||||||
|
_airFriction,
|
||||||
|
_boreHeight,
|
||||||
|
_temperature,
|
||||||
|
_barometricPressure,
|
||||||
|
_relativeHumidity,
|
||||||
|
_bc,
|
||||||
|
_dragModel,
|
||||||
|
_atmosphereModel
|
||||||
|
]]) select 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
GVAR(workingMemory) set [2, _zeroRange];
|
GVAR(workingMemory) set [2, _zeroRange];
|
||||||
|
@ -70,8 +70,6 @@ class ctrlMapEmpty;
|
|||||||
#include "CompassControl.hpp"
|
#include "CompassControl.hpp"
|
||||||
#include "CfgUIGrids.hpp"
|
#include "CfgUIGrids.hpp"
|
||||||
|
|
||||||
class ACE_Extensions {};
|
|
||||||
|
|
||||||
class ACE_Tests {
|
class ACE_Tests {
|
||||||
vehicleTransportInventory = QPATHTOF(dev\test_vehicleInventory.sqf);
|
vehicleTransportInventory = QPATHTOF(dev\test_vehicleInventory.sqf);
|
||||||
mapConfigs = QPATHTOF(dev\test_mapConfigs.sqf);
|
mapConfigs = QPATHTOF(dev\test_mapConfigs.sqf);
|
||||||
|
@ -103,47 +103,29 @@ if (_oldCompats isNotEqualTo []) then {
|
|||||||
///////////////
|
///////////////
|
||||||
private _platform = toLowerANSI (productVersion select 6);
|
private _platform = toLowerANSI (productVersion select 6);
|
||||||
|
|
||||||
if (!isServer && {_platform in ["linux", "osx"]}) then {
|
if (_platform in ["linux", "osx"]) then {
|
||||||
// Linux and OSX client ports do not support extensions at all
|
// Linux and OSX client ports do not support extensions at all
|
||||||
INFO("Operating system does not support extensions");
|
if (hasInterface) then {
|
||||||
|
WARNING("Operating system does not support extensions");
|
||||||
} else {
|
} else {
|
||||||
{
|
INFO("Operating system does not support extensions");
|
||||||
private _extension = configName _x;
|
|
||||||
private _isWindows = _platform == "windows" && {getNumber (_x >> "windows") == 1};
|
|
||||||
private _isLinux = _platform == "linux" && {getNumber (_x >> "linux") == 1};
|
|
||||||
private _isClient = hasInterface && {getNumber (_x >> "client") == 1};
|
|
||||||
private _isServer = !hasInterface && {getNumber (_x >> "server") == 1};
|
|
||||||
|
|
||||||
if ((_isWindows || _isLinux) && {_isClient || _isServer}) then {
|
|
||||||
private _versionEx = _extension callExtension "version";
|
|
||||||
|
|
||||||
if (_versionEx == "") then {
|
|
||||||
private _extensionFile = _extension;
|
|
||||||
|
|
||||||
if (productVersion select 7 == "x64") then {
|
|
||||||
_extensionFile = format ["%1_x64", _extensionFile];
|
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
("ace" callExtension ["version", []]) params [["_versionEx", "", [""]], ["_returnCode", -1, [-1]]];
|
||||||
|
|
||||||
private _platformExt = [".dll", ".so"] select (_platform == "linux");
|
if (_returnCode != 0 || {_versionEx == ""}) then {
|
||||||
_extensionFile = format ["%1%2", _extensionFile, _platformExt];
|
private _errorMsg = format ["Extension not found. [Return Code: %1]", _returnCode];
|
||||||
|
|
||||||
private _errorMsg = format ["Extension %1 not found.", _extensionFile];
|
|
||||||
ERROR(_errorMsg);
|
ERROR(_errorMsg);
|
||||||
|
|
||||||
if (hasInterface) then {
|
if (hasInterface) then {
|
||||||
["[ACE] ERROR", _errorMsg] call FUNC(errorMessage);
|
["[ACE] ERROR", _errorMsg] call FUNC(errorMessage);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Print the current extension version
|
_versionEx = _versionEx select [0, 8]; // git hash
|
||||||
INFO_2("Extension version: %1: %2",_extension,_versionEx);
|
INFO_1("Extension [Version: %1]",_versionEx);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
} forEach ("true" configClasses (configFile >> "ACE_Extensions"));
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isArray (configFile >> "ACE_Extensions" >> "extensions")) then {
|
|
||||||
WARNING("extensions[] array no longer supported");
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////
|
///////////////
|
||||||
// Check server version/addons
|
// Check server version/addons
|
||||||
|
@ -27,13 +27,6 @@ class CfgPatches {
|
|||||||
|
|
||||||
#include "CfgOptics.hpp"
|
#include "CfgOptics.hpp"
|
||||||
|
|
||||||
class ACE_Extensions {
|
|
||||||
class ace_fcs {
|
|
||||||
windows = 1;
|
|
||||||
client = 1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ACE_Tests {
|
class ACE_Tests {
|
||||||
fcs = QPATHTOF(dev\test_debugConfigs.sqf);
|
fcs = QPATHTOF(dev\test_debugConfigs.sqf);
|
||||||
};
|
};
|
||||||
|
@ -63,7 +63,7 @@ private _turretConfig = [configOf _vehicle, _turret] call EFUNC(common,getTurret
|
|||||||
};
|
};
|
||||||
} forEach (_vehicle weaponsTurret _turret);
|
} forEach (_vehicle weaponsTurret _turret);
|
||||||
|
|
||||||
private _offset = "ace_fcs" callExtension format ["%1,%2,%3,%4", _initSpeed, _airFriction, _angleTarget, _distance];
|
private _offset = ("ace" callExtension ["fcs", [_initSpeed, _airFriction, _angleTarget, _distance]]) # 0;
|
||||||
_offset = parseNumber _offset;
|
_offset = parseNumber _offset;
|
||||||
|
|
||||||
_FCSInitSpeed pushBack _initSpeed;
|
_FCSInitSpeed pushBack _initSpeed;
|
||||||
|
@ -41,7 +41,7 @@ if (_zeroDistance > 0) then {
|
|||||||
private _weaponCombo = [_weapon, _magazine, _ammo, _zeroDistance];
|
private _weaponCombo = [_weapon, _magazine, _ammo, _zeroDistance];
|
||||||
if (_weaponCombo isNotEqualTo (_gunner getVariable [QGVAR(lastWeaponCombo), []])) then {
|
if (_weaponCombo isNotEqualTo (_gunner getVariable [QGVAR(lastWeaponCombo), []])) then {
|
||||||
private _airFriction = getNumber (configFile >> "CfgAmmo" >> _ammo >> "airFriction");
|
private _airFriction = getNumber (configFile >> "CfgAmmo" >> _ammo >> "airFriction");
|
||||||
private _antiOffset = "ace_fcs" callExtension format ["%1,%2,%3,%4", _initSpeed, _airFriction, 0, _zeroDistance];
|
private _antiOffset = ("ace" callExtension ["fcs", [_initSpeed, _airFriction, 0, _zeroDistance]]) # 0;
|
||||||
_antiOffset = parseNumber _antiOffset;
|
_antiOffset = parseNumber _antiOffset;
|
||||||
|
|
||||||
_gunner setVariable [QGVAR(lastWeaponCombo), _weaponCombo];
|
_gunner setVariable [QGVAR(lastWeaponCombo), _weaponCombo];
|
||||||
|
@ -21,10 +21,3 @@ class CfgPatches {
|
|||||||
#include "CursorMenus.hpp"
|
#include "CursorMenus.hpp"
|
||||||
|
|
||||||
#include "ACE_Settings.hpp"
|
#include "ACE_Settings.hpp"
|
||||||
|
|
||||||
class ACE_Extensions {
|
|
||||||
class ace_break_line {
|
|
||||||
windows = 1;
|
|
||||||
client = 1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
@ -41,7 +41,7 @@ if (_iconFile isEqualTo "") then {
|
|||||||
_text = if ([GVAR(useListMenu), GVAR(useListMenuSelf)] select GVAR(keyDownSelfAction)) then {
|
_text = if ([GVAR(useListMenu), GVAR(useListMenuSelf)] select GVAR(keyDownSelfAction)) then {
|
||||||
format ["<img image='%1' align='left' color='%2'/><t %3>%4</t>", _iconFile, _iconColor, _textSettings, _text]
|
format ["<img image='%1' align='left' color='%2'/><t %3>%4</t>", _iconFile, _iconColor, _textSettings, _text]
|
||||||
} else {
|
} else {
|
||||||
format ["<img image='%1' align='center' color='%2'/><br/><t %3 align='center'>%4</t>", _iconFile, _iconColor, _textSettings, "ace_break_line" callExtension _text];
|
format ["<img image='%1' align='center' color='%2'/><br/><t %3 align='center'>%4</t>", _iconFile, _iconColor, _textSettings, ("ace" callExtension ["break_line", [_text]]) select 0];
|
||||||
};
|
};
|
||||||
|
|
||||||
[_ctrl, GVAR(iconCount), _text] call FUNC(ctrlSetParsedTextCached);
|
[_ctrl, GVAR(iconCount), _text] call FUNC(ctrlSetParsedTextCached);
|
||||||
|
@ -27,13 +27,4 @@ class CfgPatches {
|
|||||||
#include "CfgEventHandlers.hpp"
|
#include "CfgEventHandlers.hpp"
|
||||||
#include "CfgAmmo.hpp"
|
#include "CfgAmmo.hpp"
|
||||||
#include "CfgEden.hpp"
|
#include "CfgEden.hpp"
|
||||||
|
|
||||||
/*
|
|
||||||
class ACE_Extensions {
|
|
||||||
class ace_medical {
|
|
||||||
// Not yet used
|
|
||||||
};
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -29,13 +29,6 @@ class CfgAddons {
|
|||||||
#include "gui\mainMenu.hpp"
|
#include "gui\mainMenu.hpp"
|
||||||
#include "gui\pauseMenu.hpp"
|
#include "gui\pauseMenu.hpp"
|
||||||
|
|
||||||
class ACE_Extensions {
|
|
||||||
class ace_clipboard {
|
|
||||||
windows = 1;
|
|
||||||
client = 1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class CfgCommands {
|
class CfgCommands {
|
||||||
allowedHTMLLoadURIs[] += {
|
allowedHTMLLoadURIs[] += {
|
||||||
"https://ace3.acemod.org/version.html"
|
"https://ace3.acemod.org/version.html"
|
||||||
|
@ -19,8 +19,7 @@
|
|||||||
|
|
||||||
private _outputText = {
|
private _outputText = {
|
||||||
diag_log text (_this select 0);
|
diag_log text (_this select 0);
|
||||||
"ace_clipboard" callExtension ((_this select 0) + "
|
"ace" callExtension ["clipboard:append", [(_this select 0) + endl]];
|
||||||
");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private _text = format ["~~~~~~~~~ACE Debug~~~~~~~~~
|
private _text = format ["~~~~~~~~~ACE Debug~~~~~~~~~
|
||||||
@ -94,4 +93,4 @@ _text = format ["
|
|||||||
} forEach (allVariables _unit);
|
} forEach (allVariables _unit);
|
||||||
} forEach allUnits;
|
} forEach allUnits;
|
||||||
|
|
||||||
"ace_clipboard" callExtension "--COMPLETE--";
|
"ace" callExtension ["clipboard:complete", []];
|
||||||
|
@ -65,7 +65,7 @@ private _n = 0;
|
|||||||
private _range = 0;
|
private _range = 0;
|
||||||
|
|
||||||
if (_useABConfig) then {
|
if (_useABConfig) then {
|
||||||
_bc = parseNumber(("ace_advanced_ballistics" callExtension format["atmosphericCorrection:%1:%2:%3:%4:%5", _bc, _temperature, _barometricPressure, _relativeHumidity, _atmosphereModel]));
|
_bc = parseNumber (("ace" callExtension ["ballistics:atmospheric_correction", [_bc, _temperature, _barometricPressure, _relativeHumidity, _atmosphereModel]]) select 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _airFrictionCoef = 1;
|
private _airFrictionCoef = 1;
|
||||||
@ -99,7 +99,7 @@ while {_TOF < 6 && (_bulletPos select 1) < _targetRange} do {
|
|||||||
_trueSpeed = vectorMagnitude _trueVelocity;
|
_trueSpeed = vectorMagnitude _trueVelocity;
|
||||||
|
|
||||||
if (_useABConfig) then {
|
if (_useABConfig) then {
|
||||||
private _drag = parseNumber(("ace_advanced_ballistics" callExtension format["retard:%1:%2:%3:%4", _dragModel, _bc, _trueSpeed, _temperature]));
|
private _drag = parseNumber (("ace" callExtension ["ballistics:retard", [_dragModel, _bc, _trueSpeed, _temperature]]) select 0);
|
||||||
_bulletAccel = (vectorNormalized _trueVelocity) vectorMultiply (-1 * _drag);
|
_bulletAccel = (vectorNormalized _trueVelocity) vectorMultiply (-1 * _drag);
|
||||||
} else {
|
} else {
|
||||||
_bulletAccel = _trueVelocity vectorMultiply (_trueSpeed * _airFriction * _airFrictionCoef);
|
_bulletAccel = _trueVelocity vectorMultiply (_trueSpeed * _airFriction * _airFrictionCoef);
|
||||||
|
@ -161,11 +161,9 @@ if (_isABenabled) then {
|
|||||||
private _cacheEntry = missionNamespace getVariable format [QGVAR(%1_%2_%3_%4_%5_%6_%7), _zeroRange, _boreHeight, _ammoClass, _weaponClass, _isABenabled, _useBarrelLengthInfluence, _useAmmoTemperatureInfluence];
|
private _cacheEntry = missionNamespace getVariable format [QGVAR(%1_%2_%3_%4_%5_%6_%7), _zeroRange, _boreHeight, _ammoClass, _weaponClass, _isABenabled, _useBarrelLengthInfluence, _useAmmoTemperatureInfluence];
|
||||||
if (isNil "_cacheEntry") then {
|
if (isNil "_cacheEntry") then {
|
||||||
private _scopeBaseAngle = if (!_isABenabled) then {
|
private _scopeBaseAngle = if (!_isABenabled) then {
|
||||||
private _zeroAngle = "ace_advanced_ballistics" callExtension format ["calcZero:%1:%2:%3:%4", _zeroRange, _muzzleVelocity, _airFriction, _boreHeight];
|
parseNumber (("ace" callExtension ["ballistics:zero_vanilla", [_zeroRange, _muzzleVelocity, _airFriction, _boreHeight]]) select 0)
|
||||||
(parseNumber _zeroAngle)
|
|
||||||
} else {
|
} else {
|
||||||
private _zeroAngle = "ace_advanced_ballistics" callExtension format ["calcZeroAB:%1:%2:%3:%4:%5:%6:%7:%8:%9", _zeroRange, _muzzleVelocity, _boreHeight, EGVAR(scopes,zeroReferenceTemperature), EGVAR(scopes,zeroReferenceBarometricPressure), EGVAR(scopes,zeroReferenceHumidity), _bc, _dragModel, _atmosphereModel];
|
parseNumber (("ace" callExtension ["ballistics:zero_advanced", [_zeroRange, _muzzleVelocity, _boreHeight, EGVAR(scopes,zeroReferenceTemperature), EGVAR(scopes,zeroReferenceBarometricPressure), EGVAR(scopes,zeroReferenceHumidity), _bc, _dragModel, _atmosphereModel]]) select 0)
|
||||||
(parseNumber _zeroAngle)
|
|
||||||
};
|
};
|
||||||
if (_useAmmoTemperatureInfluence) then {
|
if (_useAmmoTemperatureInfluence) then {
|
||||||
{
|
{
|
||||||
|
@ -36,16 +36,14 @@ if (_initSpeedCoef < 0) then {
|
|||||||
_initSpeed = _initSpeed * (-1 * _initSpeedCoef);
|
_initSpeed = _initSpeed * (-1 * _initSpeedCoef);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _zeroAngle = "ace_advanced_ballistics" callExtension format ["replicateVanillaZero:%1:%2:%3", _oldZeroRange, _initSpeed, _airFriction];
|
private _vanillaZero = parseNumber (("ace" callExtension ["ballistics:replicate_vanilla_zero", [_oldZeroRange, _initSpeed, _airFriction]]) select 0);
|
||||||
private _vanillaZero = parseNumber _zeroAngle;
|
|
||||||
|
|
||||||
#ifdef DISABLE_DISPERSION
|
#ifdef DISABLE_DISPERSION
|
||||||
_vanillaZero = 0;
|
_vanillaZero = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private _trueZero = if (!_advancedBallistics) then {
|
private _trueZero = if (!_advancedBallistics) then {
|
||||||
_zeroAngle = "ace_advanced_ballistics" callExtension format ["calcZero:%1:%2:%3:%4", _newZeroRange, _initSpeed, _airFriction, _boreHeight];
|
parseNumber (("ace" callExtension ["ballistics:zero_vanilla", [_newZeroRange, _initSpeed, _airFriction, _boreHeight]]) select 0)
|
||||||
(parseNumber _zeroAngle)
|
|
||||||
} else {
|
} else {
|
||||||
// Get Weapon and Ammo Configurations
|
// Get Weapon and Ammo Configurations
|
||||||
private _AmmoCacheEntry = uiNamespace getVariable format[QEGVAR(advanced_ballistics,%1), _ammo];
|
private _AmmoCacheEntry = uiNamespace getVariable format[QEGVAR(advanced_ballistics,%1), _ammo];
|
||||||
@ -70,8 +68,19 @@ private _trueZero = if (!_advancedBallistics) then {
|
|||||||
_initSpeed = _initSpeed + _ammoTemperatureVelocityShift;
|
_initSpeed = _initSpeed + _ammoTemperatureVelocityShift;
|
||||||
};
|
};
|
||||||
|
|
||||||
_zeroAngle = "ace_advanced_ballistics" callExtension format ["calcZeroAB:%1:%2:%3:%4:%5:%6:%7:%8:%9", _newZeroRange, _initSpeed, _boreHeight, GVAR(zeroReferenceTemperature), GVAR(zeroReferenceBarometricPressure), GVAR(zeroReferenceHumidity), _ballisticCoefficients select 0, _dragModel, _atmosphereModel];
|
parseNumber (
|
||||||
(parseNumber _zeroAngle)
|
("ace" callExtension ["ballistics:zero_advanced", [
|
||||||
|
_newZeroRange,
|
||||||
|
_initSpeed,
|
||||||
|
_boreHeight,
|
||||||
|
GVAR(zeroReferenceTemperature),
|
||||||
|
GVAR(zeroReferenceBarometricPressure),
|
||||||
|
GVAR(zeroReferenceHumidity),
|
||||||
|
_ballisticCoefficients select 0,
|
||||||
|
_dragModel,
|
||||||
|
_atmosphereModel
|
||||||
|
]]) select 0
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
private _zeroAngleCorrection = _trueZero - _vanillaZero;
|
private _zeroAngleCorrection = _trueZero - _vanillaZero;
|
||||||
|
@ -48,7 +48,7 @@ private _gravity = [-sin (_bank) * cos(_scopeBaseAngle + _inclinationAngle) * -G
|
|||||||
|
|
||||||
private _useAB = missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false];
|
private _useAB = missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false];
|
||||||
if (_useAB) then {
|
if (_useAB) then {
|
||||||
_bc = parseNumber(("ace_advanced_ballistics" callExtension format["atmosphericCorrection:%1:%2:%3:%4:%5", _bc, _temperature, _barometricPressure, _relativeHumidity, _atmosphereModel]));
|
_bc = parseNumber (("ace" callExtension ["ballistics:atmospheric_correction", [_bc, _temperature, _barometricPressure, _relativeHumidity, _atmosphereModel]]) select 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _deltaT = 1 / 60;
|
private _deltaT = 1 / 60;
|
||||||
@ -58,7 +58,7 @@ while {(_TOF < 5) && {(_bulletPos # 1) < _targetRange}} do {
|
|||||||
private _trueSpeed = vectorMagnitude _trueVelocity;
|
private _trueSpeed = vectorMagnitude _trueVelocity;
|
||||||
|
|
||||||
private _bulletAccel = if (_useAB) then {
|
private _bulletAccel = if (_useAB) then {
|
||||||
private _drag = parseNumber(("ace_advanced_ballistics" callExtension format["retard:%1:%2:%3:%4", _dragModel, _bc, _trueSpeed, _temperature]));
|
private _drag = parseNumber (("ace" callExtension ["ballistics:retard", [_dragModel, _bc, _trueSpeed, _temperature]]) select 0);
|
||||||
(vectorNormalized _trueVelocity) vectorMultiply (-1 * _drag);
|
(vectorNormalized _trueVelocity) vectorMultiply (-1 * _drag);
|
||||||
} else {
|
} else {
|
||||||
_trueVelocity vectorMultiply (_trueSpeed * _airFriction);
|
_trueVelocity vectorMultiply (_trueSpeed * _airFriction);
|
||||||
|
@ -56,8 +56,7 @@ if ((_weaponInfo isEqualTo []) && {_magazineClass != ""}) then {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Scope Base Angle
|
// Scope Base Angle
|
||||||
private _zeroAngle = "ace_advanced_ballistics" callExtension format ["calcZero:%1:%2:%3:%4", _zeroRange, _muzzleVelocity, _airFriction, _boreHeight];
|
private _scopeBaseAngle = parseNumber (("ace" callExtension ["ballistics:zero_vanilla", [_zeroRange, _muzzleVelocity, _airFriction, _boreHeight]]) select 0);
|
||||||
private _scopeBaseAngle = parseNumber _zeroAngle;
|
|
||||||
|
|
||||||
_weaponInfo = [_scopeBaseAngle,_boreHeight,_airFriction,_muzzleVelocity,_bc,_dragModel,_atmosphereModel,_barrelTwist,_twistDirection,_caliber,_bulletLength,_bulletMass];
|
_weaponInfo = [_scopeBaseAngle,_boreHeight,_airFriction,_muzzleVelocity,_bc,_dragModel,_atmosphereModel,_barrelTwist,_twistDirection,_caliber,_bulletLength,_bulletMass];
|
||||||
GVAR(data) set [_key, _weaponInfo];
|
GVAR(data) set [_key, _weaponInfo];
|
||||||
|
@ -7,87 +7,59 @@ parent: wiki
|
|||||||
order: 9
|
order: 9
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Basics
|
## 1. Setup
|
||||||
|
|
||||||
### 1.1 Requirements
|
### 1.1 Requirements
|
||||||
|
|
||||||
- A compiler (VS/GCC/Clang)
|
- [Rust](https://rustup.rs/)
|
||||||
- If starting with Visual Studio, you need to make sure to use the Visual studio command prompt
|
- [cargo-make](https://crates.io/crates/cargo-make)
|
||||||
- cmake 3.0 or later in your path
|
`cargo install --no-default-features --force cargo-make`
|
||||||
|
|
||||||
### 1.2 Cross-Platform Guidelines
|
### 1.2 Recomendations
|
||||||
|
|
||||||
### 1.3 C++ basic style and naming guide
|
- [VSCode](https://code.visualstudio.com/)
|
||||||
|
- [Rust Analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
|
||||||
|
- [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)
|
||||||
|
|
||||||
### 1.4 ace_common cpp library
|
### 1.3 Rust Resources
|
||||||
|
|
||||||
|
- [Learn Rust](https://www.rust-lang.org/learn)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2 Building Extensions on Windows
|
## 2 Development
|
||||||
|
|
||||||
### 2.1 Compiling
|
### 2.1 Code Standards
|
||||||
|
|
||||||
#### 2.1.1 Windows - Creating a Visual Studio Project
|
1. [Rust API Guidelines for naming](https://rust-lang.github.io/api-guidelines/naming.html) should be followed
|
||||||
1. Open your compiling command prompt (which has cmake and your compiler)
|
2. All code should be formatted with `cargo fmt`
|
||||||
2. From this directory, you need to use cmake to build the appropriate build files. Change the -G property appropriately. run cmake --help to get a list of the options.
|
3. All code should be free from errors and warnings
|
||||||
|
4. Tests should be written where applicable
|
||||||
|
|
||||||
```
|
### 2.2 Local Development
|
||||||
cd extensions\build
|
|
||||||
cmake .. -G "Visual Studio 15 2017 Win64"
|
Running `cargo make debug` will compile the project for x32 and x64 and move the built libraries to `ace.dll` and `ace_x64.dll`.
|
||||||
|
|
||||||
|
The library can be debugged with the following `.vscode/launch.json` file. Replace all sections in `{}` with the appropriate path.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug",
|
||||||
|
"program": "{Path to Arma}\\arma3_x64.exe",
|
||||||
|
"args": ["-mod=\"{Path to CBA};{Path to ACE development folder}\""],
|
||||||
|
"cwd": "{Path to Arma}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
A Visual studio project file will now be generated in your build directory.
|
Use the `Run and Debug` tab in VSCode to launch Arma, or Press `F5`.
|
||||||
|
|
||||||
#### 2.1.2 Windows - Visual Studio - Compile only (nmake)
|
### 2.3 Release
|
||||||
1. Open your compiling command prompt (which has cmake and your compiler)
|
|
||||||
2. From this directory, you need to use cmake to build the appropriate build files. Change the -G property appropriately. run cmake --help to get a list of the options.
|
|
||||||
|
|
||||||
```
|
Release versions can be built using `cargo make release`.
|
||||||
cd extensions\build
|
|
||||||
cmake .. -G "NMake Makefiles"
|
|
||||||
nmake
|
|
||||||
```
|
|
||||||
|
|
||||||
The extensions will not be built in its appropriate project folder, for example:
|
|
||||||
|
|
||||||
```
|
|
||||||
extensions\
|
|
||||||
build\
|
|
||||||
fcs\ace_fcs.dll
|
|
||||||
somethingElse\ace_somethingElse.dll
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.2 Creating a new Extension
|
|
||||||
|
|
||||||
#### 2.2.1 Arma Config
|
|
||||||
|
|
||||||
ACE3 loads extensions defined in `ACE_Extensions` root config class and supports the following entries:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// Platform
|
|
||||||
windows = 1; // Load on Windows
|
|
||||||
linux = 1; // Load on Linux
|
|
||||||
|
|
||||||
// Type
|
|
||||||
client = 1; // Load on Client
|
|
||||||
server = 1; // Load on Server
|
|
||||||
```
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
class ACE_Extensions {
|
|
||||||
// Windows Client only extension
|
|
||||||
class tag_extension {
|
|
||||||
windows = 1;
|
|
||||||
client = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Any platform Server extension
|
|
||||||
class tag_extension2 {
|
|
||||||
windows = 1;
|
|
||||||
linux = 1;
|
|
||||||
server = 1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Combining platform and client/server values is possible to get all combinations currently supported by the game and more.
|
|
||||||
|
@ -139,7 +139,7 @@ Different `make.py` command line options include:
|
|||||||
- `increment_patch` - increments _patch_ version number (ignored with `increment_minor` or `increment_major`)
|
- `increment_patch` - increments _patch_ version number (ignored with `increment_minor` or `increment_major`)
|
||||||
- `increment_minor` - increments _minor_ version number and resets _patch_ version number to `0` (ignored with `increment_major`)
|
- `increment_minor` - increments _minor_ version number and resets _patch_ version number to `0` (ignored with `increment_major`)
|
||||||
- `increment_major` - increments _major_ version number and resets _minor_ and _patch_ version numbers to `0`
|
- `increment_major` - increments _major_ version number and resets _minor_ and _patch_ version numbers to `0`
|
||||||
- `force` - force rebuild all PBOs, even those already present in the `release` directory (combined with `compile` it will also rebuild all extensions)
|
- `force` - force rebuild all PBOs, even those already present in the `release` directory
|
||||||
- `checkexternal` - check external references (incompatible only with `<component1> <component2>` and `force <component1> <component2>`)
|
- `checkexternal` - check external references (incompatible only with `<component1> <component2>` and `force <component1> <component2>`)
|
||||||
- `release` - create release packages/archives
|
- `release` - create release packages/archives
|
||||||
- `<component1> <component2>` - build only specified component(s) (incompatible with `release`)
|
- `<component1> <component2>` - build only specified component(s) (incompatible with `release`)
|
||||||
|
122
extension/Cargo.lock
generated
Normal file
122
extension/Cargo.lock
generated
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arma-rs"
|
||||||
|
version = "1.7.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d8855a5dce884e6b98caa2aaeeabeb350dd05d94908054998b0a8191ecf6ab2"
|
||||||
|
dependencies = [
|
||||||
|
"arma-rs-proc",
|
||||||
|
"crossbeam-queue",
|
||||||
|
"libc",
|
||||||
|
"link_args",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arma-rs-proc"
|
||||||
|
version = "1.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c177e120733d13a150d2f6bedf3f5012bb19b720e430db9e8ace7ecc1609eb8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-queue"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "extension"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"arma-rs",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.132"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "link_args"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c7721e472624c9aaad27a5eb6b7c9c6045c7a396f2efb6dabaec1b640d5e89b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.99"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
26
extension/Cargo.toml
Normal file
26
extension/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "ace"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "ace"
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
arma-rs = { version = "1.10.5", features = ["uuid"] }
|
||||||
|
uuid = { version = "1.9.1", features = ["v4"] }
|
||||||
|
rand = "0.8.5"
|
||||||
|
rand_chacha = "0.3.1"
|
||||||
|
|
||||||
|
# Artillery
|
||||||
|
rayon = "1.10.0"
|
||||||
|
|
||||||
|
# Clipboard
|
||||||
|
clipboard = { version = "0.5.0", optional = true }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
git2 = "0.19.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["clipboard"]
|
59
extension/Makefile.toml
Normal file
59
extension/Makefile.toml
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
[tasks.install_x32]
|
||||||
|
command = "rustup"
|
||||||
|
args = ["toolchain", "install", "stable-i686-pc-windows-msvc"]
|
||||||
|
|
||||||
|
[tasks.build_x64_debug]
|
||||||
|
install_crate = false
|
||||||
|
command = "cargo"
|
||||||
|
args = ["+stable-x86_64-pc-windows-msvc", "build"]
|
||||||
|
|
||||||
|
[tasks.build_x64_release]
|
||||||
|
install_crate = false
|
||||||
|
command = "cargo"
|
||||||
|
args = ["+stable-x86_64-pc-windows-msvc", "build", "--release"]
|
||||||
|
|
||||||
|
[tasks.build_x32_debug]
|
||||||
|
install_crate = false
|
||||||
|
command = "cargo"
|
||||||
|
args = ["+stable-i686-pc-windows-msvc", "build", "--target", "i686-pc-windows-msvc"]
|
||||||
|
dependencies = ["install_x32"]
|
||||||
|
|
||||||
|
[tasks.build_x32_release]
|
||||||
|
install_crate = false
|
||||||
|
command = "cargo"
|
||||||
|
args = ["+stable-i686-pc-windows-msvc", "build", "--release", "--target", "i686-pc-windows-msvc"]
|
||||||
|
dependencies = ["install_x32"]
|
||||||
|
|
||||||
|
[tasks.move_x64_debug]
|
||||||
|
script_runner = "@shell"
|
||||||
|
script = '''
|
||||||
|
cp ../target/debug/ace.dll ../ace_x64.dll
|
||||||
|
'''
|
||||||
|
dependencies = ["build_x64_debug"]
|
||||||
|
|
||||||
|
[tasks.move_x64_release]
|
||||||
|
script_runner = "@shell"
|
||||||
|
script = '''
|
||||||
|
cp ../target/release/ace.dll ../ace_x64.dll
|
||||||
|
'''
|
||||||
|
dependencies = ["build_x64_release"]
|
||||||
|
|
||||||
|
[tasks.move_x32_debug]
|
||||||
|
script_runner = "@shell"
|
||||||
|
script = '''
|
||||||
|
cp ../target/i686-pc-windows-msvc/debug/ace.dll ../ace.dll
|
||||||
|
'''
|
||||||
|
dependencies = ["build_x32_debug"]
|
||||||
|
|
||||||
|
[tasks.move_x32_release]
|
||||||
|
script_runner = "@shell"
|
||||||
|
script = '''
|
||||||
|
cp ../target/i686-pc-windows-msvc/release/ace.dll ../ace.dll
|
||||||
|
'''
|
||||||
|
dependencies = ["build_x32_release"]
|
||||||
|
|
||||||
|
[tasks.debug]
|
||||||
|
dependencies = ["move_x32_debug", "move_x64_debug"]
|
||||||
|
|
||||||
|
[tasks.release]
|
||||||
|
dependencies = ["move_x32_release", "move_x64_release"]
|
10
extension/build.rs
Normal file
10
extension/build.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
fn main() {
|
||||||
|
let repo = git2::Repository::open("../").expect("Open git repo");
|
||||||
|
let head = repo
|
||||||
|
.head()
|
||||||
|
.expect("Get HEAD")
|
||||||
|
.target()
|
||||||
|
.expect("Get HEAD target");
|
||||||
|
let commit = repo.find_commit(head).expect("Find commit");
|
||||||
|
println!("cargo:rustc-env=GIT_HASH={}", commit.id());
|
||||||
|
}
|
466
extension/src/artillery/mod.rs
Normal file
466
extension/src/artillery/mod.rs
Normal file
@ -0,0 +1,466 @@
|
|||||||
|
use std::f64::consts::{FRAC_PI_2, FRAC_PI_4, PI};
|
||||||
|
|
||||||
|
use arma_rs::{Context, Group};
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
|
use crate::common::{Height, MuzzleVelocity, Temperature, GRAVITY};
|
||||||
|
|
||||||
|
mod simulate;
|
||||||
|
|
||||||
|
pub fn group() -> Group {
|
||||||
|
Group::new()
|
||||||
|
.command("calculate_table", command_calculate_table)
|
||||||
|
.command("simulate_shot", simulate::shot)
|
||||||
|
.command("find_max_angle", find_max_angle)
|
||||||
|
.command("simulate_find_solution", simulate::find_solution)
|
||||||
|
.command("get_solution", command_get_solution)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_calculate_table(
|
||||||
|
ctx: Context,
|
||||||
|
muzzle_velocity: MuzzleVelocity,
|
||||||
|
air_friction: f64,
|
||||||
|
min_elev: f64,
|
||||||
|
max_elev: f64,
|
||||||
|
high_arc: bool,
|
||||||
|
) -> (f64, u32) {
|
||||||
|
let mut min_elev = ((PI / 180.0) * min_elev).max(2.0 * (PI / 180.0)); // cap min to 2 degrees (negative elev might get messy)
|
||||||
|
let mut max_elev = ((PI / 180.0) * max_elev).min(88.0 * (PI / 180.0)); // cap max to 88 degrees (mk6)
|
||||||
|
let (best_angle, best_distance) = find_max_angle(muzzle_velocity, air_friction);
|
||||||
|
if high_arc {
|
||||||
|
min_elev = min_elev.max(best_angle);
|
||||||
|
} else {
|
||||||
|
max_elev = max_elev.min(best_angle);
|
||||||
|
}
|
||||||
|
let loop_start = if best_distance < 4000.0 { 50 } else { 100 };
|
||||||
|
let loop_inc = if best_distance < 5000.0 { 50 } else { 100 }; // simplify when range gets high
|
||||||
|
let loop_max_range = best_distance.min(30000.0) as u32; // with no air resistance, max range could go higher than 60km
|
||||||
|
|
||||||
|
let ranges = (loop_start..loop_max_range).step_by(loop_inc);
|
||||||
|
let ranges_size = ranges.len();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
if max_elev > min_elev {
|
||||||
|
ranges
|
||||||
|
.collect::<Vec<u32>>()
|
||||||
|
.into_par_iter()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(idx, range)| {
|
||||||
|
if let Err(e) = ctx.callback_data(
|
||||||
|
"ace:artillery",
|
||||||
|
"calculate_table",
|
||||||
|
(
|
||||||
|
idx as u32,
|
||||||
|
calc_range_table_line(
|
||||||
|
f64::from(range),
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
high_arc,
|
||||||
|
min_elev,
|
||||||
|
max_elev,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
eprintln!("calculate table error: {e:?}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(best_distance, ranges_size as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn command_get_solution(
|
||||||
|
range_to_hit: f64,
|
||||||
|
height_of_target: Height,
|
||||||
|
muzzle_velocity: MuzzleVelocity,
|
||||||
|
air_friction: f64,
|
||||||
|
high_arc: bool,
|
||||||
|
cross_wind: f64,
|
||||||
|
tail_wind: f64,
|
||||||
|
temperature: Temperature,
|
||||||
|
air_density: f64,
|
||||||
|
) -> (f64, f64, f64) {
|
||||||
|
let (distance, elevation, _) = simulate::find_solution(
|
||||||
|
range_to_hit,
|
||||||
|
height_of_target,
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
high_arc,
|
||||||
|
0.0,
|
||||||
|
FRAC_PI_2,
|
||||||
|
temperature,
|
||||||
|
air_density,
|
||||||
|
);
|
||||||
|
if distance == -1.0 {
|
||||||
|
return (-1.0, -1.0, -1.0);
|
||||||
|
}
|
||||||
|
let (mut x_sim, mut y_sim, mut tof) = simulate::shot(
|
||||||
|
elevation,
|
||||||
|
height_of_target,
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
cross_wind,
|
||||||
|
tail_wind,
|
||||||
|
temperature,
|
||||||
|
air_density,
|
||||||
|
);
|
||||||
|
let mut y_offset = range_to_hit - y_sim;
|
||||||
|
let mut y_correction = -y_offset;
|
||||||
|
while (distance + y_offset) - range_to_hit > 1.0 {
|
||||||
|
let (_, elevation, _) = simulate::find_solution(
|
||||||
|
range_to_hit + y_correction,
|
||||||
|
height_of_target,
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
high_arc,
|
||||||
|
0.0,
|
||||||
|
FRAC_PI_2,
|
||||||
|
temperature,
|
||||||
|
air_density,
|
||||||
|
);
|
||||||
|
(x_sim, y_sim, tof) = simulate::shot(
|
||||||
|
elevation,
|
||||||
|
height_of_target,
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
cross_wind,
|
||||||
|
tail_wind,
|
||||||
|
temperature,
|
||||||
|
air_density,
|
||||||
|
);
|
||||||
|
y_offset = range_to_hit - y_sim;
|
||||||
|
y_correction -= y_offset;
|
||||||
|
}
|
||||||
|
let angle_correction = (x_sim / y_sim).atan();
|
||||||
|
(elevation, tof, angle_correction)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_max_angle(muzzle_velocity: MuzzleVelocity, air_friction: f64) -> (f64, f64) {
|
||||||
|
if air_friction == 0.0 {
|
||||||
|
return (FRAC_PI_4, *muzzle_velocity * *muzzle_velocity / GRAVITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut best_angle = FRAC_PI_4;
|
||||||
|
let mut best_distance = -1.0;
|
||||||
|
let mut test_angle = FRAC_PI_4;
|
||||||
|
loop {
|
||||||
|
let (_, result_distance, _) = simulate::shot(
|
||||||
|
test_angle,
|
||||||
|
Height::new(0.0),
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Temperature::new_15c(),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
if result_distance > best_distance {
|
||||||
|
best_angle = test_angle;
|
||||||
|
best_distance = result_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
test_angle -= FRAC_PI_4 / 100.0;
|
||||||
|
if test_angle <= 0.0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(best_angle, best_distance)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_range_table_line(
|
||||||
|
range_to_hit: f64,
|
||||||
|
muzzle_velocity: MuzzleVelocity,
|
||||||
|
air_friction: f64,
|
||||||
|
high_arc: bool,
|
||||||
|
min_elev: f64,
|
||||||
|
max_elev: f64,
|
||||||
|
) -> Option<Vec<String>> {
|
||||||
|
let (actual_distance, line_elevation, line_tof) = simulate::find_solution(
|
||||||
|
range_to_hit,
|
||||||
|
Height::new(0.0),
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
high_arc,
|
||||||
|
min_elev,
|
||||||
|
max_elev,
|
||||||
|
Temperature::new_15c(),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
if line_tof < 0.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let (_, line_height_elevation, line_height_tof) = simulate::find_solution(
|
||||||
|
range_to_hit,
|
||||||
|
Height::new(-100.0),
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
high_arc,
|
||||||
|
min_elev,
|
||||||
|
max_elev,
|
||||||
|
Temperature::new_15c(),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (dr_elev_adjust, dr_tof_adjust) = if line_height_elevation > 0.0 {
|
||||||
|
(
|
||||||
|
format!(
|
||||||
|
"{:.1}",
|
||||||
|
(line_height_elevation - line_elevation) * 3200.0 / PI
|
||||||
|
),
|
||||||
|
format!("{:.1}", line_height_tof - line_tof),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(String::from("-"), String::from("-"))
|
||||||
|
};
|
||||||
|
|
||||||
|
let (
|
||||||
|
crosswind_offset,
|
||||||
|
headwind_offset,
|
||||||
|
tailwind_offset,
|
||||||
|
temp_dec_offset,
|
||||||
|
temp_inc_offset,
|
||||||
|
air_density_dec_offset,
|
||||||
|
air_density_inc_offset,
|
||||||
|
) = if air_friction == 0.0 {
|
||||||
|
(
|
||||||
|
String::from("-"),
|
||||||
|
String::from("-"),
|
||||||
|
String::from("-"),
|
||||||
|
String::from("-"),
|
||||||
|
String::from("-"),
|
||||||
|
String::from("-"),
|
||||||
|
String::from("-"),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
{
|
||||||
|
let (x_offset, _, _) = simulate::shot(
|
||||||
|
line_elevation,
|
||||||
|
Height::new(0.0),
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
10.0,
|
||||||
|
0.0,
|
||||||
|
Temperature::new_15c(),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
format!(
|
||||||
|
"{:0width$.1}",
|
||||||
|
x_offset.atan2(actual_distance) / 10.0 * 3200.0 / PI,
|
||||||
|
width = 1
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let (_, y_offset, _) = simulate::shot(
|
||||||
|
line_elevation,
|
||||||
|
Height::new(0.0),
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
0.0,
|
||||||
|
-10.0,
|
||||||
|
Temperature::new_15c(),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
let headwind_offset = (actual_distance - y_offset) / 10.0;
|
||||||
|
format!(
|
||||||
|
"{:0width$.precision$}",
|
||||||
|
headwind_offset,
|
||||||
|
width = 1,
|
||||||
|
precision = if headwind_offset.abs() > 9.949 { 0 } else { 1 }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let (_, y_offset, _) = simulate::shot(
|
||||||
|
line_elevation,
|
||||||
|
Height::new(0.0),
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
0.0,
|
||||||
|
10.0,
|
||||||
|
Temperature::new_15c(),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
let tailwind_offset = (actual_distance - y_offset) / 10.0;
|
||||||
|
format!(
|
||||||
|
"{:0width$.precision$}",
|
||||||
|
tailwind_offset,
|
||||||
|
width = 1,
|
||||||
|
precision = if tailwind_offset.abs() > 9.949 { 0 } else { 1 }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let (_, y_offset, _) = simulate::shot(
|
||||||
|
line_elevation,
|
||||||
|
Height::new(0.0),
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Temperature::new_celsius(5.0),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
let temp_dec_offset = (actual_distance - y_offset) / 10.0;
|
||||||
|
format!(
|
||||||
|
"{:0width$.precision$}",
|
||||||
|
temp_dec_offset,
|
||||||
|
width = 1,
|
||||||
|
precision = if temp_dec_offset.abs() > 9.949 { 0 } else { 1 }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let (_, y_offset, _) = simulate::shot(
|
||||||
|
line_elevation,
|
||||||
|
Height::new(0.0),
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Temperature::new_celsius(25.0),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
let temp_inc_offset = (actual_distance - y_offset) / 10.0;
|
||||||
|
format!(
|
||||||
|
"{:0width$.precision$}",
|
||||||
|
temp_inc_offset,
|
||||||
|
width = 1,
|
||||||
|
precision = if temp_inc_offset.abs() > 9.949 { 0 } else { 1 }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let (_, y_offset, _) = simulate::shot(
|
||||||
|
line_elevation,
|
||||||
|
Height::new(0.0),
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Temperature::new_15c(),
|
||||||
|
0.9,
|
||||||
|
);
|
||||||
|
let air_density_dec_offset = (actual_distance - y_offset) / 10.0;
|
||||||
|
format!(
|
||||||
|
"{:0width$.precision$}",
|
||||||
|
air_density_dec_offset,
|
||||||
|
width = 1,
|
||||||
|
precision = if air_density_dec_offset.abs() > 9.949 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let (_, y_offset, _) = simulate::shot(
|
||||||
|
line_elevation,
|
||||||
|
Height::new(0.0),
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Temperature::new_15c(),
|
||||||
|
1.1,
|
||||||
|
);
|
||||||
|
let air_density_inc_offset = (actual_distance - y_offset) / 10.0;
|
||||||
|
format!(
|
||||||
|
"{:0width$.precision$}",
|
||||||
|
air_density_inc_offset,
|
||||||
|
width = 1,
|
||||||
|
precision = if air_density_inc_offset.abs() > 9.949 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(vec![
|
||||||
|
format!("{:.0}", range_to_hit),
|
||||||
|
format!("{:.0}", line_elevation * 3200.0 / PI),
|
||||||
|
dr_elev_adjust,
|
||||||
|
dr_tof_adjust,
|
||||||
|
format!(
|
||||||
|
"{:.precision$}",
|
||||||
|
line_tof,
|
||||||
|
precision = if line_tof < 99.945 { 1 } else { 0 }
|
||||||
|
),
|
||||||
|
crosswind_offset,
|
||||||
|
headwind_offset,
|
||||||
|
tailwind_offset,
|
||||||
|
temp_dec_offset,
|
||||||
|
temp_inc_offset,
|
||||||
|
air_density_dec_offset,
|
||||||
|
air_density_inc_offset,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::{
|
||||||
|
sync::atomic::{AtomicI8, Ordering},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use arma_rs::{Extension, FromArma, Result, Value};
|
||||||
|
|
||||||
|
use crate::{artillery::command_calculate_table, common::MuzzleVelocity};
|
||||||
|
|
||||||
|
use super::find_max_angle;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_max_angle() {
|
||||||
|
let (best_angle, best_distance) = find_max_angle(MuzzleVelocity::new(400.0), -0.00005);
|
||||||
|
assert!((best_angle - 0.730_420_291_959_627_2).abs() < f64::EPSILON); // old ace: 0.722566
|
||||||
|
assert!((best_distance - 10_393.560_433_295_957).abs() < f64::EPSILON); // old ace: 10391.8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn range_table_line() {
|
||||||
|
let extension = Extension::build()
|
||||||
|
.command("calc", command_calculate_table)
|
||||||
|
.finish()
|
||||||
|
.testing();
|
||||||
|
let (output, code) = extension.call(
|
||||||
|
"calc",
|
||||||
|
Some(vec![
|
||||||
|
400.0.to_string(),
|
||||||
|
(-0.00005).to_string(),
|
||||||
|
(-5.0).to_string(),
|
||||||
|
80.0.to_string(),
|
||||||
|
true.to_string(),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
println!("{output:?}");
|
||||||
|
let (best, lines): (f64, i8) = FromArma::from_arma(output).unwrap();
|
||||||
|
assert!((best - 10_393.560_433_295_957).abs() < f64::EPSILON);
|
||||||
|
assert_eq!(lines, 103);
|
||||||
|
let recv: AtomicI8 = AtomicI8::new(0);
|
||||||
|
assert_eq!(code, 0);
|
||||||
|
let result = extension.callback_handler(
|
||||||
|
|name, func, data| {
|
||||||
|
recv.fetch_add(1, Ordering::SeqCst);
|
||||||
|
if name == "ace:artillery" && func == "calculate_table" {
|
||||||
|
if let Some(Value::Array(data)) = data {
|
||||||
|
if let Value::Array(line) = &data[1] {
|
||||||
|
if line[0] == Value::String(String::from("3500")) {
|
||||||
|
println!("data: {line:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if recv.load(Ordering::SeqCst) == lines {
|
||||||
|
Result::Ok(())
|
||||||
|
} else {
|
||||||
|
Result::Continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Result::Err(String::from("unexpected callback"))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Duration::from_secs(10),
|
||||||
|
);
|
||||||
|
println!("result: {result:?}");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
}
|
169
extension/src/artillery/simulate.rs
Normal file
169
extension/src/artillery/simulate.rs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
use crate::common::{Height, MuzzleVelocity, Temperature, Vector3, GRAVITY, GRAVITY_ACCEL};
|
||||||
|
|
||||||
|
const TIME_STEP: f64 = 1.0 / 60.0;
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn shot(
|
||||||
|
fire_angle_rad: f64,
|
||||||
|
height_of_target: Height,
|
||||||
|
muzzle_velocity: MuzzleVelocity,
|
||||||
|
air_friction: f64,
|
||||||
|
cross_wind: f64,
|
||||||
|
tail_wind: f64,
|
||||||
|
temperature: Temperature,
|
||||||
|
air_density: f64,
|
||||||
|
) -> (f64, f64, f64) {
|
||||||
|
let k_coefficient = -1.0 * air_density * air_friction;
|
||||||
|
let powder_effects = if air_friction == 0.0 {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
(temperature.as_kelvin() / 288.13 - 1.0) / 40.0 + 1.0
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut current_time = 0.0;
|
||||||
|
let mut current_position = Vector3::default();
|
||||||
|
let mut last_position = Vector3::default();
|
||||||
|
let mut current_velocity = Vector3::new(
|
||||||
|
0.0,
|
||||||
|
powder_effects * *muzzle_velocity * fire_angle_rad.cos(),
|
||||||
|
powder_effects * *muzzle_velocity * fire_angle_rad.sin(),
|
||||||
|
);
|
||||||
|
let wind = Vector3::new(cross_wind, tail_wind, 0.0);
|
||||||
|
|
||||||
|
while current_velocity.z() > 0.0 || current_position.z() > *height_of_target {
|
||||||
|
last_position = current_position;
|
||||||
|
let apparent_wind = wind - current_velocity;
|
||||||
|
let change_in_velocity =
|
||||||
|
GRAVITY_ACCEL + apparent_wind * (k_coefficient * apparent_wind.magnitude());
|
||||||
|
current_velocity += change_in_velocity * TIME_STEP;
|
||||||
|
current_position += current_velocity * TIME_STEP;
|
||||||
|
current_time += TIME_STEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
let last_current_ratio =
|
||||||
|
(*height_of_target - current_position.z()) / (last_position.z() - current_position.z());
|
||||||
|
let final_pos = last_position.lerp(¤t_position, last_current_ratio);
|
||||||
|
|
||||||
|
(final_pos.x(), final_pos.y(), current_time)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_assignments, clippy::too_many_arguments)]
|
||||||
|
pub fn find_solution(
|
||||||
|
range_to_hit: f64,
|
||||||
|
height_to_hit: Height,
|
||||||
|
muzzle_velocity: MuzzleVelocity,
|
||||||
|
air_friction: f64,
|
||||||
|
high_arc: bool,
|
||||||
|
min_elev: f64,
|
||||||
|
max_elev: f64,
|
||||||
|
temperature: Temperature,
|
||||||
|
air_density: f64,
|
||||||
|
) -> (f64, f64, f64) {
|
||||||
|
if air_friction == 0.0 {
|
||||||
|
let radicand = GRAVITY.mul_add(
|
||||||
|
-GRAVITY.mul_add(
|
||||||
|
range_to_hit.powi(2),
|
||||||
|
2.0 * *height_to_hit * muzzle_velocity.powi(2),
|
||||||
|
),
|
||||||
|
muzzle_velocity.powi(4),
|
||||||
|
);
|
||||||
|
if range_to_hit == 0.0 || radicand < 0.0 {
|
||||||
|
return (-1.0, -1.0, -1.0);
|
||||||
|
}
|
||||||
|
let radicand = radicand.sqrt();
|
||||||
|
let angle_root =
|
||||||
|
(muzzle_velocity.mul_add(*muzzle_velocity, radicand) / (GRAVITY * range_to_hit)).atan();
|
||||||
|
if angle_root > max_elev || angle_root < min_elev {
|
||||||
|
return (-1.0, -1.0, -1.0);
|
||||||
|
}
|
||||||
|
let tof = range_to_hit / (*muzzle_velocity * angle_root.cos());
|
||||||
|
return (range_to_hit, angle_root, tof);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut number_of_attempts = 50;
|
||||||
|
let mut result_distance = -1.0;
|
||||||
|
let mut result_time = -1.0;
|
||||||
|
let mut search_min = min_elev;
|
||||||
|
let mut search_max = max_elev;
|
||||||
|
let mut current_error = 9999.0;
|
||||||
|
let mut current_elevation = -1.0;
|
||||||
|
loop {
|
||||||
|
number_of_attempts -= 1;
|
||||||
|
current_elevation = (search_min + search_max) / 2.0;
|
||||||
|
let (_, shot_distance, shot_time) = shot(
|
||||||
|
current_elevation,
|
||||||
|
height_to_hit,
|
||||||
|
muzzle_velocity,
|
||||||
|
air_friction,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
temperature,
|
||||||
|
air_density,
|
||||||
|
);
|
||||||
|
result_distance = shot_distance;
|
||||||
|
result_time = shot_time;
|
||||||
|
if !result_distance.is_nan() {
|
||||||
|
current_error = range_to_hit - result_distance;
|
||||||
|
}
|
||||||
|
if (current_error > 0.0) ^ !high_arc {
|
||||||
|
search_max = current_elevation;
|
||||||
|
} else {
|
||||||
|
search_min = current_elevation;
|
||||||
|
}
|
||||||
|
if number_of_attempts == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (search_max - search_min) <= 0.000_025 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if current_error.abs() > (range_to_hit * 0.001 * (if high_arc { 1.0 } else { 2.0 })) {
|
||||||
|
return (-1.0, -1.0, -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
(result_distance, current_elevation, result_time)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::f64::consts::FRAC_PI_4;
|
||||||
|
|
||||||
|
use crate::common::{Height, MuzzleVelocity, Temperature};
|
||||||
|
|
||||||
|
use super::shot;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shot() {
|
||||||
|
let (x, y, result_time) = shot(
|
||||||
|
FRAC_PI_4,
|
||||||
|
Height::new(0.0),
|
||||||
|
MuzzleVelocity::new(400.0),
|
||||||
|
-0.00005,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
Temperature::new_15c(),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
assert!(x < f64::EPSILON);
|
||||||
|
assert!((y - 10_331.039_038_212_19).abs() < f64::EPSILON); // old ace: 10330.2
|
||||||
|
assert!((result_time - 50.316_666_666_665_09).abs() < f64::EPSILON); // old ace: 50.3167
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_solution() {
|
||||||
|
let (result_distance, current_elevation, result_time) = super::find_solution(
|
||||||
|
1000.0,
|
||||||
|
Height::new(0.0),
|
||||||
|
MuzzleVelocity::new(400.0),
|
||||||
|
-0.00005,
|
||||||
|
true,
|
||||||
|
-5.0,
|
||||||
|
80.0,
|
||||||
|
Temperature::new_15c(),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
assert!((result_distance - 999.628_737_358_452_9).abs() < f64::EPSILON); // old ace: 999.773
|
||||||
|
assert!((current_elevation - 1.522_375_345_230_102_5).abs() < f64::EPSILON); // old ace: 1.52238
|
||||||
|
assert!((result_time - 69.699_999_999_997_33).abs() < f64::EPSILON); // old ace: 69.7
|
||||||
|
}
|
||||||
|
}
|
138
extension/src/ballistics/atmosphere.rs
Normal file
138
extension/src/ballistics/atmosphere.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use arma_rs::FromArma;
|
||||||
|
|
||||||
|
use super::map::Map;
|
||||||
|
use crate::common::Temperature;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum AtmosphereModel {
|
||||||
|
Icao,
|
||||||
|
Asm,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromArma for AtmosphereModel {
|
||||||
|
fn from_arma(s: String) -> Result<Self, String> {
|
||||||
|
let s = s.trim_matches('"');
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"icao" => Ok(Self::Icao),
|
||||||
|
"asm" => Ok(Self::Asm),
|
||||||
|
_ => Err(String::from("unexpected model")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ROUGHNESS_LENGTHS: [f64; 10] = [0.0002, 0.0005, 0.0024, 0.03, 0.055, 0.1, 0.2, 0.4, 0.8, 1.6];
|
||||||
|
pub fn calculate_roughness_length(map: &Map, x: f64, y: f64) -> f64 {
|
||||||
|
let grid_x = (x / 50.0).floor() as i64;
|
||||||
|
let grid_y = (y / 50.0).floor() as i64;
|
||||||
|
let grid_cell = (grid_x * map.grids() as i64) + grid_y;
|
||||||
|
|
||||||
|
if grid_cell >= 0 {
|
||||||
|
let grid_cell = grid_cell as usize;
|
||||||
|
if grid_cell < map.heights().len() && grid_cell < map.building_nums().len() {
|
||||||
|
let near_building_num = map.building_num(grid_cell);
|
||||||
|
let surface_is_water = map.surface_is_water(grid_cell);
|
||||||
|
|
||||||
|
if near_building_num == 0 && surface_is_water {
|
||||||
|
return 0.0005;
|
||||||
|
}
|
||||||
|
|
||||||
|
if near_building_num >= 10 {
|
||||||
|
return 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ROUGHNESS_LENGTHS[2 + std::cmp::min(near_building_num as usize, 6)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
0.0024
|
||||||
|
}
|
||||||
|
|
||||||
|
const DRY_AIR_MOLAR_MASS: f64 = 0.028_964;
|
||||||
|
const WATOR_VAPOR_MOLAR_MASS: f64 = 0.018_016;
|
||||||
|
const UNIVERSAL_GAS_CONSTANT: f64 = 8.314_32;
|
||||||
|
const SPECIFIC_GAST_CONSTANT_DRY_AIR: f64 = UNIVERSAL_GAS_CONSTANT / DRY_AIR_MOLAR_MASS;
|
||||||
|
pub fn calculate_air_density(
|
||||||
|
temperature: Temperature,
|
||||||
|
pressure: f64,
|
||||||
|
relative_humidity: f64,
|
||||||
|
) -> f64 {
|
||||||
|
let pressure = pressure * 100.0;
|
||||||
|
if relative_humidity > 0.0 {
|
||||||
|
// 610.78 gives pressure in Pa - https://en.wikipedia.org/wiki/Density_of_air
|
||||||
|
let p_sat = 610.78
|
||||||
|
* 10_f64.powf((7.5 * temperature.as_celsius()) / (temperature.as_celsius() + 237.3));
|
||||||
|
let vapor_pressure = relative_humidity * p_sat;
|
||||||
|
let partial_pressure = pressure - vapor_pressure;
|
||||||
|
|
||||||
|
partial_pressure.mul_add(DRY_AIR_MOLAR_MASS, vapor_pressure * WATOR_VAPOR_MOLAR_MASS)
|
||||||
|
/ (UNIVERSAL_GAS_CONSTANT * temperature.as_kelvin())
|
||||||
|
} else {
|
||||||
|
pressure / (SPECIFIC_GAST_CONSTANT_DRY_AIR * temperature.as_kelvin())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const STD_AIR_DENSITY_ICAO: f64 = 1.22498;
|
||||||
|
const STD_AIR_DENSITY_ASM: f64 = 1.20886;
|
||||||
|
pub fn calculate_atmospheric_correction(
|
||||||
|
ballistic_coefficient: f64,
|
||||||
|
temperature: Temperature,
|
||||||
|
pressure: f64,
|
||||||
|
relative_humidity: f64,
|
||||||
|
atmosphere_model: AtmosphereModel,
|
||||||
|
) -> f64 {
|
||||||
|
let air_density = calculate_air_density(temperature, pressure, relative_humidity);
|
||||||
|
match atmosphere_model {
|
||||||
|
AtmosphereModel::Icao => (STD_AIR_DENSITY_ICAO / air_density) * ballistic_coefficient,
|
||||||
|
AtmosphereModel::Asm => (STD_AIR_DENSITY_ASM / air_density) * ballistic_coefficient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn speed_of_sound(temperature: Temperature) -> f64 {
|
||||||
|
331.3 * (1.0 + temperature.as_celsius() / 273.15).sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::common::Temperature;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn atmospheric_correction() {
|
||||||
|
assert!(
|
||||||
|
(super::calculate_atmospheric_correction(
|
||||||
|
0.583,
|
||||||
|
Temperature::new_15c(),
|
||||||
|
1005.0,
|
||||||
|
0.0,
|
||||||
|
crate::ballistics::AtmosphereModel::Icao
|
||||||
|
) - 0.587_784_746_752_856_4)
|
||||||
|
.abs()
|
||||||
|
< f64::EPSILON // previous ace: 0.580047
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
(super::calculate_atmospheric_correction(
|
||||||
|
0.263,
|
||||||
|
Temperature::new_celsius(25.3642),
|
||||||
|
1009.61,
|
||||||
|
0.603173,
|
||||||
|
crate::ballistics::AtmosphereModel::Icao
|
||||||
|
) - 0.275_454_853_636_429_13)
|
||||||
|
.abs()
|
||||||
|
< f64::EPSILON // previous ace: 0.275444
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn speed_of_sound() {
|
||||||
|
assert!(
|
||||||
|
(super::speed_of_sound(Temperature::new_celsius(-15.0)) - 322.074_912_997_965_27).abs()
|
||||||
|
< f64::EPSILON
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
(super::speed_of_sound(Temperature::new_celsius(0.0)) - 331.3).abs() < f64::EPSILON
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
(super::speed_of_sound(Temperature::new_15c()) - 340.275_080_511_860_5).abs()
|
||||||
|
< f64::EPSILON
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
131
extension/src/ballistics/bullet/mod.rs
Normal file
131
extension/src/ballistics/bullet/mod.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use arma_rs::Group;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
atmosphere::AtmosphereModel,
|
||||||
|
drag::DragFunction,
|
||||||
|
map::{CURRENT_MAP, MAPS},
|
||||||
|
};
|
||||||
|
use crate::common::{Temperature, Vector3};
|
||||||
|
|
||||||
|
mod model;
|
||||||
|
pub use model::Bullet;
|
||||||
|
|
||||||
|
pub fn group() -> Group {
|
||||||
|
Group::new()
|
||||||
|
.command("new", new)
|
||||||
|
.command("simulate", simulate)
|
||||||
|
.command("delete", delete)
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut BULLET_LIST: Option<HashMap<String, Bullet>> = None;
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
/// Create a new bullet and return its UUID.
|
||||||
|
fn new(
|
||||||
|
ammo_count: u64,
|
||||||
|
air_friction: f64,
|
||||||
|
ballistic_coefficients: Vec<f64>,
|
||||||
|
velocity_boundaries: Vec<f64>,
|
||||||
|
atmosphere_model: AtmosphereModel,
|
||||||
|
drag_function: DragFunction,
|
||||||
|
stability_factor: f64,
|
||||||
|
twist_direction: f64,
|
||||||
|
transonic_stability_coefficient: f64,
|
||||||
|
bullet_velocity: Vector3,
|
||||||
|
latitude: f64,
|
||||||
|
temperature: Temperature,
|
||||||
|
altitude: f64,
|
||||||
|
humidity: f64,
|
||||||
|
overcast: f64,
|
||||||
|
start_time: f64,
|
||||||
|
) -> Uuid {
|
||||||
|
let id = Uuid::new_v4();
|
||||||
|
let bullet = Bullet {
|
||||||
|
air_friction,
|
||||||
|
ballistic_coefficients,
|
||||||
|
velocity_boundaries,
|
||||||
|
atmosphere_model,
|
||||||
|
drag_function,
|
||||||
|
stability_factor,
|
||||||
|
twist_direction,
|
||||||
|
transonic_stability_coefficient,
|
||||||
|
bullet_velocity_last_frame: bullet_velocity,
|
||||||
|
latitude: latitude / 180.0 * std::f64::consts::PI,
|
||||||
|
temperature,
|
||||||
|
altitude,
|
||||||
|
humidity,
|
||||||
|
overcast,
|
||||||
|
start_time,
|
||||||
|
last_frame: start_time,
|
||||||
|
rng: rand::SeedableRng::seed_from_u64({
|
||||||
|
let k1 = (start_time / 2.0).round();
|
||||||
|
let k2 = ammo_count as f64;
|
||||||
|
(0.5 * (k1 + k2)).mul_add(k1 + k2 + 1.0, k2) as u64
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
// Safety: this is all single threaded, so no need to lock
|
||||||
|
unsafe {
|
||||||
|
if BULLET_LIST.is_none() {
|
||||||
|
BULLET_LIST = Some(HashMap::new());
|
||||||
|
}
|
||||||
|
BULLET_LIST.as_mut().unwrap().insert(id.to_string(), bullet);
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simulate a bullet for a given time. Returns the velocity
|
||||||
|
fn simulate(
|
||||||
|
id: String,
|
||||||
|
bullet_velocity: Vector3,
|
||||||
|
bullet_position: Vector3,
|
||||||
|
wind: Vector3,
|
||||||
|
altitude: f64,
|
||||||
|
time: f64,
|
||||||
|
) -> Result<Vector3, String> {
|
||||||
|
// Safety: this is all single threaded, so no need to lock
|
||||||
|
unsafe {
|
||||||
|
MAPS.as_ref().map_or_else(
|
||||||
|
|| Err("No map loaded".to_string()),
|
||||||
|
|map| {
|
||||||
|
let bullet = {
|
||||||
|
if BULLET_LIST.is_none() {
|
||||||
|
BULLET_LIST = Some(HashMap::new());
|
||||||
|
}
|
||||||
|
BULLET_LIST
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.get_mut(&id)
|
||||||
|
.ok_or_else(|| "Bullet not found".to_string())?
|
||||||
|
};
|
||||||
|
Ok(bullet.simulate(
|
||||||
|
map.get(
|
||||||
|
CURRENT_MAP
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| "No current map".to_string())?,
|
||||||
|
)
|
||||||
|
.ok_or_else(|| "Current map not loaded".to_string())?,
|
||||||
|
bullet_velocity,
|
||||||
|
bullet_position,
|
||||||
|
wind,
|
||||||
|
altitude,
|
||||||
|
time,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a bullet
|
||||||
|
/// Returns true if the bullet was found and deleted
|
||||||
|
fn delete(id: String) -> bool {
|
||||||
|
// Safety: this is all single threaded, so no need to lock
|
||||||
|
unsafe {
|
||||||
|
if BULLET_LIST.is_none() {
|
||||||
|
BULLET_LIST = Some(HashMap::new());
|
||||||
|
}
|
||||||
|
BULLET_LIST.as_mut().unwrap().remove(&id).is_some()
|
||||||
|
}
|
||||||
|
}
|
214
extension/src/ballistics/bullet/model.rs
Normal file
214
extension/src/ballistics/bullet/model.rs
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
use std::f64::consts::PI;
|
||||||
|
|
||||||
|
use rand::{distributions::Uniform, prelude::Distribution};
|
||||||
|
use rand_chacha::ChaCha8Rng;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ballistics::{
|
||||||
|
atmosphere::{
|
||||||
|
calculate_air_density, calculate_atmospheric_correction, calculate_roughness_length,
|
||||||
|
speed_of_sound, AtmosphereModel,
|
||||||
|
},
|
||||||
|
drag::{calculate_retard, DragFunction},
|
||||||
|
map::Map,
|
||||||
|
},
|
||||||
|
common::{Temperature, Vector3, GRAVITY_ACCEL},
|
||||||
|
};
|
||||||
|
|
||||||
|
const STD_AIR_DENSITY_ICAO: f64 = 1.22498;
|
||||||
|
|
||||||
|
pub struct Bullet {
|
||||||
|
pub air_friction: f64,
|
||||||
|
pub ballistic_coefficients: Vec<f64>,
|
||||||
|
pub velocity_boundaries: Vec<f64>,
|
||||||
|
pub atmosphere_model: AtmosphereModel,
|
||||||
|
pub drag_function: DragFunction,
|
||||||
|
pub stability_factor: f64,
|
||||||
|
pub twist_direction: f64,
|
||||||
|
pub transonic_stability_coefficient: f64,
|
||||||
|
pub bullet_velocity_last_frame: Vector3,
|
||||||
|
pub latitude: f64,
|
||||||
|
pub temperature: Temperature,
|
||||||
|
pub altitude: f64,
|
||||||
|
pub humidity: f64,
|
||||||
|
pub overcast: f64,
|
||||||
|
pub start_time: f64,
|
||||||
|
pub last_frame: f64,
|
||||||
|
pub rng: ChaCha8Rng,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
impl Bullet {
|
||||||
|
pub fn simulate(
|
||||||
|
&mut self,
|
||||||
|
map: &Map,
|
||||||
|
bullet_velocity_current_frame: Vector3,
|
||||||
|
bullet_position: Vector3,
|
||||||
|
mut wind: Vector3,
|
||||||
|
height_atl: f64,
|
||||||
|
tick_time: f64,
|
||||||
|
) -> Vector3 {
|
||||||
|
const EARTH_ANGULAR_SPEED: f64 = 0.000_072_92;
|
||||||
|
let mut tof = tick_time - self.start_time;
|
||||||
|
let delta_time = tick_time - self.last_frame;
|
||||||
|
|
||||||
|
let temperature = Temperature::new_celsius(
|
||||||
|
0.0065f64.mul_add(-bullet_position.z(), self.temperature.as_celsius()),
|
||||||
|
);
|
||||||
|
let pressure = 10.0f64.mul_add(-self.overcast, 1013.25)
|
||||||
|
* (1.0
|
||||||
|
- (0.0065 * (self.altitude + bullet_position.z()))
|
||||||
|
/ 0.0065f64.mul_add(self.altitude, temperature.as_kelvin()))
|
||||||
|
.powf(5.255_754_495);
|
||||||
|
|
||||||
|
self.last_frame = tick_time;
|
||||||
|
|
||||||
|
if wind.magnitude() > 0.1 {
|
||||||
|
let mut wind_attenuation = 1.0;
|
||||||
|
let wind_source_terrain = bullet_position - wind.normalize() * 100.0;
|
||||||
|
|
||||||
|
let grid_x = (wind_source_terrain.x() / 50.0).floor() as i64;
|
||||||
|
let grid_y = (wind_source_terrain.y() / 50.0).floor() as i64;
|
||||||
|
let grid_cell = grid_x * map.grids() as i64 + grid_y;
|
||||||
|
|
||||||
|
if grid_cell >= 0 {
|
||||||
|
let grid_cell = grid_cell as usize;
|
||||||
|
if grid_cell < map.heights().len() && grid_cell < map.building_nums().len() {
|
||||||
|
let grid_height = map.height(grid_cell) as f64;
|
||||||
|
if grid_height > bullet_position.z() {
|
||||||
|
let angle = ((grid_height - bullet_position.z()) / 100.0).atan();
|
||||||
|
wind_attenuation *= angle.cos().abs().powi(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if height_atl > 0.0 && height_atl < 20.0 {
|
||||||
|
let wind_source_obstacles = bullet_position - wind.normalize() * 25.0;
|
||||||
|
let roughness_length = calculate_roughness_length(
|
||||||
|
map,
|
||||||
|
wind_source_obstacles.x(),
|
||||||
|
wind_source_obstacles.y(),
|
||||||
|
);
|
||||||
|
wind_attenuation *=
|
||||||
|
0.0f64.max((height_atl / roughness_length).log(20.0 / roughness_length));
|
||||||
|
}
|
||||||
|
|
||||||
|
wind *= wind_attenuation;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut velocity_offset = Vector3::default();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut bullet_velocity = self.bullet_velocity_last_frame;
|
||||||
|
let mut time = 0.0;
|
||||||
|
while time < delta_time {
|
||||||
|
let dt = (delta_time - time).min(0.005);
|
||||||
|
let drag_ref = -self.air_friction * bullet_velocity.magnitude_squared();
|
||||||
|
let accel_ref = bullet_velocity.normalize() * drag_ref;
|
||||||
|
|
||||||
|
velocity_offset += accel_ref * dt;
|
||||||
|
bullet_velocity -= accel_ref * dt;
|
||||||
|
bullet_velocity += GRAVITY_ACCEL * dt;
|
||||||
|
|
||||||
|
time += dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut bullet_velocity = self.bullet_velocity_last_frame;
|
||||||
|
let mut time = 0.0;
|
||||||
|
tof -= delta_time;
|
||||||
|
|
||||||
|
while time < delta_time {
|
||||||
|
let dt = (delta_time - time).min(0.005 * 2.0);
|
||||||
|
|
||||||
|
let mut true_velocity = bullet_velocity - wind;
|
||||||
|
|
||||||
|
let sound_speed = speed_of_sound(temperature);
|
||||||
|
if self.transonic_stability_coefficient < 1.0
|
||||||
|
&& true_velocity.magnitude() < 1.2 * sound_speed
|
||||||
|
&& true_velocity.magnitude() > sound_speed
|
||||||
|
{
|
||||||
|
let distribution = Uniform::from(-10.0..10.0);
|
||||||
|
let offset = Vector3::new(
|
||||||
|
distribution.sample(&mut self.rng),
|
||||||
|
distribution.sample(&mut self.rng),
|
||||||
|
distribution.sample(&mut self.rng),
|
||||||
|
);
|
||||||
|
let coef = 1.0 - self.transonic_stability_coefficient;
|
||||||
|
let true_speed = true_velocity.magnitude();
|
||||||
|
true_velocity += offset * coef;
|
||||||
|
true_velocity = true_velocity.normalize() * true_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
let drag = if self.ballistic_coefficients.len() == self.velocity_boundaries.len() + 1 {
|
||||||
|
let mut ballistic_coefficient = self.ballistic_coefficients[0];
|
||||||
|
for (i, boundry) in self.velocity_boundaries.iter().enumerate().rev() {
|
||||||
|
if true_velocity.magnitude() < *boundry {
|
||||||
|
ballistic_coefficient = self.ballistic_coefficients[i + 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ballistic_coefficient = calculate_atmospheric_correction(
|
||||||
|
ballistic_coefficient,
|
||||||
|
temperature,
|
||||||
|
pressure,
|
||||||
|
self.humidity,
|
||||||
|
self.atmosphere_model,
|
||||||
|
);
|
||||||
|
|
||||||
|
calculate_retard(
|
||||||
|
self.drag_function,
|
||||||
|
ballistic_coefficient,
|
||||||
|
true_velocity.magnitude(),
|
||||||
|
speed_of_sound(temperature),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let air_density = calculate_air_density(temperature, pressure, self.humidity);
|
||||||
|
let air_friction = self.air_friction * air_density / STD_AIR_DENSITY_ICAO;
|
||||||
|
|
||||||
|
-air_friction * true_velocity.magnitude_squared()
|
||||||
|
};
|
||||||
|
|
||||||
|
let accel = true_velocity.normalize() * drag;
|
||||||
|
|
||||||
|
velocity_offset -= accel * dt;
|
||||||
|
bullet_velocity -= accel * dt;
|
||||||
|
|
||||||
|
if tof > 0.0 {
|
||||||
|
let bullet_dir = bullet_velocity.x().atan2(bullet_velocity.y());
|
||||||
|
let drift_accel = self.twist_direction
|
||||||
|
* (0.048_225_1 * (self.stability_factor + 1.2))
|
||||||
|
/ tof.powf(0.17);
|
||||||
|
let drift_velocity = 0.058_102_5 * (self.stability_factor + 1.2) * tof.powf(0.83);
|
||||||
|
let drag_correction = (drift_velocity / true_velocity.magnitude()) * drag;
|
||||||
|
let magnitude = (drift_accel + drag_correction) * dt;
|
||||||
|
let offset = Vector3::new(
|
||||||
|
(bullet_dir + PI / 2.0).sin() * magnitude,
|
||||||
|
(bullet_dir + PI / 2.0).cos() * magnitude,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
|
velocity_offset += offset;
|
||||||
|
bullet_velocity += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lat = self.latitude;
|
||||||
|
let accel = Vector3::new(
|
||||||
|
2.0 * EARTH_ANGULAR_SPEED
|
||||||
|
* bullet_velocity
|
||||||
|
.y()
|
||||||
|
.mul_add(lat.sin(), -bullet_velocity.z() * lat.cos()),
|
||||||
|
2.0 * EARTH_ANGULAR_SPEED * -(bullet_velocity.x() * lat.sin()),
|
||||||
|
2.0 * EARTH_ANGULAR_SPEED * (bullet_velocity.x() * lat.cos()),
|
||||||
|
);
|
||||||
|
|
||||||
|
velocity_offset += accel * dt;
|
||||||
|
bullet_velocity += accel * dt + GRAVITY_ACCEL * dt;
|
||||||
|
|
||||||
|
tof += dt;
|
||||||
|
time += dt;
|
||||||
|
}
|
||||||
|
self.bullet_velocity_last_frame = bullet_velocity_current_frame + velocity_offset;
|
||||||
|
velocity_offset
|
||||||
|
}
|
||||||
|
}
|
197
extension/src/ballistics/drag.rs
Normal file
197
extension/src/ballistics/drag.rs
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
#![allow(clippy::approx_constant)]
|
||||||
|
|
||||||
|
use arma_rs::FromArma;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum DragFunction {
|
||||||
|
G1,
|
||||||
|
G2,
|
||||||
|
G5,
|
||||||
|
G6,
|
||||||
|
G7,
|
||||||
|
G8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromArma for DragFunction {
|
||||||
|
fn from_arma(s: String) -> Result<Self, String> {
|
||||||
|
match s.as_str() {
|
||||||
|
"1" => Ok(Self::G1),
|
||||||
|
"2" => Ok(Self::G2),
|
||||||
|
"5" => Ok(Self::G5),
|
||||||
|
"6" => Ok(Self::G6),
|
||||||
|
"7" => Ok(Self::G7),
|
||||||
|
"8" => Ok(Self::G8),
|
||||||
|
_ => Err(format!("Unknown drag function: {s}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DragFunction {
|
||||||
|
#[must_use]
|
||||||
|
pub fn drag_coefficients(&self) -> Vec<f64> {
|
||||||
|
match self {
|
||||||
|
Self::G1 => vec![
|
||||||
|
0.2629, 0.2558, 0.2487, 0.2413, 0.2344, 0.2278, 0.2214, 0.2155, 0.2104, 0.2061,
|
||||||
|
0.2032, 0.2020, 0.2034, 0.2165, 0.2230, 0.2313, 0.2417, 0.2546, 0.2706, 0.2901,
|
||||||
|
0.3136, 0.3415, 0.3734, 0.4084, 0.4448, 0.4805, 0.5136, 0.5427, 0.5677, 0.5883,
|
||||||
|
0.6053, 0.6191, 0.6393, 0.6518, 0.6589, 0.6621, 0.6625, 0.6607, 0.6573, 0.6528,
|
||||||
|
0.6474, 0.6413, 0.6347, 0.6280, 0.6210, 0.6141, 0.6072, 0.6003, 0.5934, 0.5867,
|
||||||
|
0.5804, 0.5743, 0.5685, 0.5630, 0.5577, 0.5527, 0.5481, 0.5438, 0.5397, 0.5325,
|
||||||
|
0.5264, 0.5211, 0.5168, 0.5133, 0.5105, 0.5084, 0.5067, 0.5054, 0.5040, 0.5030,
|
||||||
|
0.5022, 0.5016, 0.5010, 0.5006, 0.4998, 0.4995, 0.4992, 0.4990, 0.4988,
|
||||||
|
],
|
||||||
|
Self::G2 => vec![
|
||||||
|
0.2303, 0.2298, 0.2287, 0.2271, 0.2251, 0.2227, 0.2196, 0.2156, 0.2107, 0.2048,
|
||||||
|
0.1980, 0.1905, 0.1828, 0.1758, 0.1702, 0.1669, 0.1664, 0.1667, 0.1682, 0.1711,
|
||||||
|
0.1761, 0.1831, 0.2004, 0.2589, 0.3492, 0.3983, 0.4075, 0.4103, 0.4114, 0.4106,
|
||||||
|
0.4089, 0.4068, 0.4046, 0.4021, 0.3966, 0.3904, 0.3835, 0.3759, 0.3678, 0.3594,
|
||||||
|
0.3512, 0.3432, 0.3356, 0.3282, 0.3213, 0.3149, 0.3089, 0.3033, 0.2982, 0.2933,
|
||||||
|
0.2889, 0.2846, 0.2806, 0.2768, 0.2731, 0.2696, 0.2663, 0.2632, 0.2602, 0.2572,
|
||||||
|
0.2543, 0.2515, 0.2487, 0.2460, 0.2433, 0.2408, 0.2382, 0.2357, 0.2333, 0.2309,
|
||||||
|
0.2262, 0.2217, 0.2173, 0.2132, 0.2091, 0.2052, 0.2014, 0.1978, 0.1944, 0.1912,
|
||||||
|
0.1851, 0.1794, 0.1741, 0.1693, 0.1648,
|
||||||
|
],
|
||||||
|
Self::G5 => vec![
|
||||||
|
0.1710, 0.1719, 0.1727, 0.1732, 0.1734, 0.1730, 0.1718, 0.1696, 0.1668, 0.1637,
|
||||||
|
0.1603, 0.1566, 0.1529, 0.1497, 0.1473, 0.1463, 0.1489, 0.1583, 0.1672, 0.1815,
|
||||||
|
0.2051, 0.2413, 0.2884, 0.3379, 0.3785, 0.4032, 0.4147, 0.4201, 0.4278, 0.4338,
|
||||||
|
0.4373, 0.4392, 0.4403, 0.4406, 0.4401, 0.4386, 0.4362, 0.4328, 0.4286, 0.4237,
|
||||||
|
0.4182, 0.4121, 0.4057, 0.3991, 0.3926, 0.3861, 0.3800, 0.3741, 0.3684, 0.3630,
|
||||||
|
0.3578, 0.3529, 0.3481, 0.3435, 0.3391, 0.3349, 0.3269, 0.3194, 0.3125, 0.3060,
|
||||||
|
0.2999, 0.2942, 0.2889, 0.2838, 0.2790, 0.2745, 0.2703, 0.2662, 0.2624, 0.2588,
|
||||||
|
0.2553, 0.2488, 0.2429, 0.2376, 0.2326, 0.2280,
|
||||||
|
],
|
||||||
|
Self::G6 => vec![
|
||||||
|
0.2617, 0.2553, 0.2491, 0.2432, 0.2376, 0.2324, 0.2278, 0.2238, 0.2205, 0.2177,
|
||||||
|
0.2155, 0.2138, 0.2126, 0.2121, 0.2122, 0.2132, 0.2154, 0.2194, 0.2229, 0.2297,
|
||||||
|
0.2449, 0.2732, 0.3141, 0.3597, 0.3994, 0.4261, 0.4402, 0.4465, 0.4490, 0.4497,
|
||||||
|
0.4494, 0.4482, 0.4464, 0.4441, 0.4390, 0.4336, 0.4279, 0.4221, 0.4162, 0.4102,
|
||||||
|
0.4042, 0.3981, 0.3919, 0.3855, 0.3788, 0.3721, 0.3652, 0.3583, 0.3515, 0.3447,
|
||||||
|
0.3381, 0.3314, 0.3249, 0.3185, 0.3122, 0.3060, 0.3000, 0.2941, 0.2883, 0.2772,
|
||||||
|
0.2668, 0.2574, 0.2487, 0.2407, 0.2333, 0.2265, 0.2202, 0.2144, 0.2089, 0.2039,
|
||||||
|
0.1991, 0.1947, 0.1905, 0.1866, 0.1794, 0.1730, 0.1673, 0.1621, 0.1574,
|
||||||
|
],
|
||||||
|
Self::G7 => vec![
|
||||||
|
0.1198, 0.1197, 0.1196, 0.1194, 0.1193, 0.1194, 0.1194, 0.1194, 0.1193, 0.1193,
|
||||||
|
0.1194, 0.1193, 0.1194, 0.1197, 0.1202, 0.1207, 0.1215, 0.1226, 0.1242, 0.1266,
|
||||||
|
0.1306, 0.1368, 0.1464, 0.1660, 0.2054, 0.2993, 0.3803, 0.4015, 0.4043, 0.4034,
|
||||||
|
0.4014, 0.3987, 0.3955, 0.3884, 0.3810, 0.3732, 0.3657, 0.3580, 0.3440, 0.3376,
|
||||||
|
0.3315, 0.3260, 0.3209, 0.3160, 0.3117, 0.3078, 0.3042, 0.3010, 0.2980, 0.2951,
|
||||||
|
0.2922, 0.2892, 0.2864, 0.2835, 0.2807, 0.2779, 0.2752, 0.2725, 0.2697, 0.2670,
|
||||||
|
0.2643, 0.2615, 0.2588, 0.2561, 0.2533, 0.2506, 0.2479, 0.2451, 0.2424, 0.2368,
|
||||||
|
0.2313, 0.2258, 0.2205, 0.2154, 0.2106, 0.2060, 0.2017, 0.1975, 0.1935, 0.1861,
|
||||||
|
0.1793, 0.1730, 0.1672, 0.1618,
|
||||||
|
],
|
||||||
|
Self::G8 => vec![
|
||||||
|
0.2105, 0.2105, 0.2104, 0.2104, 0.2103, 0.2103, 0.2103, 0.2103, 0.2103, 0.2102,
|
||||||
|
0.2102, 0.2102, 0.2102, 0.2102, 0.2103, 0.2103, 0.2104, 0.2104, 0.2105, 0.2106,
|
||||||
|
0.2109, 0.2183, 0.2571, 0.3358, 0.4068, 0.4378, 0.4476, 0.4493, 0.4477, 0.4450,
|
||||||
|
0.4419, 0.4353, 0.4283, 0.4208, 0.4133, 0.4059, 0.3986, 0.3915, 0.3845, 0.3777,
|
||||||
|
0.3710, 0.3645, 0.3581, 0.3519, 0.3458, 0.3400, 0.3343, 0.3288, 0.3234, 0.3182,
|
||||||
|
0.3131, 0.3081, 0.3032, 0.2983, 0.2937, 0.2891, 0.2845, 0.2802, 0.2720, 0.2642,
|
||||||
|
0.2569, 0.2499, 0.2432, 0.2368, 0.2308, 0.2251, 0.2197, 0.2147, 0.2101, 0.2058,
|
||||||
|
0.2019, 0.1983, 0.1950, 0.1890, 0.1837, 0.1791, 0.1750, 0.1713,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn mach_numbers(self) -> Vec<f64> {
|
||||||
|
match self {
|
||||||
|
Self::G1 => vec![
|
||||||
|
0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.70,
|
||||||
|
0.725, 0.75, 0.775, 0.80, 0.825, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0, 1.025,
|
||||||
|
1.05, 1.075, 1.10, 1.125, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55,
|
||||||
|
1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25,
|
||||||
|
2.30, 2.35, 2.40, 2.45, 2.50, 2.60, 2.70, 2.80, 2.90, 3.00, 3.10, 3.20, 3.30, 3.40,
|
||||||
|
3.50, 3.60, 3.70, 3.80, 3.90, 4.00, 4.20, 4.40, 4.60, 4.80, 5.00,
|
||||||
|
],
|
||||||
|
Self::G2 => vec![
|
||||||
|
0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65,
|
||||||
|
0.70, 0.75, 0.775, 0.80, 0.825, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0, 1.025,
|
||||||
|
1.05, 1.075, 1.10, 1.125, 1.15, 1.175, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50,
|
||||||
|
1.55, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20,
|
||||||
|
2.25, 2.30, 2.35, 2.40, 2.45, 2.50, 2.55, 2.60, 2.65, 2.70, 2.75, 2.80, 2.85, 2.90,
|
||||||
|
2.95, 3.00, 3.10, 3.20, 3.30, 3.40, 3.50, 3.60, 3.70, 3.80, 3.90, 4.00, 4.20, 4.40,
|
||||||
|
4.60, 4.80, 5.00,
|
||||||
|
],
|
||||||
|
Self::G5 => vec![
|
||||||
|
0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65,
|
||||||
|
0.70, 0.75, 0.80, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0, 1.025, 1.05, 1.075,
|
||||||
|
1.10, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75,
|
||||||
|
1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25, 2.30, 2.35, 2.40, 2.45,
|
||||||
|
2.50, 2.60, 2.70, 2.80, 2.90, 3.00, 3.10, 3.20, 3.30, 3.40, 3.50, 3.60, 3.70, 3.80,
|
||||||
|
3.90, 4.00, 4.20, 4.40, 4.60, 4.80, 5.00,
|
||||||
|
],
|
||||||
|
Self::G6 => vec![
|
||||||
|
0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65,
|
||||||
|
0.70, 0.75, 0.80, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0, 1.025, 1.05, 1.075,
|
||||||
|
1.10, 1.125, 1.15, 1.175, 1.20, 1.225, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55,
|
||||||
|
1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25,
|
||||||
|
2.30, 2.35, 2.40, 2.45, 2.50, 2.60, 2.70, 2.80, 2.90, 3.00, 3.10, 3.20, 3.30, 3.40,
|
||||||
|
3.50, 3.60, 3.70, 3.80, 3.90, 4.00, 4.20, 4.40, 4.60, 4.80, 5.00,
|
||||||
|
],
|
||||||
|
Self::G7 => vec![
|
||||||
|
0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65,
|
||||||
|
0.70, 0.725, 0.75, 0.775, 0.80, 0.825, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0,
|
||||||
|
1.025, 1.05, 1.075, 1.10, 1.125, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.50, 1.55,
|
||||||
|
1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25,
|
||||||
|
2.30, 2.35, 2.40, 2.45, 2.50, 2.55, 2.60, 2.65, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95,
|
||||||
|
3.00, 3.10, 3.20, 3.30, 3.40, 3.50, 3.60, 3.70, 3.80, 3.90, 4.00, 4.20, 4.40, 4.60,
|
||||||
|
4.80, 5.00,
|
||||||
|
],
|
||||||
|
Self::G8 => vec![
|
||||||
|
0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65,
|
||||||
|
0.70, 0.75, 0.80, 0.825, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0, 1.025, 1.05,
|
||||||
|
1.075, 1.10, 1.125, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60,
|
||||||
|
1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25, 2.30,
|
||||||
|
2.35, 2.40, 2.45, 2.50, 2.60, 2.70, 2.80, 2.90, 3.00, 3.10, 3.20, 3.30, 3.40, 3.50,
|
||||||
|
3.60, 3.70, 3.80, 3.90, 4.00, 4.20, 4.40, 4.60, 4.80, 5.00,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const BC_CONVERSION_FACTOR: f64 = 0.000_684_18;
|
||||||
|
pub fn calculate_retard(
|
||||||
|
drag_function: DragFunction,
|
||||||
|
ballistic_coefficient: f64,
|
||||||
|
velocity: f64,
|
||||||
|
mach: f64,
|
||||||
|
) -> f64 {
|
||||||
|
let m = velocity / mach;
|
||||||
|
for (i, mach_number) in drag_function.mach_numbers().iter().enumerate() {
|
||||||
|
if *mach_number >= m {
|
||||||
|
let previous = std::cmp::max(0, i - 1);
|
||||||
|
let previous_drag_coefficient = drag_function.drag_coefficients()[previous];
|
||||||
|
let previous_mach_number = drag_function.mach_numbers()[previous];
|
||||||
|
let cd = previous_drag_coefficient
|
||||||
|
+ (drag_function.drag_coefficients()[i] - previous_drag_coefficient)
|
||||||
|
* (m - previous_mach_number)
|
||||||
|
/ (drag_function.mach_numbers()[i] - previous_mach_number);
|
||||||
|
return BC_CONVERSION_FACTOR * (cd / ballistic_coefficient) * velocity.powi(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::ballistics::{atmosphere::speed_of_sound, Temperature};
|
||||||
|
|
||||||
|
use super::DragFunction;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn retard() {
|
||||||
|
assert!(
|
||||||
|
(super::calculate_retard(
|
||||||
|
DragFunction::G1,
|
||||||
|
0.583,
|
||||||
|
89.0,
|
||||||
|
speed_of_sound(Temperature::new_15c())
|
||||||
|
) - 2.103_812_727_313_926)
|
||||||
|
.abs()
|
||||||
|
< f64::EPSILON // old ace: 2.10381
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
56
extension/src/ballistics/map/mod.rs
Normal file
56
extension/src/ballistics/map/mod.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use arma_rs::Group;
|
||||||
|
|
||||||
|
mod model;
|
||||||
|
pub use model::Map;
|
||||||
|
|
||||||
|
pub fn group() -> Group {
|
||||||
|
Group::new().command("init", init).command("set", set)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static mut MAPS: Option<HashMap<String, Map>> = None;
|
||||||
|
pub static mut CURRENT_MAP: Option<String> = None;
|
||||||
|
|
||||||
|
/// Initializes a new map, and sets it as the current map
|
||||||
|
///
|
||||||
|
/// Safety: Arma will only call this from its main thread
|
||||||
|
fn init(name: String, size: u32) -> bool {
|
||||||
|
unsafe {
|
||||||
|
if MAPS.is_none() {
|
||||||
|
MAPS = Some(HashMap::new());
|
||||||
|
}
|
||||||
|
CURRENT_MAP = Some(name.clone());
|
||||||
|
if MAPS.as_ref().unwrap().contains_key(&name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
MAPS.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.insert(name, Map::new(size, (size / 50) + 1));
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the height, number of objects, and surface type of a grid square
|
||||||
|
///
|
||||||
|
/// Safety: Arma will only call this from its main thread
|
||||||
|
fn set(grid: u64, height: i64, num_objects: i64, surface_is_water: bool) -> Result<(), String> {
|
||||||
|
unsafe {
|
||||||
|
MAPS.as_mut()
|
||||||
|
.ok_or_else(|| "Map array not initialized".to_string())?
|
||||||
|
.get_mut(
|
||||||
|
CURRENT_MAP
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| "No current map".to_string())?,
|
||||||
|
)
|
||||||
|
.map_or_else(
|
||||||
|
|| Err(format!("no map: {}", CURRENT_MAP.as_ref().unwrap())),
|
||||||
|
|map| {
|
||||||
|
map.set_height(grid as usize, height);
|
||||||
|
map.set_building_num(grid as usize, num_objects);
|
||||||
|
map.set_surface_is_water(grid as usize, surface_is_water);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
68
extension/src/ballistics/map/model.rs
Normal file
68
extension/src/ballistics/map/model.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Map {
|
||||||
|
heights: Vec<i64>,
|
||||||
|
building_nums: Vec<i64>,
|
||||||
|
surface_is_water: Vec<bool>,
|
||||||
|
size: u32,
|
||||||
|
grids: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Map {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(size: u32, grids: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
heights: vec![0; (size * grids) as usize],
|
||||||
|
building_nums: vec![0; (size * grids) as usize],
|
||||||
|
surface_is_water: vec![false; (size * grids) as usize],
|
||||||
|
size,
|
||||||
|
grids,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn heights(&self) -> &Vec<i64> {
|
||||||
|
&self.heights
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn height(&self, cell: usize) -> i64 {
|
||||||
|
self.heights[cell]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_height(&mut self, cell: usize, height: i64) {
|
||||||
|
self.heights[cell] = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn building_nums(&self) -> &Vec<i64> {
|
||||||
|
&self.building_nums
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn building_num(&self, index: usize) -> i64 {
|
||||||
|
self.building_nums[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_building_num(&mut self, index: usize, num: i64) {
|
||||||
|
self.building_nums[index] = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn surface_is_water(&self, index: usize) -> bool {
|
||||||
|
self.surface_is_water[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_surface_is_water(&mut self, index: usize, is_water: bool) {
|
||||||
|
self.surface_is_water[index] = is_water;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn size(&self) -> u32 {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn grids(&self) -> u32 {
|
||||||
|
self.grids
|
||||||
|
}
|
||||||
|
}
|
156
extension/src/ballistics/mod.rs
Normal file
156
extension/src/ballistics/mod.rs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
use arma_rs::Group;
|
||||||
|
|
||||||
|
use crate::common::{MuzzleVelocity, Temperature};
|
||||||
|
|
||||||
|
mod atmosphere;
|
||||||
|
mod bullet;
|
||||||
|
mod drag;
|
||||||
|
mod map;
|
||||||
|
mod zero;
|
||||||
|
|
||||||
|
use self::{
|
||||||
|
atmosphere::{calculate_atmospheric_correction, speed_of_sound},
|
||||||
|
drag::calculate_retard,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use self::{atmosphere::AtmosphereModel, drag::DragFunction, map::Map};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum BallisticModel {
|
||||||
|
/// Air friction
|
||||||
|
Vanilla(f64),
|
||||||
|
Advanced(AdvancedBallistics),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AdvancedBallistics {
|
||||||
|
temperature: Temperature,
|
||||||
|
pressure: f64,
|
||||||
|
relative_humidity: f64,
|
||||||
|
ballistic_coefficient: f64,
|
||||||
|
drag_function: DragFunction,
|
||||||
|
atmosphere_model: AtmosphereModel,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AdvancedBallistics {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn temperature(&self) -> Temperature {
|
||||||
|
self.temperature
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn pressure(&self) -> f64 {
|
||||||
|
self.pressure
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn relative_humidity(&self) -> f64 {
|
||||||
|
self.relative_humidity
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn ballistic_coefficient(&self) -> f64 {
|
||||||
|
self.ballistic_coefficient
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn drag_function(&self) -> DragFunction {
|
||||||
|
self.drag_function
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn atmosphere_model(&self) -> AtmosphereModel {
|
||||||
|
self.atmosphere_model
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn group() -> Group {
|
||||||
|
Group::new()
|
||||||
|
.command("retard", retard)
|
||||||
|
.command("atmospheric_correction", atmospheric_correction)
|
||||||
|
.command("replicate_vanilla_zero", replicate_vanilla_zero)
|
||||||
|
.command("zero_vanilla", zero_vanilla)
|
||||||
|
.command("zero_advanced", zero_advanced)
|
||||||
|
.group("bullet", bullet::group())
|
||||||
|
.group("map", map::group())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retard(
|
||||||
|
drag_function: DragFunction,
|
||||||
|
ballistic_coefficient: f64,
|
||||||
|
velocity: f64,
|
||||||
|
temperature: Temperature,
|
||||||
|
) -> f64 {
|
||||||
|
calculate_retard(
|
||||||
|
drag_function,
|
||||||
|
ballistic_coefficient,
|
||||||
|
velocity,
|
||||||
|
speed_of_sound(temperature),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn atmospheric_correction(
|
||||||
|
ballistic_coefficient: f64,
|
||||||
|
temperature: Temperature,
|
||||||
|
pressure: f64,
|
||||||
|
relative_humidity: f64,
|
||||||
|
atmosphere_model: AtmosphereModel,
|
||||||
|
) -> f64 {
|
||||||
|
calculate_atmospheric_correction(
|
||||||
|
ballistic_coefficient,
|
||||||
|
temperature,
|
||||||
|
pressure,
|
||||||
|
relative_humidity,
|
||||||
|
atmosphere_model,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replicate_vanilla_zero(
|
||||||
|
zero_range: f64,
|
||||||
|
muzzle_velocity: MuzzleVelocity,
|
||||||
|
air_friction: f64,
|
||||||
|
) -> f64 {
|
||||||
|
zero::replicate_vanilla(zero_range, muzzle_velocity, air_friction)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zero_vanilla(
|
||||||
|
zero_range: f64,
|
||||||
|
muzzle_velocity: MuzzleVelocity,
|
||||||
|
air_friction: f64,
|
||||||
|
bore_height: f64,
|
||||||
|
) -> f64 {
|
||||||
|
zero::calculate(
|
||||||
|
zero_range,
|
||||||
|
muzzle_velocity,
|
||||||
|
bore_height,
|
||||||
|
BallisticModel::Vanilla(air_friction),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn zero_advanced(
|
||||||
|
zero_range: f64,
|
||||||
|
muzzle_velocity: MuzzleVelocity,
|
||||||
|
bore_height: f64,
|
||||||
|
temperature: Temperature,
|
||||||
|
pressure: f64,
|
||||||
|
relative_humidity: f64,
|
||||||
|
ballistic_coefficient: f64,
|
||||||
|
drag_function: DragFunction,
|
||||||
|
atmosphere_model: AtmosphereModel,
|
||||||
|
) -> f64 {
|
||||||
|
zero::calculate(
|
||||||
|
zero_range,
|
||||||
|
muzzle_velocity,
|
||||||
|
bore_height,
|
||||||
|
BallisticModel::Advanced(AdvancedBallistics {
|
||||||
|
temperature,
|
||||||
|
pressure,
|
||||||
|
relative_humidity,
|
||||||
|
ballistic_coefficient,
|
||||||
|
drag_function,
|
||||||
|
atmosphere_model,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
160
extension/src/ballistics/zero.rs
Normal file
160
extension/src/ballistics/zero.rs
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
use crate::common::{MuzzleVelocity, Vector3, GRAVITY};
|
||||||
|
|
||||||
|
use super::{drag::calculate_retard, BallisticModel};
|
||||||
|
|
||||||
|
pub fn replicate_vanilla(
|
||||||
|
zero_range: f64,
|
||||||
|
muzzle_velocity: MuzzleVelocity,
|
||||||
|
air_friction: f64,
|
||||||
|
) -> f64 {
|
||||||
|
let max_delta_time = 0.05;
|
||||||
|
let mut time = 0.0;
|
||||||
|
let mut current_shot_position = Vector3::default();
|
||||||
|
let mut current_shot_velocity = Vector3::new(*muzzle_velocity, 0.0, 0.0);
|
||||||
|
|
||||||
|
while time < 8.0 {
|
||||||
|
let dist_left = zero_range - current_shot_position.x();
|
||||||
|
let traveled = current_shot_velocity.x() * max_delta_time;
|
||||||
|
if dist_left < traveled {
|
||||||
|
let delta_time = dist_left / current_shot_velocity.x();
|
||||||
|
current_shot_velocity -= Vector3::new(0.0, GRAVITY * delta_time, 0.0);
|
||||||
|
current_shot_position += current_shot_velocity * delta_time;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let delta_time = max_delta_time;
|
||||||
|
current_shot_position += current_shot_velocity * delta_time;
|
||||||
|
time += delta_time;
|
||||||
|
current_shot_velocity +=
|
||||||
|
current_shot_velocity * (current_shot_velocity.magnitude() * air_friction * delta_time);
|
||||||
|
current_shot_velocity -= Vector3::new(0.0, GRAVITY * delta_time, 0.0);
|
||||||
|
}
|
||||||
|
(-(current_shot_position.y() / zero_range).atan()).to_degrees()
|
||||||
|
}
|
||||||
|
|
||||||
|
const SPEED_OF_SOUND_AT_15C: f64 = 340.275;
|
||||||
|
|
||||||
|
pub fn calculate(
|
||||||
|
zero_range: f64,
|
||||||
|
muzzle_velocity: MuzzleVelocity,
|
||||||
|
bore_height: f64,
|
||||||
|
ballistic_model: BallisticModel,
|
||||||
|
) -> f64 {
|
||||||
|
let mut zero_angle = 0.0f64;
|
||||||
|
let delta_time = 1.0 / 100.0f64.max(zero_range);
|
||||||
|
|
||||||
|
for _ in 0..10 {
|
||||||
|
let mut lx = 0.0;
|
||||||
|
let mut ly = 0.0;
|
||||||
|
|
||||||
|
let mut px = 0.0;
|
||||||
|
let mut py = -bore_height / 100.0;
|
||||||
|
|
||||||
|
let gx = zero_angle.sin() * -GRAVITY;
|
||||||
|
let gy = zero_angle.cos() * -GRAVITY;
|
||||||
|
|
||||||
|
let mut vx = zero_angle.cos() * *muzzle_velocity;
|
||||||
|
let mut vy = zero_angle.sin() * *muzzle_velocity;
|
||||||
|
|
||||||
|
let mut tof = 0.0;
|
||||||
|
|
||||||
|
while tof < 8.0 && px < zero_range {
|
||||||
|
lx = px;
|
||||||
|
ly = py;
|
||||||
|
|
||||||
|
let v = vx.hypot(vy);
|
||||||
|
|
||||||
|
let (ax, ay) = match &ballistic_model {
|
||||||
|
BallisticModel::Vanilla(air_friction) => {
|
||||||
|
((vx * v * air_friction) + gx, (vy * v * air_friction) + gy)
|
||||||
|
}
|
||||||
|
BallisticModel::Advanced(advanced) => {
|
||||||
|
let retard = calculate_retard(
|
||||||
|
advanced.drag_function(),
|
||||||
|
super::atmosphere::calculate_atmospheric_correction(
|
||||||
|
advanced.ballistic_coefficient(),
|
||||||
|
advanced.temperature(),
|
||||||
|
advanced.pressure(),
|
||||||
|
advanced.relative_humidity(),
|
||||||
|
advanced.atmosphere_model(),
|
||||||
|
),
|
||||||
|
v,
|
||||||
|
SPEED_OF_SOUND_AT_15C,
|
||||||
|
);
|
||||||
|
((vx / v).mul_add(-retard, gx), (vy / v).mul_add(-retard, gy))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
px += vx * delta_time * 0.5;
|
||||||
|
py += vy * delta_time * 0.5;
|
||||||
|
vx += ax * delta_time;
|
||||||
|
vy += ay * delta_time;
|
||||||
|
px += vx * delta_time * 0.5;
|
||||||
|
py += vy * delta_time * 0.5;
|
||||||
|
|
||||||
|
tof += delta_time;
|
||||||
|
}
|
||||||
|
let y = ly + (zero_range - lx) * (py - ly) / (px - lx);
|
||||||
|
let offset = -(y / zero_range).atan();
|
||||||
|
zero_angle += offset;
|
||||||
|
|
||||||
|
if offset.abs() < 0.00001 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zero_angle.to_degrees()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{
|
||||||
|
ballistics::{
|
||||||
|
AdvancedBallistics, AtmosphereModel, BallisticModel, DragFunction, Temperature,
|
||||||
|
},
|
||||||
|
common::MuzzleVelocity,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn replicate_vanilla_zero() {
|
||||||
|
assert!(
|
||||||
|
(super::replicate_vanilla(200.0, MuzzleVelocity::new(89.0), 0.3)
|
||||||
|
- 0.164_673_237_568_344_37)
|
||||||
|
.abs()
|
||||||
|
< f64::EPSILON // old ace: 0.164672
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn calc_zero_vanilla() {
|
||||||
|
assert!(
|
||||||
|
(super::calculate(
|
||||||
|
200.0,
|
||||||
|
MuzzleVelocity::new(89.0),
|
||||||
|
1.5,
|
||||||
|
BallisticModel::Vanilla(0.3)
|
||||||
|
) - 0.132_818_571_102_032_27)
|
||||||
|
.abs()
|
||||||
|
< f64::EPSILON // old ace: 0.132818
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn calc_zero_advanced() {
|
||||||
|
assert!(
|
||||||
|
(super::calculate(
|
||||||
|
200.0,
|
||||||
|
MuzzleVelocity::new(89.0),
|
||||||
|
1.5,
|
||||||
|
BallisticModel::Advanced(AdvancedBallistics {
|
||||||
|
ballistic_coefficient: 0.583,
|
||||||
|
temperature: Temperature::new_15c(),
|
||||||
|
pressure: 1005.0,
|
||||||
|
relative_humidity: 0.0,
|
||||||
|
atmosphere_model: AtmosphereModel::Icao,
|
||||||
|
drag_function: DragFunction::G1,
|
||||||
|
})
|
||||||
|
) - 7.509_855_403_805_35)
|
||||||
|
.abs()
|
||||||
|
< f64::EPSILON // old ace: 7.51363
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
32
extension/src/break_line.rs
Normal file
32
extension/src/break_line.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
pub fn break_line(line: String) -> String {
|
||||||
|
let mut out = Vec::with_capacity(line.len() / 14);
|
||||||
|
let words: Vec<&str> = line.split(' ').collect();
|
||||||
|
let mut buffer = String::new();
|
||||||
|
for word in words {
|
||||||
|
if buffer.len() + word.len() > 14 {
|
||||||
|
out.push(buffer.trim_end().to_string());
|
||||||
|
buffer = String::new();
|
||||||
|
}
|
||||||
|
buffer.push_str(word);
|
||||||
|
buffer.push(' ');
|
||||||
|
}
|
||||||
|
if !buffer.is_empty() {
|
||||||
|
out.push(buffer.trim_end().to_string());
|
||||||
|
}
|
||||||
|
out.join("<br/>")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn break_line() {
|
||||||
|
assert_eq!(
|
||||||
|
super::break_line("The advanced combat environment mod is now rusty".to_string()),
|
||||||
|
"The advanced<br/>combat<br/>environment<br/>mod is now<br/>rusty"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
super::break_line("test hello can you hear me? I'm the arma man".to_string()),
|
||||||
|
"test hello can<br/>you hear me?<br/>I'm the arma<br/>man"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
48
extension/src/clipboard.rs
Normal file
48
extension/src/clipboard.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use arma_rs::{loadout::Loadout, FromArma, Group};
|
||||||
|
|
||||||
|
use clipboard::{ClipboardContext, ClipboardProvider};
|
||||||
|
|
||||||
|
pub fn group() -> Group {
|
||||||
|
Group::new()
|
||||||
|
.command("clear", clear)
|
||||||
|
.command("append", append)
|
||||||
|
.command("complete", complete)
|
||||||
|
.command("loadout", loadout)
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut BUFFER: String = String::new();
|
||||||
|
|
||||||
|
pub fn clear() {
|
||||||
|
// Safety: this is all single threaded, so no need to lock
|
||||||
|
unsafe {
|
||||||
|
BUFFER.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(text: String) {
|
||||||
|
// Safety: this is all single threaded, so no need to lock
|
||||||
|
unsafe {
|
||||||
|
BUFFER.push_str(&text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn complete() -> Result<(), String> {
|
||||||
|
// Safety: this is all single threaded, so no need to lock
|
||||||
|
let mut ctx = ClipboardContext::new().map_err(|e| e.to_string())?;
|
||||||
|
ctx.set_contents(unsafe { BUFFER.clone() })
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
unsafe {
|
||||||
|
BUFFER = String::new();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loadout() -> Option<String> {
|
||||||
|
let mut ctx = ClipboardContext::new().ok()?;
|
||||||
|
let content = ctx.get_contents().ok()?;
|
||||||
|
match Loadout::from_arma(content.clone()) {
|
||||||
|
Ok(_) => return Some(content),
|
||||||
|
Err(e) => eprintln!("Loadout parsing error: {}", e),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
5
extension/src/common/mod.rs
Normal file
5
extension/src/common/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod types;
|
||||||
|
pub use types::*;
|
||||||
|
|
||||||
|
pub const GRAVITY: f64 = 9.80665;
|
||||||
|
pub const GRAVITY_ACCEL: Vector3 = Vector3::new(0.0, 0.0, -GRAVITY);
|
33
extension/src/common/types/height.rs
Normal file
33
extension/src/common/types/height.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use arma_rs::FromArma;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
/// Height in meters
|
||||||
|
pub struct Height(f64);
|
||||||
|
|
||||||
|
impl Height {
|
||||||
|
pub fn new(height: f64) -> Self {
|
||||||
|
Self(height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromArma for Height {
|
||||||
|
fn from_arma(value: String) -> Result<Self, String> {
|
||||||
|
Ok(Self(value.parse::<f64>().map_err(|_| "Invalid height")?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<f64> for Height {
|
||||||
|
fn as_ref(&self) -> &f64 {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Height {
|
||||||
|
type Target = f64;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
11
extension/src/common/types/mod.rs
Normal file
11
extension/src/common/types/mod.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
mod height;
|
||||||
|
pub use height::Height;
|
||||||
|
|
||||||
|
mod muzzle_velocity;
|
||||||
|
pub use muzzle_velocity::MuzzleVelocity;
|
||||||
|
|
||||||
|
mod temperature;
|
||||||
|
pub use temperature::Temperature;
|
||||||
|
|
||||||
|
mod vector3;
|
||||||
|
pub use vector3::Vector3;
|
38
extension/src/common/types/muzzle_velocity.rs
Normal file
38
extension/src/common/types/muzzle_velocity.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use arma_rs::FromArma;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
/// Muzzle velocity in m/s
|
||||||
|
pub struct MuzzleVelocity(f64);
|
||||||
|
|
||||||
|
impl MuzzleVelocity {
|
||||||
|
#[allow(dead_code)] // Only used in tests
|
||||||
|
pub fn new(muzzle_velocity: f64) -> Self {
|
||||||
|
Self(muzzle_velocity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromArma for MuzzleVelocity {
|
||||||
|
fn from_arma(value: String) -> Result<Self, String> {
|
||||||
|
Ok(Self(
|
||||||
|
value
|
||||||
|
.parse::<f64>()
|
||||||
|
.map_err(|_| "Invalid muzzle velocity")?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<f64> for MuzzleVelocity {
|
||||||
|
fn as_ref(&self) -> &f64 {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for MuzzleVelocity {
|
||||||
|
type Target = f64;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
60
extension/src/common/types/temperature.rs
Normal file
60
extension/src/common/types/temperature.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use arma_rs::FromArma;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct Temperature(f64);
|
||||||
|
|
||||||
|
impl Temperature {
|
||||||
|
pub fn new_celsius(value: f64) -> Self {
|
||||||
|
Self(value + 273.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn new_15c() -> Self {
|
||||||
|
Self::new_kelvin(288.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn new_kelvin(value: f64) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_fahrenheit(value: f64) -> Self {
|
||||||
|
Self((value - 32.0) * 5.0 / 9.0 + 273.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_celsius(self) -> f64 {
|
||||||
|
self.0 - 273.15
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn as_kelvin(self) -> f64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_fahrenheit(self) -> f64 {
|
||||||
|
(self.0 - 273.15) * 9.0 / 5.0 + 32.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromArma for Temperature {
|
||||||
|
fn from_arma(s: String) -> Result<Self, String> {
|
||||||
|
if s.is_empty() {
|
||||||
|
return Err(String::from("unexpected empty string"));
|
||||||
|
}
|
||||||
|
match s.chars().next().unwrap() {
|
||||||
|
'c' => {
|
||||||
|
let temp = s[1..].parse::<f64>().map_err(|e| format!("{e}"))?;
|
||||||
|
Ok(Self::new_celsius(temp))
|
||||||
|
}
|
||||||
|
'f' => {
|
||||||
|
let temp = s[1..].parse::<f64>().map_err(|e| format!("{e}"))?;
|
||||||
|
Ok(Self::new_fahrenheit(temp))
|
||||||
|
}
|
||||||
|
'k' => {
|
||||||
|
let temp = s[1..].parse::<f64>().map_err(|e| format!("{e}"))?;
|
||||||
|
Ok(Self::new_kelvin(temp))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let temp = s.parse::<f64>().map_err(|e| format!("{e}"))?;
|
||||||
|
Ok(Self::new_celsius(temp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
173
extension/src/common/types/vector3.rs
Normal file
173
extension/src/common/types/vector3.rs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
use arma_rs::{FromArma, IntoArma};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||||
|
pub struct Vector3 {
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
z: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vector3 {
|
||||||
|
pub const fn new(x: f64, y: f64, z: f64) -> Self {
|
||||||
|
Self { x, y, z }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn x(&self) -> f64 {
|
||||||
|
self.x
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn y(&self) -> f64 {
|
||||||
|
self.y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn z(&self) -> f64 {
|
||||||
|
self.z
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn magnitude(&self) -> f64 {
|
||||||
|
self.magnitude_squared().sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn magnitude_squared(&self) -> f64 {
|
||||||
|
self.x
|
||||||
|
.mul_add(self.x, self.y.mul_add(self.y, self.z * self.z))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normalize(&self) -> Self {
|
||||||
|
let magnitude = self.magnitude();
|
||||||
|
Self {
|
||||||
|
x: self.x / magnitude,
|
||||||
|
y: self.y / magnitude,
|
||||||
|
z: self.z / magnitude,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lerp(&self, other: &Self, t: f64) -> Self {
|
||||||
|
Self {
|
||||||
|
x: (other.x - self.x).mul_add(t, self.x),
|
||||||
|
y: (other.y - self.y).mul_add(t, self.y),
|
||||||
|
z: (other.z - self.z).mul_add(t, self.z),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromArma for Vector3 {
|
||||||
|
fn from_arma(s: String) -> Result<Self, String> {
|
||||||
|
let data = <[f64; 3]>::from_arma(s)?;
|
||||||
|
Ok(Self {
|
||||||
|
x: data[0],
|
||||||
|
y: data[1],
|
||||||
|
z: data[2],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoArma for Vector3 {
|
||||||
|
fn to_arma(&self) -> arma_rs::Value {
|
||||||
|
arma_rs::Value::Array(vec![
|
||||||
|
arma_rs::Value::Number(self.x),
|
||||||
|
arma_rs::Value::Number(self.y),
|
||||||
|
arma_rs::Value::Number(self.z),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add for Vector3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x + other.x,
|
||||||
|
y: self.y + other.y,
|
||||||
|
z: self.z + other.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::AddAssign for Vector3 {
|
||||||
|
fn add_assign(&mut self, other: Self) {
|
||||||
|
self.x += other.x;
|
||||||
|
self.y += other.y;
|
||||||
|
self.z += other.z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Sub for Vector3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x - other.x,
|
||||||
|
y: self.y - other.y,
|
||||||
|
z: self.z - other.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::SubAssign for Vector3 {
|
||||||
|
fn sub_assign(&mut self, other: Self) {
|
||||||
|
self.x -= other.x;
|
||||||
|
self.y -= other.y;
|
||||||
|
self.z -= other.z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul for Vector3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x * other.x,
|
||||||
|
y: self.y * other.y,
|
||||||
|
z: self.z * other.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<f64> for Vector3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, other: f64) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x * other,
|
||||||
|
y: self.y * other,
|
||||||
|
z: self.z * other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::MulAssign for Vector3 {
|
||||||
|
fn mul_assign(&mut self, other: Self) {
|
||||||
|
self.x *= other.x;
|
||||||
|
self.y *= other.y;
|
||||||
|
self.z *= other.z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::MulAssign<f64> for Vector3 {
|
||||||
|
fn mul_assign(&mut self, other: f64) {
|
||||||
|
self.x *= other;
|
||||||
|
self.y *= other;
|
||||||
|
self.z *= other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Div for Vector3 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn div(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x / other.x,
|
||||||
|
y: self.y / other.y,
|
||||||
|
z: self.z / other.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::DivAssign for Vector3 {
|
||||||
|
fn div_assign(&mut self, other: Self) {
|
||||||
|
self.x /= other.x;
|
||||||
|
self.y /= other.y;
|
||||||
|
self.z /= other.z;
|
||||||
|
}
|
||||||
|
}
|
79
extension/src/fcs.rs
Normal file
79
extension/src/fcs.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use crate::common::GRAVITY;
|
||||||
|
|
||||||
|
const SIMULATION_STEP: f64 = 0.05;
|
||||||
|
const MAX_ITERATIONS: u16 = 600;
|
||||||
|
const MAX_ELEVATION: f64 = 20.0;
|
||||||
|
const PRECISION: f64 = 0.1;
|
||||||
|
|
||||||
|
fn trace_bullet(
|
||||||
|
init_speed: f64,
|
||||||
|
air_friction: f64,
|
||||||
|
angle: f64,
|
||||||
|
angle_target: f64,
|
||||||
|
distance: f64,
|
||||||
|
) -> f64 {
|
||||||
|
let mut vel_x = angle.to_radians().cos() * init_speed;
|
||||||
|
let mut vel_y = angle.to_radians().sin() * init_speed;
|
||||||
|
let pos_target_x = angle_target.to_radians().cos() * distance;
|
||||||
|
let pos_target_y = angle_target.to_radians().sin() * distance;
|
||||||
|
let mut pos_x = 0.0;
|
||||||
|
let mut pos_y = 0.0;
|
||||||
|
let mut last_pos_x = 0.0;
|
||||||
|
let mut last_pos_y = 0.0;
|
||||||
|
|
||||||
|
for _ in 0..MAX_ITERATIONS {
|
||||||
|
last_pos_x = pos_x;
|
||||||
|
last_pos_y = pos_y;
|
||||||
|
let vel_mag = vel_x.hypot(vel_y);
|
||||||
|
pos_x += vel_x * SIMULATION_STEP * 0.5;
|
||||||
|
pos_y += vel_y * SIMULATION_STEP * 0.5;
|
||||||
|
vel_x += SIMULATION_STEP * (vel_x * vel_mag * air_friction);
|
||||||
|
vel_y += SIMULATION_STEP * (vel_y * vel_mag).mul_add(air_friction, -GRAVITY);
|
||||||
|
pos_x += vel_x * SIMULATION_STEP * 0.5;
|
||||||
|
pos_y += vel_y * SIMULATION_STEP * 0.5;
|
||||||
|
if pos_x >= pos_target_x {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let coef = (pos_target_x - last_pos_x) / (pos_x - last_pos_x);
|
||||||
|
(pos_y - last_pos_y).mul_add(coef, last_pos_y) - pos_target_y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_solution(init_speed: f64, air_friction: f64, angle_target: f64, distance: f64) -> f64 {
|
||||||
|
if trace_bullet(
|
||||||
|
init_speed,
|
||||||
|
air_friction,
|
||||||
|
MAX_ELEVATION,
|
||||||
|
angle_target,
|
||||||
|
distance,
|
||||||
|
) < 0.0
|
||||||
|
{
|
||||||
|
return MAX_ELEVATION - angle_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut a1 = angle_target;
|
||||||
|
let mut a2 = MAX_ELEVATION;
|
||||||
|
let mut f1 = trace_bullet(init_speed, air_friction, a1, angle_target, distance);
|
||||||
|
if f1.abs() <= PRECISION {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
while f1.abs() > PRECISION {
|
||||||
|
let f2 = trace_bullet(init_speed, air_friction, a2, angle_target, distance);
|
||||||
|
let tmp = a2 - f2 * (a2 - a1) / (f2 - f1);
|
||||||
|
a1 = a2;
|
||||||
|
a2 = tmp;
|
||||||
|
f1 = f2;
|
||||||
|
}
|
||||||
|
a2 - angle_target
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::get_solution;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_solution() {
|
||||||
|
assert!((get_solution(400.0, 0.0, 28.0, 950.0) - -8.0).abs() < f64::EPSILON);
|
||||||
|
}
|
||||||
|
}
|
35
extension/src/lib.rs
Normal file
35
extension/src/lib.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#![deny(clippy::all)]
|
||||||
|
#![deny(missing_debug_implementations)]
|
||||||
|
|
||||||
|
//! ACE3 Extension for quick maths and OS APIs
|
||||||
|
|
||||||
|
use arma_rs::{arma, Extension, Group};
|
||||||
|
|
||||||
|
mod artillery;
|
||||||
|
pub mod ballistics;
|
||||||
|
mod break_line;
|
||||||
|
mod common;
|
||||||
|
mod fcs;
|
||||||
|
|
||||||
|
#[cfg(feature = "clipboard")]
|
||||||
|
mod clipboard;
|
||||||
|
|
||||||
|
#[arma]
|
||||||
|
fn init() -> Extension {
|
||||||
|
Extension::build()
|
||||||
|
.version(env!("GIT_HASH").to_string())
|
||||||
|
.group("artillery", artillery::group())
|
||||||
|
.group("ballistics", ballistics::group())
|
||||||
|
.group(
|
||||||
|
"clipboard",
|
||||||
|
if cfg!(feature = "clipboard") {
|
||||||
|
clipboard::group()
|
||||||
|
} else {
|
||||||
|
Group::new()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.command("break_line", break_line::break_line)
|
||||||
|
.command("fcs", fcs::get_solution)
|
||||||
|
.command("version", || env!("GIT_HASH"))
|
||||||
|
.finish()
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
BasedOnStyle: Google
|
|
||||||
IndentWidth: 4
|
|
||||||
ColumnLimit: 160
|
|
||||||
DerivePointerAlignment: false
|
|
||||||
PointerAlignment: Left
|
|
||||||
NamespaceIndentation: All
|
|
||||||
IncludeBlocks: Merge
|
|
||||||
|
|
||||||
AllowShortBlocksOnASingleLine: true
|
|
@ -1,166 +0,0 @@
|
|||||||
cmake_minimum_required (VERSION 3.14)
|
|
||||||
project (ACE)
|
|
||||||
|
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
add_definitions(/DWINVER=0x0600 /D_WIN32_WINNT=0x0600)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (NOT CMAKE_BUILD_TYPE AND CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
message(STATUS "No build type selected, default to Debug")
|
|
||||||
set(CMAKE_BUILD_TYPE "Debug")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(DEVEL "DEVEL" OFF)
|
|
||||||
option(USE_BULLET "USE_BULLET" OFF)
|
|
||||||
option(USE_DIRECTX "USE_DIRECTX" OFF)
|
|
||||||
option(USE_64BIT_BUILD "USE_64BIT_BUILD" OFF)
|
|
||||||
option(USE_STATIC_LINKING "USE_STATIC_LINKING" ON)
|
|
||||||
|
|
||||||
if(CMAKE_GENERATOR_PLATFORM MATCHES "x64")
|
|
||||||
set(USE_64BIT_BUILD ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
SET(CMAKE_CXX_FLAGS "-std=c++11 -pedantic -pedantic-errors -march=i686 -m32 -O2 -s -fPIC -fpermissive")
|
|
||||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS "-static-libgcc -static-libstdc++")
|
|
||||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
||||||
message(ERROR "SUPPORT NOT COMPLETE")
|
|
||||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
|
||||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS) # Disable MSVC *_s function warnings
|
|
||||||
|
|
||||||
if(USE_64BIT_BUILD)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /Qpar-report:2") # Default SSE2
|
|
||||||
else()
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /arch:SSE2 /Qpar-report:2")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} /D _DEBUG /MTd /Zi /Ob0 /Od /RTC1")
|
|
||||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} /MT /O1 /Ob1 /D NDEBUG")
|
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} /MT /O2 /Ob2 /D NDEBUG")
|
|
||||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} /MT /Zi /O2 /Ob1 /D NDEBUG")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include_directories("common")
|
|
||||||
|
|
||||||
if(USE_BULLET)
|
|
||||||
# Dependencies
|
|
||||||
#
|
|
||||||
set(BACKUP_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY})
|
|
||||||
set(BACKUP_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
|
|
||||||
set(BACKUP_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
|
||||||
|
|
||||||
add_definitions(-DUSE_DIRECTX)
|
|
||||||
add_subdirectory(lib/bullet3)
|
|
||||||
|
|
||||||
set_target_properties(App_BasicExample PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(App_HelloWorld PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(App_ExampleBrowser PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(Bullet2FileLoader PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(Bullet3Collision PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(Bullet3Dynamics PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(Bullet3Geometry PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(Bullet3Common PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(Bullet3OpenCL_clew PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(BulletCollision PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(BulletDynamics PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(BulletFileLoader PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(BulletSoftBody PROPERTIES FOLDER Bullet3)
|
|
||||||
|
|
||||||
set_target_properties(BulletWorldImporter PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(BulletXmlWorldImporter PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(ConvexDecomposition PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(GIMPACTUtils PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(gtest PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(gwen PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(HACD PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(OpenGLWindow PROPERTIES FOLDER Bullet3)
|
|
||||||
set_target_properties(LinearMath PROPERTIES FOLDER Bullet3)
|
|
||||||
|
|
||||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${BACKUP_ARCHIVE_OUTPUT_DIRECTORY})
|
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${BACKUP_LIBRARY_OUTPUT_DIRECTORY})
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${BACKUP_RUNTIME_OUTPUT_DIRECTORY})
|
|
||||||
|
|
||||||
include_directories(BEFORE "lib/bullet3/src")
|
|
||||||
endif()
|
|
||||||
if(USE_DIRECTX)
|
|
||||||
add_definitions(-DUSE_DIRECTX)
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
|
|
||||||
find_package (DirectX)
|
|
||||||
link_directories (BEFORE ${DirectX_D3DX11_LIBRARY})
|
|
||||||
include_directories (BEFORE ${DirectX_D3DX11_INCLUDE_DIR} )
|
|
||||||
endif()
|
|
||||||
if(DEVEL)
|
|
||||||
add_definitions(-DDEVEL)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
string(TIMESTAMP ACE_BUILDSTAMP "%Y-%m-%dT%H:%M:%SZ")
|
|
||||||
# Get current version from addon
|
|
||||||
file(READ "../addons/main/script_version.hpp" script_version)
|
|
||||||
string(REGEX MATCH "#define MAJOR ([0-9]*)" "x" outputX ${script_version})
|
|
||||||
set(ACE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
|
||||||
string(REGEX MATCH "#define MINOR ([0-9]*)" "x" outputX ${script_version})
|
|
||||||
set(ACE_VERSION_MINOR ${CMAKE_MATCH_1})
|
|
||||||
string(REGEX MATCH "#define PATCHLVL ([0-9]*)" outputX ${script_version})
|
|
||||||
set(ACE_VERSION_REVISION ${CMAKE_MATCH_1})
|
|
||||||
EXECUTE_PROCESS(COMMAND git rev-parse --verify HEAD
|
|
||||||
OUTPUT_VARIABLE T_ACE_VERSION_BUILD
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
)
|
|
||||||
string(SUBSTRING ${T_ACE_VERSION_BUILD} 0 7 ACE_VERSION_BUILD )
|
|
||||||
|
|
||||||
message("Setting ACE Version: " ${ACE_VERSION_MAJOR}.${ACE_VERSION_MINOR}.${ACE_VERSION_REVISION}-${ACE_VERSION_BUILD})
|
|
||||||
|
|
||||||
configure_file(
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/common/ace_version.hpp.in"
|
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/common/ace_version.hpp"
|
|
||||||
@ONLY)
|
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
configure_file(
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/common/ace_version_win32.rc.in"
|
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/common/ace_version_win32.rc"
|
|
||||||
@ONLY)
|
|
||||||
set(GLOBAL_RC ${CMAKE_CURRENT_BINARY_DIR}/common/ace_version_win32.rc)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/common)
|
|
||||||
set(GLOBAL_SOURCES ${GLOBAL_RC})
|
|
||||||
|
|
||||||
|
|
||||||
# Add extensions to build here
|
|
||||||
add_subdirectory(common)
|
|
||||||
|
|
||||||
# Extensions
|
|
||||||
|
|
||||||
add_subdirectory(fcs)
|
|
||||||
add_subdirectory(break_line)
|
|
||||||
add_subdirectory(clipboard)
|
|
||||||
add_subdirectory(advanced_ballistics)
|
|
||||||
#add_subdirectory(medical) # After medical re-write this extension is no longer used
|
|
||||||
add_subdirectory(artillerytables)
|
|
||||||
|
|
||||||
# Test Extension for dynamically loading/unloading built extensions; does not build in release
|
|
||||||
if (DEVEL)
|
|
||||||
add_subdirectory(dynload)
|
|
||||||
add_subdirectory(tests)
|
|
||||||
if(USE_DIRECTX)
|
|
||||||
add_subdirectory(lib/directxtk)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
# GTest
|
|
||||||
option(ENABLE_GTEST "ENABLE_GTEST" ON)
|
|
||||||
if (ENABLE_GTEST)
|
|
||||||
include(FetchContent)
|
|
||||||
FetchContent_Declare(googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG main)
|
|
||||||
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
|
||||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
||||||
FetchContent_MakeAvailable(googletest)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message("Build Type: ${CMAKE_BUILD_TYPE}")
|
|
@ -1,711 +0,0 @@
|
|||||||
#include "shared.hpp"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <random>
|
|
||||||
#include <cmath>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "vector.hpp"
|
|
||||||
|
|
||||||
#define DELTA_T 0.005
|
|
||||||
#define GRAVITY 9.8066f
|
|
||||||
#define DEGREES(X) (X * 180 / M_PI)
|
|
||||||
#define ABSOLUTE_ZERO_IN_CELSIUS -273.15f
|
|
||||||
#define KELVIN(t) (t - ABSOLUTE_ZERO_IN_CELSIUS)
|
|
||||||
#define CELSIUS(t) (t + ABSOLUTE_ZERO_IN_CELSIUS)
|
|
||||||
#define EARTH_ANGULAR_SPEED 0.00007292f
|
|
||||||
#define UNIVERSAL_GAS_CONSTANT 8.314f
|
|
||||||
#define WATER_VAPOR_MOLAR_MASS 0.018016f
|
|
||||||
#define DRY_AIR_MOLAR_MASS 0.028964f
|
|
||||||
#define SPECIFIC_GAS_CONSTANT_DRY_AIR 287.058f
|
|
||||||
#define STD_AIR_DENSITY_ICAO 1.22498f
|
|
||||||
#define STD_AIR_DENSITY_ASM 1.20885f
|
|
||||||
#define BC_CONVERSION_FACTOR 0.00068418f
|
|
||||||
#define SPEED_OF_SOUND(t) (331.3 * std::sqrt(1 + t / 273.15f))
|
|
||||||
#define SPEED_OF_SOUND_AT_15C 340.275
|
|
||||||
|
|
||||||
struct Bullet {
|
|
||||||
double airFriction;
|
|
||||||
double caliber;
|
|
||||||
double bulletLength;
|
|
||||||
double bulletMass;
|
|
||||||
std::vector<double> ballisticCoefficients;
|
|
||||||
std::vector<double> velocityBoundaries;
|
|
||||||
char* atmosphereModel;
|
|
||||||
int dragModel;
|
|
||||||
std::vector<double> muzzleVelocities;
|
|
||||||
std::vector<double> barrelLengths;
|
|
||||||
double stabilityFactor;
|
|
||||||
double twistDirection;
|
|
||||||
double transonicStabilityCoef;
|
|
||||||
ace::vector3<double> bulletVelocityPreviousFrame;
|
|
||||||
ace::vector3<double> origin;
|
|
||||||
double latitude;
|
|
||||||
double temperature;
|
|
||||||
double altitude;
|
|
||||||
double humidity;
|
|
||||||
double overcast;
|
|
||||||
double startTime;
|
|
||||||
double lastFrame;
|
|
||||||
unsigned randSeed;
|
|
||||||
std::default_random_engine randGenerator;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Map {
|
|
||||||
std::vector<int> gridHeights;
|
|
||||||
std::vector<int> gridBuildingNums;
|
|
||||||
std::vector<int> gridSurfaceIsWater;
|
|
||||||
int mapSize;
|
|
||||||
int mapGrids;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Bullet> bulletDatabase;
|
|
||||||
std::unordered_map<std::string, Map> mapDatabase;
|
|
||||||
std::string worldName = "";
|
|
||||||
Map* map = &mapDatabase[""];
|
|
||||||
|
|
||||||
double calculateRoughnessLength(double posX, double posY) {
|
|
||||||
// Source: http://es.ucsc.edu/~jnoble/wind/extrap/index.html
|
|
||||||
double roughness_lengths[10] = {0.0002, 0.0005, 0.0024, 0.03, 0.055, 0.1, 0.2, 0.4, 0.8, 1.6};
|
|
||||||
|
|
||||||
int gridX = (int)floor(posX / 50);
|
|
||||||
int gridY = (int)floor(posY / 50);
|
|
||||||
int gridCell = gridX * map->mapGrids + gridY;
|
|
||||||
|
|
||||||
if (gridCell >= 0 && (std::size_t)gridCell < map->gridHeights.size() && (std::size_t)gridCell < map->gridBuildingNums.size()) {
|
|
||||||
int nearBuildings = map->gridBuildingNums[gridCell];
|
|
||||||
int surfaceIsWater = map->gridSurfaceIsWater[gridCell];
|
|
||||||
|
|
||||||
if (nearBuildings == 0 && surfaceIsWater == 1) {
|
|
||||||
return 0.0005;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nearBuildings >= 10) {
|
|
||||||
return 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
return roughness_lengths[2 + std::min(nearBuildings, 6)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0.0024;
|
|
||||||
}
|
|
||||||
|
|
||||||
double calculateAirDensity(double temperature, double pressure, double relativeHumidity) {
|
|
||||||
pressure = pressure * 100;
|
|
||||||
|
|
||||||
if (relativeHumidity > 0) {
|
|
||||||
// 610.78 gives pressure in Pa - https://en.wikipedia.org/wiki/Density_of_air
|
|
||||||
double _pSat = 610.78 * pow(10, ((7.5 * temperature) / (temperature + 237.3)));
|
|
||||||
double vaporPressure = relativeHumidity * _pSat;
|
|
||||||
double partialPressure = pressure - vaporPressure;
|
|
||||||
|
|
||||||
return (partialPressure * DRY_AIR_MOLAR_MASS + vaporPressure * WATER_VAPOR_MOLAR_MASS) / (UNIVERSAL_GAS_CONSTANT * KELVIN(temperature));
|
|
||||||
} else {
|
|
||||||
return pressure / (SPECIFIC_GAS_CONSTANT_DRY_AIR * KELVIN(temperature));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double calculateAtmosphericCorrection(double ballisticCoefficient, double temperature, double pressure, double relativeHumidity, const char *atmosphereModel) {
|
|
||||||
double airDensity = calculateAirDensity(temperature, pressure, relativeHumidity);
|
|
||||||
|
|
||||||
if (!strcmp(atmosphereModel, "ICAO")) {
|
|
||||||
return (STD_AIR_DENSITY_ICAO / airDensity) * ballisticCoefficient;
|
|
||||||
} else {
|
|
||||||
return (STD_AIR_DENSITY_ASM / airDensity) * ballisticCoefficient;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drag Functions from: http://www.jbmballistics.com/ballistics/downloads/text/
|
|
||||||
const std::vector<double> dragCoefficientsG1 = { 0.2629, 0.2558, 0.2487, 0.2413, 0.2344, 0.2278, 0.2214, 0.2155, 0.2104, 0.2061, 0.2032, 0.2020, 0.2034, 0.2165, 0.2230, 0.2313, 0.2417, 0.2546, 0.2706, 0.2901, 0.3136, 0.3415, 0.3734, 0.4084, 0.4448, 0.4805, 0.5136, 0.5427, 0.5677, 0.5883, 0.6053, 0.6191, 0.6393, 0.6518, 0.6589, 0.6621, 0.6625, 0.6607, 0.6573, 0.6528, 0.6474, 0.6413, 0.6347, 0.6280, 0.6210, 0.6141, 0.6072, 0.6003, 0.5934, 0.5867, 0.5804, 0.5743, 0.5685, 0.5630, 0.5577, 0.5527, 0.5481, 0.5438, 0.5397, 0.5325, 0.5264, 0.5211, 0.5168, 0.5133, 0.5105, 0.5084, 0.5067, 0.5054, 0.5040, 0.5030, 0.5022, 0.5016, 0.5010, 0.5006, 0.4998, 0.4995, 0.4992, 0.4990, 0.4988 };
|
|
||||||
const std::vector<double> machNumbersG1 = { 0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.70, 0.725, 0.75, 0.775, 0.80, 0.825, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0, 1.025, 1.05, 1.075, 1.10, 1.125, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25, 2.30, 2.35, 2.40, 2.45, 2.50, 2.60, 2.70, 2.80, 2.90, 3.00, 3.10, 3.20, 3.30, 3.40, 3.50, 3.60, 3.70, 3.80, 3.90, 4.00, 4.20, 4.40, 4.60, 4.80, 5.00 };
|
|
||||||
const std::vector<double> dragCoefficientsG2 = { 0.2303, 0.2298, 0.2287, 0.2271, 0.2251, 0.2227, 0.2196, 0.2156, 0.2107, 0.2048, 0.1980, 0.1905, 0.1828, 0.1758, 0.1702, 0.1669, 0.1664, 0.1667, 0.1682, 0.1711, 0.1761, 0.1831, 0.2004, 0.2589, 0.3492, 0.3983, 0.4075, 0.4103, 0.4114, 0.4106, 0.4089, 0.4068, 0.4046, 0.4021, 0.3966, 0.3904, 0.3835, 0.3759, 0.3678, 0.3594, 0.3512, 0.3432, 0.3356, 0.3282, 0.3213, 0.3149, 0.3089, 0.3033, 0.2982, 0.2933, 0.2889, 0.2846, 0.2806, 0.2768, 0.2731, 0.2696, 0.2663, 0.2632, 0.2602, 0.2572, 0.2543, 0.2515, 0.2487, 0.2460, 0.2433, 0.2408, 0.2382, 0.2357, 0.2333, 0.2309, 0.2262, 0.2217, 0.2173, 0.2132, 0.2091, 0.2052, 0.2014, 0.1978, 0.1944, 0.1912, 0.1851, 0.1794, 0.1741, 0.1693, 0.1648 };
|
|
||||||
const std::vector<double> machNumbersG2 = { 0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.775, 0.80, 0.825, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0, 1.025, 1.05, 1.075, 1.10, 1.125, 1.15, 1.175, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25, 2.30, 2.35, 2.40, 2.45, 2.50, 2.55, 2.60, 2.65, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.10, 3.20, 3.30, 3.40, 3.50, 3.60, 3.70, 3.80, 3.90, 4.00, 4.20, 4.40, 4.60, 4.80, 5.00 };
|
|
||||||
const std::vector<double> dragCoefficientsG5 = { 0.1710, 0.1719, 0.1727, 0.1732, 0.1734, 0.1730, 0.1718, 0.1696, 0.1668, 0.1637, 0.1603, 0.1566, 0.1529, 0.1497, 0.1473, 0.1463, 0.1489, 0.1583, 0.1672, 0.1815, 0.2051, 0.2413, 0.2884, 0.3379, 0.3785, 0.4032, 0.4147, 0.4201, 0.4278, 0.4338, 0.4373, 0.4392, 0.4403, 0.4406, 0.4401, 0.4386, 0.4362, 0.4328, 0.4286, 0.4237, 0.4182, 0.4121, 0.4057, 0.3991, 0.3926, 0.3861, 0.3800, 0.3741, 0.3684, 0.3630, 0.3578, 0.3529, 0.3481, 0.3435, 0.3391, 0.3349, 0.3269, 0.3194, 0.3125, 0.3060, 0.2999, 0.2942, 0.2889, 0.2838, 0.2790, 0.2745, 0.2703, 0.2662, 0.2624, 0.2588, 0.2553, 0.2488, 0.2429, 0.2376, 0.2326, 0.2280 };
|
|
||||||
const std::vector<double> machNumbersG5 = { 0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0, 1.025, 1.05, 1.075, 1.10, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25, 2.30, 2.35, 2.40, 2.45, 2.50, 2.60, 2.70, 2.80, 2.90, 3.00, 3.10, 3.20, 3.30, 3.40, 3.50, 3.60, 3.70, 3.80, 3.90, 4.00, 4.20, 4.40, 4.60, 4.80, 5.00 };
|
|
||||||
const std::vector<double> dragCoefficientsG6 = { 0.2617, 0.2553, 0.2491, 0.2432, 0.2376, 0.2324, 0.2278, 0.2238, 0.2205, 0.2177, 0.2155, 0.2138, 0.2126, 0.2121, 0.2122, 0.2132, 0.2154, 0.2194, 0.2229, 0.2297, 0.2449, 0.2732, 0.3141, 0.3597, 0.3994, 0.4261, 0.4402, 0.4465, 0.4490, 0.4497, 0.4494, 0.4482, 0.4464, 0.4441, 0.4390, 0.4336, 0.4279, 0.4221, 0.4162, 0.4102, 0.4042, 0.3981, 0.3919, 0.3855, 0.3788, 0.3721, 0.3652, 0.3583, 0.3515, 0.3447, 0.3381, 0.3314, 0.3249, 0.3185, 0.3122, 0.3060, 0.3000, 0.2941, 0.2883, 0.2772, 0.2668, 0.2574, 0.2487, 0.2407, 0.2333, 0.2265, 0.2202, 0.2144, 0.2089, 0.2039, 0.1991, 0.1947, 0.1905, 0.1866, 0.1794, 0.1730, 0.1673, 0.1621, 0.1574 };
|
|
||||||
const std::vector<double> machNumbersG6 = { 0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0, 1.025, 1.05, 1.075, 1.10, 1.125, 1.15, 1.175, 1.20, 1.225, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25, 2.30, 2.35, 2.40, 2.45, 2.50, 2.60, 2.70, 2.80, 2.90, 3.00, 3.10, 3.20, 3.30, 3.40, 3.50, 3.60, 3.70, 3.80, 3.90, 4.00, 4.20, 4.40, 4.60, 4.80, 5.00 };
|
|
||||||
const std::vector<double> dragCoefficientsG7 = { 0.1198, 0.1197, 0.1196, 0.1194, 0.1193, 0.1194, 0.1194, 0.1194, 0.1193, 0.1193, 0.1194, 0.1193, 0.1194, 0.1197, 0.1202, 0.1207, 0.1215, 0.1226, 0.1242, 0.1266, 0.1306, 0.1368, 0.1464, 0.1660, 0.2054, 0.2993, 0.3803, 0.4015, 0.4043, 0.4034, 0.4014, 0.3987, 0.3955, 0.3884, 0.3810, 0.3732, 0.3657, 0.3580, 0.3440, 0.3376, 0.3315, 0.3260, 0.3209, 0.3160, 0.3117, 0.3078, 0.3042, 0.3010, 0.2980, 0.2951, 0.2922, 0.2892, 0.2864, 0.2835, 0.2807, 0.2779, 0.2752, 0.2725, 0.2697, 0.2670, 0.2643, 0.2615, 0.2588, 0.2561, 0.2533, 0.2506, 0.2479, 0.2451, 0.2424, 0.2368, 0.2313, 0.2258, 0.2205, 0.2154, 0.2106, 0.2060, 0.2017, 0.1975, 0.1935, 0.1861, 0.1793, 0.1730, 0.1672, 0.1618 };
|
|
||||||
const std::vector<double> machNumbersG7 = { 0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.725, 0.75, 0.775, 0.80, 0.825, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0, 1.025, 1.05, 1.075, 1.10, 1.125, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25, 2.30, 2.35, 2.40, 2.45, 2.50, 2.55, 2.60, 2.65, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.10, 3.20, 3.30, 3.40, 3.50, 3.60, 3.70, 3.80, 3.90, 4.00, 4.20, 4.40, 4.60, 4.80, 5.00 };
|
|
||||||
const std::vector<double> dragCoefficientsG8 = { 0.2105, 0.2105, 0.2104, 0.2104, 0.2103, 0.2103, 0.2103, 0.2103, 0.2103, 0.2102, 0.2102, 0.2102, 0.2102, 0.2102, 0.2103, 0.2103, 0.2104, 0.2104, 0.2105, 0.2106, 0.2109, 0.2183, 0.2571, 0.3358, 0.4068, 0.4378, 0.4476, 0.4493, 0.4477, 0.4450, 0.4419, 0.4353, 0.4283, 0.4208, 0.4133, 0.4059, 0.3986, 0.3915, 0.3845, 0.3777, 0.3710, 0.3645, 0.3581, 0.3519, 0.3458, 0.3400, 0.3343, 0.3288, 0.3234, 0.3182, 0.3131, 0.3081, 0.3032, 0.2983, 0.2937, 0.2891, 0.2845, 0.2802, 0.2720, 0.2642, 0.2569, 0.2499, 0.2432, 0.2368, 0.2308, 0.2251, 0.2197, 0.2147, 0.2101, 0.2058, 0.2019, 0.1983, 0.1950, 0.1890, 0.1837, 0.1791, 0.1750, 0.1713 };
|
|
||||||
const std::vector<double> machNumbersG8 = { 0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.825, 0.85, 0.875, 0.90, 0.925, 0.95, 0.975, 1.0, 1.025, 1.05, 1.075, 1.10, 1.125, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25, 2.30, 2.35, 2.40, 2.45, 2.50, 2.60, 2.70, 2.80, 2.90, 3.00, 3.10, 3.20, 3.30, 3.40, 3.50, 3.60, 3.70, 3.80, 3.90, 4.00, 4.20, 4.40, 4.60, 4.80, 5.00 };
|
|
||||||
|
|
||||||
const std::vector<std::vector<double>> dragCoefficients = { {}, dragCoefficientsG1, dragCoefficientsG2, {}, {}, dragCoefficientsG5, dragCoefficientsG6, dragCoefficientsG7, dragCoefficientsG8, {} };
|
|
||||||
const std::vector<std::vector<double>> machNumbers = { {}, machNumbersG1, machNumbersG2, {},{}, machNumbersG5, machNumbersG6, machNumbersG7, machNumbersG8,{} };
|
|
||||||
|
|
||||||
double calculateRetard(int DragFunction, double DragCoefficient, double Velocity, double Mach) {
|
|
||||||
int idx = std::max(0, std::min(DragFunction, 9));
|
|
||||||
double m = Velocity / Mach;
|
|
||||||
|
|
||||||
for (int i = 0; i < (int)machNumbers[idx].size(); i++) {
|
|
||||||
if (machNumbers[idx][i] >= m) {
|
|
||||||
int previousIdx = std::max(0, i - 1);
|
|
||||||
double previousDragCoefficient = dragCoefficients[idx][previousIdx];
|
|
||||||
double previousMachNumber = machNumbers[idx][previousIdx];
|
|
||||||
double cd = previousDragCoefficient + (dragCoefficients[idx][i] - previousDragCoefficient) * (m - previousMachNumber) / (machNumbers[idx][i] - previousMachNumber);
|
|
||||||
return BC_CONVERSION_FACTOR * (cd / DragCoefficient) * std::pow(Velocity, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
float replicateVanillaZero(float zeroRange, float muzzleVelocity, float airFriction) {
|
|
||||||
const float maxDeltaT = 0.05f;
|
|
||||||
float time = 0.0f;
|
|
||||||
ace::vector3<float> curShotPos = { 0, 0, 0 };
|
|
||||||
ace::vector3<float> speed = { muzzleVelocity, 0, 0 };
|
|
||||||
|
|
||||||
while (time < 8.0f) {
|
|
||||||
float distLeft = zeroRange - curShotPos.x();
|
|
||||||
float traveled = speed.x() * maxDeltaT;
|
|
||||||
if (distLeft < traveled) {
|
|
||||||
float deltaT = distLeft / speed.x();
|
|
||||||
speed -= { 0, GRAVITY * deltaT, 0 };
|
|
||||||
curShotPos += speed * deltaT;
|
|
||||||
time += deltaT;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
float deltaT = maxDeltaT;
|
|
||||||
curShotPos += speed * deltaT;
|
|
||||||
time += deltaT;
|
|
||||||
speed += speed * (speed.magnitude() * airFriction * deltaT);
|
|
||||||
speed -= { 0, GRAVITY * deltaT, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -std::atan(curShotPos.y() / zeroRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
double calculateVanillaZero(double zeroRange, double muzzleVelocity, double airFriction, double boreHeight) {
|
|
||||||
double zeroAngle = 0.0f;
|
|
||||||
double deltaT = 1.0 / std::max(100.0, zeroRange);
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
double lx = 0.0f;
|
|
||||||
double ly = 0.0f;
|
|
||||||
|
|
||||||
double px = 0.0f;
|
|
||||||
double py = -boreHeight / 100.0f;
|
|
||||||
|
|
||||||
double gx = std::sin(zeroAngle) * -GRAVITY;
|
|
||||||
double gy = std::cos(zeroAngle) * -GRAVITY;
|
|
||||||
|
|
||||||
double vx = std::cos(zeroAngle) * muzzleVelocity;
|
|
||||||
double vy = std::sin(zeroAngle) * muzzleVelocity;
|
|
||||||
|
|
||||||
double tof = 0.0f;
|
|
||||||
double v = 0.0f;
|
|
||||||
|
|
||||||
while (tof < 8.0f && px < zeroRange) {
|
|
||||||
lx = px;
|
|
||||||
ly = py;
|
|
||||||
|
|
||||||
v = std::sqrt(vx*vx + vy*vy);
|
|
||||||
|
|
||||||
double ax = vx * v * airFriction;
|
|
||||||
double ay = vy * v * airFriction;
|
|
||||||
ax += gx;
|
|
||||||
ay += gy;
|
|
||||||
|
|
||||||
px += vx * deltaT * 0.5;
|
|
||||||
py += vy * deltaT * 0.5;
|
|
||||||
vx += ax * deltaT;
|
|
||||||
vy += ay * deltaT;
|
|
||||||
px += vx * deltaT * 0.5;
|
|
||||||
py += vy * deltaT * 0.5;
|
|
||||||
|
|
||||||
tof += deltaT;
|
|
||||||
}
|
|
||||||
|
|
||||||
double y = ly + (zeroRange - lx) * (py - ly) / (px - lx);
|
|
||||||
double offset = -std::atan(y / zeroRange);
|
|
||||||
zeroAngle += offset;
|
|
||||||
|
|
||||||
if (std::abs(offset) < 0.00001f) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return zeroAngle;
|
|
||||||
}
|
|
||||||
|
|
||||||
double calculateAdvancedZero(double zeroRange, double muzzleVelocity, double boreHeight, double temperature, double pressure, double humidity, double ballisticCoefficient, int dragModel, char* atmosphereModel) {
|
|
||||||
double zeroAngle = 0.0f;
|
|
||||||
double deltaT = 1.0 / std::max(100.0, zeroRange);
|
|
||||||
|
|
||||||
ballisticCoefficient = calculateAtmosphericCorrection(ballisticCoefficient, temperature, pressure, humidity, atmosphereModel);
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
double lx = 0.0f;
|
|
||||||
double ly = 0.0f;
|
|
||||||
|
|
||||||
double px = 0.0f;
|
|
||||||
double py = -boreHeight / 100.0f;
|
|
||||||
|
|
||||||
double gx = std::sin(zeroAngle) * -GRAVITY;
|
|
||||||
double gy = std::cos(zeroAngle) * -GRAVITY;
|
|
||||||
|
|
||||||
double vx = std::cos(zeroAngle) * muzzleVelocity;
|
|
||||||
double vy = std::sin(zeroAngle) * muzzleVelocity;
|
|
||||||
|
|
||||||
double tof = 0.0f;
|
|
||||||
double v = 0.0f;
|
|
||||||
|
|
||||||
while (tof < 8.0f && px < zeroRange) {
|
|
||||||
lx = px;
|
|
||||||
ly = py;
|
|
||||||
|
|
||||||
v = std::sqrt(vx*vx + vy*vy);
|
|
||||||
|
|
||||||
double retard = calculateRetard(dragModel, ballisticCoefficient, v, SPEED_OF_SOUND_AT_15C);
|
|
||||||
double ax = vx / v * -retard;
|
|
||||||
double ay = vy / v * -retard;
|
|
||||||
ax += gx;
|
|
||||||
ay += gy;
|
|
||||||
|
|
||||||
px += vx * deltaT * 0.5;
|
|
||||||
py += vy * deltaT * 0.5;
|
|
||||||
vx += ax * deltaT;
|
|
||||||
vy += ay * deltaT;
|
|
||||||
px += vx * deltaT * 0.5;
|
|
||||||
py += vy * deltaT * 0.5;
|
|
||||||
|
|
||||||
tof += deltaT;
|
|
||||||
}
|
|
||||||
|
|
||||||
double y = ly + (zeroRange - lx) * (py - ly) / (px - lx);
|
|
||||||
double offset = -std::atan(y / zeroRange);
|
|
||||||
zeroAngle += offset;
|
|
||||||
|
|
||||||
if (std::abs(offset) < 0.00001f) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return zeroAngle;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
EXPORT void __stdcall RVExtensionVersion(char *output, int outputSize);
|
|
||||||
EXPORT void __stdcall RVExtension(char *output, int outputSize, const char *function);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __stdcall RVExtensionVersion(char *output, int outputSize)
|
|
||||||
{
|
|
||||||
strncpy(output, ACE_FULL_VERSION_STR, outputSize - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __stdcall RVExtension(char *output, int outputSize, const char *function)
|
|
||||||
{
|
|
||||||
ZERO_OUTPUT();
|
|
||||||
std::stringstream outputStr;
|
|
||||||
if (!strcmp(function, "version")) {
|
|
||||||
strncpy(output, ACE_FULL_VERSION_STR, outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
}
|
|
||||||
|
|
||||||
char* input = _strdup(function);
|
|
||||||
char* token = NULL;
|
|
||||||
char* next_token = NULL;
|
|
||||||
char* mode = strtok_s(input, ":", &next_token);
|
|
||||||
|
|
||||||
if (!strcmp(mode, "retard")) {
|
|
||||||
double ballisticCoefficient = 1.0;
|
|
||||||
int dragModel = 1;
|
|
||||||
double velocity = 0.0;
|
|
||||||
double temperature = 15.0;
|
|
||||||
double retard = 0.0;
|
|
||||||
|
|
||||||
dragModel = strtol(strtok_s(NULL, ":", &next_token), NULL, 10);
|
|
||||||
ballisticCoefficient = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
velocity = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
temperature = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
|
|
||||||
retard = calculateRetard(dragModel, ballisticCoefficient, velocity, SPEED_OF_SOUND(temperature));
|
|
||||||
// int n = sprintf(output, "%f", retard);
|
|
||||||
|
|
||||||
outputStr << retard;
|
|
||||||
strncpy(output, outputStr.str().c_str(), outputSize - 1);
|
|
||||||
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
} else if (!strcmp(mode, "atmosphericCorrection")) {
|
|
||||||
double ballisticCoefficient = 1.0;
|
|
||||||
double temperature = 15.0;
|
|
||||||
double pressure = 1013.25;
|
|
||||||
double humidity = 0.0;
|
|
||||||
char* atmosphereModel;
|
|
||||||
|
|
||||||
ballisticCoefficient = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
temperature = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
pressure = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
humidity = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
atmosphereModel = strtok_s(NULL, ":", &next_token);
|
|
||||||
|
|
||||||
ballisticCoefficient = calculateAtmosphericCorrection(ballisticCoefficient, temperature, pressure, humidity, atmosphereModel);
|
|
||||||
//int n = sprintf(output, "%f", ballisticCoefficient);
|
|
||||||
outputStr << ballisticCoefficient;
|
|
||||||
strncpy(output, outputStr.str().c_str(), outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
} else if (!strcmp(mode, "new")) {
|
|
||||||
unsigned int index = 0;
|
|
||||||
unsigned int ammoCount = 0;
|
|
||||||
double airFriction = 0.0;
|
|
||||||
char* ballisticCoefficientArray;
|
|
||||||
char* ballisticCoefficient;
|
|
||||||
std::vector<double> ballisticCoefficients;
|
|
||||||
char* velocityBoundaryArray;
|
|
||||||
char* velocityBoundary;
|
|
||||||
std::vector<double> velocityBoundaries;
|
|
||||||
char* atmosphereModel;
|
|
||||||
int dragModel = 1;
|
|
||||||
double stabilityFactor = 1.5;
|
|
||||||
int twistDirection = 1;
|
|
||||||
double transonicStabilityCoef = 1;
|
|
||||||
char* bulletVelocityArray;
|
|
||||||
char* bulletVelocityEntry;
|
|
||||||
std::vector<double> bulletVelocity;
|
|
||||||
char* originArray;
|
|
||||||
char* originEntry;
|
|
||||||
std::vector<double> origin;
|
|
||||||
double latitude = 0.0;
|
|
||||||
double temperature = 0.0;
|
|
||||||
double altitude = 0.0;
|
|
||||||
double humidity = 0.0;
|
|
||||||
double overcast = 0.0;
|
|
||||||
double tickTime = 0.0;
|
|
||||||
|
|
||||||
index = strtol(strtok_s(NULL, ":", &next_token), NULL, 10);
|
|
||||||
ammoCount = strtol(strtok_s(NULL, ":", &next_token), NULL, 10);
|
|
||||||
airFriction = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
ballisticCoefficientArray = strtok_s(NULL, ":", &next_token);
|
|
||||||
ballisticCoefficientArray++;
|
|
||||||
ballisticCoefficientArray[strlen(ballisticCoefficientArray) - 1] = 0;
|
|
||||||
ballisticCoefficient = strtok_s(ballisticCoefficientArray, ",", &token);
|
|
||||||
while (ballisticCoefficient != NULL) {
|
|
||||||
ballisticCoefficients.push_back(strtod(ballisticCoefficient, NULL));
|
|
||||||
ballisticCoefficient = strtok_s(NULL, ",", &token);
|
|
||||||
}
|
|
||||||
velocityBoundaryArray = strtok_s(NULL, ":", &next_token);
|
|
||||||
velocityBoundaryArray++;
|
|
||||||
velocityBoundaryArray[strlen(velocityBoundaryArray) - 1] = 0;
|
|
||||||
velocityBoundary = strtok_s(velocityBoundaryArray, ",", &token);
|
|
||||||
while (velocityBoundary != NULL) {
|
|
||||||
velocityBoundaries.push_back(strtod(velocityBoundary, NULL));
|
|
||||||
velocityBoundary = strtok_s(NULL, ",", &token);
|
|
||||||
}
|
|
||||||
atmosphereModel = strtok_s(NULL, ":", &next_token);
|
|
||||||
dragModel = strtol(strtok_s(NULL, ":", &next_token), NULL, 10);
|
|
||||||
stabilityFactor = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
twistDirection = strtol(strtok_s(NULL, ":", &next_token), NULL, 10);
|
|
||||||
transonicStabilityCoef = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
originArray = strtok_s(NULL, ":", &next_token);
|
|
||||||
originArray++;
|
|
||||||
originArray[strlen(originArray) - 1] = 0;
|
|
||||||
originEntry = strtok_s(originArray, ",", &token);
|
|
||||||
while (originEntry != NULL) {
|
|
||||||
origin.push_back(strtod(originEntry, NULL));
|
|
||||||
originEntry = strtok_s(NULL, ",", &token);
|
|
||||||
}
|
|
||||||
bulletVelocityArray = strtok_s(NULL, ":", &next_token);
|
|
||||||
bulletVelocityArray++;
|
|
||||||
bulletVelocityArray[strlen(bulletVelocityArray) - 1] = 0;
|
|
||||||
bulletVelocityEntry = strtok_s(bulletVelocityArray, ",", &token);
|
|
||||||
while (bulletVelocityEntry != NULL) {
|
|
||||||
bulletVelocity.push_back(strtod(bulletVelocityEntry, NULL));
|
|
||||||
bulletVelocityEntry = strtok_s(NULL, ",", &token);
|
|
||||||
}
|
|
||||||
latitude = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
temperature = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
altitude = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
humidity = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
overcast = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
tickTime = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
|
|
||||||
while (index >= bulletDatabase.size()) {
|
|
||||||
Bullet bullet;
|
|
||||||
bulletDatabase.push_back(bullet);
|
|
||||||
}
|
|
||||||
|
|
||||||
bulletDatabase[index].airFriction = airFriction;
|
|
||||||
bulletDatabase[index].ballisticCoefficients = ballisticCoefficients;
|
|
||||||
bulletDatabase[index].velocityBoundaries = velocityBoundaries;
|
|
||||||
bulletDatabase[index].atmosphereModel = atmosphereModel;
|
|
||||||
bulletDatabase[index].dragModel = dragModel;
|
|
||||||
bulletDatabase[index].stabilityFactor = stabilityFactor;
|
|
||||||
bulletDatabase[index].twistDirection = twistDirection;
|
|
||||||
bulletDatabase[index].transonicStabilityCoef = transonicStabilityCoef;
|
|
||||||
bulletDatabase[index].bulletVelocityPreviousFrame = { bulletVelocity[0], bulletVelocity[1], bulletVelocity[2] };
|
|
||||||
bulletDatabase[index].origin = { origin[0], origin[1], origin[2] };
|
|
||||||
bulletDatabase[index].latitude = latitude / 180 * M_PI;
|
|
||||||
bulletDatabase[index].temperature = temperature;
|
|
||||||
bulletDatabase[index].altitude = altitude;
|
|
||||||
bulletDatabase[index].humidity = humidity;
|
|
||||||
bulletDatabase[index].overcast = overcast;
|
|
||||||
bulletDatabase[index].startTime = tickTime;
|
|
||||||
bulletDatabase[index].lastFrame = tickTime;
|
|
||||||
if (transonicStabilityCoef < 1) {
|
|
||||||
unsigned int k1 = (unsigned)round(tickTime / 2);
|
|
||||||
unsigned int k2 = ammoCount;
|
|
||||||
bulletDatabase[index].randSeed = (unsigned int)(0.5 * (k1 + k2) * (k1 + k2 + 1) + k2);
|
|
||||||
bulletDatabase[index].randGenerator.seed(bulletDatabase[index].randSeed);
|
|
||||||
}
|
|
||||||
|
|
||||||
strncpy(output, "", outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
} else if (!strcmp(mode, "simulate")) {
|
|
||||||
// simulate:0:[-0.109985,542.529,-3.98301]:[3751.57,5332.23,214.252]:[0.598153,2.38829,0]:28.6:0:0.481542:0:215.16
|
|
||||||
unsigned int index = 0;
|
|
||||||
char* velocityArray;
|
|
||||||
double velocity[3] = { 0.0, 0.0, 0.0 };
|
|
||||||
char* positionArray;
|
|
||||||
double position[3] = { 0.0, 0.0, 0.0 };
|
|
||||||
char* windArray;
|
|
||||||
double wind[3];
|
|
||||||
double heightAGL = 0.0;
|
|
||||||
double tickTime = 0.0;
|
|
||||||
|
|
||||||
index = strtol(strtok_s(NULL, ":", &next_token), NULL, 10);
|
|
||||||
velocityArray = strtok_s(NULL, ":", &next_token);
|
|
||||||
velocityArray++;
|
|
||||||
velocityArray[strlen(velocityArray) - 1] = 0;
|
|
||||||
velocity[0] = strtod(strtok_s(velocityArray, ",", &token), NULL);
|
|
||||||
velocity[1] = strtod(strtok_s(NULL, ",", &token), NULL);
|
|
||||||
velocity[2] = strtod(strtok_s(NULL, ",", &token), NULL);
|
|
||||||
positionArray = strtok_s(NULL, ":", &next_token);
|
|
||||||
positionArray++;
|
|
||||||
positionArray[strlen(positionArray) - 1] = 0;
|
|
||||||
position[0] = strtod(strtok_s(positionArray, ",", &token), NULL);
|
|
||||||
position[1] = strtod(strtok_s(NULL, ",", &token), NULL);
|
|
||||||
position[2] = strtod(strtok_s(NULL, ",", &token), NULL);
|
|
||||||
windArray = strtok_s(NULL, ":", &next_token);
|
|
||||||
windArray++;
|
|
||||||
windArray[strlen(windArray) - 1] = 0;
|
|
||||||
wind[0] = strtod(strtok_s(windArray, ",", &token), NULL);
|
|
||||||
wind[1] = strtod(strtok_s(NULL, ",", &token), NULL);
|
|
||||||
wind[2] = strtod(strtok_s(NULL, ",", &token), NULL);
|
|
||||||
heightAGL = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
tickTime = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
|
|
||||||
ace::vector3<double> bulletVelocityCurrentFrame = { velocity[0], velocity[1], velocity[2] };
|
|
||||||
ace::vector3<double> bulletPosition = { position[0], position[1], position[2] };
|
|
||||||
ace::vector3<double> windVelocity = { wind[0], wind[1], wind[2] };
|
|
||||||
ace::vector3<double> gravityAccel = { 0, 0, -GRAVITY };
|
|
||||||
double ballisticCoefficient = 1.0;
|
|
||||||
double dragRef = 0.0;
|
|
||||||
double drag = 0.0;
|
|
||||||
double TOF = tickTime - bulletDatabase[index].startTime;
|
|
||||||
double deltaT = tickTime - bulletDatabase[index].lastFrame;
|
|
||||||
ace::vector3<double> trueVelocity;
|
|
||||||
double temperature = bulletDatabase[index].temperature - 0.0065 * bulletPosition.z();
|
|
||||||
double pressure = (1013.25 - 10 * bulletDatabase[index].overcast) * pow(1 - (0.0065 * (bulletDatabase[index].altitude + bulletPosition.z())) / (KELVIN(temperature) + 0.0065 * bulletDatabase[index].altitude), 5.255754495);
|
|
||||||
ace::vector3<double> velocityOffset;
|
|
||||||
|
|
||||||
bulletDatabase[index].lastFrame = tickTime;
|
|
||||||
|
|
||||||
if (windVelocity.magnitude() > 0.1) {
|
|
||||||
double windAttenuation = 1.0;
|
|
||||||
ace::vector3<double> windSourceTerrain;
|
|
||||||
|
|
||||||
windSourceTerrain = bulletPosition - windVelocity.normalize() * 100;
|
|
||||||
|
|
||||||
int gridX = (int)floor(windSourceTerrain.x() / 50);
|
|
||||||
int gridY = (int)floor(windSourceTerrain.y() / 50);
|
|
||||||
int gridCell = gridX * map->mapGrids + gridY;
|
|
||||||
|
|
||||||
if (gridCell >= 0 && (std::size_t)gridCell < map->gridHeights.size() && (std::size_t)gridCell < map->gridBuildingNums.size()) {
|
|
||||||
double gridHeight = map->gridHeights[gridCell];
|
|
||||||
|
|
||||||
if (gridHeight > bulletPosition.z()) {
|
|
||||||
double angle = atan((gridHeight - bulletPosition.z()) / 100);
|
|
||||||
windAttenuation *= pow(abs(cos(angle)), 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (heightAGL > 0 && heightAGL < 20) {
|
|
||||||
ace::vector3<double> windSourceObstacles;
|
|
||||||
|
|
||||||
windSourceObstacles = bulletPosition - windVelocity.normalize() * 25;
|
|
||||||
|
|
||||||
double roughnessLength = calculateRoughnessLength(windSourceObstacles.x(), windSourceObstacles.y());
|
|
||||||
windAttenuation *= std::max(0.0, log(heightAGL / roughnessLength) / log(20 / roughnessLength));
|
|
||||||
}
|
|
||||||
|
|
||||||
windVelocity = windVelocity * windAttenuation;
|
|
||||||
}
|
|
||||||
|
|
||||||
ace::vector3<double> bulletVelocity = bulletDatabase[index].bulletVelocityPreviousFrame;
|
|
||||||
double time = 0.0;
|
|
||||||
|
|
||||||
while (time < deltaT) {
|
|
||||||
double dt = std::min(deltaT - time, DELTA_T);
|
|
||||||
|
|
||||||
dragRef = -bulletDatabase[index].airFriction * bulletVelocity.magnitude_squared();
|
|
||||||
|
|
||||||
ace::vector3<double> accelRef = bulletVelocity.normalize() * dragRef;
|
|
||||||
|
|
||||||
velocityOffset += accelRef * dt;
|
|
||||||
bulletVelocity -= accelRef * dt;
|
|
||||||
bulletVelocity += gravityAccel * dt;
|
|
||||||
|
|
||||||
time += dt;
|
|
||||||
}
|
|
||||||
|
|
||||||
bulletVelocity = bulletDatabase[index].bulletVelocityPreviousFrame;
|
|
||||||
time = 0.0;
|
|
||||||
TOF -= deltaT;
|
|
||||||
|
|
||||||
while (time < deltaT) {
|
|
||||||
double dt = std::min(deltaT - time, DELTA_T * 2);
|
|
||||||
|
|
||||||
trueVelocity = bulletVelocity - windVelocity;
|
|
||||||
|
|
||||||
if (bulletDatabase[index].transonicStabilityCoef < 1.0f && trueVelocity.magnitude() < 1.2 * SPEED_OF_SOUND(temperature) && trueVelocity.magnitude() > SPEED_OF_SOUND(temperature)) {
|
|
||||||
std::uniform_real_distribution<double> distribution(-10.0, 10.0);
|
|
||||||
ace::vector3<double> offset(distribution(bulletDatabase[index].randGenerator), distribution(bulletDatabase[index].randGenerator), distribution(bulletDatabase[index].randGenerator));
|
|
||||||
double coef = 1.0f - bulletDatabase[index].transonicStabilityCoef;
|
|
||||||
|
|
||||||
double trueSpeed = trueVelocity.magnitude();
|
|
||||||
trueVelocity += offset * coef;
|
|
||||||
trueVelocity = trueVelocity.normalize() * trueSpeed;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (bulletDatabase[index].ballisticCoefficients.size() == bulletDatabase[index].velocityBoundaries.size() + 1) {
|
|
||||||
ballisticCoefficient = bulletDatabase[index].ballisticCoefficients[0];
|
|
||||||
for (int i = (int)bulletDatabase[index].velocityBoundaries.size() - 1; i >= 0; i = i - 1) {
|
|
||||||
if (trueVelocity.magnitude() < bulletDatabase[index].velocityBoundaries[i]) {
|
|
||||||
ballisticCoefficient = bulletDatabase[index].ballisticCoefficients[i + 1];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ballisticCoefficient = calculateAtmosphericCorrection(ballisticCoefficient, temperature, pressure, bulletDatabase[index].humidity, bulletDatabase[index].atmosphereModel);
|
|
||||||
|
|
||||||
drag = calculateRetard(bulletDatabase[index].dragModel, ballisticCoefficient, trueVelocity.magnitude(), SPEED_OF_SOUND(temperature));
|
|
||||||
} else {
|
|
||||||
double airDensity = calculateAirDensity(temperature, pressure, bulletDatabase[index].humidity);
|
|
||||||
double airFriction = bulletDatabase[index].airFriction * airDensity / STD_AIR_DENSITY_ICAO;
|
|
||||||
|
|
||||||
drag = -airFriction * trueVelocity.magnitude_squared();
|
|
||||||
}
|
|
||||||
|
|
||||||
ace::vector3<double> accel = trueVelocity.normalize() * drag;
|
|
||||||
|
|
||||||
velocityOffset -= accel * dt;
|
|
||||||
bulletVelocity -= accel * dt;
|
|
||||||
|
|
||||||
if (TOF > 0) {
|
|
||||||
double bulletDir = atan2(bulletVelocity.x(), bulletVelocity.y());
|
|
||||||
double driftAccel = bulletDatabase[index].twistDirection * (0.0482251 * (bulletDatabase[index].stabilityFactor + 1.2)) / pow(TOF, 0.17);
|
|
||||||
double driftVelocity = 0.0581025 *(bulletDatabase[index].stabilityFactor + 1.2) * pow(TOF, 0.83);
|
|
||||||
double dragCorrection = (driftVelocity / trueVelocity.magnitude()) * drag;
|
|
||||||
double magnitude = (driftAccel + dragCorrection) * dt;
|
|
||||||
ace::vector3<double> offset(sin(bulletDir + M_PI / 2) * magnitude, cos(bulletDir + M_PI / 2) * magnitude, 0);
|
|
||||||
velocityOffset += offset;
|
|
||||||
bulletVelocity += offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
double lat = bulletDatabase[index].latitude;
|
|
||||||
accel.x(2 * EARTH_ANGULAR_SPEED * +(bulletVelocity.y() * sin(lat) - bulletVelocity.z() * cos(lat)));
|
|
||||||
accel.y(2 * EARTH_ANGULAR_SPEED * -(bulletVelocity.x() * sin(lat)));
|
|
||||||
accel.z(2 * EARTH_ANGULAR_SPEED * +(bulletVelocity.x() * cos(lat)));
|
|
||||||
|
|
||||||
velocityOffset += accel * dt;
|
|
||||||
bulletVelocity += accel * dt + gravityAccel * dt;
|
|
||||||
|
|
||||||
TOF += dt;
|
|
||||||
time += dt;
|
|
||||||
}
|
|
||||||
|
|
||||||
bulletDatabase[index].bulletVelocityPreviousFrame = bulletVelocityCurrentFrame + velocityOffset;
|
|
||||||
|
|
||||||
outputStr << "[" << velocityOffset.x() << "," << velocityOffset.y() << "," << velocityOffset.z() << "]";
|
|
||||||
strncpy(output, outputStr.str().c_str(), outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
} else if (!strcmp(mode, "set")) {
|
|
||||||
int height = 0;
|
|
||||||
int numObjects = 0;
|
|
||||||
int surfaceIsWater = 0;
|
|
||||||
|
|
||||||
height = strtol(strtok_s(NULL, ":", &next_token), NULL, 10);
|
|
||||||
numObjects = strtol(strtok_s(NULL, ":", &next_token), NULL, 10);
|
|
||||||
surfaceIsWater = strtol(strtok_s(NULL, ":", &next_token), NULL, 10);
|
|
||||||
|
|
||||||
map->gridHeights.push_back(height);
|
|
||||||
map->gridBuildingNums.push_back(numObjects);
|
|
||||||
map->gridSurfaceIsWater.push_back(surfaceIsWater);
|
|
||||||
|
|
||||||
strncpy(output, outputStr.str().c_str(), outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
} else if (!strcmp(mode, "init")) {
|
|
||||||
int mapSize = 0;
|
|
||||||
int mapGrids = 0;
|
|
||||||
unsigned int gridCells = 0;
|
|
||||||
|
|
||||||
worldName = strtok_s(NULL, ":", &next_token);
|
|
||||||
mapSize = strtol(strtok_s(NULL, ":", &next_token), NULL, 10);
|
|
||||||
|
|
||||||
mapGrids = (int)ceil((double)mapSize / 50.0) + 1;
|
|
||||||
gridCells = mapGrids * mapGrids;
|
|
||||||
|
|
||||||
map = &mapDatabase[worldName];
|
|
||||||
if (map->gridHeights.size() == gridCells) {
|
|
||||||
outputStr << "Terrain already initialized";
|
|
||||||
strncpy(output, outputStr.str().c_str(), outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
}
|
|
||||||
|
|
||||||
map->mapSize = mapSize;
|
|
||||||
map->mapGrids = mapGrids;
|
|
||||||
map->gridHeights.clear();
|
|
||||||
map->gridBuildingNums.clear();
|
|
||||||
map->gridSurfaceIsWater.clear();
|
|
||||||
map->gridHeights.reserve(gridCells);
|
|
||||||
map->gridBuildingNums.reserve(gridCells);
|
|
||||||
map->gridSurfaceIsWater.reserve(gridCells);
|
|
||||||
|
|
||||||
strncpy(output, outputStr.str().c_str(), outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
} else if (!strcmp(mode, "replicateVanillaZero")) {
|
|
||||||
float zeroRange = strtof(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
float initSpeed = strtof(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
float airFriction = strtof(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
|
|
||||||
float zeroAngle = replicateVanillaZero(zeroRange, initSpeed, airFriction);
|
|
||||||
|
|
||||||
outputStr << DEGREES(zeroAngle);
|
|
||||||
strncpy(output, outputStr.str().c_str(), outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
} else if (!strcmp(mode, "calcZero")) {
|
|
||||||
double zeroRange = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
double initSpeed = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
double airFriction = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
double boreHeight = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
|
|
||||||
double zeroAngle = calculateVanillaZero(zeroRange, initSpeed, airFriction, boreHeight);
|
|
||||||
|
|
||||||
outputStr << DEGREES(zeroAngle);
|
|
||||||
strncpy(output, outputStr.str().c_str(), outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
} else if (!strcmp(mode, "calcZeroAB")) {
|
|
||||||
double zeroRange = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
double muzzleVelocity = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
double boreHeight = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
double temperature = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
double pressure = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
double humidity = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
double ballisticCoefficient = strtod(strtok_s(NULL, ":", &next_token), NULL);
|
|
||||||
int dragModel = strtol(strtok_s(NULL, ":", &next_token), NULL, 10);
|
|
||||||
char* atmosphereModel = strtok_s(NULL, ":", &next_token);
|
|
||||||
|
|
||||||
double zeroAngle = calculateAdvancedZero(zeroRange, muzzleVelocity, boreHeight, temperature, pressure, humidity, ballisticCoefficient, dragModel, atmosphereModel);
|
|
||||||
|
|
||||||
outputStr << DEGREES(zeroAngle);
|
|
||||||
strncpy(output, outputStr.str().c_str(), outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
}
|
|
||||||
strncpy(output, outputStr.str().c_str(), outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
set(ACE_EXTENSION_NAME "ace_advanced_ballistics")
|
|
||||||
|
|
||||||
file(GLOB SOURCES *.h *.hpp *.c *.cpp)
|
|
||||||
add_library( ${ACE_EXTENSION_NAME} SHARED ${SOURCES} ${GLOBAL_SOURCES})
|
|
||||||
target_link_libraries(${ACE_EXTENSION_NAME} ace_common)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES PREFIX "")
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES FOLDER Extensions)
|
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_END_STATIC 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Copy and rename DLL to root
|
|
||||||
if(USE_64BIT_BUILD)
|
|
||||||
set(FINAL_DLL_NAME ${ACE_EXTENSION_NAME}_x64.dll)
|
|
||||||
else()
|
|
||||||
set(FINAL_DLL_NAME ${ACE_EXTENSION_NAME}.dll)
|
|
||||||
endif()
|
|
||||||
add_custom_command(TARGET ${ACE_EXTENSION_NAME} POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${ACE_EXTENSION_NAME}> ${PROJECT_SOURCE_DIR}/../${FINAL_DLL_NAME}
|
|
||||||
)
|
|
@ -1,33 +0,0 @@
|
|||||||
set(ACE_EXTENSION_NAME "ace_artillerytables")
|
|
||||||
|
|
||||||
file(GLOB SOURCES *.h *.hpp *.c *.cpp)
|
|
||||||
add_library( ${ACE_EXTENSION_NAME} SHARED ${SOURCES} ${GLOBAL_SOURCES})
|
|
||||||
target_link_libraries(${ACE_EXTENSION_NAME} ace_common)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES PREFIX "")
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES FOLDER Extensions)
|
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_END_STATIC 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Copy and rename DLL to root
|
|
||||||
if(USE_64BIT_BUILD)
|
|
||||||
set(FINAL_DLL_NAME ${ACE_EXTENSION_NAME}_x64.dll)
|
|
||||||
else()
|
|
||||||
set(FINAL_DLL_NAME ${ACE_EXTENSION_NAME}.dll)
|
|
||||||
endif()
|
|
||||||
add_custom_command(TARGET ${ACE_EXTENSION_NAME} POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${ACE_EXTENSION_NAME}> ${PROJECT_SOURCE_DIR}/../${FINAL_DLL_NAME}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (TARGET gtest_main) # Add Tests
|
|
||||||
enable_testing()
|
|
||||||
set(ACE_TEST_NAME ${ACE_EXTENSION_NAME}_test)
|
|
||||||
add_executable(${ACE_TEST_NAME} tests/tester.cpp ${SOURCES})
|
|
||||||
target_link_libraries(${ACE_TEST_NAME} ace_common)
|
|
||||||
target_link_libraries(${ACE_TEST_NAME} ${ACE_EXTENSION_NAME})
|
|
||||||
target_link_libraries(${ACE_TEST_NAME} gtest_main)
|
|
||||||
add_test(${ACE_TEST_NAME} ${ACE_TEST_NAME})
|
|
||||||
set_target_properties(${ACE_TEST_NAME} PROPERTIES FOLDER Tests)
|
|
||||||
endif()
|
|
@ -1,283 +0,0 @@
|
|||||||
/*
|
|
||||||
* artillerytables.hpp
|
|
||||||
* Author: PabstMirror
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <iomanip>
|
|
||||||
#include <future>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "artillerytables.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
static const double timeStep = 1.0 / 60;
|
|
||||||
static const double rangeSearchErrorMax = 0.001; // ratio * distance
|
|
||||||
static const double rangeSearchAngleConvergance = 0.000025;
|
|
||||||
static const double gravityABS = 9.8066;
|
|
||||||
static const ace::vector3<double> gravityAccl(0, 0, -1 * gravityABS);
|
|
||||||
|
|
||||||
// Globals:
|
|
||||||
std::vector<std::future<std::string>> fWorkers;
|
|
||||||
unsigned int getLineIndex = 0;
|
|
||||||
|
|
||||||
std::tuple<double, double, double> simulateShot(const double _fireAngleRad, const double _muzzleVelocity, const double _heightOfTarget, const double _crossWind, const double _tailWind, const double _temperature, const double _airDensity, double _airFriction) {
|
|
||||||
// returns: dist traveled to the side (crosswind), dist traveled foward (headwind), time of flight
|
|
||||||
// note: if shot never reaches height of target, then results are undefined (use negative)
|
|
||||||
const double kCoefficient = -1.0 * _airDensity * _airFriction;
|
|
||||||
const double powderEffects = (_airFriction) ? ((_temperature + 273.13) / 288.13 - 1) / 40 + 1 : 1.0;
|
|
||||||
|
|
||||||
double currentTime = 0;
|
|
||||||
ace::vector3<double> currentPosition(0, 0, 0);
|
|
||||||
ace::vector3<double> lastPosition(currentPosition);
|
|
||||||
ace::vector3<double> currentVelocity(0, powderEffects * _muzzleVelocity * cos(_fireAngleRad), powderEffects * _muzzleVelocity * sin(_fireAngleRad));
|
|
||||||
const ace::vector3<double> wind(_crossWind, _tailWind, 0);
|
|
||||||
|
|
||||||
while ((currentVelocity.z() > 0) || (currentPosition.z() >= _heightOfTarget)) {
|
|
||||||
lastPosition = currentPosition;
|
|
||||||
ace::vector3<double> apparentWind = wind - currentVelocity;
|
|
||||||
ace::vector3<double> changeInVelocity = gravityAccl + apparentWind * (kCoefficient * apparentWind.magnitude());
|
|
||||||
currentVelocity += changeInVelocity * timeStep;
|
|
||||||
currentPosition += currentVelocity * timeStep;
|
|
||||||
currentTime += timeStep;
|
|
||||||
}
|
|
||||||
|
|
||||||
const double lastCurrentRatio((_heightOfTarget - currentPosition.z()) / (lastPosition.z() - currentPosition.z()));
|
|
||||||
ace::vector3<double> finalPos = lastPosition.lerp(currentPosition, lastCurrentRatio);
|
|
||||||
|
|
||||||
return { finalPos.x(), finalPos.y(), currentTime };
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<double, double> findMaxAngle(const double _muzzleVelocity, const double _airFriction) {
|
|
||||||
// retrns: angle that goes the furthest, max distance traveled
|
|
||||||
if (_airFriction == 0) {
|
|
||||||
return { M_PI_4, _muzzleVelocity * _muzzleVelocity / gravityABS };
|
|
||||||
}
|
|
||||||
// With air resitsnce, max distance angle won't be 45 degrees
|
|
||||||
double bestAngle = M_PI_4;
|
|
||||||
double bestDistance = -1;
|
|
||||||
double testResultDist;
|
|
||||||
for (double testAngle = M_PI_4; testAngle > 0; testAngle -= (M_PI_4 / 100.0)) {
|
|
||||||
std::tie(std::ignore, testResultDist, std::ignore) = simulateShot(testAngle, _muzzleVelocity, 0, 0, 0, 15, 1, _airFriction);
|
|
||||||
if (testResultDist > bestDistance) {
|
|
||||||
bestAngle = testAngle;
|
|
||||||
bestDistance = testResultDist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { bestAngle, bestDistance };
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<double, double, double> simulateFindSolution(const double _rangeToHit, const double _heightToHit, const double _muzzleVelocity, const double _airFriction, const double _minElev, const double _maxElev, const bool _highArc) {
|
|
||||||
// returns: actual distance traveled, elevation, time of flight
|
|
||||||
double searchMin = _minElev;
|
|
||||||
double searchMax = _maxElev;
|
|
||||||
|
|
||||||
if (!_airFriction) {
|
|
||||||
// can do trivial ballistics physics to get angle, could compute tof as well, but run through sim once to ensure consistancy (uses positive value of g)
|
|
||||||
double radicand = pow(_muzzleVelocity, 4) - gravityABS * (gravityABS * pow(_rangeToHit, 2) + 2 * _heightToHit * pow(_muzzleVelocity, 2));
|
|
||||||
if ((!_rangeToHit) || (radicand < 0)) { // radican't
|
|
||||||
return { -1, -1, -1 };
|
|
||||||
}
|
|
||||||
radicand = sqrt(radicand);
|
|
||||||
double angleRoot = atan((pow(_muzzleVelocity, 2) + radicand) / (gravityABS * _rangeToHit));
|
|
||||||
if ((angleRoot > _maxElev) || (angleRoot < _minElev)) {
|
|
||||||
angleRoot = atan((pow(_muzzleVelocity, 2) - radicand) / (gravityABS * _rangeToHit));
|
|
||||||
}
|
|
||||||
if ((angleRoot > _maxElev) || (angleRoot < _minElev)) {
|
|
||||||
return { -1, -1, -1 };
|
|
||||||
};
|
|
||||||
const double tof = _rangeToHit / (_muzzleVelocity * cos(angleRoot));
|
|
||||||
return { _rangeToHit, angleRoot, tof };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int numberOfAttempts = 0;
|
|
||||||
double resultDistance = -1;
|
|
||||||
double resultTime = -1;
|
|
||||||
double currentError = 9999;
|
|
||||||
double currentElevation = -1;
|
|
||||||
do {
|
|
||||||
if (numberOfAttempts++ > 50) break; // for safetey, min/max should converge long before
|
|
||||||
currentElevation = (searchMin + searchMax) / 2.0;
|
|
||||||
std::tie(std::ignore, resultDistance, resultTime) = simulateShot(currentElevation, _muzzleVelocity, _heightToHit, 0, 0, 15, 1, _airFriction);
|
|
||||||
currentError = _rangeToHit - resultDistance;
|
|
||||||
// printf("elev %f [%f, %f]range%f\n goes %f [%f]\n", currentElevation, searchMin, searchMax, (searchMax - searchMin), resultDistance, currentError);
|
|
||||||
if ((currentError > 0) ^ (!_highArc)) {
|
|
||||||
searchMax = currentElevation;
|
|
||||||
} else {
|
|
||||||
searchMin = currentElevation;
|
|
||||||
}
|
|
||||||
} while ((searchMax - searchMin) > rangeSearchAngleConvergance);
|
|
||||||
|
|
||||||
// printf("[%f, %f] Actuall [%f] err [%f of %f]\n", _rangeToHit, _heightToHit, resultDistance, currentError, (_rangeToHit * rangeSearchErrorMax * (_highArc ? 1.0 : 2.0)));
|
|
||||||
// On some low angle shots, it will really struggle to converge because of precision issues
|
|
||||||
if ((abs(currentError) > (_rangeToHit * rangeSearchErrorMax * (_highArc ? 1.0 : 2.0)))) {
|
|
||||||
return { -1, -1, -1 };
|
|
||||||
}
|
|
||||||
return { resultDistance, currentElevation, resultTime };
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeNumber(std::stringstream & ss, double _num, const int _widthInt, const int _widthDec) {
|
|
||||||
if ((_num < 0) && (_num > -0.05)) { // hard coded fix -0.0 rounding errors
|
|
||||||
_num = 0;
|
|
||||||
}
|
|
||||||
if (_widthInt > 1) {
|
|
||||||
ss << std::setfill('0');
|
|
||||||
}
|
|
||||||
ss << std::fixed << std::setw(_widthInt) << std::setprecision(_widthDec) << _num;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string simulateCalcRangeTableLine(const double _rangeToHit, const double _muzzleVelocity, const double _airFriction, const double _minElev, const double _maxElev, const bool _highArc) {
|
|
||||||
double actualDistance, lineElevation, lineTimeOfFlight;
|
|
||||||
std::tie(actualDistance, lineElevation, lineTimeOfFlight) = simulateFindSolution(_rangeToHit, 0, _muzzleVelocity, _airFriction, _minElev, _maxElev, _highArc);
|
|
||||||
if (lineTimeOfFlight < 0) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
double actualDistanceHeight, lineHeightElevation, lineHeightTimeOfFlight;
|
|
||||||
std::tie(actualDistanceHeight, lineHeightElevation, lineHeightTimeOfFlight) = simulateFindSolution(_rangeToHit, -100, _muzzleVelocity, _airFriction, _minElev, _maxElev, _highArc);
|
|
||||||
|
|
||||||
|
|
||||||
std::stringstream returnSS;
|
|
||||||
|
|
||||||
returnSS << "[\"";
|
|
||||||
writeNumber(returnSS, _rangeToHit, 0, 0);
|
|
||||||
returnSS << "\",\"";
|
|
||||||
writeNumber(returnSS, lineElevation * 3200.0 / M_PI, 0, 0);
|
|
||||||
returnSS << "\",\"";
|
|
||||||
|
|
||||||
if (lineHeightElevation > 0) {
|
|
||||||
const double drElevAdjust = lineHeightElevation - lineElevation;
|
|
||||||
const double drTofAdjust = lineHeightTimeOfFlight - lineTimeOfFlight;
|
|
||||||
writeNumber(returnSS, drElevAdjust * 3200.0 / M_PI, 0, 0);
|
|
||||||
returnSS << "\",\"";
|
|
||||||
writeNumber(returnSS, drTofAdjust, 0, 1);
|
|
||||||
} else {
|
|
||||||
// low angle shots won't be able to adjust down further
|
|
||||||
returnSS << "-\",\"-";
|
|
||||||
}
|
|
||||||
returnSS << "\",\"";
|
|
||||||
writeNumber(returnSS, lineTimeOfFlight, 0, ((lineTimeOfFlight < 99.945) ? 1 : 0)); // round TOF when high
|
|
||||||
returnSS << "\",\"";
|
|
||||||
|
|
||||||
if (_airFriction) {
|
|
||||||
// Calc corrections:
|
|
||||||
double xOffset, yOffset;
|
|
||||||
// Crosswind
|
|
||||||
std::tie(xOffset, std::ignore, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 10, 0, 15, 1, _airFriction);
|
|
||||||
const double crosswindOffsetRad = atan2(xOffset, actualDistance) / 10;
|
|
||||||
// Headwind
|
|
||||||
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, -10, 15, 1, _airFriction);
|
|
||||||
const double headwindOffset = (actualDistance - yOffset) / 10;
|
|
||||||
// Tailwind
|
|
||||||
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 10, 15, 1, _airFriction);
|
|
||||||
const double tailwindOffset = (actualDistance - yOffset) / 10;
|
|
||||||
// Air Temp Dec
|
|
||||||
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 0, 5, 1, _airFriction);
|
|
||||||
const double tempDecOffset = (actualDistance - yOffset) / 10;
|
|
||||||
// Air Temp Inc
|
|
||||||
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 0, 25, 1, _airFriction);
|
|
||||||
const double tempIncOffset = (actualDistance - yOffset) / 10;
|
|
||||||
// Air Density Dec
|
|
||||||
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 0, 15, 0.9, _airFriction);
|
|
||||||
const double airDensityDecOffset = (actualDistance - yOffset) / 10;
|
|
||||||
// Air Density Inc
|
|
||||||
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 0, 15, 1.1, _airFriction);
|
|
||||||
const double airDensityIncOffset = (actualDistance - yOffset) / 10;
|
|
||||||
|
|
||||||
writeNumber(returnSS, crosswindOffsetRad * 3200.0 / M_PI, 1, 1);
|
|
||||||
returnSS << "\",\"";
|
|
||||||
writeNumber(returnSS, headwindOffset, 1, (abs(headwindOffset) > 9.949) ? 0 : 1);
|
|
||||||
returnSS << "\",\"";
|
|
||||||
writeNumber(returnSS, tailwindOffset, 1, (abs(tailwindOffset) > 9.949) ? 0 : 1);
|
|
||||||
returnSS << "\",\"";
|
|
||||||
writeNumber(returnSS, tempDecOffset, 1, (abs(tempDecOffset) > 9.949) ? 0 : 1);
|
|
||||||
returnSS << "\",\"";
|
|
||||||
writeNumber(returnSS, tempIncOffset, 1, (abs(tempIncOffset) > 9.949) ? 0 : 1);
|
|
||||||
returnSS << "\",\"";
|
|
||||||
writeNumber(returnSS, airDensityDecOffset, 1, (abs(airDensityDecOffset) > 9.949) ? 0 : 1);
|
|
||||||
returnSS << "\",\"";
|
|
||||||
writeNumber(returnSS, airDensityIncOffset, 1, (abs(airDensityIncOffset) > 9.949) ? 0 : 1);
|
|
||||||
returnSS << "\"]";
|
|
||||||
} else {
|
|
||||||
returnSS << "-\",\"-\",\"-\",\"-\",\"-\",\"-\",\"-\"]"; // 7 dashes
|
|
||||||
}
|
|
||||||
return (returnSS.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void __stdcall RVExtensionVersion(char* output, int outputSize) {
|
|
||||||
strncpy(output, ACE_FULL_VERSION_STR, outputSize - 1);
|
|
||||||
}
|
|
||||||
void __stdcall RVExtension(char* output, int outputSize, const char* function) {
|
|
||||||
if (!strcmp(function, "version")) {
|
|
||||||
RVExtensionVersion(output, outputSize);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
strncpy(output, "error - use args version of callExtension", outputSize - 1);
|
|
||||||
}
|
|
||||||
int __stdcall RVExtensionArgs(char* output, int outputSize, const char* function, const char** args, int argsCnt) {
|
|
||||||
if (!strcmp(function, "version")) {
|
|
||||||
RVExtensionVersion(output, outputSize);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(function, "start")) {
|
|
||||||
if (argsCnt != 5) { return -2; } // Error: not enough args
|
|
||||||
const double muzzleVelocity = strtod(args[0], NULL);
|
|
||||||
const double airFriction = strtod(args[1], NULL);
|
|
||||||
double minElev = (M_PI / 180.0) * strtod(args[2], NULL);
|
|
||||||
double maxElev = (M_PI / 180.0) * strtod(args[3], NULL);
|
|
||||||
const bool highArc = !strcmp(args[4], "true");
|
|
||||||
|
|
||||||
// Reset workers:
|
|
||||||
fWorkers.clear();
|
|
||||||
getLineIndex = 0;
|
|
||||||
|
|
||||||
double bestAngle, bestDistance;
|
|
||||||
std::tie(bestAngle, bestDistance) = findMaxAngle(muzzleVelocity, airFriction);
|
|
||||||
|
|
||||||
minElev = std::max(minElev, 2 * (M_PI / 180.0)); // cap min to 2 degrees (negative elev might get messy)
|
|
||||||
maxElev = std::min(maxElev, 88 * (M_PI / 180.0)); // cap max to 88 degrees (mk6)
|
|
||||||
if (highArc) {
|
|
||||||
minElev = std::max(minElev, bestAngle);
|
|
||||||
} else {
|
|
||||||
maxElev = std::min(maxElev, bestAngle);
|
|
||||||
}
|
|
||||||
const double loopStart = (bestDistance < 4000) ? 50 : 100;
|
|
||||||
const double loopInc = (bestDistance < 5000) ? 50 : 100; // simplify when range gets high
|
|
||||||
const double loopMaxRange = std::min(bestDistance, 30000.0); // with no air resistance, max range could go higher than 60km
|
|
||||||
|
|
||||||
if (maxElev > minElev) { // don't bother if we can't hit anything (e.g. mortar in low mode)
|
|
||||||
for (double range = loopStart; range < loopMaxRange; range += loopInc) {
|
|
||||||
fWorkers.emplace_back(std::async(&simulateCalcRangeTableLine, range, muzzleVelocity, airFriction, minElev, maxElev, highArc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream outputStr; // debug max distance and thead count
|
|
||||||
outputStr << "[" << bestDistance << "," << fWorkers.size() << "]";
|
|
||||||
strncpy(output, outputStr.str().c_str(), outputSize - 1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(function, "getline")) {
|
|
||||||
// 1 = data on line, 2 - data not ready, 3 - done
|
|
||||||
std::string result = "";
|
|
||||||
std::future_status workerStatus;
|
|
||||||
|
|
||||||
while (result.empty()) {
|
|
||||||
if (getLineIndex >= fWorkers.size()) {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
workerStatus = fWorkers[getLineIndex].wait_for(std::chrono::seconds(0));
|
|
||||||
if (workerStatus != std::future_status::ready) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
result = fWorkers[getLineIndex].get();
|
|
||||||
getLineIndex++;
|
|
||||||
}
|
|
||||||
strncpy(output, result.c_str(), outputSize - 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
strncpy(output, "error - invalid function", outputSize - 1);
|
|
||||||
return RETURN_INVALID_FUNCTION; // Error: function not valid
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
/*
|
|
||||||
* artillerytables.hpp
|
|
||||||
* Author: PabstMirror
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ace libs:
|
|
||||||
#include "vector.hpp"
|
|
||||||
#include "shared.hpp"
|
|
||||||
#define RETURN_INVALID_FUNCTION -1001
|
|
||||||
#define RETURN_WRONG_ARG_COUNT -1002
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
EXPORT void __stdcall RVExtension(char* output, int outputSize, const char* function);
|
|
||||||
EXPORT int __stdcall RVExtensionArgs(char* output, int outputSize, const char* function, const char** argv, int argc);
|
|
||||||
EXPORT void __stdcall RVExtensionVersion(char* output, int outputSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<double, double, double> simulateShot(const double _fireAngleRad, const double _muzzleVelocity, const double _heightOfTarget, const double _crossWind, const double _tailWind, const double _temperature, const double _airDensity, double _airFriction);
|
|
||||||
std::tuple<double, double> findMaxAngle(const double _muzzleVelocity, const double _airFriction);
|
|
||||||
std::tuple<double, double, double> simulateFindSolution(const double _rangeToHit, const double _heightToHit, const double _muzzleVelocity, const double _airFriction, const double _minElev, const double _maxElev, const bool _highArc);
|
|
||||||
void writeNumber(std::stringstream & ss, double _num, const int _widthInt, const int _widthDec);
|
|
||||||
std::string simulateCalcRangeTableLine(const double _rangeToHit, const double _muzzleVelocity, const double _airFriction, const double _minElev, const double _maxElev, const bool _highArc);
|
|
@ -1,100 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <chrono>
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#include "../artillerytables.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
__declspec(dllexport) void __stdcall RVExtension(char* output, int outputSize, const char* function);
|
|
||||||
__declspec(dllexport) int __stdcall RVExtensionArgs(char* output, int outputSize, const char* function, const char** argv, int argc);
|
|
||||||
__declspec(dllexport) void __stdcall RVExtensionVersion(char* output, int outputSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace test_ace_artillerytables {
|
|
||||||
TEST(Extension, VersionOld) {
|
|
||||||
char output[256];
|
|
||||||
char function[] = "version";
|
|
||||||
RVExtension(output, 256, function);
|
|
||||||
std::cout << "VersionOld: " << output << "\n";
|
|
||||||
ASSERT_STREQ(output, ACE_FULL_VERSION_STR);
|
|
||||||
}
|
|
||||||
TEST(Extension, VersionRVExtensionVersion) {
|
|
||||||
char output[256];
|
|
||||||
RVExtensionVersion(output, 256);
|
|
||||||
std::cout << "VersionExtension: " << output << "\n";
|
|
||||||
ASSERT_STREQ(output, ACE_FULL_VERSION_STR);
|
|
||||||
}
|
|
||||||
TEST(Extension, VersionArray) {
|
|
||||||
char output[256];
|
|
||||||
char function[] = "version";
|
|
||||||
int extReturn = RVExtensionArgs(output, 256, function, NULL, 0);
|
|
||||||
std::cout << "VersionNew: " << output << "\n";
|
|
||||||
ASSERT_EQ(extReturn, 0);
|
|
||||||
ASSERT_STREQ(output, ACE_FULL_VERSION_STR);
|
|
||||||
}
|
|
||||||
TEST(Extension, InvalidFuncOld) {
|
|
||||||
char output[256];
|
|
||||||
char function[] = "blah";
|
|
||||||
RVExtension(output, 256, function);
|
|
||||||
ASSERT_STREQ(output, "error - use args version of callExtension");
|
|
||||||
}
|
|
||||||
TEST(Extension, InvalidFuncArray) {
|
|
||||||
char output[256];
|
|
||||||
char function[] = "blah";
|
|
||||||
int extReturn = RVExtensionArgs(output, 256, function, nullptr, 0);
|
|
||||||
std::cout << "InvalidFunc: " << output << "\n";
|
|
||||||
ASSERT_EQ(extReturn, RETURN_INVALID_FUNCTION);
|
|
||||||
ASSERT_STREQ(output, "error - invalid function");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TEST(Extension, TestRun) {
|
|
||||||
// very basic test that it runs the correct number of lines
|
|
||||||
char output[256];
|
|
||||||
|
|
||||||
// Start:
|
|
||||||
char function1[] = "start";
|
|
||||||
const char* args1[] = { "400", "-0.00005", "-5", "80", "true" };
|
|
||||||
auto t1 = std::chrono::high_resolution_clock::now();
|
|
||||||
int ret1 = RVExtensionArgs(output, 256, function1, args1, 5);
|
|
||||||
auto t2 = std::chrono::high_resolution_clock::now();
|
|
||||||
std::printf("ret: %d - %s\n", ret1, output);
|
|
||||||
std::printf("func %s: %1.1f ms\n", function1, std::chrono::duration<double, std::milli>(t2 - t1).count());
|
|
||||||
ASSERT_STREQ(output, "[10391.8,103]");
|
|
||||||
ASSERT_EQ(ret1, 0);
|
|
||||||
|
|
||||||
int lines = 0;
|
|
||||||
auto t3 = std::chrono::high_resolution_clock::now();
|
|
||||||
char function2[] = "getline";
|
|
||||||
int ret2 = 0;
|
|
||||||
while (ret2 != 3) { // dumb spin
|
|
||||||
ret2 = RVExtensionArgs(output, 256, function2, NULL, 0);
|
|
||||||
if (ret2 == 1) {
|
|
||||||
lines++;
|
|
||||||
// std::printf("ret: %d - %s\n", ret2, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto t4 = std::chrono::high_resolution_clock::now();
|
|
||||||
std::printf("func %s: %1.1f ms with %d lines\n", function2, std::chrono::duration<double, std::milli>(t3 - t2).count(), lines);
|
|
||||||
|
|
||||||
std::printf("callExtensions finished in %1.1f ms\n", std::chrono::duration<double, std::milli>(t4 - t1).count());
|
|
||||||
|
|
||||||
ASSERT_EQ(lines, 69);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
// Misc Testing code
|
|
||||||
// Determine realistic air firiction values
|
|
||||||
//double mv = 241;
|
|
||||||
//std::printf(" %f m/s\n", mv);
|
|
||||||
//double range;
|
|
||||||
//for (double ar = 0; ar > -0.00015; ar -= 0.00001) {
|
|
||||||
// std::tie(std::ignore, range) = findMaxAngle(mv, ar);
|
|
||||||
// printf("[%f] = %f\n", ar, range);
|
|
||||||
//}
|
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
|
||||||
std::cout << "Starting tests!\n";
|
|
||||||
return RUN_ALL_TESTS();
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
set(ACE_EXTENSION_NAME "ace_break_line")
|
|
||||||
|
|
||||||
file(GLOB SOURCES *.h *.hpp *.c *.cpp)
|
|
||||||
add_library( ${ACE_EXTENSION_NAME} SHARED ${SOURCES} ${GLOBAL_SOURCES})
|
|
||||||
target_link_libraries(${ACE_EXTENSION_NAME} ace_common)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES PREFIX "")
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES FOLDER Extensions)
|
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_END_STATIC 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Copy and rename DLL to root
|
|
||||||
if(USE_64BIT_BUILD)
|
|
||||||
set(FINAL_DLL_NAME ${ACE_EXTENSION_NAME}_x64.dll)
|
|
||||||
else()
|
|
||||||
set(FINAL_DLL_NAME ${ACE_EXTENSION_NAME}.dll)
|
|
||||||
endif()
|
|
||||||
add_custom_command(TARGET ${ACE_EXTENSION_NAME} POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${ACE_EXTENSION_NAME}> ${PROJECT_SOURCE_DIR}/../${FINAL_DLL_NAME}
|
|
||||||
)
|
|
@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* ace_break_line.cpp
|
|
||||||
*
|
|
||||||
* Takes a string and insert as many line breaks as needed so it fits a given width
|
|
||||||
*
|
|
||||||
* Takes:
|
|
||||||
* Localized string as string
|
|
||||||
* Example: "Check weapon temperature"
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* String with line breaks
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "shared.hpp"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#define MAXCHARACTERS 14
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
EXPORT void __stdcall RVExtension(char *output, int outputSize, const char *function);
|
|
||||||
EXPORT void __stdcall RVExtensionVersion(char *output, int outputSize) {
|
|
||||||
strncpy(output, ACE_FULL_VERSION_STR, outputSize - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> splitString(const std::string & input) {
|
|
||||||
std::istringstream ss(input);
|
|
||||||
std::string token;
|
|
||||||
|
|
||||||
std::vector<std::string> output;
|
|
||||||
while (std::getline(ss, token, ' ')) {
|
|
||||||
output.push_back(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string addLineBreaks(const std::vector<std::string> &words) {
|
|
||||||
std::stringstream sstream;
|
|
||||||
size_t numChar = 0;
|
|
||||||
size_t i = 0;
|
|
||||||
|
|
||||||
while (i < words.size()) {
|
|
||||||
if (numChar == 0) {
|
|
||||||
sstream << words[i];
|
|
||||||
numChar += words[i].size();
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
if (numChar + 1 + words[i].size() > MAXCHARACTERS) {
|
|
||||||
sstream << "<br/>";
|
|
||||||
numChar = 0;
|
|
||||||
} else {
|
|
||||||
sstream << " " << words[i];
|
|
||||||
numChar += 1 + words[i].size();
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sstream.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void __stdcall RVExtension(char *output, int outputSize, const char *function) {
|
|
||||||
ZERO_OUTPUT();
|
|
||||||
if (!strcmp(function, "version")) {
|
|
||||||
strncpy(output, ACE_FULL_VERSION_STR, outputSize - 1);
|
|
||||||
} else {
|
|
||||||
strncpy(output, addLineBreaks(splitString(function)).c_str(), outputSize - 1);
|
|
||||||
}
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
set(ACE_EXTENSION_NAME "ace_clipboard")
|
|
||||||
|
|
||||||
file(GLOB SOURCES *.h *.hpp *.c *.cpp)
|
|
||||||
add_library( ${ACE_EXTENSION_NAME} SHARED ${SOURCES} ${GLOBAL_SOURCES})
|
|
||||||
target_link_libraries(${ACE_EXTENSION_NAME} ace_common)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES PREFIX "")
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES FOLDER Extensions)
|
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1)
|
|
||||||
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_END_STATIC 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Copy and rename DLL to root
|
|
||||||
if(USE_64BIT_BUILD)
|
|
||||||
set(FINAL_DLL_NAME ${ACE_EXTENSION_NAME}_x64.dll)
|
|
||||||
else()
|
|
||||||
set(FINAL_DLL_NAME ${ACE_EXTENSION_NAME}.dll)
|
|
||||||
endif()
|
|
||||||
add_custom_command(TARGET ${ACE_EXTENSION_NAME} POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${ACE_EXTENSION_NAME}> ${PROJECT_SOURCE_DIR}/../${FINAL_DLL_NAME}
|
|
||||||
)
|
|
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* ace_clipboard.cpp
|
|
||||||
*
|
|
||||||
* Takes a string and copies it to the clipboard; bypasses arma 8k clippy limit. Windows only.
|
|
||||||
*
|
|
||||||
* Takes:
|
|
||||||
* Localized string as string
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* None
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "shared.hpp"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
EXPORT void __stdcall RVExtension(char *output, int outputSize, const char *function);
|
|
||||||
EXPORT void __stdcall RVExtensionVersion(char *output, int outputSize) {
|
|
||||||
strncpy(output, ACE_FULL_VERSION_STR, outputSize - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string gClipboardData;
|
|
||||||
|
|
||||||
void __stdcall RVExtension(char *output, int outputSize, const char *function) {
|
|
||||||
std::string cur_input(function);
|
|
||||||
std::string result;
|
|
||||||
|
|
||||||
ZERO_OUTPUT();
|
|
||||||
|
|
||||||
if (cur_input.length() < 1) {
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(function, "version")) {
|
|
||||||
std::strncpy(output, ACE_FULL_VERSION_STR, outputSize - 1);
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
if (!strcmp(function, "--COMPLETE--")) {
|
|
||||||
if (OpenClipboard(NULL) == 0) {
|
|
||||||
result = "OpenClipboard() failed, GetLastError=" + GetLastError();
|
|
||||||
} else {
|
|
||||||
if (EmptyClipboard() == 0) {
|
|
||||||
result = "EmptyClipboard() failed, GetLastError=" + GetLastError();
|
|
||||||
} else {
|
|
||||||
// GPTR = GMEM_FIXED + GMEM_ZEROINIT, returns a ptr, no need for GlobalLock/GlobalUnlock
|
|
||||||
char *pClipboardData = (char *)GlobalAlloc(GPTR, gClipboardData.length());
|
|
||||||
|
|
||||||
if (pClipboardData == NULL) {
|
|
||||||
result = "GlobalAlloc() failed, GetLastError=" + GetLastError();
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
}
|
|
||||||
strncpy(pClipboardData, gClipboardData.c_str(), gClipboardData.length());
|
|
||||||
|
|
||||||
// if success, system owns the memory, if fail, free it from the heap
|
|
||||||
if (SetClipboardData(CF_TEXT, pClipboardData) == NULL) {
|
|
||||||
result = "SetClipboardData() failed, GetLastError=" + GetLastError();
|
|
||||||
GlobalFree(pClipboardData);
|
|
||||||
} else {
|
|
||||||
if (CloseClipboard() == 0) {
|
|
||||||
result = "CloseClipboard() failed, GetLastError=" + GetLastError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gClipboardData = "";
|
|
||||||
} else {
|
|
||||||
gClipboardData = gClipboardData + cur_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.length() > 1) {
|
|
||||||
strncpy(output, result.c_str(), outputSize - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EXTENSION_RETURN();
|
|
||||||
}
|
|
@ -1,215 +0,0 @@
|
|||||||
# - try to find DirectX include directories and libraries
|
|
||||||
#
|
|
||||||
# Once done this will define:
|
|
||||||
#
|
|
||||||
# DirectX_XYZ_FOUND - system has the XYZ API
|
|
||||||
# DirectX_XYZ_INCLUDE_FOUND - system has the include for the XYZ API
|
|
||||||
# DirectX_XYZ_INCLUDE_DIR - include directory for the XYZ API
|
|
||||||
# DirectX_XYZ_LIBRARY - path/name for the XYZ library
|
|
||||||
#
|
|
||||||
# Where XYZ can be any of:
|
|
||||||
#
|
|
||||||
# DDRAW
|
|
||||||
# D3D
|
|
||||||
# D3DX
|
|
||||||
# D3D8
|
|
||||||
# D3DX8
|
|
||||||
# D3D9
|
|
||||||
# D3DX9
|
|
||||||
# D3D10
|
|
||||||
# D3D10_1
|
|
||||||
# D3DX10
|
|
||||||
# D3D11
|
|
||||||
# D3D11_1
|
|
||||||
# D3D11_2
|
|
||||||
# D3DX11
|
|
||||||
# D2D1
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
include (CheckIncludeFileCXX)
|
|
||||||
include (FindPackageMessage)
|
|
||||||
|
|
||||||
|
|
||||||
if (WIN32)
|
|
||||||
|
|
||||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
||||||
set (DirectX_ARCHITECTURE x64)
|
|
||||||
else ()
|
|
||||||
set (DirectX_ARCHITECTURE x86)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Can't use "$ENV{ProgramFiles(x86)}" to avoid violating CMP0053. See
|
|
||||||
# http://public.kitware.com/pipermail/cmake-developers/2014-October/023190.html
|
|
||||||
set (ProgramFiles_x86 "ProgramFiles(x86)")
|
|
||||||
if ("$ENV{${ProgramFiles_x86}}")
|
|
||||||
set (ProgramFiles "$ENV{${ProgramFiles_x86}}")
|
|
||||||
else ()
|
|
||||||
set (ProgramFiles "$ENV{ProgramFiles}")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
find_path (DirectX_ROOT_DIR
|
|
||||||
Include/d3d9.h
|
|
||||||
PATHS
|
|
||||||
"$ENV{DXSDK_DIR}"
|
|
||||||
"${ProgramFiles}/Microsoft DirectX SDK (June 2010)"
|
|
||||||
"${ProgramFiles}/Microsoft DirectX SDK (February 2010)"
|
|
||||||
"${ProgramFiles}/Microsoft DirectX SDK (March 2009)"
|
|
||||||
"${ProgramFiles}/Microsoft DirectX SDK (August 2008)"
|
|
||||||
"${ProgramFiles}/Microsoft DirectX SDK (June 2008)"
|
|
||||||
"${ProgramFiles}/Microsoft DirectX SDK (March 2008)"
|
|
||||||
"${ProgramFiles}/Microsoft DirectX SDK (November 2007)"
|
|
||||||
"${ProgramFiles}/Microsoft DirectX SDK (August 2007)"
|
|
||||||
"${ProgramFiles}/Microsoft DirectX SDK"
|
|
||||||
DOC "DirectX SDK root directory"
|
|
||||||
)
|
|
||||||
if (DirectX_ROOT_DIR)
|
|
||||||
set (DirectX_INC_SEARCH_PATH "${DirectX_ROOT_DIR}/Include")
|
|
||||||
set (DirectX_LIB_SEARCH_PATH "${DirectX_ROOT_DIR}/Lib/${DirectX_ARCHITECTURE}")
|
|
||||||
set (DirectX_BIN_SEARCH_PATH "${DirectX_ROOT_DIR}/Utilities/bin/x86")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# With VS 2011 and Windows 8 SDK, the DirectX SDK is included as part of
|
|
||||||
# the Windows SDK.
|
|
||||||
#
|
|
||||||
# See also:
|
|
||||||
# - http://msdn.microsoft.com/en-us/library/windows/desktop/ee663275.aspx
|
|
||||||
if (DEFINED MSVC_VERSION AND NOT ${MSVC_VERSION} LESS 1700)
|
|
||||||
set (USE_WINSDK_HEADERS TRUE)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Find a header in the DirectX SDK
|
|
||||||
macro (find_dxsdk_header var_name header)
|
|
||||||
set (include_dir_var "DirectX_${var_name}_INCLUDE_DIR")
|
|
||||||
set (include_found_var "DirectX_${var_name}_INCLUDE_FOUND")
|
|
||||||
find_path (${include_dir_var} ${header}
|
|
||||||
HINTS ${DirectX_INC_SEARCH_PATH}
|
|
||||||
DOC "The directory where ${header} resides"
|
|
||||||
CMAKE_FIND_ROOT_PATH_BOTH
|
|
||||||
)
|
|
||||||
if (${include_dir_var})
|
|
||||||
set (${include_found_var} TRUE)
|
|
||||||
find_package_message (${var_name}_INC "Found ${header} header: ${${include_dir_var}}/${header}" "[${${include_dir_var}}]")
|
|
||||||
endif ()
|
|
||||||
mark_as_advanced (${include_found_var})
|
|
||||||
endmacro ()
|
|
||||||
|
|
||||||
# Find a library in the DirectX SDK
|
|
||||||
macro (find_dxsdk_library var_name library)
|
|
||||||
# DirectX SDK
|
|
||||||
set (library_var "DirectX_${var_name}_LIBRARY")
|
|
||||||
find_library (${library_var} ${library}
|
|
||||||
HINTS ${DirectX_LIB_SEARCH_PATH}
|
|
||||||
DOC "The directory where ${library} resides"
|
|
||||||
CMAKE_FIND_ROOT_PATH_BOTH
|
|
||||||
)
|
|
||||||
if (${library_var})
|
|
||||||
find_package_message (${var_name}_LIB "Found ${library} library: ${${library_var}}" "[${${library_var}}]")
|
|
||||||
endif ()
|
|
||||||
mark_as_advanced (${library_var})
|
|
||||||
endmacro ()
|
|
||||||
|
|
||||||
# Find a header in the Windows SDK
|
|
||||||
macro (find_winsdk_header var_name header)
|
|
||||||
if (USE_WINSDK_HEADERS)
|
|
||||||
# Windows SDK
|
|
||||||
set (include_dir_var "DirectX_${var_name}_INCLUDE_DIR")
|
|
||||||
set (include_found_var "DirectX_${var_name}_INCLUDE_FOUND")
|
|
||||||
check_include_file_cxx (${header} ${include_found_var})
|
|
||||||
set (${include_dir_var})
|
|
||||||
mark_as_advanced (${include_found_var})
|
|
||||||
else ()
|
|
||||||
find_dxsdk_header (${var_name} ${header})
|
|
||||||
endif ()
|
|
||||||
endmacro ()
|
|
||||||
|
|
||||||
# Find a library in the Windows SDK
|
|
||||||
macro (find_winsdk_library var_name library)
|
|
||||||
if (USE_WINSDK_HEADERS)
|
|
||||||
# XXX: We currently just assume the library exists
|
|
||||||
set (library_var "DirectX_${var_name}_LIBRARY")
|
|
||||||
set (${library_var} ${library})
|
|
||||||
mark_as_advanced (${library_var})
|
|
||||||
else ()
|
|
||||||
find_dxsdk_library (${var_name} ${library})
|
|
||||||
endif ()
|
|
||||||
endmacro ()
|
|
||||||
|
|
||||||
# Combine header and library variables into an API found variable
|
|
||||||
macro (find_combined var_name inc_var_name lib_var_name)
|
|
||||||
if (DirectX_${inc_var_name}_INCLUDE_FOUND AND DirectX_${lib_var_name}_LIBRARY)
|
|
||||||
set (DirectX_${var_name}_FOUND 1)
|
|
||||||
find_package_message (${var_name} "Found ${var_name} API" "[${DirectX_${lib_var_name}_LIBRARY}][${DirectX_${inc_var_name}_INCLUDE_DIR}]")
|
|
||||||
endif ()
|
|
||||||
endmacro ()
|
|
||||||
|
|
||||||
find_winsdk_header (DDRAW ddraw.h)
|
|
||||||
find_winsdk_library (DDRAW ddraw)
|
|
||||||
find_combined (DDRAW DDRAW DDRAW)
|
|
||||||
|
|
||||||
if (CMAKE_GENERATOR_TOOLSET MATCHES "_xp$")
|
|
||||||
# Windows 7 SDKs, used by XP toolset, do not include d3d.h
|
|
||||||
find_dxsdk_header (D3D d3d.h)
|
|
||||||
else ()
|
|
||||||
find_winsdk_header (D3D d3d.h)
|
|
||||||
endif ()
|
|
||||||
find_combined (D3D D3D DDRAW)
|
|
||||||
|
|
||||||
find_dxsdk_header (D3DX d3dx.h)
|
|
||||||
find_combined (D3DX D3DX D3DX)
|
|
||||||
|
|
||||||
find_dxsdk_header (D3D8 d3d8.h)
|
|
||||||
find_dxsdk_library (D3D8 d3d8)
|
|
||||||
find_combined (D3D8 D3D8 D3D8)
|
|
||||||
|
|
||||||
find_dxsdk_header (D3DX8 d3dx8.h)
|
|
||||||
find_dxsdk_library (D3DX8 d3dx8)
|
|
||||||
find_combined (D3DX8 D3DX8 D3DX8)
|
|
||||||
|
|
||||||
find_winsdk_header (D3D9 d3d9.h)
|
|
||||||
find_winsdk_library (D3D9 d3d9)
|
|
||||||
find_combined (D3D9 D3D9 D3D9)
|
|
||||||
|
|
||||||
find_dxsdk_header (D3DX9 d3dx9.h)
|
|
||||||
find_dxsdk_library (D3DX9 d3dx9)
|
|
||||||
find_combined (D3DX9 D3DX9 D3DX9)
|
|
||||||
|
|
||||||
find_winsdk_header (DXGI dxgi.h)
|
|
||||||
find_winsdk_header (DXGI1_2 dxgi1_2.h)
|
|
||||||
find_winsdk_header (DXGI1_3 dxgi1_3.h)
|
|
||||||
find_winsdk_library (DXGI dxgi)
|
|
||||||
|
|
||||||
find_winsdk_header (D3D10 d3d10.h)
|
|
||||||
find_winsdk_library (D3D10 d3d10)
|
|
||||||
find_combined (D3D10 D3D10 D3D10)
|
|
||||||
|
|
||||||
find_winsdk_header (D3D10_1 d3d10_1.h)
|
|
||||||
find_winsdk_library (D3D10_1 d3d10_1)
|
|
||||||
find_combined (D3D10_1 D3D10_1 D3D10_1)
|
|
||||||
|
|
||||||
find_dxsdk_header (D3DX10 d3dx10.h)
|
|
||||||
find_dxsdk_library (D3DX10 d3dx10)
|
|
||||||
find_combined (D3DX10 D3DX10 D3DX10)
|
|
||||||
|
|
||||||
find_winsdk_header (D3D11 d3d11.h)
|
|
||||||
find_winsdk_library (D3D11 d3d11)
|
|
||||||
find_combined (D3D11 D3D11 D3D11)
|
|
||||||
find_winsdk_header (D3D11_1 d3d11_1.h)
|
|
||||||
find_combined (D3D11_1 D3D11_1 D3D11)
|
|
||||||
find_winsdk_header (D3D11_2 d3d11_2.h)
|
|
||||||
find_combined (D3D11_2 D3D11_2 D3D11)
|
|
||||||
|
|
||||||
find_dxsdk_header (D3DX11 d3dx11.h)
|
|
||||||
find_dxsdk_library (D3DX11 d3dx11)
|
|
||||||
find_combined (D3DX11 D3DX11 D3DX11)
|
|
||||||
|
|
||||||
find_winsdk_header (D2D1 d2d1.h)
|
|
||||||
find_winsdk_library (D2D1 d2d1)
|
|
||||||
find_combined (D2D1 D2D1 D2D1)
|
|
||||||
|
|
||||||
find_program (DirectX_FXC_EXECUTABLE fxc
|
|
||||||
HINTS ${DirectX_BIN_SEARCH_PATH}
|
|
||||||
DOC "Path to fxc.exe executable."
|
|
||||||
)
|
|
||||||
|
|
||||||
endif ()
|
|
@ -1,18 +0,0 @@
|
|||||||
file(GLOB_RECURSE ACE_COMMON_SOURCES *.h *.hpp *.c *.cpp)
|
|
||||||
|
|
||||||
|
|
||||||
file(GLOB ACE_BASE_COMMON_SOURCES *.h *.hpp *.c *.cpp)
|
|
||||||
#file(GLOB ACE_P3D_SOURCES p3d/*.h p3d/*.hpp p3d/*.c p3d/*.cpp)
|
|
||||||
#file(GLOB ACE_PBO_SOURCES pbo/*.h pbo/*.hpp pbo/*.c pbo/*.cpp)
|
|
||||||
#file(GLOB ACE_SIMULATION_SOURCES simulation/*.h simulation/*.hpp simulation/*.c simulation/*.cpp)
|
|
||||||
#file(GLOB ACE_DIRECTX_SOURCES directx/*.h directx/*.hpp directx/*.c directx/*.cpp)
|
|
||||||
#file(GLOB ACE_GLM_SOURCES glm/*.h glm/*.hpp glm/*.c glm/*.cpp)
|
|
||||||
|
|
||||||
SOURCE_GROUP("common" FILES ${ACE_BASE_COMMON_SOURCES})
|
|
||||||
#SOURCE_GROUP("p3d" FILES ${ACE_P3D_SOURCES})
|
|
||||||
#SOURCE_GROUP("pbo" FILES ${ACE_PBO_SOURCES})
|
|
||||||
#SOURCE_GROUP("simulation" FILES ${ACE_SIMULATION_SOURCES})
|
|
||||||
#SOURCE_GROUP("directx" FILES ${ACE_DIRECTX_SOURCES})
|
|
||||||
#SOURCE_GROUP("glm" FILES ${ACE_GLM_SOURCES})
|
|
||||||
|
|
||||||
add_library(ace_common STATIC ${ACE_GLM_SOURCES} ${ACE_BASE_COMMON_SOURCES} ${ACE_P3D_SOURCES} ${ACE_PBO_SOURCES} ${ACE_SIMULATION_SOURCES} ${ACE_DIRECTX_SOURCES})
|
|
@ -1,4 +0,0 @@
|
|||||||
|
|
||||||
int test(int var) {
|
|
||||||
return var;
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "targetver.h"
|
|
||||||
#include "ace_version.hpp"
|
|
@ -1,4 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#define ACE_VERSION_STR "@ACE_VERSION_MAJOR@.@ACE_VERSION_MINOR@.@ACE_VERSION_REVISION@"
|
|
||||||
#define ACE_FULL_VERSION_STR "@ACE_VERSION_MAJOR@.@ACE_VERSION_MINOR@.@ACE_VERSION_REVISION@-@ACE_VERSION_BUILD@"
|
|
@ -1,47 +0,0 @@
|
|||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#ifndef DEBUG
|
|
||||||
#define VER_DEBUG 0
|
|
||||||
#else
|
|
||||||
#define VER_DEBUG VS_FF_DEBUG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
|
||||||
FILEVERSION @ACE_VERSION_MAJOR@, @ACE_VERSION_MINOR@, @ACE_VERSION_REVISION@
|
|
||||||
PRODUCTVERSION @ACE_VERSION_MAJOR@, @ACE_VERSION_MINOR@, @ACE_VERSION_REVISION@
|
|
||||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
|
||||||
FILEFLAGS (VS_FF_PRIVATEBUILD|VS_FF_PRERELEASE|VER_DEBUG)
|
|
||||||
FILEOS VOS__WINDOWS32
|
|
||||||
FILETYPE VFT_DLL
|
|
||||||
FILESUBTYPE VFT2_UNKNOWN
|
|
||||||
BEGIN
|
|
||||||
BLOCK "StringFileInfo"
|
|
||||||
BEGIN
|
|
||||||
BLOCK "040904E4"
|
|
||||||
BEGIN
|
|
||||||
VALUE "CompanyName", "ACE3 Team"
|
|
||||||
VALUE "FileDescription", "@ACE_VERSION_MAJOR@.@ACE_VERSION_MINOR@.@ACE_VERSION_REVISION@-@ACE_VERSION_BUILD@"
|
|
||||||
VALUE "FileVersion", "@ACE_VERSION_MAJOR@.@ACE_VERSION_MINOR@.@ACE_VERSION_REVISION@-@ACE_VERSION_BUILD@"
|
|
||||||
VALUE "ProductName", "ACE3"
|
|
||||||
VALUE "ProductVersion", "@ACE_VERSION_MAJOR@.@ACE_VERSION_MINOR@.@ACE_VERSION_REVISION@-@ACE_VERSION_BUILD@"
|
|
||||||
VALUE "Build Date", "@ACE_BUILDSTAMP@"
|
|
||||||
END
|
|
||||||
END
|
|
||||||
|
|
||||||
BLOCK "VarFileInfo"
|
|
||||||
BEGIN
|
|
||||||
/* The following line should only be modified for localized versions. */
|
|
||||||
/* It consists of any number of WORD,WORD pairs, with each pair */
|
|
||||||
/* describing a language,codepage combination supported by the file. */
|
|
||||||
/* */
|
|
||||||
/* For example, a file might have values "0x409,1252" indicating that it */
|
|
||||||
/* supports English language (0x409) in the Windows ANSI codepage (1252). */
|
|
||||||
|
|
||||||
VALUE "Translation", 0x409, 1252
|
|
||||||
|
|
||||||
END
|
|
||||||
END
|
|
||||||
|
|
||||||
#endif
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user