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:
BrettMayson 2024-08-17 09:50:38 -06:00 committed by GitHub
parent 1cd48c52c3
commit 043b3907fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
411 changed files with 3976 additions and 74980 deletions

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -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
View 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
View File

@ -0,0 +1,11 @@
[workspace]
resolver = "2"
members = [
"extension"
]
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
strip = true

BIN
ace.dll Normal file

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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
ace_x64.dll Normal file

Binary file not shown.

View File

@ -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 {};

View File

@ -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;
};
};

View File

@ -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); } forEach GVAR(allBullets)
if (_deleted) then {
GVAR(allBullets) = GVAR(allBullets) - [objNull];
};

View File

@ -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.
* *
@ -62,11 +62,11 @@ if (_abort) exitWith {};
// Get Weapon and Ammo Configurations // Get Weapon and Ammo Configurations
private _AmmoCacheEntry = uiNamespace getVariable format[QGVAR(%1), _ammo]; private _AmmoCacheEntry = uiNamespace getVariable format[QGVAR(%1), _ammo];
if (isNil "_AmmoCacheEntry") then { if (isNil "_AmmoCacheEntry") then {
_AmmoCacheEntry = _ammo call FUNC(readAmmoDataFromConfig); _AmmoCacheEntry = _ammo call FUNC(readAmmoDataFromConfig);
}; };
private _WeaponCacheEntry = uiNamespace getVariable format[QGVAR(%1), _weapon]; private _WeaponCacheEntry = uiNamespace getVariable format[QGVAR(%1), _weapon];
if (isNil "_WeaponCacheEntry") then { if (isNil "_WeaponCacheEntry") then {
_WeaponCacheEntry = _weapon call FUNC(readWeaponDataFromConfig); _WeaponCacheEntry = _weapon call FUNC(readWeaponDataFromConfig);
}; };
_AmmoCacheEntry params ["_airFriction", "_caliber", "_bulletLength", "_bulletMass", "_transonicStabilityCoef", "_dragModel", "_ballisticCoefficients", "_velocityBoundaries", "_atmosphereModel", "_ammoTempMuzzleVelocityShifts", "_muzzleVelocityTable", "_barrelLengthTable", "_muzzleVelocityVariationSD"]; _AmmoCacheEntry params ["_airFriction", "_caliber", "_bulletLength", "_bulletMass", "_transonicStabilityCoef", "_dragModel", "_ballisticCoefficients", "_velocityBoundaries", "_atmosphereModel", "_ammoTempMuzzleVelocityShifts", "_muzzleVelocityTable", "_barrelLengthTable", "_muzzleVelocityVariationSD"];
@ -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]];
};

View File

@ -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 {};
}; };

View File

@ -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);
}; };

View File

@ -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 {

View File

@ -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 "";

View File

@ -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 {

View File

@ -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);

View File

@ -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"

View File

@ -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"

View 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

View 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]

View 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

View File

@ -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

View 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

View File

@ -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;

View 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

View File

@ -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

View File

@ -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

View File

@ -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);
}; };

View File

@ -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];

View File

@ -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);

View File

@ -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 {
INFO("Operating system does not support extensions");
};
} else { } else {
{ ("ace" callExtension ["version", []]) params [["_versionEx", "", [""]], ["_returnCode", -1, [-1]]];
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 { if (_returnCode != 0 || {_versionEx == ""}) then {
private _versionEx = _extension callExtension "version"; private _errorMsg = format ["Extension not found. [Return Code: %1]", _returnCode];
ERROR(_errorMsg);
if (_versionEx == "") then { if (hasInterface) then {
private _extensionFile = _extension; ["[ACE] ERROR", _errorMsg] call FUNC(errorMessage);
if (productVersion select 7 == "x64") then {
_extensionFile = format ["%1_x64", _extensionFile];
};
private _platformExt = [".dll", ".so"] select (_platform == "linux");
_extensionFile = format ["%1%2", _extensionFile, _platformExt];
private _errorMsg = format ["Extension %1 not found.", _extensionFile];
ERROR(_errorMsg);
if (hasInterface) then {
["[ACE] ERROR", _errorMsg] call FUNC(errorMessage);
};
} else {
// Print the current extension version
INFO_2("Extension version: %1: %2",_extension,_versionEx);
};
}; };
} forEach ("true" configClasses (configFile >> "ACE_Extensions")); } else {
_versionEx = _versionEx select [0, 8]; // git hash
INFO_1("Extension [Version: %1]",_versionEx);
};
}; };
if (isArray (configFile >> "ACE_Extensions" >> "extensions")) then {
WARNING("extensions[] array no longer supported");
};
/////////////// ///////////////
// Check server version/addons // Check server version/addons

View File

@ -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);
}; };

View File

@ -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;

View File

@ -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];

View File

@ -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;
};
};

View File

@ -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);

View File

@ -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

View File

@ -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"

View File

@ -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", []];

View File

@ -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);

View File

@ -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 {
{ {

View File

@ -36,25 +36,23 @@ 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];
if (isNil "_AmmoCacheEntry") then { if (isNil "_AmmoCacheEntry") then {
_AmmoCacheEntry = _ammo call EFUNC(advanced_ballistics,readAmmoDataFromConfig); _AmmoCacheEntry = _ammo call EFUNC(advanced_ballistics,readAmmoDataFromConfig);
}; };
private _WeaponCacheEntry = uiNamespace getVariable format[QEGVAR(advanced_ballistics,%1), _weapon]; private _WeaponCacheEntry = uiNamespace getVariable format[QEGVAR(advanced_ballistics,%1), _weapon];
if (isNil "_WeaponCacheEntry") then { if (isNil "_WeaponCacheEntry") then {
_WeaponCacheEntry = _weapon call EFUNC(advanced_ballistics,readWeaponDataFromConfig); _WeaponCacheEntry = _weapon call EFUNC(advanced_ballistics,readWeaponDataFromConfig);
}; };
_AmmoCacheEntry params ["_airFriction", "_caliber", "_bulletLength", "_bulletMass", "_transonicStabilityCoef", "_dragModel", "_ballisticCoefficients", "_velocityBoundaries", "_atmosphereModel", "_ammoTempMuzzleVelocityShifts", "_muzzleVelocityTable", "_barrelLengthTable", "_muzzleVelocityVariationSD"]; _AmmoCacheEntry params ["_airFriction", "_caliber", "_bulletLength", "_bulletMass", "_transonicStabilityCoef", "_dragModel", "_ballisticCoefficients", "_velocityBoundaries", "_atmosphereModel", "_ammoTempMuzzleVelocityShifts", "_muzzleVelocityTable", "_barrelLengthTable", "_muzzleVelocityVariationSD"];
@ -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;

View File

@ -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);

View File

@ -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];

View File

@ -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.

View File

@ -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
View 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
View 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
View 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
View 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());
}

View 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());
}
}

View 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(&current_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
}
}

View 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
);
}
}

View 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()
}
}

View 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
}
}

View 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
);
}
}

View 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(())
},
)
}
}

View 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
}
}

View 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,
}),
)
}

View 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
);
}
}

View 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"
);
}
}

View 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
}

View 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);

View 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
}
}

View 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;

View 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
}
}

View 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))
}
}
}
}

View 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
View 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
View 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()
}

View File

@ -1,9 +0,0 @@
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 160
DerivePointerAlignment: false
PointerAlignment: Left
NamespaceIndentation: All
IncludeBlocks: Merge
AllowShortBlocksOnASingleLine: true

View File

@ -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}")

View File

@ -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();
}

View File

@ -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}
)

View File

@ -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()

View File

@ -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
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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}
)

View File

@ -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();
}

View File

@ -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}
)

View File

@ -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();
}

View File

@ -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 ()

View File

@ -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})

View File

@ -1,4 +0,0 @@
int test(int var) {
return var;
}

View File

@ -1,4 +0,0 @@
#pragma once
#include "targetver.h"
#include "ace_version.hpp"

View File

@ -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@"

View File

@ -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