mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1588644fec | ||
|
|
0abd592038 | ||
|
|
909e799bdd | ||
|
|
5a9f181aff | ||
|
|
b0ba22a43d | ||
|
|
c89faebe4e | ||
|
|
059e149590 |
No files matched your search
@@ -1,14 +1,8 @@
|
||||
|
||||
cmake/ @asarium
|
||||
|
||||
ci/ @asarium @z64555
|
||||
|
||||
code/executor/ @asarium
|
||||
|
||||
code/scpui/ @asarium
|
||||
|
||||
code/scripting/ @asarium
|
||||
|
||||
code/controlconfig/ @z64555
|
||||
|
||||
code/model/modelanimation*.* @bmagnu
|
||||
@@ -15,22 +15,20 @@ jobs:
|
||||
configuration: [FastDebug, Release]
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-8a6bbbe
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-a8ca321
|
||||
steps:
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-lin
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
# setup-python: 'false'
|
||||
# aqtversion: ==1.1.3
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
@@ -109,30 +107,30 @@ jobs:
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-win
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt (32 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.arch == 'Win32' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win32_msvc2017
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
# - name: Install Qt (64 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.arch == 'x64' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win64_msvc2017_64
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
- name: Cache Qt
|
||||
id: cache-qt-win
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt (32 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.arch == 'Win32' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win32_msvc2017
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
- name: Install Qt (64 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.arch == 'x64' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win64_msvc2017_64
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
@@ -146,7 +144,7 @@ jobs:
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_USE_SPEECH="ON" \
|
||||
-DFSO_USE_VOICEREC="ON" -DMSVC_SIMD_INSTRUCTIONS="$SIMD" \
|
||||
-DFSO_BUILD_QTFRED=OFF -DFSO_BUILD_TESTS=ON \
|
||||
-DFSO_BUILD_QTFRED=ON -DFSO_BUILD_TESTS=ON \
|
||||
-DFSO_INSTALL_DEBUG_FILES="ON" -A "$ARCHITECTURE" \
|
||||
-G "Visual Studio 16 2019" -T "v142" ..
|
||||
- name: Compile
|
||||
@@ -210,97 +208,3 @@ jobs:
|
||||
DATACORDER_USER: ${{ secrets.DATACORDER_USER }}
|
||||
DATACORDER_SSHPASS: ${{ secrets.DATACORDER_PASSWORD }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh nightly
|
||||
|
||||
build_mac:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [FastDebug, Release]
|
||||
compiler: [clang]
|
||||
arch: [x86_64]
|
||||
name: Mac
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-mac
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# cached: ${{ steps.cache-qt-mac.outputs.cache-hit }}
|
||||
# setup-python: 'false'
|
||||
# aqtversion: ==1.1.3
|
||||
# py7zrversion: '==0.19.*'
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: Set up test version
|
||||
shell: bash
|
||||
run: |
|
||||
[[ "${{ github.ref }}" =~ ^refs\/heads\/test\/(.*)$ ]]
|
||||
# Override the revision string so that the builds are named correctly
|
||||
echo "set(FSO_VERSION_REVISION_STR ${BASH_REMATCH[1]})" > "version_override.cmake"]
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
ARCHITECTURE: ${{ matrix.arch }}
|
||||
JOB_CMAKE_OPTIONS: ${{ matrix.cmake_options }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
run: LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH ninja all
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
XDG_RUNTIME_DIR: /root
|
||||
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
|
||||
- name: Package build result
|
||||
working-directory: ./build
|
||||
run: cd bin && tar -cvzf macos-build.tar *.app
|
||||
- name: Upload build result
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: mac-${{ matrix.configuration }}
|
||||
path: ${{ github.workspace }}/build/bin/macos-build.tar
|
||||
mac_zip:
|
||||
name: Build Mac distribution zip
|
||||
needs: build_mac
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-748b19d
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: '0'
|
||||
ref: '${{ github.ref }}'
|
||||
- name: Download Release builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: mac-Release
|
||||
path: builds
|
||||
- name: Download FastDebug builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: mac-FastDebug
|
||||
path: builds
|
||||
- name: Create Distribution package
|
||||
working-directory: ./builds
|
||||
run: $GITHUB_WORKSPACE/ci/linux/create_dist_pack.sh Mac
|
||||
- name: Upload result package
|
||||
working-directory: ./builds
|
||||
env:
|
||||
INDIEGAMES_USER: ${{ secrets.INDIEGAMES_USER }}
|
||||
INDIEGAMES_SSHPASS: ${{ secrets.INDIEGAMES_PASSWORD }}
|
||||
DATACORDER_USER: ${{ secrets.DATACORDER_USER }}
|
||||
DATACORDER_SSHPASS: ${{ secrets.DATACORDER_PASSWORD }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh nightly
|
||||
@@ -16,17 +16,6 @@ jobs:
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
# Creates a release package page on github
|
||||
# create_release.inputs: (notable inputs)
|
||||
# allowUpdates=false if true, update an existing release if it exists
|
||||
# artifactErrorsFailBuild=false if true, fails the build should artifact read/write errors occur
|
||||
# artifacts optional set of paths to artifacts to upload to the release. comma delimited.
|
||||
# removeArtifacts=false if true, remove existing artifacts
|
||||
# replaceArtifacts=false if true, replace/overwrite existing artifacts
|
||||
# create_release.outputs:
|
||||
# id id of the created release
|
||||
# html_url HTML URL of the release
|
||||
# upload_url FTP URL of the release where assets are uploaded
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
|
||||
with:
|
||||
@@ -34,65 +23,41 @@ jobs:
|
||||
release_name: Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
- name: Store upload URL
|
||||
run: |
|
||||
echo -n "${{ steps.create_release.outputs.upload_url }}" > upload_url
|
||||
- name: Persist upload URL
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: upload_url
|
||||
path: ${{ github.workspace }}/upload_url
|
||||
|
||||
build_linux:
|
||||
strategy:
|
||||
matrix:
|
||||
# Run this job for each configuration
|
||||
# The current config running is accesible with `matrix.configuration`
|
||||
configuration: [FastDebug, Release]
|
||||
name: Linux
|
||||
needs: create_release # Don't run this job until create_release is done and successful
|
||||
needs: create_release
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-8a6bbbe
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-a8ca321
|
||||
steps:
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-lin
|
||||
# uses: actions/cache@v1
|
||||
# cache the Qt directory so we don't have to reinstall it every time
|
||||
# cache.inputs:
|
||||
# path - A list of files, directories, and wildcard patterns to cache and restore. See @actions/glob for supported patterns.
|
||||
# key - An explicit key for restoring and saving the cache
|
||||
# restore-keys - An ordered list of keys to use for restoring the cache if no cache hit occurred for key
|
||||
# cache.outputs:
|
||||
# cache-hit true when an exact match was found for the key, false otherwise.
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# installs Qt in the action workflow
|
||||
# install-qt-action.inputs:
|
||||
# version desired qt version to install
|
||||
# host Host platform of Qt, default to platform used by runs-on
|
||||
# target target platform of Qt, default to desktop
|
||||
# arch target architecture of Qt, only used for windows or android builds
|
||||
# dir root dir Qt will be installed to
|
||||
# install-deps if true, auto-install dependencies on linux. Default true
|
||||
# modules whitespace delimited list string of additional addon modules to install
|
||||
# archives whitespace delimited list string of Qt archives to install
|
||||
# cached if true, Don't download Qt but set ENVARs; use with cache.outputs.cache-hit
|
||||
# setup-python Set to false to skip using setup-python to find/download a valid python version
|
||||
# set-env if true, set ENVARs; default = false
|
||||
# aqtversion version of aqtinstall to use; default = 2.0.0
|
||||
# py7zrversion version of py7zr to use; default = 0.16.1
|
||||
# extra additional arguments to pass to aqinstall
|
||||
# ENVARS set:
|
||||
# Qt5_DIR / Qt6_DIR
|
||||
# Qt bin dir appended to PATH
|
||||
# QT_PLUGIN_PATH, QML2_IMPORT_PATH, PKG_CONFIG_PATH, LD_LIBRARY_PATH
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
# setup-python: 'false'
|
||||
# aqtversion: ==1.1.3
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
# checks-out your repository under $GITHUB_WORKSPACE, so your workflow can access it.
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true # `true` to checkout submodules, `recursive` to recursively checkout submodules
|
||||
submodules: true
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
@@ -117,15 +82,6 @@ jobs:
|
||||
run: $GITHUB_WORKSPACE/ci/linux/generate_appimage.sh $GITHUB_WORKSPACE/build/install
|
||||
- name: Upload build result
|
||||
uses: actions/upload-artifact@v2
|
||||
# Upload the url file for use with other runners
|
||||
# upload-artifact.inputs:
|
||||
# if-no-files-found=warn What to do if path fails to find any files.
|
||||
# warn Display a warning but do not fail the action
|
||||
# error Display an error and fail the action
|
||||
# ignore No display, No fail
|
||||
# retention-days=0 Days to keep the artifact.
|
||||
# 0 Use default retention policy of the repo
|
||||
# 1~90 Min 1, Max 90 days to keep the artifact unless changed by repo settings page
|
||||
with:
|
||||
name: linux-${{ matrix.configuration }}
|
||||
path: ${{ github.workspace }}/build/install/*.AppImage
|
||||
@@ -139,37 +95,38 @@ jobs:
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: '0' # value 0 to fetch all history and tags for all branches
|
||||
fetch-depth: '0'
|
||||
- name: Download upload URL
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: upload_url
|
||||
path: .
|
||||
- name: Extract upload URL
|
||||
id: extractUploadUrl
|
||||
run: echo "::set-output name=upload_url::$(cat upload_url)"
|
||||
- name: Download Release builds
|
||||
# Grab the release builds
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: linux-Release
|
||||
path: builds
|
||||
- name: Download FastDebug builds
|
||||
# Grab the debug builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: linux-FastDebug
|
||||
path: builds
|
||||
- name: Create Distribution package
|
||||
# Zip both builds together
|
||||
id: generate_package
|
||||
working-directory: ./builds
|
||||
run: $GITHUB_WORKSPACE/ci/linux/create_dist_pack.sh Linux
|
||||
- name: Upload result package
|
||||
# Stash the result to artifact filespace
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ steps.generate_package.outputs.package_name }}
|
||||
path: ${{ steps.generate_package.outputs.package_path }}
|
||||
- name: Publish result package
|
||||
# publish the zipped result
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
files: ${{ steps.generate_package.outputs.package_path }}
|
||||
upload_url: ${{ steps.extractUploadUrl.outputs.upload_url }}
|
||||
asset_path: ${{ steps.generate_package.outputs.package_path }}
|
||||
asset_name: ${{ steps.generate_package.outputs.package_name }}
|
||||
asset_content_type: ${{ steps.generate_package.outputs.package_mime }}
|
||||
|
||||
build_windows:
|
||||
strategy:
|
||||
@@ -182,37 +139,33 @@ jobs:
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
# Checkout repo
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
# - name: Cache Qt
|
||||
# # Cache Qt
|
||||
# id: cache-qt-win
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt (32 bit)
|
||||
# # Install Qt 32bit if matrix.arch == Win32
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.arch == 'Win32' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win32_msvc2017
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
# - name: Install Qt (64 bit)
|
||||
# # Install Qt 64bit if matrix.arch == x64
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.arch == 'x64' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win64_msvc2017_64
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
- name: Cache Qt
|
||||
id: cache-qt-win
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt (32 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.arch == 'Win32' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win32_msvc2017
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
- name: Install Qt (64 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.arch == 'x64' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win64_msvc2017_64
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
@@ -226,7 +179,7 @@ jobs:
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_USE_SPEECH="ON" \
|
||||
-DFSO_USE_VOICEREC="ON" -DMSVC_SIMD_INSTRUCTIONS="$SIMD" \
|
||||
-DFSO_BUILD_QTFRED=OFF -DFSO_BUILD_TESTS=ON \
|
||||
-DFSO_BUILD_QTFRED=ON -DFSO_BUILD_TESTS=ON \
|
||||
-DFSO_INSTALL_DEBUG_FILES="ON" -A "$ARCHITECTURE" \
|
||||
-G "Visual Studio 16 2019" -T "v142" ..
|
||||
- name: Compile
|
||||
@@ -249,37 +202,39 @@ jobs:
|
||||
with:
|
||||
name: windows-${{ matrix.configuration }}-${{ matrix.arch }}-${{ matrix.simd }}
|
||||
path: ${{ github.workspace }}/build/install/*
|
||||
|
||||
windows_zip:
|
||||
name: Build Windows distribution zip
|
||||
needs: build_windows
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-748b19d
|
||||
strategy:
|
||||
fail-fast: false # Run the other jobs in the matrix instead of failing them
|
||||
matrix:
|
||||
arch: [Win32, x64]
|
||||
simd: [SSE2, AVX]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: '0'
|
||||
|
||||
- name: Download upload URL
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: upload_url
|
||||
path: .
|
||||
- name: Extract upload URL
|
||||
id: extractUploadUrl
|
||||
run: echo "::set-output name=upload_url::$(cat upload_url)"
|
||||
- name: Download Release builds
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: windows-Release-${{ matrix.arch }}-${{ matrix.simd }}
|
||||
path: builds
|
||||
|
||||
- name: Download FastDebug builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: windows-FastDebug-${{ matrix.arch }}-${{ matrix.simd }}
|
||||
path: builds
|
||||
|
||||
- name: Create Distribution package
|
||||
id: generate_package
|
||||
working-directory: ./builds
|
||||
@@ -288,127 +243,21 @@ jobs:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
SIMD: ${{ matrix.simd }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/create_dist_pack.sh Windows
|
||||
|
||||
- name: Upload result package
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ steps.generate_package.outputs.package_name }}
|
||||
path: ${{ steps.generate_package.outputs.package_path }}
|
||||
|
||||
- name: Upload debug package
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ steps.generate_package.outputs.debug_name }}
|
||||
path: ${{ steps.generate_package.outputs.debug_path }}
|
||||
|
||||
- name: Publish packages
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
files: |
|
||||
${{ steps.generate_package.outputs.package_path }}
|
||||
${{ steps.generate_package.outputs.debug_path }}
|
||||
|
||||
build_mac:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [FastDebug, Release]
|
||||
compiler: [clang]
|
||||
arch: [x86_64]
|
||||
name: Mac
|
||||
needs: create_release
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-mac
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# cached: ${{ steps.cache-qt-mac.outputs.cache-hit }}
|
||||
# setup-python: 'false'
|
||||
# aqtversion: ==1.1.3
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
ARCHITECTURE: ${{ matrix.arch }}
|
||||
JOB_CMAKE_OPTIONS: ${{ matrix.cmake_options }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
run: LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH ninja all
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
XDG_RUNTIME_DIR: /root
|
||||
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
|
||||
- name: Package build result
|
||||
working-directory: ./build
|
||||
run: cd bin && tar -cvzf macos-build.tar *.app
|
||||
- name: Upload build result
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: mac-${{ matrix.configuration }}
|
||||
path: ${{ github.workspace }}/build/bin/macos-build.tar
|
||||
mac_zip:
|
||||
name: Build Mac distribution zip
|
||||
needs: build_mac
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-748b19d
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: '0'
|
||||
ref: '${{ github.ref }}'
|
||||
- name: Download Release builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: mac-Release
|
||||
path: builds
|
||||
- name: Download FastDebug builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: mac-FastDebug
|
||||
path: builds
|
||||
- name: Create Distribution package
|
||||
id: generate_package
|
||||
working-directory: ./builds
|
||||
run: $GITHUB_WORKSPACE/ci/linux/create_dist_pack.sh Mac
|
||||
- name: Upload result package
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ steps.generate_package.outputs.package_name }}
|
||||
path: ${{ steps.generate_package.outputs.package_path }}
|
||||
- name: Publish result package
|
||||
uses: softprops/action-gh-release@v1
|
||||
upload_url: ${{ steps.extractUploadUrl.outputs.upload_url }}
|
||||
asset_path: ${{ steps.generate_package.outputs.package_path }}
|
||||
asset_name: ${{ steps.generate_package.outputs.package_name }}
|
||||
asset_content_type: ${{ steps.generate_package.outputs.package_mime }}
|
||||
- name: Upload debug symbols
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
files: ${{ steps.generate_package.outputs.package_path }}
|
||||
|
||||
post_build:
|
||||
name: Post builds to Nebula and Forum
|
||||
needs: [linux_zip, windows_zip, mac_zip]
|
||||
uses: ./.github/workflows/post-build-release.yaml
|
||||
with:
|
||||
linux_result: ${{ needs.linux_zip.result }}
|
||||
windows_result: ${{ needs.windows_zip.result }}
|
||||
mac_result: ${{ needs.mac_zip.result }}
|
||||
releaseTag: ${{ github.ref_name }}
|
||||
secrets: inherit
|
||||
upload_url: ${{ steps.extractUploadUrl.outputs.upload_url }}
|
||||
asset_path: ${{ steps.generate_package.outputs.debug_path }}
|
||||
asset_name: ${{ steps.generate_package.outputs.debug_name }}
|
||||
asset_content_type: ${{ steps.generate_package.outputs.debug_mime }}
|
||||
@@ -1,53 +0,0 @@
|
||||
name: Publish Standalone docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'nightly_*'
|
||||
- 'release_*'
|
||||
|
||||
jobs:
|
||||
build_images:
|
||||
if: github.repository == 'scp-fs2open/fs2open.github.com'
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [FastDebug, Release]
|
||||
include:
|
||||
- configuration: FastDebug
|
||||
tag_suffix: -debug
|
||||
- configuration: Release
|
||||
tag_suffix: ""
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: crazy-max/ghaction-docker-meta@v1
|
||||
with:
|
||||
images: ghcr.io/scp-fs2open/standalone_server
|
||||
tag-latest: false
|
||||
|
||||
- name: Login to GitHub Docker Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.CR_PAT }}
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/standalone/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.docker_meta.outputs.tags }}${{ matrix.tag_suffix }}
|
||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||
build-args: |
|
||||
build_type=${{ matrix.configuration }}
|
||||
@@ -15,22 +15,20 @@ jobs:
|
||||
configuration: [FastDebug, Release]
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-8a6bbbe
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-a8ca321
|
||||
steps:
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-lin
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
# setup-python: 'false'
|
||||
# aqtversion: ==1.1.3
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
@@ -122,30 +120,30 @@ jobs:
|
||||
[[ "${{ github.ref }}" =~ ^refs\/heads\/test\/(.*)$ ]]
|
||||
# Override the revision string so that the builds are named correctly
|
||||
echo "set(FSO_VERSION_REVISION_STR ${BASH_REMATCH[1]})" > "version_override.cmake"
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-win
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt (32 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.arch == 'Win32' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win32_msvc2017
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
# - name: Install Qt (64 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.arch == 'x64' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win64_msvc2017_64
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
- name: Cache Qt
|
||||
id: cache-qt-win
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt (32 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.arch == 'Win32' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win32_msvc2017
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
- name: Install Qt (64 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.arch == 'x64' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win64_msvc2017_64
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
@@ -159,7 +157,7 @@ jobs:
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_USE_SPEECH="ON" \
|
||||
-DFSO_USE_VOICEREC="ON" -DMSVC_SIMD_INSTRUCTIONS="$SIMD" \
|
||||
-DFSO_BUILD_QTFRED=OFF -DFSO_BUILD_TESTS=ON \
|
||||
-DFSO_BUILD_QTFRED=ON -DFSO_BUILD_TESTS=ON \
|
||||
-DFSO_INSTALL_DEBUG_FILES="ON" -A "$ARCHITECTURE" \
|
||||
-G "Visual Studio 16 2019" -T "v142" ..
|
||||
- name: Compile
|
||||
@@ -224,96 +222,3 @@ jobs:
|
||||
DATACORDER_USER: ${{ secrets.DATACORDER_USER }}
|
||||
DATACORDER_SSHPASS: ${{ secrets.DATACORDER_PASSWORD }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh test
|
||||
|
||||
build_mac:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [FastDebug, Release]
|
||||
compiler: [clang]
|
||||
arch: [x86_64]
|
||||
name: Mac
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-mac
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# cached: ${{ steps.cache-qt-mac.outputs.cache-hit }}
|
||||
# setup-python: 'false'
|
||||
# aqtversion: ==1.1.3
|
||||
# py7zrversion: '==0.19.*'
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: Set up test version
|
||||
shell: bash
|
||||
run: |
|
||||
[[ "${{ github.ref }}" =~ ^refs\/heads\/test\/(.*)$ ]]
|
||||
# Override the revision string so that the builds are named correctly
|
||||
echo "set(FSO_VERSION_REVISION_STR ${BASH_REMATCH[1]})" > "version_override.cmake"
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
ARCHITECTURE: ${{ matrix.arch }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
run: LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH ninja all
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
XDG_RUNTIME_DIR: /root
|
||||
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
|
||||
- name: Package build result
|
||||
working-directory: ./build
|
||||
run: cd bin && tar -cvzf macos-build.tar *.app
|
||||
- name: Upload build result
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: mac-${{ matrix.configuration }}
|
||||
path: ${{ github.workspace }}/build/bin/macos-build.tar
|
||||
mac_zip:
|
||||
name: Build Mac distribution zip
|
||||
needs: build_mac
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-748b19d
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: '0'
|
||||
ref: '${{ github.ref }}'
|
||||
- name: Download Release builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: mac-Release
|
||||
path: builds
|
||||
- name: Download FastDebug builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: mac-FastDebug
|
||||
path: builds
|
||||
- name: Create Distribution package
|
||||
working-directory: ./builds
|
||||
run: $GITHUB_WORKSPACE/ci/linux/create_dist_pack.sh Mac
|
||||
- name: Upload result package
|
||||
working-directory: ./builds
|
||||
env:
|
||||
INDIEGAMES_USER: ${{ secrets.INDIEGAMES_USER }}
|
||||
INDIEGAMES_SSHPASS: ${{ secrets.INDIEGAMES_PASSWORD }}
|
||||
DATACORDER_USER: ${{ secrets.DATACORDER_USER }}
|
||||
DATACORDER_SSHPASS: ${{ secrets.DATACORDER_PASSWORD }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh test
|
||||
@@ -1,62 +0,0 @@
|
||||
name: Build caches for master
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
QT_VERSION: 5.12.9
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
compiler: [gcc-5, gcc-10]
|
||||
cmake_options: [""]
|
||||
include:
|
||||
# Also include configurations that check if the code compiles without the graphics backends
|
||||
- configuration: Debug
|
||||
compiler: gcc-10
|
||||
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
|
||||
- configuration: Release
|
||||
compiler: gcc-10
|
||||
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-8a6bbbe
|
||||
steps:
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-lin
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
# setup-python: 'false'
|
||||
# aqtversion: ==1.1.3
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ runner.os }}-${{ matrix.configuration }}-${{ matrix.compiler }}
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
JOB_CMAKE_OPTIONS: ${{ matrix.cmake_options }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
run: LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH ninja -k 20 all
|
||||
@@ -1,82 +0,0 @@
|
||||
name: Post Release builds
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
# Trigger when called by other workflows
|
||||
inputs:
|
||||
linux_result: # job.result; either success, failure, canceled or skipped
|
||||
required: true
|
||||
type: string
|
||||
windows_result: # job.result
|
||||
required: true
|
||||
type: string
|
||||
mac_result: # job.result
|
||||
required: true
|
||||
type: string
|
||||
releaseTag:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
workflow_dispatch:
|
||||
# Trigger manually from Github
|
||||
inputs:
|
||||
releaseTag:
|
||||
description: Release Tag
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
GITHUB_REPO: ${{ github.repository }} # repo this workflow was called from
|
||||
INDIEGAMES_USER: ${{ secrets.INDIEGAMES_USER }}
|
||||
INDIEGAMES_SSHPASS: ${{ secrets.INDIEGAMES_PASSWORD }}
|
||||
NEBULA_USER: ${{ secrets.NEBULA_USER }}
|
||||
NEBULA_PASSWORD: ${{ secrets.NEBULA_PASSWORD }}
|
||||
HLP_API: ${{ secrets.HLP_API }}
|
||||
HLP_KEY: ${{ secrets.HLP_KEY }}
|
||||
RELEASE_TAG: ${{ inputs.releaseTag }}
|
||||
|
||||
jobs:
|
||||
post_builds:
|
||||
name: Post builds on Nebula and the forums
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Caller
|
||||
# Name of what called this yaml. Used to debug auto/manual step being triggered
|
||||
run: echo "github.event_name= ${{ github.event_name }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
ref: ${{ env.RELEASE_TAG }}
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: pip install -r ci/post/requirements.txt
|
||||
|
||||
- name: Post Builds (Auto trigger)
|
||||
if: ${{ (github.event_name == 'workflow_call') || (github.event_name == 'push') }}
|
||||
env:
|
||||
LINUX_RESULT: ${{ inputs.linux_result }}
|
||||
WINDOWS_RESULT: ${{ inputs.windows_result }}
|
||||
MAC_RESULT: ${{ inputs.mac_result }}
|
||||
GITHUB_REPO: ${{ github.repository }} # repo this workflow was called from
|
||||
INDIEGAMES_USER: ${{ secrets.INDIEGAMES_USER }}
|
||||
INDIEGAMES_SSHPASS: ${{ secrets.INDIEGAMES_PASSWORD }}
|
||||
NEBULA_USER: ${{ secrets.NEBULA_USER }}
|
||||
NEBULA_PASSWORD: ${{ secrets.NEBULA_PASSWORD }}
|
||||
HLP_API: ${{ secrets.HLP_API }}
|
||||
HLP_KEY: ${{ secrets.HLP_KEY }}
|
||||
RELEASE_TAG: ${{ inputs.releaseTag }}
|
||||
run: python ci/post/main.py release
|
||||
|
||||
- name: Post Builds (Manual trigger)
|
||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
# assume user knows what they're doing...
|
||||
env:
|
||||
LINUX_RESULT: success
|
||||
WINDOWS_RESULT: success
|
||||
MAC_RESULT: success
|
||||
run: |
|
||||
echo "releaseTag= ${{ env.RELEASE_TAG }}"
|
||||
echo "repository= ${{ github.repository }}"
|
||||
python ci/post/main.py release
|
||||
@@ -0,0 +1,153 @@
|
||||
name: Check a pull request
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
env:
|
||||
QT_VERSION: 5.12.9
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
compiler: [gcc-5, gcc-10, clang-9]
|
||||
cmake_options: [""]
|
||||
include:
|
||||
# Also include configurations that check if the code compiles without the graphics backends
|
||||
- configuration: Debug
|
||||
compiler: gcc-10
|
||||
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
|
||||
- configuration: Release
|
||||
compiler: gcc-10
|
||||
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-e977d36
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
setup-python: 'false'
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
JOB_CMAKE_OPTIONS: ${{ matrix.cmake_options }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
run: LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH ninja -k 20 all
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
XDG_RUNTIME_DIR: /root
|
||||
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
|
||||
- name: Run Clang Tidy
|
||||
# Clang-tidy reuses the precompiled headers so this only makes sense for the clang compilers
|
||||
if: startsWith(matrix.compiler, 'clang-')
|
||||
run: $GITHUB_WORKSPACE/ci/linux/clang_tidy.sh ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}
|
||||
- name: Process clang-tidy warnings
|
||||
if: startsWith(matrix.compiler, 'clang-')
|
||||
uses: asarium/clang-tidy-action@v1
|
||||
with:
|
||||
fixesFile: clang-fixes.yaml
|
||||
|
||||
build_windows:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
compiler: [MSVC]
|
||||
arch: [Win32, x64]
|
||||
name: Windows
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Prepare Environment
|
||||
run: choco install ninja
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache Qt
|
||||
id: cache-qt-win
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt (32 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.compiler == 'MSVC' && matrix.arch == 'Win32' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win32_msvc2017
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
- name: Install Qt (64 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.compiler == 'MSVC' && matrix.arch == 'x64' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win64_msvc2017_64
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
ARCHITECTURE: ${{ matrix.arch }}
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
if [ "$COMPILER" = "MinGW" ]; then
|
||||
cmake -DFSO_USE_SPEECH="OFF" -DFSO_FATAL_WARNINGS="ON" -DFSO_USE_VOICEREC="OFF" -DFSO_BUILD_TESTS="ON" \
|
||||
-DFSO_BUILD_FRED2="OFF" -DCMAKE_BUILD_TYPE=$CONFIGURATION -G "Ninja" ..
|
||||
else
|
||||
cmake -DFSO_USE_SPEECH="ON" -DFSO_FATAL_WARNINGS="ON" -DFSO_USE_VOICEREC="OFF" -DFSO_BUILD_TESTS="ON" \
|
||||
-DMSVC_SIMD_INSTRUCTIONS=SSE2 -DFSO_BUILD_FRED2="ON" -G "Visual Studio 16 2019" \
|
||||
-DFSO_BUILD_QTFRED=ON -T "v142" -A "$ARCHITECTURE" ..
|
||||
fi
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$COMPILER" = "MinGW" ]; then
|
||||
cmake --build . --config "$CONFIGURATION"
|
||||
else
|
||||
cmake --build . --config "$CONFIGURATION" -- /verbosity:minimal
|
||||
fi
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$COMPILER" = "MinGW" ]; then
|
||||
./bin/unittests --gtest_shuffle
|
||||
else
|
||||
"./bin/$CONFIGURATION/unittests" --gtest_shuffle
|
||||
fi
|
||||
@@ -0,0 +1,48 @@
|
||||
name: Publish Standalone docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build_images:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [FastDebug, Release]
|
||||
include:
|
||||
- configuration: FastDebug
|
||||
latest_tag: latest_debug
|
||||
nightly_tag: nightly_debug
|
||||
release_tag: release_debug
|
||||
- configuration: Release
|
||||
latest_tag: latest
|
||||
nightly_tag: nightly
|
||||
release_tag: release
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
- name: Docker login
|
||||
run: echo ${{ secrets.DOCKER_ACCESSTOKEN }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
- name: Docker build
|
||||
run: docker build --build-arg build_type=${{ matrix.configuration }} -f docker/standalone/Dockerfile -t ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }} .
|
||||
|
||||
- name: Push latest tag
|
||||
run: |
|
||||
docker push ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }}
|
||||
|
||||
- name: Push nightly tag
|
||||
if: contains(github.ref, 'refs/tags/nightly_')
|
||||
run: |
|
||||
docker tag ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }} ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.nightly_tag }}
|
||||
docker push ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.nightly_tag }}
|
||||
|
||||
- name: Push release tag
|
||||
if: contains(github.ref, 'refs/tags/release_')
|
||||
run: |
|
||||
docker tag ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }} ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.release_tag }}
|
||||
docker push ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.release_tag }}
|
||||
@@ -1,220 +0,0 @@
|
||||
name: Check a pull request
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
env:
|
||||
QT_VERSION: 5.12.9
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
compiler: [gcc-5, gcc-10, clang-9]
|
||||
cmake_options: [""]
|
||||
include:
|
||||
# Also include configurations that check if the code compiles without the graphics backends
|
||||
- configuration: Debug
|
||||
compiler: gcc-10
|
||||
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
|
||||
- configuration: Release
|
||||
compiler: gcc-10
|
||||
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-8a6bbbe
|
||||
steps:
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-lin
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
# setup-python: 'false'
|
||||
# aqtversion: ==1.1.3
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ runner.os }}-${{ matrix.configuration }}-${{ matrix.compiler }}
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
JOB_CMAKE_OPTIONS: ${{ matrix.cmake_options }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
run: LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH ninja -k 20 all
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
XDG_RUNTIME_DIR: /root
|
||||
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
|
||||
- name: Run Clang Tidy
|
||||
# Clang-tidy reuses the precompiled headers so this only makes sense for the clang compilers
|
||||
if: startsWith(matrix.compiler, 'clang-')
|
||||
run: $GITHUB_WORKSPACE/ci/linux/clang_tidy.sh ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}
|
||||
- name: Process clang-tidy warnings
|
||||
if: startsWith(matrix.compiler, 'clang-')
|
||||
uses: asarium/clang-tidy-action@v1
|
||||
with:
|
||||
fixesFile: clang-fixes.yaml
|
||||
|
||||
build_windows:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
compiler: [MSVC]
|
||||
arch: [Win32, x64]
|
||||
name: Windows
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Prepare Environment
|
||||
run: choco install ninja
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-win
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt (32 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.compiler == 'MSVC' && matrix.arch == 'Win32' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win32_msvc2017
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
# - name: Install Qt (64 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.compiler == 'MSVC' && matrix.arch == 'x64' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win64_msvc2017_64
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
ARCHITECTURE: ${{ matrix.arch }}
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
if [ "$COMPILER" = "MinGW" ]; then
|
||||
cmake -DFSO_USE_SPEECH="OFF" -DFSO_FATAL_WARNINGS="ON" -DFSO_USE_VOICEREC="OFF" -DFSO_BUILD_TESTS="ON" \
|
||||
-DFSO_BUILD_FRED2="OFF" -DCMAKE_BUILD_TYPE=$CONFIGURATION -G "Ninja" ..
|
||||
else
|
||||
cmake -DFSO_USE_SPEECH="ON" -DFSO_FATAL_WARNINGS="ON" -DFSO_USE_VOICEREC="OFF" -DFSO_BUILD_TESTS="ON" \
|
||||
-DMSVC_SIMD_INSTRUCTIONS=SSE2 -DFSO_BUILD_FRED2="ON" -G "Visual Studio 16 2019" \
|
||||
-DFSO_BUILD_QTFRED=OFF -T "v142" -A "$ARCHITECTURE" ..
|
||||
fi
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$COMPILER" = "MinGW" ]; then
|
||||
cmake --build . --config "$CONFIGURATION"
|
||||
else
|
||||
cmake --build . --config "$CONFIGURATION" -- /verbosity:minimal
|
||||
fi
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$COMPILER" = "MinGW" ]; then
|
||||
./bin/unittests --gtest_shuffle
|
||||
else
|
||||
"./bin/$CONFIGURATION/unittests" --gtest_shuffle
|
||||
fi
|
||||
|
||||
build_mac:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
compiler: [clang]
|
||||
arch: [x86_64]
|
||||
cmake_options: [""]
|
||||
name: Mac
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-mac
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# cached: ${{ steps.cache-qt-mac.outputs.cache-hit }}
|
||||
# setup-python: 'false'
|
||||
# aqtversion: ==1.1.3
|
||||
# py7zrversion: '==0.19.*'
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ runner.os }}-${{ matrix.configuration }}-${{ matrix.compiler }}
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
ARCHITECTURE: ${{ matrix.arch }}
|
||||
JOB_CMAKE_OPTIONS: ${{ matrix.cmake_options }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
run: LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH ninja all
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
XDG_RUNTIME_DIR: /root
|
||||
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
|
||||
- name: Run Clang Tidy
|
||||
# Clang-tidy reuses the precompiled headers so this only makes sense for the clang compilers
|
||||
if: startsWith(matrix.compiler, 'clang-')
|
||||
run: $GITHUB_WORKSPACE/ci/linux/clang_tidy.sh ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}
|
||||
- name: Process clang-tidy warnings
|
||||
if: startsWith(matrix.compiler, 'clang-')
|
||||
uses: asarium/clang-tidy-action@v1
|
||||
with:
|
||||
fixesFile: clang-fixes.yaml
|
||||
@@ -4,4 +4,3 @@
|
||||
[submodule "lib/libRocket"]
|
||||
path = lib/libRocket
|
||||
url = https://github.com/asarium/libRocket.git
|
||||
|
||||
+5
-16
@@ -39,18 +39,11 @@ endif(NOT CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES)
|
||||
# Override standard CMake configurations
|
||||
set(CMAKE_CONFIGURATION_TYPES Debug Release FastDebug CACHE STRING "Available configurations" FORCE)
|
||||
|
||||
set(FSO_LANGUAGES C CXX)
|
||||
|
||||
if(APPLE)
|
||||
set(FSO_LANGUAGES ${FSO_LANGUAGES} OBJC OBJCXX)
|
||||
endif()
|
||||
|
||||
PROJECT(FS2_Open LANGUAGES ${FSO_LANGUAGES})
|
||||
PROJECT(FS2_Open)
|
||||
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
# Currently only GCC support ccache properly without generating additional warnings
|
||||
# Enable ccache on Mac even for clang
|
||||
if(CCACHE_PROGRAM AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR APPLE))
|
||||
if(CCACHE_PROGRAM AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
if(NOT CMAKE_C_COMPILER_LAUNCHER)
|
||||
message(STATUS "Using ccache for C compilation: ${CCACHE_PROGRAM}")
|
||||
SET(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
|
||||
@@ -89,11 +82,9 @@ IF(RESET_INSTALL_PREFIX)
|
||||
ENDIF(NOT $ENV{FS2PATH} STREQUAL "")
|
||||
ENDIF(RESET_INSTALL_PREFIX)
|
||||
|
||||
IF(WIN32 OR APPLE)
|
||||
OPTION(FSO_USE_SPEECH "Use text-to-speach libraries" ON)
|
||||
ENDIF(WIN32 OR APPLE)
|
||||
|
||||
IF (WIN32)
|
||||
OPTION(FSO_USE_SPEECH "Use Windows specific text-to-speach libraries" ON)
|
||||
|
||||
OPTION(FSO_USE_VOICEREC "Enable voice recognition support" ON)
|
||||
|
||||
OPTION(FSO_BUILD_FRED2 "Build FRED2 binary" ON)
|
||||
@@ -216,10 +207,8 @@ include(package)
|
||||
include(doxygen)
|
||||
|
||||
# Print used options to log
|
||||
IF(WIN32 OR APPLE)
|
||||
message(STATUS "Using text to speech: ${FSO_USE_SPEECH}")
|
||||
ENDIF()
|
||||
IF (WIN32)
|
||||
message(STATUS "Using text to speech: ${FSO_USE_SPEECH}")
|
||||
message(STATUS "Using voice recogition: ${FSO_USE_VOICEREC}")
|
||||
message(STATUS "Building FRED2: ${FSO_BUILD_FRED2}")
|
||||
elseif(UNIX)
|
||||
|
||||
-438
@@ -3,439 +3,6 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [22.2.0] - 2022-08-06
|
||||
|
||||
```
|
||||
Changelog note:
|
||||
As of this version, in an attempt to make the changelog easier to navigate and read,
|
||||
each section will have collapsible subsections, ordered alphabetically by topic.
|
||||
Headlining features will always be on top.
|
||||
```
|
||||
### Added
|
||||
|
||||
<details><summary> <b>Big Feature: SEXP Containers</b> </summary>
|
||||
|
||||
> As opposed to a SEXP variable, which stores only one value, a SEXP container stores a collection of values.
|
||||
> Consequently, containers allow for handling data in much more sophisticated ways than variables.
|
||||
> Containers come in two types:
|
||||
> * List container - an ordered sequence of values, sort of a cross between Python lists and deques
|
||||
> * Map container - pairs of keys associated with data, like Lua tables or Python dictionaries
|
||||
> Further details in the [SEXP Containers announcement thread](https://www.hard-light.net/forums/index.php?topic=98209.0)
|
||||
</details>
|
||||
|
||||
<details><summary> <b>Big Feature: LuaAI</b> </summary>
|
||||
|
||||
> LuaAI allows scripters to add a new AI Goal (like ai-chase) that will run script instead of internal logic.
|
||||
> Adding a Lua AI section in a -sexp.tbm and a few functions to a corresponding -sct.tbm to define the AI logic in certain conditions. More documentation on these functions is available in the LuaAISEXP-Object of the Scripting Docs.
|
||||
</details>
|
||||
|
||||
<details><summary>AI:</summary>
|
||||
|
||||
- `$AI ignores aspect lock for leading:` Flag, which lets AI continue to lead their target while performing an Aspect Lock
|
||||
</details>
|
||||
|
||||
<details><summary>Animation: </summary>
|
||||
|
||||
- `pause on reverse` Flag
|
||||
</details>
|
||||
|
||||
<details><summary>Controls: </summary>
|
||||
|
||||
- Handle international keyboard layouts
|
||||
- Re-Add `-joy-info` command line flag, for slightly quicker Joystick GUID retrieval
|
||||
- Add `-controlconfig_tbl` command line flag to print out a template controlconfigdefaults.tbl
|
||||
</details>
|
||||
|
||||
<details><summary>FRED: </summary>
|
||||
|
||||
- Support the full number of nebula poofs using a multi-select list
|
||||
- Add the ability to edit nebula fog colors, since that previously was not part of the FRED UI
|
||||
- Flags `same-arrival-warp-when-docked` and `same-departure-warp-when-docked` to allow docked ships to still use custom warp parameters
|
||||
- Allow highlighting custom-colored events by drawing a border on them
|
||||
- Can now rotate objects relative to other objects in the Object Editor
|
||||
- Dialog to calculate relative distance and orientation between ships
|
||||
- Flag "Point using uvec" to point ships while incorporating the ship's uvec
|
||||
- `ai-attackable-if-no-collide` flag, which prevents turrets from ignoring a target with `no_collide`
|
||||
</details>
|
||||
|
||||
<details><summary>QtFRED: </summary>
|
||||
|
||||
- Reinforcements Dialog
|
||||
- Implemented Replace Data for Number and String
|
||||
- Texture Replacement Dialog
|
||||
- TBL Viewer in the Ship Editor
|
||||
</details>
|
||||
|
||||
<details><summary>Graphics: </summary>
|
||||
|
||||
- Allow particles to use a random chance for spawning through the `+Chance` parameter
|
||||
- Command-line parameter `-fov_cockpit` which allows setting an FoV for the cockpit model separately from the viewport FoV. This allows using custom FoVs without distorting the cockpit
|
||||
- Allow user to export environment map to a PNG from the F3 Lab by pressing M
|
||||
- Weapon lights: Configurable lighting data for weapons, both on a per-weapon basis and globally via lighting profiles
|
||||
- Implemented debug sphere queuing and rendering
|
||||
</details>
|
||||
|
||||
<details><summary>Localization: </summary>
|
||||
|
||||
- Make strings for Match Speed Indicator, Glide Indicator, and Afterburner Indicator translateable
|
||||
</details>
|
||||
|
||||
<details><summary>Modding: </summary>
|
||||
|
||||
- `game_settings.tbl` setting `$Supernova hits at zero:`, which causes Supernovae to hit when the timer gets to 0, instead of the default of hitting 5 seconds before the time is up
|
||||
- `game_settings.tbl` settings `$Show-subtitle uses pixels:` and `$Show-subtitle base resolution:`, which can be used in conjunction to scale subtitles
|
||||
- `game_settings.tbl` setting `$Always warn player about unbound keys used in Directives Gauge:`, to show such warning to players when needed (without it these warnings only apply to Training Missions)
|
||||
- Ship flags `fail-sound-locked-primary` and `fail-sound-locked-secondary` to allow the firing fail sound to play if the player tries to fire weapons that are locked
|
||||
- Subsystem Flag "hide turret from loadout stats"
|
||||
- Allow weapon to override turret name with `$Turret Name:`
|
||||
</details>
|
||||
|
||||
<details><summary>Multiplayer: </summary>
|
||||
|
||||
- Save PXO stats to local pilot file
|
||||
</details>
|
||||
|
||||
<details><summary>Performance: </summary>
|
||||
|
||||
- Preload subspace tunnel if the `mission-set-subspace` SEXP is used in the mission
|
||||
</details>
|
||||
|
||||
<details><summary>SEXPs: </summary>
|
||||
|
||||
- `cancel-future-waves`, for stopping wings that still had waves queued from spawning
|
||||
- Added optional flag to `show-subtitle-text` and `show-subtitle-image` to not change the subtitle aspect ratio
|
||||
- `set-nav-color` and `set-nav-visited-color`, which allows setting custom colors to Navpoints in a mission
|
||||
</details>
|
||||
|
||||
<details><summary>Scripting: </summary>
|
||||
|
||||
- Support for decals (see `decaldefinition` in the Scripting documentation)
|
||||
- Missile list iterator: `mn.getMissileList()`
|
||||
- Campaign Start hook: `On Campaign Begin`
|
||||
- Player Loaded hook: `On Player Loaded`
|
||||
- Added a few functions to get information on HUD gauges (check `HudGauge` in the Scripting documentation)
|
||||
- Function `ship.checkVisibility` which checks if a ship can appear on the viewer's radar
|
||||
- Function `hu.flashTargetBox` which causes the target box to flash
|
||||
- Function `hu.getTargetDistance` to return the distance to target as reported by the HUD
|
||||
- Function `mn.hasLineOfSight` which checks if one point has line of sight to the other
|
||||
- Function `mn.getLineOfSightFirstIntersect` which checks for line of sight and returns the distance of the first interruption if not
|
||||
- Variable `gr.CurrentResizeMode`, which can get or set the scaling mode to be used by the `gr.*` drawing methods
|
||||
- Function `gr.draw3dLine`, which draws a line in space between two points
|
||||
- Allow SCPUI to run `$On Briefing Stage:` hooks
|
||||
- New bindings to allow proper hooking into control actions based on the control's enumeration name
|
||||
- Allow scripted controls to override hardcoded functionality
|
||||
- Fennel script support
|
||||
- Lua BitOp 1.0.2 extension library, allowing the usage of bit operations
|
||||
</details>
|
||||
|
||||
<details><summary>Technical: </summary>
|
||||
|
||||
- Class encapsulation for Volition's linked list
|
||||
- Improve a debug console message to be more informative
|
||||
- Type-safe `TIMESTAMP` class
|
||||
- Add `timestamp_since()` function
|
||||
- Add methods to compare timestamps, calculate deltas from an existing timestamp and a number of milliseconds, and to calculate deltas between timestamps
|
||||
- Create a command line option for logging data packets
|
||||
- Add more information to Multi-related debug logging
|
||||
- Enable Doxygen for QTFred
|
||||
- Set up Mac builds on CI, allowing builds to be published automatically for Mac
|
||||
- Add some logging to `waitAsync`
|
||||
</details>
|
||||
|
||||
### Changed
|
||||
|
||||
<details><summary>Game: </summary>
|
||||
|
||||
- Only display one popup when the player gets to the main hall
|
||||
- Allow an unlimited number of ship flags to be parsed at once
|
||||
</details>
|
||||
|
||||
<details><summary>AI: </summary>
|
||||
|
||||
- Improve Player Orders to allow for a nearly unlimited number of player orders, as opposed to the 32 before
|
||||
- Update `ai_turn_towards_vector` to use a target velocity for banking
|
||||
</details>
|
||||
|
||||
<details><summary>Audio: </summary>
|
||||
|
||||
- Allow unlimited soundtracks and spooled music (Previously limited to 30 and 50 respectively)
|
||||
- Allow unlimited sound effects
|
||||
- Dynamically set the maximum number of audio channels based on how many the user's system can support
|
||||
</details>
|
||||
|
||||
<details><summary>FRED: </summary>
|
||||
|
||||
- CTRL+V is now add-paste (used to be CTRL+P)
|
||||
- CTRL+SHIFT+V is now overwrite-paste (used to be CTRL+V)
|
||||
- Replace `Always_show_goals` with `Toggle_showing_goals` which toggles whether the goals are shown, and works in any mission (instead of only Training Missions)
|
||||
- No longer disable icon dropdowns for models that have their own icons
|
||||
- No longer change the ship class when the icon type is a special popup
|
||||
- Make the number of player orders FRED can handle dynamic
|
||||
- Restore original Error Checking behavior where the function would exit after one check (for consistency)
|
||||
- Exclude player ships from Error Checking for ships in a wing sharing the same orders
|
||||
- Split "No subsystems found" and "Not all subsystems have a record in ships.tbl" warnings into two different warning flags
|
||||
</details>
|
||||
|
||||
<details><summary>Graphics: </summary>
|
||||
|
||||
- Improved light attenuation to look more real
|
||||
- Increase POF vertex limit, allowing up to `2^32 - 1` vertices to be used by a single subobject
|
||||
</details>
|
||||
|
||||
<details><summary>Performance: </summary>
|
||||
|
||||
- Delay `ai_new_maybe_reposition_attack_subsys` to only run once a second. Before this change, it could take around 13% of runtime in crowded missions
|
||||
- Cached subsystem indexing, speeding up subsystem lookups, which run for multiple ships, multiple times per frame
|
||||
</details>
|
||||
|
||||
<details><summary>Scripting: </summary>
|
||||
|
||||
- Enhance help text for Lua SEXPs
|
||||
- Converted the following Lua Hooks to the new format to add documentation: `On Death`, `On Game Init`, `On Ship Collision`
|
||||
</details>
|
||||
|
||||
<details><summary>Technical: </summary>
|
||||
|
||||
- Turn `insertion_sort` into a templated function that uses assignment
|
||||
- CMake: Update `PREBUILT_VERSION_NAME` to latest (mac libs)
|
||||
- Optimized space used in the new POF version
|
||||
- Cleanup model code
|
||||
- Cleanup beam light functions by consolidating some duplicated code and removing unreachable logic
|
||||
- Cleanup wing wave code
|
||||
- Move special parsing functions into new `parsehi.cpp` and `parsehi.h` files
|
||||
- Actually clear delayed SSM parsing data when it's no longer needed
|
||||
- Allow C++ to interact more with LuaTables, allowing it to extract userdata like objects or ships from them
|
||||
- Use `SCP_string` for constructing multi/event log lines
|
||||
- Replace `+Orders Accepted:` Bitfield with flag list
|
||||
- Handle deprecated command line flags programmatically, avoiding lots of code duplication
|
||||
- Set up debug_filter.cfg to have default filters that can be turned off
|
||||
</details>
|
||||
|
||||
### Removed
|
||||
|
||||
<details><summary>FRED: </summary>
|
||||
|
||||
- Remove the `Allow Daisy-Chained Docking` mission flag
|
||||
- Remove "Delete Item" from the right-click menu in the Event Editor
|
||||
</details>
|
||||
|
||||
<details><summary>Technical: </summary>
|
||||
|
||||
- Remove light filtering data which didn't work and increased code complexity
|
||||
- Remove a pointless assert that checked if `SEXP_NODE_INCREMENT` (a define, set at 250) was greater than zero
|
||||
</details>
|
||||
|
||||
### Fixed
|
||||
|
||||
<details><summary>Game: </summary>
|
||||
|
||||
- Missing whitespace in credits
|
||||
- Stopped destroyed submodels from showing in Techroom
|
||||
- Prevent a crash when an object dies in the same frame as their goal
|
||||
- Always record primary bank ammo capacity during ship creation, preventing situations where a ballistic weapon would accidentally be set to zero ammo
|
||||
- Properly remove ships from the Techroom when they're not in the Tech Database
|
||||
- Load the correct Techroom data after a Tech Database Reset
|
||||
- Don't tick Supernova timer while the game is paused
|
||||
- Make `set-camera-facing` use default identity orientation when the `$Use host orientation` flag is active and no host is present
|
||||
- When a hotkey text replacement string refers to an unbound control, use the name of the binding instead of "none"
|
||||
- Fix usage of default tech database flags
|
||||
- Prevent a crash in the loadout screen with a weapons-locked ship
|
||||
- Fix two crashes in the F3 lab
|
||||
- End the campaign properly when a mission is skipped, preventing crashing in some situations
|
||||
- Prevent a crash when a message is sent from an invalid source
|
||||
- Prevent a broken campaign state if the player quits the game from the options menu during the debriefing
|
||||
- Add some checks before converting a message to Command
|
||||
- Prevent accidentally skipping cutscenes caused by hitting the spacebar during a loading screen
|
||||
- Fixed an issue where ships would start with the wrong hull values in Red Alert missions
|
||||
</details>
|
||||
|
||||
<details><summary>AI: </summary>
|
||||
|
||||
- Fix turret swarm weapons by removing a few checks that are no longer valid
|
||||
- Fix a few issues with maneuvers
|
||||
</details>
|
||||
|
||||
<details><summary>Animations: </summary>
|
||||
|
||||
- Fix broken turret animations in the Techroom
|
||||
</details>
|
||||
|
||||
<details><summary>Audio: </summary>
|
||||
|
||||
- Fix cockpit engine sound volume changing unexpectedly
|
||||
- Fix a timing bug that would sometimes prevent music from being initialized when re-playing a mission
|
||||
- Check all possible extensions for audio files
|
||||
</details>
|
||||
|
||||
<details><summary>Controls: </summary>
|
||||
|
||||
- Flush the mouse state after toggling between `Use_mouse_to_fly` states, preventing situations where a stale mouse state would be used
|
||||
- Fix a conflict between Free Look View controls and directional thrust controls
|
||||
</details>
|
||||
|
||||
<details><summary>FRED: </summary>
|
||||
|
||||
- Prevent Ship Select Dialog from crashing if there are too many IFFs
|
||||
- Support saving nebula fog colors
|
||||
- Properly save `nav-carry-status` flag
|
||||
- Don't treat a bare `<argument>` as an error
|
||||
- Prevent adding duplicated variables and display an error to the user when they try
|
||||
- Clean up some parse problems
|
||||
- Fix assignment of alt-name when using the hash
|
||||
- Guard against off-by-one errors in `replace_one` and `replace_all`
|
||||
- Improve error checking of campaign loop branches
|
||||
- Improve error reporting for bad SEXP nodes
|
||||
- Prevent a crash with paste/add-paste when the clipboard was empty
|
||||
- Prevent rounding/conversion issues that caused FRED to think an angle or position was modified when it wasn't
|
||||
- Skip absent ships or wings when running `clear-goals`, instead of stopping the whole operation
|
||||
</details>
|
||||
|
||||
<details><summary>Graphics: </summary>
|
||||
|
||||
- Disable Lightshafts while the Supernova glare effect is active. This partial fix ensures the glare shows up with the correct brightness
|
||||
- Update and correctly load environment maps in F3 Lab
|
||||
- Correctly set textures and Team Colors with `change-ship-class`
|
||||
- Fixes Team Colors not working with `show-ship`
|
||||
- Correct positioning and angles for background bitmaps and suns
|
||||
- Clean up ship arrival and departure effects
|
||||
- Fix clearing a skybox model when it isn't the mission default
|
||||
- Fix an assertion with Fireball LODs which would wrongly trigger if the number of LODs was the same as the maximum
|
||||
</details>
|
||||
|
||||
<details><summary>HUD: </summary>
|
||||
|
||||
- Make HUD Target distance consistent
|
||||
- Fix a crash with scripted HUDs
|
||||
- Correctly update RTT Cockpit Gauges with `change-ship-class`
|
||||
- Ignore HUD brackets with a size of zero or less (instead of just crashing)
|
||||
- Squad menu wasn't scrolling properly with PageDown in some cases
|
||||
- Fix issues with loading of hud config presets
|
||||
- Fix parsing issues with HUD color presets
|
||||
- Continue parsing remaining gauges if one isn't found
|
||||
- Use translated strings both when saving and loading
|
||||
- Fix an issue where a target's orders would be displayed wrongly in the target box
|
||||
- Fix asteroid brackets disappearing or displaying wrong values
|
||||
</details>
|
||||
|
||||
<details><summary>Modding: </summary>
|
||||
|
||||
- Delay processing `$Player Weapon Precedence:` until after parsing, avoiding ships starting with the wrong weapons in some situations
|
||||
- Make the `$Player Weapon Precedence:` list actually give precedence
|
||||
- Empty `+Orders Accepted List:` entries were causing the ship to accept the default orders, instead of no orders as would be correct
|
||||
- Show a Warning if a turret submodel is not an immediate child of the base object and handle it as a single-part object to prevent a crash
|
||||
</details>
|
||||
|
||||
<details><summary>Multiplayer: </summary>
|
||||
|
||||
- Limit client collisions to prevent bugs, such as flinging players with high pings who collided multiple times between packet updates
|
||||
- Fix multiple timestamp issues
|
||||
- Multi messaging and multi pause screen
|
||||
- Make sure maintenance stuff (pings, voice, file xfer, etc.) isn't affected by pausing or time compression
|
||||
- Sync issues caused by options menu and screenshots
|
||||
- Prevent an error with long messages in PXO Chat by using proper buffer size
|
||||
- Fix some issues with Multiplayer SEXPs
|
||||
- Try to handle incompatible sexp packets better
|
||||
- Fix operator name issue with debug messages
|
||||
- Enable the new show-subtitle-text options
|
||||
- Improve interpolation, fixing the jerkiness that used to occur when the interpolation code basically guessed the timing on when packets were coming in
|
||||
- Fix rollback primary collisions, vastly improving accuracy on moving targets even at high pings
|
||||
- Fix the standalone ship being added to ship list
|
||||
- Stop non-printable keys adding text to multi message buffer
|
||||
- Prevent the standalone from crashing if a player tried to switch secondaries in a ship that doesn't have secondaries
|
||||
- Fix a standalone crash from disabled uniform buffer manager
|
||||
- Prevent a crash related to Observer-type players
|
||||
- Fix network issues on MacOS
|
||||
</details>
|
||||
|
||||
<details><summary>Scripting: </summary>
|
||||
|
||||
- Make hook conditions consistent and handle combinations that didn't work properly before
|
||||
- Fix `maybePlayCutscene` arguments
|
||||
- Add `isActive` checks for new-style script hooks
|
||||
- Sync the two `VIRTVAR`s for Orientation in subsystems and submodel instances, which behaved in different ways
|
||||
- Fixed a few timing and framerate related issues for playing movies via script
|
||||
- Ensure the Lua interpreter has enough space in its stack, and allow reserving more space if necessary
|
||||
- Fixed an memory leak in the Lua interpreter caused by string-eval sexps, which never cleaned up the generated code
|
||||
</details>
|
||||
|
||||
<details><summary>SEXPs: </summary>
|
||||
|
||||
- Avoid setting .rest for `Locked_sexp_true/false` with malformed SEXPs
|
||||
- Fix some memory management problems in the `for-*` operators, avoiding a memory leak
|
||||
- Fix crash when removing form-on-wing order with `remove-goal`
|
||||
- Fix `show-subtitle` SEXP which was missing the `width` argument
|
||||
- Reset volumes changed via `adjust-audio-volume` when the mission closes
|
||||
- Prevent too many dynamic SEXPs from breaking the SEXP system
|
||||
</details>
|
||||
|
||||
<details><summary>Technical: </summary>
|
||||
|
||||
- Check if index is valid before accessing `Weapon_info`, preventing CTDs under certain conditions
|
||||
- When `weapon_create` was called while `MAX_WEAPONS` was reached, the previous algorithm didn't achieve desired results, and simply locked up the game. Instead, we now simply won't allow a new weapon to be created in such condition
|
||||
- Refactored Supernova code and fixed some state transition issues in it
|
||||
- Prevent crashes in the event of a subsystem mismatch during `change_ship_type`
|
||||
- Change include order to fix compilation of unit tests on Mac
|
||||
- Patch freeing of SEXP nodes, preventing a crash in certain conditions
|
||||
- Fix a memory leak in model code
|
||||
- Fix a crash in the Ship Creation code if a ship's name was exactly the maximum allowed length
|
||||
- Removed vestigial bit-depth asserts for aabitmaps, preventing crashes with certain image types
|
||||
- Prevent a crash when Escort Priority was parsed for a ship not present in the Escort List
|
||||
- Fix a bunch of graphics functions to respect GR_STUB, preventing problems in standalone server
|
||||
- Clean up line endings in several files (replaced CRLF with LF basically everywhere)
|
||||
- Fixed a timestamp issue which helps lay the groundwork to add In-Game Joining to multiplayer
|
||||
- Prevent pilot and campaign files from having invalid values, by clamping them within the expected ranges when saving and loading
|
||||
- Also warn the player if such an invalid value was detected, telling them which setting had the wrong value and what it was reset to
|
||||
- Try to avoid assertions by preventing invalid types from being handled by `obj_team()`
|
||||
- Shut down the SEXP system before the Scripting system, otherwise a crash could happen if the SEXP system held references to Lua
|
||||
</details>
|
||||
|
||||
----
|
||||
|
||||
## [22.0.0] - 2022-04-01 ([Thread](https://www.hard-light.net/forums/index.php?topic=98125.0))
|
||||
### Changed
|
||||
- Controls5 PR (aka multi-joy)
|
||||
- Timing system upgrade
|
||||
- Model animation code upgrades
|
||||
|
||||
|
||||
## [21.4.0] - 2021-09-27 ([Thread](https://www.hard-light.net/forums/index.php?topic=97802.0))
|
||||
### Changed
|
||||
- Multiple features; see thread
|
||||
- Multiple bugfixes; see thread
|
||||
### Deprecations
|
||||
- `script-eval` has been deprecated in favor of `script-eval-block`
|
||||
- renamed `hud-set-retail-gauge-active` to `hud-set-builtin-gauge-active`
|
||||
|
||||
|
||||
## [21.2.0] - 2021-05-02 ([Thread](https://www.hard-light.net/forums/index.php?topic=97561.0))
|
||||
### Changed
|
||||
- Added a dynamic action system for more modder control of effects
|
||||
- Removed fireball limit
|
||||
- Fixed bomb intercept in multiplayer
|
||||
- Changed nebula fog distance from a linear to exponential model
|
||||
- Turrets on moving subobjects enabled
|
||||
- LZ4 Compression added for game files
|
||||
|
||||
|
||||
## [21.0.0] - 2021-01-27 ([Thread](https://www.hard-light.net/forums/index.php?topic=97293.0))
|
||||
### Changed
|
||||
- More OpenGL optimizations
|
||||
- Full type information output for Lua documentation. Very useful for writing Lua scripts.
|
||||
- A new -weaponspew command-line option for printing MediaVP-style statistics, plus a weapon comparison spreadsheet, to the debug log.
|
||||
- Improved ship lookup behavior in SEXPs for improved performance.
|
||||
- BPTC/BC7 texture compression support
|
||||
- Refactoring and new features for the ship lab.
|
||||
- Missile multi-lock. It's finally in an official build!
|
||||
- Enhancements to the model code in preparation for cool features in 21.2.
|
||||
- IPv6 support for multiplayer
|
||||
- A lot of general fixes for multiplayer.
|
||||
### Deprecated
|
||||
- Dropped support for Windows XP
|
||||
- Scripting:
|
||||
- `ba.getFrametime()`: The parameter value was used incorrectly inside the implementation. To avoid breaking existing scripts and to improve readability this has been split into `ba.getMissionFrametime()` and `ba.getRealFrametime()`.
|
||||
- `gr.drawMonochromeImage()`: `gr.drawImage()` got a new parameter for drawing monochrome images and is more flexible in general so the monochrome variant is no longer needed.
|
||||
|
||||
|
||||
## [19.0.0] - 2020-01-25 ([Thread](https://www.hard-light.net/forums/index.php?topic=96226.0))
|
||||
### Meta
|
||||
- With this release we decided to drop the "`3.Major_revision.Minor_revision`" versioning scheme in favor of a year based scheme since the Major and Minor versions did not have much meaning anymore. Instead the scheme will now be "`<year>.<number that is incremented every release>.0`". The last 0 is still there because some of our systems expect that. It will be gone at some point.
|
||||
@@ -458,7 +25,6 @@ Headlining features will always be on top.
|
||||
### Fixed
|
||||
- Refactored bitmap slot handling and removed the fixed upper limit on the number of bitmaps. No more bmpman corruption!
|
||||
|
||||
|
||||
## [3.8.0] - 2017-08-22 ([Release Thread](https://www.hard-light.net/forums/index.php?topic=93812.0))
|
||||
### Added
|
||||
- Support for APNG animations: [APNG](https://en.wikipedia.org/wiki/APNG) is a variant of the established PNG format which allows to store animations instead of single frames in a PNG file. This change allows to use these animation files in FSO which significantly improves usability for modders since they no longer have to store all individual frames as separate images.
|
||||
@@ -483,7 +49,6 @@ Headlining features will always be on top.
|
||||
### Fixed
|
||||
- "Tons" of bugfixes
|
||||
|
||||
|
||||
## [3.7.2] - 2015-04-23 ([Release thread](http://www.hard-light.net/forums/index.php?topic=89597.0))
|
||||
### Added
|
||||
- MediaVPS 2014 Support
|
||||
@@ -514,19 +79,16 @@ Headlining features will always be on top.
|
||||
- Raised the per-model debris limit to 48 (previously 32).
|
||||
- Increased the per-frame debris limit to 96 (previously 64)
|
||||
|
||||
|
||||
## [3.6.18] - 2013-03-01 ([Release thread](http://www.hard-light.net/forums/index.php?topic=83889.0))
|
||||
### Fixed
|
||||
- Critical damage bug which caused heavy balance issues in the game
|
||||
|
||||
|
||||
## [3.6.16] - 2013-01-31 ([Release thread](http://www.hard-light.net/forums/index.php?topic=83577.0))
|
||||
### Added
|
||||
- Diaspora TC support
|
||||
### Changed
|
||||
- Performance Improvements
|
||||
|
||||
|
||||
## [3.6.14] - 2012-10-23 - ([Release thread](https://www.hard-light.net/forums/index.php?topic=82648.0))
|
||||
### Added
|
||||
- Objecttype tables support for deciding how enemies respond to ship classes when disabled
|
||||
|
||||
@@ -133,7 +133,7 @@ if ($DeployBuild) {
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX="$env:APPVEYOR_BUILD_FOLDER/../install" -DFSO_USE_SPEECH="ON" `
|
||||
-DFSO_USE_VOICEREC="ON" -DMSVC_SIMD_INSTRUCTIONS="$($buildConfig.SimdType)" `
|
||||
-DQT5_INSTALL_ROOT="$($buildConfig.QtDir)" -DFSO_BUILD_QTFRED=OFF `
|
||||
-DQT5_INSTALL_ROOT="$($buildConfig.QtDir)" -DFSO_BUILD_QTFRED=ON `
|
||||
-DFSO_INSTALL_DEBUG_FILES="ON" `
|
||||
-G "$($buildConfig.Generator)" -T "$($buildConfig.Toolset)" ..
|
||||
|
||||
@@ -153,7 +153,7 @@ if ($DeployBuild) {
|
||||
Push-AppveyorArtifact "$($PackageName)-debug-$($buildConfig.PackageType).7z"
|
||||
} else {
|
||||
cmake -DFSO_USE_SPEECH="ON" -DFSO_FATAL_WARNINGS="ON" -DFSO_USE_VOICEREC="ON" -DFSO_BUILD_TESTS="ON" -DMSVC_SIMD_INSTRUCTIONS=SSE2 `
|
||||
-DFSO_BUILD_QTFRED=OFF -DQT5_INSTALL_ROOT="$env:QT_DIR" -DFSO_BUILD_FRED2="ON" `
|
||||
-DFSO_BUILD_QTFRED=ON -DQT5_INSTALL_ROOT="$env:QT_DIR" -DFSO_BUILD_FRED2="ON" `
|
||||
-G "$Env:CMAKE_GENERATOR" -T "$Env:PlatformToolset" ..
|
||||
|
||||
cmake --build . --config "$Env:CONFIGURATION" -- /verbosity:minimal
|
||||
|
||||
@@ -19,5 +19,5 @@ BASE_COMMIT=$(git merge-base $1 $2)
|
||||
echo "Running clang-tidy on changed files"
|
||||
git diff -U0 --no-color "$BASE_COMMIT..$2" | \
|
||||
$HERE/clang-tidy-diff.py -path "$(pwd)/build" -p1 \
|
||||
-regex '(code(?!((\/graphics\/shaders\/compiled)|(\/globalincs\/windebug)))|freespace2|qtfred|test|build|tools)\/.*\.(cpp|h)' \
|
||||
-regex '(code(?!\/graphics\/shaders\/compiled)|freespace2|qtfred|test|build|tools)\/.*\.(cpp|h)' \
|
||||
-clang-tidy-binary /usr/bin/clang-tidy-9 -j$(nproc) -export-fixes "$(pwd)/clang-fixes.yaml"
|
||||
@@ -15,16 +15,8 @@ if [ "$COMPILER" = "clang-9" ]; then
|
||||
fi
|
||||
|
||||
LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH
|
||||
if [ "$RUNNER_OS" = "macOS" ]; then
|
||||
CXXFLAGS="-mtune=generic -pipe -Wno-unknown-pragmas"
|
||||
CFLAGS="-mtune=generic -pipe -Wno-unknown-pragmas"
|
||||
PLATFORM_CMAKE_OPTIONS=""
|
||||
export CMAKE_OSX_ARCHITECTURES="$ARCHITECTURE"
|
||||
else
|
||||
CXXFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
CFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
PLATFORM_CMAKE_OPTIONS="-DFSO_BUILD_APPIMAGE=ON"
|
||||
fi
|
||||
CXXFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
CFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
|
||||
CMAKE_OPTIONS="$JOB_CMAKE_OPTIONS"
|
||||
if [[ "$COMPILER" =~ ^clang.*$ ]]; then
|
||||
@@ -38,10 +30,7 @@ export CXXFLAGS
|
||||
export CFLAGS
|
||||
export LD_LIBRARY_PATH
|
||||
|
||||
if [ "$RUNNER_OS" = "macOS" ]; then
|
||||
brew install ninja
|
||||
fi
|
||||
cmake -G Ninja -DFSO_FATAL_WARNINGS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON $CMAKE_OPTIONS $PLATFORM_CMAKE_OPTIONS \
|
||||
-DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DCMAKE_BUILD_TYPE=$CONFIGURATION \
|
||||
-DFFMPEG_USE_PRECOMPILED=ON -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON -DFSO_BUILD_QTFRED=OFF \
|
||||
cmake -G Ninja -DFSO_FATAL_WARNINGS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON $CMAKE_OPTIONS \
|
||||
-DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_BUILD_APPIMAGE=ON -DCMAKE_BUILD_TYPE=$CONFIGURATION \
|
||||
-DFFMPEG_USE_PRECOMPILED=ON -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON -DFSO_BUILD_QTFRED=ON \
|
||||
-DSHADERS_ENABLE_COMPILATION=ON ..
|
||||
@@ -25,12 +25,6 @@ elif [ "$OS" = "Windows" ]; then
|
||||
echo "::set-output name=debug_path::$(pwd)/$(get_package_name)-debug-$ARCH-$SIMD.7z"
|
||||
echo "::set-output name=debug_name::$(get_package_name)-debug-$ARCH-$SIMD.7z"
|
||||
echo "::set-output name=debug_mime::$(file -b --mime-type "$(pwd)/$(get_package_name)-debug-$ARCH-$SIMD.7z")"
|
||||
elif [ "$OS" = "Mac" ]; then
|
||||
tar -cvzf "$(get_package_name)-builds-Mac.tar.gz" *
|
||||
|
||||
echo "::set-output name=package_path::$(pwd)/$(get_package_name)-builds-Mac.tar.gz"
|
||||
echo "::set-output name=package_name::$(get_package_name)-builds-Mac.tar.gz"
|
||||
echo "::set-output name=package_mime::$(file -b --mime-type "$(pwd)/$(get_package_name)-builds-Mac.tar.gz")"
|
||||
else
|
||||
echo "Invalid OS: $OS"
|
||||
fi
|
||||
@@ -5,7 +5,7 @@ HERE=$(dirname "$SCRIPT")
|
||||
|
||||
export LD_LIBRARY_PATH=$(pwd)/bin/lib:$LD_LIBRARY_PATH
|
||||
|
||||
if [ "$CONFIGURATION" = "Debug"] && [[ "$RUNNER_OS" != "macOS" ]] ; then
|
||||
if [ "$CONFIGURATION" = "Debug" ]; then
|
||||
valgrind --leak-check=full --error-exitcode=1 --gen-suppressions=all \
|
||||
--suppressions="$HERE/valgrind.supp" ./bin/unittests --gtest_shuffle
|
||||
else
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
# Retrives a log of commit messages and attempts to pretty them into a human-readbile format
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
import sys
|
||||
|
||||
from datetime import datetime
|
||||
from subprocess import check_output
|
||||
|
||||
MAX_PAGES = 100 # maximum number of PR pages to go through
|
||||
warnings = 0
|
||||
|
||||
def get_last_page(response: requests.Response) -> int:
|
||||
"""
|
||||
@brief Extract the last page number from the response header
|
||||
|
||||
@param[in] response The response from a GET call
|
||||
|
||||
@returns The page number of the last page, or MAX_PAGES
|
||||
"""
|
||||
|
||||
# The link header is a dictionary in string form, and for that matter the
|
||||
# value is in front of the key. It is of the form '<<url1>>; rel=<key1>, <url2>, <key2>'
|
||||
link = response.headers['link']
|
||||
link = link.split(',')
|
||||
for x in link:
|
||||
y = x.split(';') # <url>; rel=<key>
|
||||
if "last" not in y[1]: # look for rel='last'
|
||||
continue
|
||||
# grab the page number from the URL
|
||||
# group(0) is `page=123`, group(1) is `123`
|
||||
last_page = int( re.search(r'page=(\d+)', y[0]).group(1) )
|
||||
if not last_page:
|
||||
print("Error: Could not find page number in url! URL: %s", y[0])
|
||||
sys.exit(1)
|
||||
|
||||
if last_page > MAX_PAGES:
|
||||
return MAX_PAGES
|
||||
else:
|
||||
return last_page
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Retrieves a log of commit messages and attempts to pretty them into a human-readable format")
|
||||
parser.add_argument("start_date",
|
||||
help="Day of the earliest commit to add to the log.")
|
||||
parser.add_argument("-u", "--user", nargs=2, metavar=("NAME", "TOKEN"), default=["", ""],
|
||||
help="Github username and token/password")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
os.chdir(os.path.dirname(__file__)) # Change working directory to this file's directory
|
||||
start_date = datetime.strptime(args.start_date, "%Y-%m-%d")
|
||||
|
||||
print("start_date: {}".format(start_date))
|
||||
|
||||
# Retrive the git log for master, since start date, order by date, in ascending order, use YYYY-MM-DD date format,
|
||||
# each line is of the form "<author short date> | <message>", only grab commits from master,
|
||||
# output to `fsofordigest.txt`,
|
||||
|
||||
# check_output(("git log master", "--after=2021-09-15", "--date-order", "--reverse", "--date=short",
|
||||
# "--pretty=\"%as | %s\"", "--first-parent",
|
||||
# "--output fsofordigest.txt"), text=True)
|
||||
|
||||
# The commit messages are either of the form "<body> (#<PR_number>)" or "Merge pull request #<PR_number> from <source_repo>"
|
||||
# For our purposes, we want them be all of the form "<body> (#<PR_number>)"
|
||||
# Typically, the second form does not have a usable body, so we'll have to pull the PR title from github
|
||||
|
||||
# Github's REST API allows us to retrieve PR's, but due to it being HTTP requests it has to page in the data.
|
||||
# We can loop over the GET request and check for a merge date.
|
||||
# The GET request doesn't have sorting by merge date, but does have sorting by the last time it was updated... which is usually
|
||||
# also the merge date but not always.
|
||||
|
||||
# Key attributes within the PR JSON objects:
|
||||
# "merged_at: <date>" # Date the PR was merged in
|
||||
# "merged: <bool>" # If the PR was merged yet
|
||||
# "html_url: <string>" # URL of the PR's HEAD commit, used by some clients to pull in the PR branch
|
||||
# "number: <int>" # The PR id number
|
||||
# "title: <string>" # Title of the PR
|
||||
# "body: <string>" # The body paragraph detailing the PR
|
||||
# "labels: [label_object]" # All labels attached to this PR
|
||||
|
||||
changelog = {}
|
||||
url = "https://api.github.com/repos/scp-fs2open/fs2open.github.com/pulls"
|
||||
page = 1
|
||||
last_page = MAX_PAGES
|
||||
while page <= last_page:
|
||||
# GET <url>?base=master&sort=updated&direction=desc&state=closed&page=1
|
||||
response = requests.get(url,
|
||||
auth=(args.user[0], args.user[1]),
|
||||
params={
|
||||
"base": "master",
|
||||
"direction": "desc",
|
||||
"page": page,
|
||||
"state": "closed",
|
||||
"sort": "updated"
|
||||
})
|
||||
|
||||
# Check if good response
|
||||
if (response.status_code != 200) :
|
||||
print("Error: ", response.status_code)
|
||||
print(response.json()['message'])
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("Request successful: 200")
|
||||
print("page={}".format(page))
|
||||
|
||||
# Update the last_page index at first iteration and possibly last iteration
|
||||
# This should cover the edge case of a new PR being added to the database before this script is finished
|
||||
if (page == 1) or (page == last_page):
|
||||
last_page = get_last_page(response)
|
||||
print("last_page={}".format(last_page))
|
||||
|
||||
# assume pulls are organized from newest to oldest
|
||||
for pull in response.json():
|
||||
if (pull['state'] != 'closed'):
|
||||
# This pull is not merged, so ignore
|
||||
print("Found irrelevant pull, skipping...")
|
||||
print("state: {}; number: {}".format(pull['state'], pull['number']))
|
||||
continue
|
||||
|
||||
if (pull['merged_at'] == None):
|
||||
# This pull wasn't merged, skip
|
||||
print("Found closed pull #{} ({}), skipping...".format(pull['number'], pull['closed_at']))
|
||||
continue
|
||||
|
||||
merge_date = datetime.strptime(pull['merged_at'], "%Y-%m-%dT%H:%M:%SZ")
|
||||
updated_date = datetime.strptime(pull['updated_at'], "%Y-%m-%dT%H:%M:%SZ")
|
||||
if (merge_date < start_date) and (updated_date >= start_date):
|
||||
# This pull is too old, but was updated within our timeframe. skip
|
||||
print("Found old pull #{} ({}), skipping...".format(pull['number'], pull['merged_at']))
|
||||
continue
|
||||
|
||||
if (updated_date < start_date):
|
||||
# This pull is too old
|
||||
print("Found old pull #{} ({}), stopping loop...".format(pull['number'], pull['updated_at']))
|
||||
page = last_page + 1
|
||||
break
|
||||
|
||||
# Add the pull to the changelog, indexed by its pull.number
|
||||
if pull['number'] not in changelog:
|
||||
# only add if the key isn't there already
|
||||
changelog[pull['number']] = {
|
||||
"date": merge_date,
|
||||
"title": pull['title'],
|
||||
"labels": pull['labels'],
|
||||
"url": pull['html_url']
|
||||
}
|
||||
print("Added pull #{} ({})".format(pull['number'], merge_date))
|
||||
# Else, silently ignore duplicate (for now...)
|
||||
|
||||
page += 1 # iterate to next page
|
||||
|
||||
|
||||
# Write the changelog to file
|
||||
# 2021-09-28 | (#3661) Fix homing child weapons in niche circumstances
|
||||
print("Writing to change.log...")
|
||||
str = ''
|
||||
for key, value in changelog.items():
|
||||
str += "{} | (#{}) {} \n".format(value['date'].strftime("%Y-%m-%d"), key, value['title'])
|
||||
|
||||
with open("change.log", "w") as file:
|
||||
file.write(str)
|
||||
|
||||
print("Done!")
|
||||
|
||||
main()
|
||||
@@ -1,228 +0,0 @@
|
||||
import re # regex module
|
||||
from ftplib import FTP, error_perm
|
||||
from itertools import groupby
|
||||
from typing import List, Tuple, Dict
|
||||
|
||||
import requests # HTTP requests module
|
||||
|
||||
from util import retry_multi, GLOBAL_TIMEOUT # from util.py
|
||||
|
||||
|
||||
class ReleaseFile:
|
||||
"""! Class representing a Released file on Nebula
|
||||
|
||||
`name`: str
|
||||
Mod (or build) name,
|
||||
`url`: str
|
||||
Primary host URL,
|
||||
`group`: str
|
||||
Mod group string,
|
||||
`subgroup`: str
|
||||
Mod subgroup string,
|
||||
`mirrors`: List[str]
|
||||
List of URL's of FTP mirrors
|
||||
"""
|
||||
def __init__(self, name, url, group, subgroup=None, mirrors=None):
|
||||
if mirrors is None:
|
||||
mirrors = []
|
||||
self.mirrors = mirrors
|
||||
self.subgroup = subgroup
|
||||
self.group = group
|
||||
self.url = url
|
||||
self.name = name
|
||||
|
||||
self.base_url = "/".join(url.split('/')[0:-1]) + "/"
|
||||
self.filename = url.split('/')[-1]
|
||||
|
||||
# A list of tuples of (filename, hash)
|
||||
self.content_hashes = None
|
||||
|
||||
self.hash = None
|
||||
self.size = 0
|
||||
|
||||
def __repr__(self):
|
||||
return repr((self.name))
|
||||
|
||||
|
||||
class SourceFile:
|
||||
"""! Class represeting a source file
|
||||
|
||||
`name`: str
|
||||
File name,
|
||||
`url`: str
|
||||
FTP URL,
|
||||
`group`
|
||||
<unknown>
|
||||
|
||||
@details More details
|
||||
"""
|
||||
def __init__(self, name, url, group):
|
||||
self.group = group
|
||||
self.url = url
|
||||
self.name = name
|
||||
|
||||
|
||||
class FileGroup:
|
||||
"""! Represents a file group
|
||||
|
||||
`name`: str
|
||||
Name of this group
|
||||
`files`: List[ReleaseFile]
|
||||
List of files within this group
|
||||
`mainFile`: str
|
||||
If this FileGroup has a subgroup, `mainFile` is the head of that group
|
||||
`subFiles`: List[ReleaseFile]
|
||||
Files within a subgroup
|
||||
"""
|
||||
|
||||
def __init__(self, name, files: List[ReleaseFile]):
|
||||
self.files = files
|
||||
self.name = name
|
||||
|
||||
if len(files) == 1:
|
||||
self.mainFile = files[0]
|
||||
self.subFiles = {}
|
||||
else:
|
||||
self.mainFile = None
|
||||
subFiles = []
|
||||
for file in files:
|
||||
# We only have subcategories for Windows where SSE2 is the main group
|
||||
if file.subgroup == "SSE2":
|
||||
self.mainFile = file
|
||||
else:
|
||||
subFiles.append(file)
|
||||
|
||||
self.subFiles = dict(((x[0], next(x[1])) for x in groupby(subFiles, lambda f: f.subgroup)))
|
||||
|
||||
|
||||
def get_release_files(tag_name, config) -> Tuple[List[ReleaseFile], Dict[str, SourceFile]]:
|
||||
"""! Brief Gets the binary and source files from the Github Release server
|
||||
|
||||
@param[in] `tag_name` Git tag of the current release
|
||||
@param[in] `config` confi metadata set in main.py
|
||||
|
||||
@returns `List[ReleaseFile]` List of release files
|
||||
@returns `Dict[str, SourceFile]` Dictionary of source files
|
||||
|
||||
@details Sends an `HTTP GET` request to github using their REST API to retrieve metadata. The files are not
|
||||
actually downloaded here, just their metadata is gathered and organized in their respective container for later
|
||||
use.
|
||||
"""
|
||||
|
||||
@retry_multi(5) # retry at most 5 times
|
||||
def execute_request(path):
|
||||
"""!
|
||||
@brief Performs a GET request with the given path. To be used with Github's REST API.
|
||||
@returns If successful, returns a .JSON object
|
||||
"""
|
||||
headers = {
|
||||
"Accept": "application/vnd.github.v3+json"
|
||||
}
|
||||
url = "https://api.github.com" + path
|
||||
|
||||
# GET https://api.github.com/<path> Accept: "application/vnd.github.v3+json"
|
||||
|
||||
response = requests.get(url, headers=headers, timeout=GLOBAL_TIMEOUT)
|
||||
|
||||
response.raise_for_status() # Raise a RequestException if we failed, and trigger retry
|
||||
|
||||
return response.json()
|
||||
|
||||
build_group_regex = re.compile("fs2_open_.*-builds-([^.-]*)(-([^.]*))?.*") # regex for matching binary .zip's and .7z's
|
||||
source_file_regex = re.compile("fs2_open_.*-source-([^.]*)?.*") # regex for matching source .zip's and .7z's
|
||||
|
||||
# Get the github release metadata of the given tag name
|
||||
response = execute_request(
|
||||
"/repos/{}/releases/tags/{}".format(config["github"]["repo"], tag_name))
|
||||
|
||||
# Extract the binary and source files from the response["asset"] metadata
|
||||
binary_files = []
|
||||
source_files = {}
|
||||
for asset in response["assets"]:
|
||||
url = asset["browser_download_url"]
|
||||
name = asset["name"]
|
||||
|
||||
group_match = build_group_regex.match(name)
|
||||
|
||||
if group_match is not None:
|
||||
platform = group_match.group(1)
|
||||
# x64 is the Visual Studio name but for consistency we need Win64
|
||||
if platform == "x64":
|
||||
platform = "Win64"
|
||||
|
||||
binary_files.append(ReleaseFile(name, url, platform, group_match.group(3)))
|
||||
else:
|
||||
group_match = source_file_regex.match(name)
|
||||
|
||||
if group_match is None:
|
||||
continue
|
||||
|
||||
group = group_match.group(1)
|
||||
|
||||
source_files[group] = SourceFile(name, url, group)
|
||||
|
||||
binary_files.sort(key=lambda ReleaseFile: ReleaseFile.name)
|
||||
|
||||
return binary_files, source_files
|
||||
|
||||
|
||||
def get_ftp_files(build_type, tag_name, config) -> List[ReleaseFile] :
|
||||
"""!
|
||||
@brief Gets file metadata for nightlies hosted on FTP, as determined by config["ftp"] attributes
|
||||
|
||||
@param [in] `build_type` Unknown str
|
||||
@param [in] `tag_name` Github tag name of the release
|
||||
@param [in] `config` config metadata set in main.py
|
||||
"""
|
||||
|
||||
tag_regex = re.compile("nightly_(.*)")
|
||||
build_group_regex = re.compile("nightly_.*-builds-([^.]+).*")
|
||||
|
||||
files = []
|
||||
try:
|
||||
with FTP(config["ftp"]["host"], config["ftp"]["user"], config["ftp"]["pass"]) as ftp:
|
||||
# extract version
|
||||
version_str = tag_regex.match(tag_name).group(1)
|
||||
|
||||
# extract filepath w/ version
|
||||
# then list all ftp hits with that path
|
||||
path_template = config["ftp"]["path"]
|
||||
path = path_template.format(type=build_type, version=version_str)
|
||||
file_entries = list(ftp.mlsd(path, ["type"]))
|
||||
|
||||
# get all ftp hits of type file
|
||||
for entry in file_entries:
|
||||
if entry[1]["type"] == "file":
|
||||
files.append(entry[0])
|
||||
except error_perm:
|
||||
print("Received permanent FTP error!")
|
||||
return []
|
||||
|
||||
out_data = []
|
||||
for file in files:
|
||||
# from the file list, extract only nightly files
|
||||
file_match = build_group_regex.match(file)
|
||||
if file_match is None:
|
||||
print("Ignoring non nightly file '{}'".format(file))
|
||||
continue
|
||||
|
||||
group_match = file_match.group(1)
|
||||
primary_url = None
|
||||
mirrors = []
|
||||
|
||||
# x64 is the name Visual Studio uses but Win64 works better for us since that gets displayed in the nightly post
|
||||
if "x64" in group_match:
|
||||
group_match = group_match.replace("x64", "Win64")
|
||||
|
||||
# construct the download URL list for all mirrors. The first listed ftp location is taken as the Primary
|
||||
for mirror in config["ftp"]["mirrors"]:
|
||||
download_url = mirror.format(type=build_type, version=version_str, file=file)
|
||||
if primary_url is None:
|
||||
primary_url = download_url
|
||||
else:
|
||||
mirrors.append(download_url)
|
||||
|
||||
# Form the List[ReleaseFile] list with the download URL links
|
||||
out_data.append(ReleaseFile(file, primary_url, group_match, None, mirrors))
|
||||
|
||||
return out_data
|
||||
@@ -1,103 +0,0 @@
|
||||
from typing import List
|
||||
|
||||
import requests
|
||||
import semantic_version
|
||||
from mako.template import Template
|
||||
|
||||
from file_list import ReleaseFile
|
||||
|
||||
|
||||
class ForumAPI:
|
||||
POST_SIZE_MAX = 50000
|
||||
"""!
|
||||
Maximum number of characters allowed in a post, including BBCode (03/17/2022)
|
||||
|
||||
Check with HLP admins occasionally to verify this limit, they can set it in the forum's admin panel
|
||||
"""
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
def create_post(self, title, content, board):
|
||||
"""!
|
||||
@brief Create a post on the forums
|
||||
|
||||
@param[in] title Title of the post
|
||||
@param[in] content Body of the post
|
||||
@param[in] board Which board/forum to post to
|
||||
|
||||
@returns { "thread_url": <url_of_created_thread> }
|
||||
"""
|
||||
|
||||
if (self.config["hlp"]["api"] == "") or (self.config["hlp"]["key"] == ""):
|
||||
print("Post failed! No API or API_KEY given!")
|
||||
return
|
||||
|
||||
resp = requests.post(self.config["hlp"]["api"], data={
|
||||
"api_key": self.config["hlp"]["key"],
|
||||
"board": str(board),
|
||||
"subject": title,
|
||||
"body": content
|
||||
})
|
||||
|
||||
if resp.status_code != 200:
|
||||
print("Post failed! Response: %s" %resp.text)
|
||||
|
||||
return resp.json()
|
||||
|
||||
def post_nightly(self, date, revision, files, log, success):
|
||||
"""!
|
||||
@brief Post a new Nightly topic on the forums using nightly.mako
|
||||
|
||||
@param[in] date The date to use
|
||||
@param[in] version The build's version
|
||||
@param[in] groups dict[Any, file_list.FileGroup]; FileGroups of SSE2, AVX, etc.
|
||||
@param[in] sources URL's of builds on github or fs2net
|
||||
"""
|
||||
|
||||
print("Posting nightly thread...")
|
||||
|
||||
title = "Nightly: {} - Revision {}".format(date, revision)
|
||||
|
||||
template = Template(filename=self.config["templates"]["nightly"])
|
||||
rendered = template.render_unicode(**{
|
||||
"date": date,
|
||||
"revision": revision,
|
||||
"files": files,
|
||||
"log": log,
|
||||
"success": success
|
||||
})
|
||||
|
||||
print("Creating post...")
|
||||
self.create_post(title, rendered, self.config["nightly"]["hlp_board"])
|
||||
|
||||
def post_release(self, date, version: semantic_version.Version, groups, sources):
|
||||
"""!
|
||||
@brief Post a new Release topic on the forums using release.mako.
|
||||
|
||||
@param[in] date The date to use
|
||||
@param[in] version The build's version
|
||||
@param[in] groups dict[Any, file_list.FileGroup]; FileGroups of SSE2, AVX, etc.
|
||||
@param[in] sources URL's of builds on github or fs2net
|
||||
|
||||
@returns If Successful, the URL of the created post
|
||||
"""
|
||||
|
||||
print("Posting release thread...")
|
||||
|
||||
title = "Release: {}".format(version)
|
||||
|
||||
template = Template(
|
||||
filename=self.config["templates"]["release"].format(major=version.major, minor=version.minor),
|
||||
module_directory='/tmp/mako_modules')
|
||||
rendered = template.render_unicode(**{
|
||||
"date": date,
|
||||
"version": version,
|
||||
"groups": groups,
|
||||
"sources": sources
|
||||
}).strip("\n")
|
||||
|
||||
print("Creating post...")
|
||||
|
||||
json = self.create_post(title, rendered, self.config["release"]["hlp_board"])
|
||||
return json["thread_url"]
|
||||
@@ -1,85 +0,0 @@
|
||||
# Class for use with interfacing with github via HTTP requests
|
||||
|
||||
import requests
|
||||
|
||||
from util import retry_multi, GLOBAL_TIMEOUT
|
||||
|
||||
class GithubAPI:
|
||||
headers = {
|
||||
"Accept": "application/vnd.github.v3+json"
|
||||
}
|
||||
url_root = "https://api.github.com"
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
@retry_multi(5)
|
||||
def get(self, path):
|
||||
"""!
|
||||
@brief Performs a GET request with the given path. To be used with Github's REST API.
|
||||
@returns If successful, returns a .JSON object
|
||||
"""
|
||||
url = self.url_root + path
|
||||
|
||||
# GET https://api.github.com/<path> Accept: "application/vnd.github.v3+json"
|
||||
|
||||
response = requests.get(url, headers=self.headers, timeout=GLOBAL_TIMEOUT)
|
||||
|
||||
response.raise_for_status() # Raise a RequestException if we failed, and trigger retry
|
||||
|
||||
return response.json()
|
||||
|
||||
def get_release(self, tag_name):
|
||||
"""!
|
||||
@brief Retrieves the Github Release metadata as a JSON object
|
||||
@see https://docs.github.com/en/rest/reference/releases#get-a-release-by-tag-name
|
||||
"""
|
||||
return self.get("/repos/{}/releases/tags/{}".format(self.config["github"]["repo"], tag_name))
|
||||
|
||||
|
||||
def patch(self, path, data):
|
||||
"""!
|
||||
@brief Performs a PATCH request with the given path and body data (as dictionary)
|
||||
@returns If successful, returns a .JSON object
|
||||
"""
|
||||
url = self.url_root + path
|
||||
|
||||
response = requests.patch(url, headers=self.headers, timeout=GLOBAL_TIMEOUT, data=data)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def update_release(self, release_id, tag_name="", name="", body="", draft=None, prerelease=None):
|
||||
"""!
|
||||
@brief Updates a Github Release
|
||||
@param[in] release_id The id of the release, as obtained by get_release()
|
||||
@param[in] tag_name (optional) The git tag this release is associated with
|
||||
@param[in] name (optional) The name of the release
|
||||
@param[in] body (optional) Body text describing the release
|
||||
@param[in] draft (optional) If true, the release is marked as a "draft"
|
||||
@param[in] prerelease (optional) If true, the release is marked as a "prerelease"
|
||||
|
||||
@note If an optional argument is not provided, then its associated data is not changed
|
||||
"""
|
||||
path = "/repos/{}/releases/{}".format(self.config["github"]["repo"], release_id)
|
||||
|
||||
# Add to the data JSON if options are specified
|
||||
data = {}
|
||||
if tag_name != "":
|
||||
data["tag_name"] = tag_name
|
||||
|
||||
if name != "":
|
||||
data["name"] = name
|
||||
|
||||
if body != "":
|
||||
data["body"] = body
|
||||
|
||||
if draft != None:
|
||||
data["draft"] = data
|
||||
|
||||
if prerelease != None:
|
||||
data["prerelease"] = prerelease
|
||||
|
||||
self.patch(path, data)
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import os
|
||||
import hashlib
|
||||
import tarfile
|
||||
import traceback
|
||||
import zipfile
|
||||
from tempfile import NamedTemporaryFile
|
||||
from zipfile import ZipFile
|
||||
|
||||
import requests
|
||||
import sys
|
||||
from mako.template import Template
|
||||
|
||||
from file_list import ReleaseFile
|
||||
from util import GLOBAL_TIMEOUT
|
||||
|
||||
|
||||
def _download_file(url, dest_file, session):
|
||||
print("Downloading " + url)
|
||||
# NOTE the stream=True parameter
|
||||
r = session.get(url, stream=True, timeout=GLOBAL_TIMEOUT)
|
||||
for chunk in r.iter_content(chunk_size=1024):
|
||||
if chunk: # filter out keep-alive new chunks
|
||||
dest_file.write(chunk)
|
||||
dest_file.flush()
|
||||
|
||||
|
||||
def _gen_hash(fileobj, hash_alg):
|
||||
h = hashlib.new(hash_alg)
|
||||
while True:
|
||||
data = fileobj.read(4096)
|
||||
|
||||
if not data:
|
||||
break
|
||||
|
||||
h.update(data)
|
||||
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def get_hashed_file_list(file: ReleaseFile, session: requests.Session = None, hash_alg: str = "sha256"):
|
||||
if session is None:
|
||||
session = requests.Session()
|
||||
|
||||
with NamedTemporaryFile('w+b', suffix=file.filename) as local_file:
|
||||
_download_file(file.url, local_file, session)
|
||||
|
||||
filename = local_file.name
|
||||
hash_list = []
|
||||
|
||||
local_file.seek(0)
|
||||
file.hash = _gen_hash(local_file, hash_alg)
|
||||
file.size = os.stat(filename).st_size
|
||||
|
||||
try:
|
||||
if tarfile.is_tarfile(filename):
|
||||
with tarfile.open(filename) as archive:
|
||||
for entry in archive:
|
||||
if not entry.isfile():
|
||||
continue
|
||||
|
||||
print("Computing hash for " + entry.name)
|
||||
fileobj = archive.extractfile(entry)
|
||||
hash_list.append((entry.path, _gen_hash(fileobj, hash_alg)))
|
||||
elif zipfile.is_zipfile(filename):
|
||||
with ZipFile(filename) as archive:
|
||||
for entry in archive.infolist():
|
||||
# Python 3.6 has is_dir but that version is relatively new so we'll use the "safe" version here
|
||||
if entry.filename.endswith('/'):
|
||||
continue
|
||||
|
||||
print("Computing hash for " + entry.filename)
|
||||
with archive.open(entry) as fileobj:
|
||||
hash_list.append((entry.filename, _gen_hash(fileobj, hash_alg)))
|
||||
else:
|
||||
raise NotImplementedError("Unsupported archive type!")
|
||||
except:
|
||||
traceback.print_exception(*sys.exc_info())
|
||||
return
|
||||
|
||||
file.content_hashes = hash_list
|
||||
|
||||
|
||||
def render_installer_config(version, groups, config):
|
||||
template = Template(filename=config["templates"]["installer"], module_directory='/tmp/mako_modules')
|
||||
return template.render(**{
|
||||
"version": version,
|
||||
"groups": groups,
|
||||
}).strip("\n")
|
||||
-305
@@ -1,305 +0,0 @@
|
||||
# Post-build python script
|
||||
# Uploads build packages to Nebula and creates a forum post on https://www.hard-light.net
|
||||
# Uses the following environment variables (Set by the github action workflow before execution)
|
||||
|
||||
# FSO versioning
|
||||
# FSO_VERSION_MAJOR
|
||||
# FSO_VERSION_MINOR
|
||||
# FSO_VERSION_BUILD
|
||||
|
||||
# Credentials into IndieGames file host
|
||||
# INDIEGAMES_USER:
|
||||
# INDIEGAMES_SSHPASS:
|
||||
|
||||
# Credentials into Nebula Mod Database
|
||||
# NEBULA_USER:
|
||||
# NEBULA_PASSWORD:
|
||||
|
||||
# Credentials into hard-light.net for forum post
|
||||
# HLP_API:
|
||||
# HLP_KEY:
|
||||
|
||||
# LINUX_RESULT: bool, if the linux builds were sucessfully uploaded
|
||||
# WINDOWS_RESULT: bool, if the windows builds were successfully uploaded
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from subprocess import check_output
|
||||
from datetime import datetime
|
||||
from itertools import groupby
|
||||
|
||||
import semantic_version
|
||||
|
||||
# local .py files
|
||||
import file_list
|
||||
import installer
|
||||
import nebula
|
||||
import forum
|
||||
|
||||
# Compile regexes for extracting version components
|
||||
MAJOR_VERSION_PATTERN = re.compile("(?:set_if_not_defined|set)\(FSO_VERSION_MAJOR (\d+)\)")
|
||||
MINOR_VERSION_PATTERN = re.compile("(?:set_if_not_defined|set)\(FSO_VERSION_MINOR (\d+)\)")
|
||||
BUILD_VERSION_PATTERN = re.compile("(?:set_if_not_defined|set)\(FSO_VERSION_BUILD (\d+)\)")
|
||||
REVISION_VERSION_PATTERN = re.compile("(?:set_if_not_defined|set)\(FSO_VERSION_REVISION (\d+)\)")
|
||||
REVISION_STR_VERSION_PATTERN = re.compile("(?:set_if_not_defined|set)\(FSO_VERSION_REVISION_STR (\w*)\)")
|
||||
|
||||
LOG_FORMAT = """
|
||||
------------------------------------------------------------------------%n
|
||||
commit %h%nAuthor: %an <%ad>%n
|
||||
Commit: %cn <%cd>%n%n %s
|
||||
"""
|
||||
DATEFORMAT_VERSION = "%Y%m%d"
|
||||
DATEFORMAT_FORUM = "%d %B %Y"
|
||||
|
||||
def _match_version_number(text, regex):
|
||||
"""! Extracts the version component represented by `regex` from `text`
|
||||
|
||||
@param [in] `text` Filename with version string 'MAJOR.MINOR.BUILD'
|
||||
@param [in] `regex` Regex pattern of component to look for
|
||||
|
||||
@return The version component as integer
|
||||
"""
|
||||
match = regex.search(text)
|
||||
return int(match.group(1))
|
||||
|
||||
def _match_version_str(text, regex):
|
||||
"""! Extracts the version component represented by `regex` from `text`
|
||||
|
||||
@param [in] `text` Filename with version string 'MAJOR.MINOR.BUILD'
|
||||
@param [in] `regex` Regex pattern of component to look for
|
||||
|
||||
@return The version component as integer
|
||||
"""
|
||||
match = regex.search(text)
|
||||
return match.group(1)
|
||||
|
||||
|
||||
def get_source_version(date_version: datetime, tag_name: str) -> semantic_version.Version:
|
||||
"""! Retrieves the version string from version_override.cmake (and version.cmake)
|
||||
|
||||
@param[in] `date_version` Date this build is published for release
|
||||
@param[in] `tag_name` Tag of this release, used to determine if release, rc, or nightly
|
||||
|
||||
@return If release: `FSO_VERSION_MAJOR.FSO_VERSION_MINOR.FSO_VERSION_BUILD`
|
||||
@return If rc: `FSO_VERSION_MAJOR.FSO_VERSION_MINOR.FSO_VERSION_BUILD-FSO_VERSION_REVISION_STR`
|
||||
@return If nightly: `FSO_VERSION_MAJOR.FSO_VERSION_MINOR.FSO_VERSION_BUILD-FSO_VERSION_REVISION_STR` (uses both version_override.cmake and version.cmake)
|
||||
|
||||
@note This is the version used to identify in Nebula and the forums. version_override.cmake is used for the builds
|
||||
made by CI tools and version.cmake is used for builds made by devs.
|
||||
"""
|
||||
|
||||
with open(os.path.join("..", "..", "version_override.cmake"), "r") as f:
|
||||
filetext = f.read()
|
||||
major = _match_version_number(filetext, MAJOR_VERSION_PATTERN)
|
||||
minor = _match_version_number(filetext, MINOR_VERSION_PATTERN)
|
||||
build = _match_version_number(filetext, BUILD_VERSION_PATTERN)
|
||||
# revision = _match_version_number(filetext, REVISION_VERSION_PATTERN)
|
||||
revision_str = _match_version_str(filetext, REVISION_STR_VERSION_PATTERN)
|
||||
|
||||
print("Parsing version_override.cmake...")
|
||||
if "rc" in tag_name.lower():
|
||||
version = semantic_version.Version("{}.{}.{}-{}".format(major, minor, build, revision_str))
|
||||
|
||||
elif "release" in tag_name.lower():
|
||||
version = semantic_version.Version("{}.{}.{}".format(major, minor, build))
|
||||
|
||||
elif "nightly" in tag_name.lower():
|
||||
# Nightly's version_override.cmake only has FSO_VERSION_REVISION and FSO_VERSION_REVISION_STR set
|
||||
# So, we have to pull the rest of the version string from version.cmake
|
||||
print("Parsing version.cmake...")
|
||||
with open(os.path.join("..", "..", "cmake", "version.cmake"), "r") as f:
|
||||
major = _match_version_number(filetext, MAJOR_VERSION_PATTERN)
|
||||
minor = _match_version_number(filetext, MINOR_VERSION_PATTERN)
|
||||
build = _match_version_number(filetext, BUILD_VERSION_PATTERN)
|
||||
version = semantic_version.Version("{}.{}.{}-{}".format(major, minor, build, revision_str))
|
||||
|
||||
else:
|
||||
print(" ERROR: malformed tag_name %s" % tag_name)
|
||||
sys.exit(1)
|
||||
|
||||
print(" version: {}".format(version))
|
||||
print(" version.prerelease: {}".format(version.prerelease))
|
||||
return version
|
||||
|
||||
|
||||
def main():
|
||||
os.chdir(os.path.dirname(__file__)) # Change working directory to this file's directory
|
||||
|
||||
# Aggregate configuration data in a dictionary
|
||||
config = {
|
||||
"github": {
|
||||
"repo": os.environ["GITHUB_REPO"],
|
||||
},
|
||||
"ftp": {
|
||||
"host": "scp.indiegames.us",
|
||||
"user": os.environ["INDIEGAMES_USER"],
|
||||
"pass": os.environ["INDIEGAMES_SSHPASS"],
|
||||
"path": "public_html/builds/{type}/{version}/",
|
||||
"mirrors": (
|
||||
"https://porphyrion.feralhosting.com/datacorder/builds/{type}/{version}/{file}",
|
||||
"http://scp.indiegames.us/builds/{type}/{version}/{file}",
|
||||
),
|
||||
},
|
||||
"nebula": {
|
||||
"user": os.environ["NEBULA_USER"],
|
||||
"password": os.environ["NEBULA_PASSWORD"],
|
||||
},
|
||||
"hlp": {
|
||||
"api": os.environ["HLP_API"],
|
||||
"key": os.environ["HLP_KEY"],
|
||||
},
|
||||
"templates": {
|
||||
"nightly": "nightly.mako",
|
||||
"release": "release.mako",
|
||||
},
|
||||
"nightly": {
|
||||
"hlp_board": 173,
|
||||
},
|
||||
"release": {
|
||||
"hlp_board": 50,
|
||||
},
|
||||
}
|
||||
|
||||
# bail if this script is run from a workflow that doesn't identify as a release or nightly
|
||||
if sys.argv[1] not in ("release", "nightly"):
|
||||
print("ERROR: Invalid release mode %s passed. Expected release or nightly!" % sys.argv[1])
|
||||
sys.exit(1)
|
||||
|
||||
linux_success = os.environ["LINUX_RESULT"] == "success"
|
||||
windows_success = os.environ["WINDOWS_RESULT"] == "success"
|
||||
if (sys.argv[1] == "release"):
|
||||
if not (linux_success and windows_success):
|
||||
# bail if this is a release and either build workflow failed
|
||||
print("ERROR: One or more builds failed to publish! Cannot post release.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
if not (linux_success or windows_success):
|
||||
# bail if this is a nightly build and both build workflows failed
|
||||
print("ERROR: All builds failed to publish! Cannot post nightly.")
|
||||
sys.exit(1)
|
||||
|
||||
# bail if tag_name doesn't match with sys.argv[1]
|
||||
tag_name = os.environ["RELEASE_TAG"] ##!< commit tag string
|
||||
if (sys.argv[1] == "release") and (not tag_name.startswith("release_")):
|
||||
print("ERROR: Invalid tag name detected. Expected release_ prefix for release builds but found %s (full ref = %s)" % tag_name, os.environ["GITHUB_REF"])
|
||||
sys.exit(1)
|
||||
|
||||
if (sys.argv[1] == "nightly") and (not tag_name.startswith("nightly_")):
|
||||
print("ERROR: Invalid tag name detected. Expected nightly_ prefix for nightly builds but found %s (full ref = %s)" % tag_name, os.environ["GITHUB_REF"])
|
||||
sys.exit(1)
|
||||
|
||||
# bail if tag_name does not exist in the repo
|
||||
if not check_output(("git", "show-ref", "--tags", "refs/tags/%s" % tag_name), text=True):
|
||||
print("ERROR: Couldn't find tag %s in repo (git show-ref)!" % tag_name)
|
||||
sys.exit(1)
|
||||
|
||||
# Populate version and date
|
||||
date = datetime.now() ##!< current date
|
||||
version = get_source_version(date.strftime(DATEFORMAT_VERSION), tag_name) ##!< form full version string
|
||||
|
||||
# find the previous tag release (used by nightly)
|
||||
tags = check_output(("git", "for-each-ref", "--sort=-taggerdate", "--format='%(tag)'", "refs/tags"), text=True).splitlines() # retrieve all tags in the repo by using git on the shell
|
||||
# NOTE: check_output returns canonical string representation. use repr() on strings its being compared to
|
||||
previous_tag = None
|
||||
found = False
|
||||
for tag in tags:
|
||||
if found:
|
||||
# Look for the "previous" tag with the same configuration ('release_' or 'nightly_')
|
||||
# Assumes older tags have a higher index than newer
|
||||
if tag.startswith("\'" + sys.argv[1] + "_"):
|
||||
previous_tag = tag
|
||||
break
|
||||
elif tag == repr(tag_name):
|
||||
# Ok, found the tag
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
# couldn't find tag, bail
|
||||
print("ERROR: Couldn't find tag %s in repo! (git for-each-ref)" % tag_name)
|
||||
sys.exit(1)
|
||||
|
||||
if (sys.argv[1] == "nightly") and (not previous_tag):
|
||||
# couldn't find previous tag, bail
|
||||
print("ERROR: Could not find a previous tag for %s!" % tag_name)
|
||||
sys.exit(1)
|
||||
|
||||
if sys.argv[1] == "release":
|
||||
# Release specific action
|
||||
print("Retrieving file list...")
|
||||
files, sources = file_list.get_release_files(tag_name, config)
|
||||
|
||||
print("Hashing files...")
|
||||
for file in files:
|
||||
installer.get_hashed_file_list(file)
|
||||
|
||||
# Construct the file groups
|
||||
# split the file list into a dictionary of {group: files} pairs using `groupby()`
|
||||
# Since `groupby()` returns a iterator reference to the original files list, we have to do a weird hand toss
|
||||
# to save it as a dictionary
|
||||
## NOTE z64: files need to be sorted or else key duplication will occur!
|
||||
groups = {key: file_list.FileGroup(key, list(group)) for key, group in groupby(files, lambda g: g.group)}
|
||||
|
||||
# Error check for all group keys and subkeys to be used in the mako. Do this here for better debugging QoL
|
||||
if "Win32" in groups.keys():
|
||||
if "AVX" not in groups["Win32"].subFiles.keys():
|
||||
print("ERROR: No Win32-AVX builds were detected!")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("ERROR: No Win32 builds were detected!")
|
||||
sys.exit(1)
|
||||
|
||||
if "Win64" in groups.keys():
|
||||
if "AVX" not in groups["Win64"].subFiles.keys():
|
||||
print("ERROR: No x64-AVX builds were detected!")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("ERROR: No x64 builds were detected!")
|
||||
sys.exit(1)
|
||||
|
||||
if "Linux" not in groups.keys():
|
||||
print("ERROR: No Linux builds were detected!")
|
||||
sys.exit(1)
|
||||
|
||||
# z64: What dose this do???
|
||||
# print(installer.render_installer_config(version, groups, config))
|
||||
|
||||
# Publish release builds to Nebula
|
||||
print("Publishing to Nebula...")
|
||||
nebula.submit_release(
|
||||
nebula.render_nebula_release(version, "rc" if version.prerelease else "stable", files, config),
|
||||
config)
|
||||
|
||||
# Publish forum post, using the release.mako template
|
||||
print("Publishing to Hard-light.net...")
|
||||
fapi = forum.ForumAPI(config)
|
||||
fapi.post_release(date.strftime(DATEFORMAT_FORUM), version, groups, sources)
|
||||
|
||||
else:
|
||||
# Dead for now, nightly publish is done elsewhere
|
||||
# Nightly specific action
|
||||
print("Retrieving file list...")
|
||||
files = file_list.get_ftp_files("nightly", tag_name, config)
|
||||
|
||||
print("Hashing files...")
|
||||
for file in files:
|
||||
installer.get_hashed_file_list(file)
|
||||
|
||||
print("Publishing to Nebula...")
|
||||
nebula.submit_release(
|
||||
nebula.render_nebula_release(version, "nightly", files, config),
|
||||
config)
|
||||
|
||||
# Compile commit messages into a log with all commits between this nightly and the previous nightly
|
||||
log = check_output(("git", "log", "%s^..%s^" % (previous_tag, tag_name), "--no-merges", "--stat", "--pretty=format:\"%s\"" % LOG_FORMAT.strip()))
|
||||
|
||||
# Publish forum post, using the nighty.mako template
|
||||
print("Publishing to Hard-light.net...")
|
||||
fapi = forum.ForumAPI(config)
|
||||
fapi.post_nightly(date.strftime(DATEFORMAT_FORUM), os.environ["GITHUB_SHA"], files, log, (linux_success and windows_success))
|
||||
|
||||
print("Done!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,248 +0,0 @@
|
||||
import os.path
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import requests
|
||||
|
||||
from file_list import ReleaseFile
|
||||
from util import retry_multi, GLOBAL_TIMEOUT
|
||||
|
||||
LINUX_KEY = "Linux"
|
||||
MACOSX_KEY = "Mac"
|
||||
WIN32_SSE2_KEY = "Win32-SSE2"
|
||||
WIN64_SSE2_KEY = "Win64-SSE2"
|
||||
WIN32_AVX_KEY = "Win32-AVX"
|
||||
WIN64_AVX_KEY = "Win64-AVX"
|
||||
|
||||
metadata = {
|
||||
'type': 'engine',
|
||||
'title': 'FSO',
|
||||
'notes': '',
|
||||
'banner': 'https://fsnebula.org/storage/0d/e7/bf64bcdea9a9c115969cfb784e1ca457d24a7c2da4fc6f213521c3bb6abb.png',
|
||||
'screenshots': [],
|
||||
'videos': [],
|
||||
'first_release': '2017-09-24',
|
||||
'cmdline': '',
|
||||
'mod_flag': ['FSO'],
|
||||
'tile': 'https://fsnebula.org/storage/ec/cc/0bf23e028c26d5175ff52d003bff85b0a17b0ddfc1130d65bdf6d36f6324.png',
|
||||
'logo': None,
|
||||
'release_thread': None,
|
||||
'description': '',
|
||||
'id': 'FSO',
|
||||
'packages': [],
|
||||
}
|
||||
|
||||
platforms = {
|
||||
LINUX_KEY: 'linux',
|
||||
MACOSX_KEY: 'macosx',
|
||||
WIN32_SSE2_KEY: 'windows',
|
||||
WIN64_SSE2_KEY: 'windows',
|
||||
WIN32_AVX_KEY: 'windows',
|
||||
WIN64_AVX_KEY: 'windows'
|
||||
}
|
||||
|
||||
envs = {
|
||||
LINUX_KEY: 'linux && x86_64', # Linux only has 64bit builds
|
||||
MACOSX_KEY: 'macosx',
|
||||
WIN32_SSE2_KEY: 'windows',
|
||||
WIN64_SSE2_KEY: 'windows && x86_64',
|
||||
WIN32_AVX_KEY: 'windows && avx',
|
||||
WIN64_AVX_KEY: 'windows && avx && x86_64'
|
||||
}
|
||||
|
||||
subdirs = {
|
||||
WIN32_SSE2_KEY: 'x86',
|
||||
WIN64_SSE2_KEY: 'x64',
|
||||
WIN32_AVX_KEY: 'x86_avx',
|
||||
WIN64_AVX_KEY: 'x64_avx'
|
||||
}
|
||||
|
||||
|
||||
def render_nebula_release(version, stability, files, config):
|
||||
meta = metadata.copy()
|
||||
meta['version'] = str(version)
|
||||
meta['stability'] = stability # This can be one of ('stable', 'rc', 'nightly')
|
||||
|
||||
for file in files:
|
||||
if file.content_hashes is None:
|
||||
# The extraction probably failed which is why we don't have any hashes
|
||||
continue
|
||||
|
||||
group = file.group
|
||||
|
||||
# release.py sets subgroup but nightly.py doesn't which means we have to normalize group here.
|
||||
if file.subgroup:
|
||||
group += '-' + file.subgroup
|
||||
|
||||
pkg = {
|
||||
'name': group,
|
||||
'notes': '',
|
||||
'is_vp': False,
|
||||
'files': [{
|
||||
'dest': platforms[group] + '/' + subdirs.get(group, ''),
|
||||
'filesize': file.size,
|
||||
'checksum': ['sha256', file.hash],
|
||||
'urls': [file.url] + file.mirrors,
|
||||
'filename': file.name
|
||||
}],
|
||||
'environment': envs.get(group),
|
||||
'filelist': [],
|
||||
'status': 'required',
|
||||
'folder': None,
|
||||
'dependencies': [],
|
||||
'executables': []
|
||||
}
|
||||
|
||||
for fn, checksum in file.content_hashes:
|
||||
if group in subdirs:
|
||||
dest_fn = subdirs[group] + '/' + fn
|
||||
else:
|
||||
dest_fn = fn
|
||||
|
||||
# If the group is a known platform then we put the binaries into a separate subfolder for each platform
|
||||
if group in platforms:
|
||||
dest_fn = platforms[group] + '/' + dest_fn
|
||||
|
||||
pkg['filelist'].append({
|
||||
'orig_name': fn,
|
||||
'checksum': ('sha256', checksum),
|
||||
'archive': file.name,
|
||||
'filename': dest_fn
|
||||
})
|
||||
|
||||
if group == LINUX_KEY and fn.endswith('.AppImage'):
|
||||
if 'qtfred' in fn:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'QtFRED Debug'
|
||||
else:
|
||||
label = 'QtFRED'
|
||||
|
||||
else:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'Fast Debug'
|
||||
else:
|
||||
label = None
|
||||
|
||||
props = {
|
||||
"x64": True, # All Linux builds are 64-bit
|
||||
"sse2": True, # Linux builds are forced to compile with SSE2 but not AVX
|
||||
"avx": False,
|
||||
"avx2": False,
|
||||
}
|
||||
|
||||
pkg['executables'].append({
|
||||
'file': dest_fn,
|
||||
'label': label,
|
||||
'properties': props,
|
||||
})
|
||||
elif group == MACOSX_KEY and fn.startswith(os.path.basename(fn) + '.app/'):
|
||||
if 'qtfred' in fn:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'QtFRED Debug'
|
||||
else:
|
||||
label = 'QtFRED'
|
||||
else:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'Fast Debug'
|
||||
else:
|
||||
label = None
|
||||
|
||||
props = {
|
||||
"x64": True, # All Mac builds are 64-bit
|
||||
"sse2": True, # There are no forced options on mac but 64-bit always implies SSE2 so we set that here
|
||||
"avx": False,
|
||||
"avx2": False,
|
||||
}
|
||||
|
||||
pkg['executables'].append({
|
||||
'file': dest_fn,
|
||||
'label': label,
|
||||
'properties': props,
|
||||
})
|
||||
elif group.startswith('Win') and fn.endswith('.exe'):
|
||||
if 'fred2' in fn:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'FRED2 Debug'
|
||||
else:
|
||||
label = 'FRED2'
|
||||
|
||||
elif 'qtfred' in fn:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'QtFRED Debug'
|
||||
else:
|
||||
label = 'QtFRED'
|
||||
|
||||
else:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'Fast Debug'
|
||||
else:
|
||||
label = None
|
||||
|
||||
props = {
|
||||
"x64": "x64" in fn,
|
||||
"sse2": "SSE2" in fn or "AVX" in fn, # AVX implies SSE2
|
||||
"avx": "AVX" in fn, # This conveniently also covers the AVX2 case since AVX2 implies AVX
|
||||
"avx2": "AVX2" in fn,
|
||||
}
|
||||
|
||||
pkg['executables'].append({
|
||||
'file': dest_fn,
|
||||
'label': label,
|
||||
'properties': props,
|
||||
})
|
||||
|
||||
meta["packages"].append(pkg)
|
||||
|
||||
return meta
|
||||
|
||||
|
||||
@retry_multi(5)
|
||||
def nebula_request(session, kind, path, **kwargs):
|
||||
uri = "https://fsnebula.org/api/1/{}".format(path)
|
||||
|
||||
request_args = {
|
||||
"timeout": GLOBAL_TIMEOUT
|
||||
}
|
||||
request_args.update(kwargs)
|
||||
|
||||
return session.request(kind, uri, **request_args)
|
||||
|
||||
|
||||
def submit_release(meta, config):
|
||||
try:
|
||||
with requests.Session() as session:
|
||||
print('Logging into Nebula...')
|
||||
result = nebula_request(session, 'post', 'login', data={
|
||||
'user': config['nebula']['user'],
|
||||
'password': config['nebula']['password']
|
||||
})
|
||||
|
||||
if result.status_code != 200:
|
||||
print('Login failed!')
|
||||
return False
|
||||
|
||||
data = result.json()
|
||||
if not data['result']:
|
||||
print('Login failed!')
|
||||
return False
|
||||
|
||||
print('Submitting release...')
|
||||
result = nebula_request(session, 'post', 'mod/release', headers={
|
||||
'X-KN-TOKEN': data['token']
|
||||
}, json=meta)
|
||||
|
||||
if result.status_code != 200:
|
||||
print('Request failed!')
|
||||
return False
|
||||
|
||||
data = result.json()
|
||||
if not data['result']:
|
||||
print('ERROR: ' + data['reason'])
|
||||
return False
|
||||
|
||||
print('Success!')
|
||||
return True
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
traceback.print_exception(exc_type, exc_value, exc_traceback)
|
||||
return False
|
||||
@@ -1,27 +0,0 @@
|
||||
Here is the nightly for ${date} - Revision ${revision}
|
||||
|
||||
% if not success:
|
||||
[b][color=red]At least one of the nightly builds failed![/color][/b]
|
||||
% endif
|
||||
% for file in files:
|
||||
|
||||
|
||||
Group: ${file.group}
|
||||
[url=${file.url}]${file.name}[/url] \
|
||||
%for mirror in file.mirrors:
|
||||
%if loop.first:
|
||||
(\
|
||||
%endif
|
||||
[url=${mirror}]Mirror[/url]\
|
||||
%if loop.last:
|
||||
)\
|
||||
%else:
|
||||
, \
|
||||
%endif
|
||||
%endfor
|
||||
|
||||
% endfor
|
||||
|
||||
[code]
|
||||
${log}
|
||||
[/code]
|
||||
@@ -1,68 +0,0 @@
|
||||
|
||||
<%def name="build(file)">[url=${file.url}]${file.filename}[/url]</%def>
|
||||
|
||||
[b]Change log:[/b] (chronologically ordered)
|
||||
PLACEHOLDER
|
||||
|
||||
[b]Deprecations:[/b]
|
||||
PLACEHOLDER
|
||||
|
||||
Deprecations are a mechanism in FSO where a certain feature or aspect of the engine has changed or is no longer supported. Since this would normally break existing mods we have the mod table feature "[tt]$Target Version:[/tt]" with which a mod can specify what version of FSO it was developed with. The features listed above will be removed or changed when the target version of a mod is at least the version released in this post.
|
||||
|
||||
[size=5pt]Previous [url=https://www.hard-light.net/forums/index.php?topic=98360.0]22.2.0 Release Thread[/url][/size]
|
||||
|
||||
Launchers, if you don't have one already:
|
||||
[b]All platforms: [/b] For every day use, we recommend [url=https://www.hard-light.net/forums/index.php?topic=94068.0]Knossos[/url], an integrated solution for downloading and launching mods.
|
||||
|
||||
[hidden=Alternative Launchers]
|
||||
Cross-platform: [url=https://www.hard-light.net/forums/index.php?topic=89162]wxLauncher 0.12.x Test Build[/url] (ongoing project for a unified launcher, you should upgrade to the latest RC/test build if you have not yet)
|
||||
[b]Important:[/b] For best compatibility with FSO 3.8 and later you should use at least wxLauncher 0.12.
|
||||
|
||||
Windows: [url=http://scp.fsmods.net/builds/Launcher55g.zip]Launcher 5.5g[/url] ([url=http://scp.indiegames.us/builds/Launcher55g.zip]Mirror[/url]) ([url=https://www.mediafire.com/?wdvzn7hhhzh418m]Mirror[/url]) Not compatible with Windows 8+, use wxLauncher above
|
||||
OS X: Soulstorm's [url=https://www.hard-light.net/forums/index.php/topic,51391.0.html]OS X Launcher 3.0[/url]
|
||||
Linux: [url=https://www.hard-light.net/forums/index.php/topic,53206.0.html]YAL[/url] or [url=https://www.hard-light.net/wiki/index.php/Fs2_open_on_Linux/Graphics_Settings]by hand[/url] or whatever you can figure out.[/hidden]
|
||||
|
||||
[img]http://scp.indiegames.us/img/windows-icon.png[/img] [color=green][size=12pt]Windows (32/64-bit)[/size][/color]
|
||||
[size=8pt]Compiled using GitHub Actions on Windows Server 2019 (10.0.17763), Visual Studio Enterprise 2019[/size]
|
||||
|
||||
[b]64-bit:[/b] ${build(groups["Win64"].mainFile)}
|
||||
|
||||
[b]32-bit:[/b] ${build(groups["Win32"].mainFile)}
|
||||
[size=8pt]This one is based on the SSE2 Optimizations from the MSVC Compiler.[/size]
|
||||
|
||||
[hidden=Alternative builds]
|
||||
|
||||
[b]64-bit AVX:[/b] ${build(groups["Win64"].subFiles["AVX"])}
|
||||
[size=8pt]This one is based on the AVX Optimizations from the MSVC Compiler (fastest build if your CPU supports AVX instructions).[/size]
|
||||
|
||||
|
||||
[b]32-bit AVX:[/b] ${build(groups["Win32"].subFiles["AVX"])}
|
||||
[size=8pt]This one is based on the AVX Optimizations from the MSVC Compiler.[/size]
|
||||
|
||||
[b]What are those SSE, SSE2 and AVX builds I keep seeing everywhere?[/b]
|
||||
[url=https://www.hard-light.net/forums/index.php?topic=65628.0]Your answer is in this topic.[/url]
|
||||
Don't want to deal with that? Use [url=https://www.hard-light.net/forums/index.php?topic=94068.0]Knossos[/url] and it will download the best build specifically for your PC!
|
||||
[/hidden]
|
||||
|
||||
[img]http://scp.indiegames.us/img/linux-icon.png[/img] [color=green][size=12pt]Linux 64-bit[/size][/color]
|
||||
[size=8pt]Compiled with Ubuntu 16.04 LTS 64-bit, GCC 5[/size]
|
||||
${build(groups["Linux"].mainFile)}
|
||||
|
||||
These builds use a mechanism called [url=https://appimage.org/]AppImage[/url] which should allow these builds to run on most Linux distributions. However, we recommend that you compile your own builds which will result in less issues.
|
||||
Alternatively, if there is a package in your software repository then you should use that. If you are the maintainer of such a package for a distribution then let us know and we will include that here.
|
||||
|
||||
|
||||
[img]http://scp.indiegames.us/img/mac-icon.png[/img] [color=green][size=12pt]OS X[/size][/color]
|
||||
[b][color=red]Not available[/color][/b] We recently lost access to our Mac CI environment which we usually used for compiling these builds so for the time being, there will be no builds for this OS.
|
||||
|
||||
[hidden=TrackIR Users]
|
||||
[size=12pt]Important!![/size]
|
||||
An external DLL is required for FSO to use TrackIR functions. The following DLL is simply unpacked in to your main FreeSpace2 root dir.
|
||||
TrackIR is only supported on Windows.
|
||||
[url=https://www.mediafire.com/download.php?4zw024zrh44etse]TrackIR SCP DLL[/url] ([url=http://scp.fsmods.net/builds/scptrackir.zip]Mirror[/url]) ([url=http://scp.indiegames.us/builds/scptrackir.zip]Mirror[/url])[/hidden]
|
||||
|
||||
Known issues:
|
||||
[list]
|
||||
[li][url=https://github.com/scp-fs2open/fs2open.github.com/issues]Github issues[/url] and [url=https://github.com/scp-fs2open/fs2open.github.com/pulls]pending pull requests[/url][/li]
|
||||
[li]See the list of [url=https://github.com/scp-fs2open/fs2open.github.com/projects/8]bugs ranked by priority[/url][/li]
|
||||
[/list]
|
||||
@@ -1,3 +0,0 @@
|
||||
Mako==1.2.2
|
||||
requests==2.26.0
|
||||
semantic-version==2.8.5
|
||||
@@ -1,46 +0,0 @@
|
||||
# miscellaneous utilities
|
||||
|
||||
import os
|
||||
import time
|
||||
from functools import wraps
|
||||
|
||||
from requests import RequestException
|
||||
|
||||
GLOBAL_TIMEOUT = 30
|
||||
|
||||
def retry_multi(max_retries):
|
||||
"""!
|
||||
@brief Decorator - Retry a function `max_retries` times.
|
||||
|
||||
@param [in] `max_retries` Maximum number of times to retry the decorated function before giving up
|
||||
|
||||
@details Intended for use with functions from the `request` module, only catches `RequestException`'s
|
||||
@note This has been copied from https://stackoverflow.com/a/23892489
|
||||
"""
|
||||
|
||||
def retry(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
num_retries = 0
|
||||
while num_retries <= max_retries:
|
||||
try:
|
||||
ret = func(*args, **kwargs)
|
||||
break
|
||||
except RequestException:
|
||||
if num_retries == max_retries:
|
||||
raise
|
||||
num_retries += 1
|
||||
time.sleep(5)
|
||||
return ret
|
||||
|
||||
return wrapper
|
||||
|
||||
return retry
|
||||
|
||||
|
||||
def expand_config_vars(config):
|
||||
"""
|
||||
@brief Expands the shell variable for the repo string from `config`
|
||||
"""
|
||||
|
||||
config["git"]["repo"] = os.path.expandvars(config["git"]["repo"])
|
||||
@@ -31,7 +31,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
cd ..
|
||||
done
|
||||
else
|
||||
eval $CMAKE -DCMAKE_BUILD_TYPE=$CONFIGURATION -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON -DFSO_BUILD_QTFRED=OFF ..
|
||||
eval $CMAKE -DCMAKE_BUILD_TYPE=$CONFIGURATION -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON -DFSO_BUILD_QTFRED=ON ..
|
||||
fi
|
||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
if [ "$BUILD_DEPLOYMENT" = true ]; then
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
buildType:
|
||||
default: release
|
||||
description: R
|
||||
choices:
|
||||
release:
|
||||
short: Release
|
||||
long: Release builds
|
||||
buildType: Release
|
||||
fastDebug:
|
||||
short: FastDebug
|
||||
long: Debug with optimizations
|
||||
buildType: FastDebug
|
||||
debug:
|
||||
short: Debug
|
||||
long: Debug without optimizations
|
||||
buildType: Debug
|
||||
@@ -1,3 +1,15 @@
|
||||
|
||||
include(WriteCompilerDetectionHeader)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED 11)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
get_property(CMAKE_CXX_KNOWN_FEATURES GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
|
||||
|
||||
write_compiler_detection_header(
|
||||
FILE "${GENERATED_SOURCE_DIR}/scp_compiler_detection.h"
|
||||
PREFIX SCP
|
||||
COMPILERS AppleClang Clang GNU MSVC
|
||||
FEATURES ${CMAKE_CXX_KNOWN_FEATURES}
|
||||
VERSION 3.2
|
||||
)
|
||||
+1
-1
@@ -8,7 +8,7 @@ if(DOXYGEN_FOUND)
|
||||
FILE(TO_NATIVE_PATH ${OUTPUT_DIR} OUTPUT_DIR)
|
||||
SET(OUTPUT_DIR \"${OUTPUT_DIR}\")
|
||||
|
||||
SET(INPUTS "${CMAKE_CURRENT_SOURCE_DIR}/code" "${CMAKE_CURRENT_SOURCE_DIR}/freespace2" "${CMAKE_CURRENT_SOURCE_DIR}/fred2" "${CMAKE_CURRENT_SOURCE_DIR}/wxfred2" "${CMAKE_CURRENT_SOURCE_DIR}/qtfred")
|
||||
SET(INPUTS "${CMAKE_CURRENT_SOURCE_DIR}/code" "${CMAKE_CURRENT_SOURCE_DIR}/freespace2" "${CMAKE_CURRENT_SOURCE_DIR}/fred2" "${CMAKE_CURRENT_SOURCE_DIR}/wxfred2")
|
||||
|
||||
SET(INPUT_DIRS)
|
||||
FOREACH(DIR IN LISTS INPUTS)
|
||||
|
||||
@@ -9,8 +9,6 @@ if (WIN32)
|
||||
if (NOT HAVE_SAPI_H)
|
||||
message(SEND_ERROR "sapi.h could not be found on your platform. Please disable speech support.")
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
# it should just work
|
||||
else()
|
||||
message(SEND_ERROR "Text to Speech is not supported on this platform!")
|
||||
endif()
|
||||
|
||||
@@ -10,7 +10,6 @@ SET(WIN32_LIBS
|
||||
winmm
|
||||
ws2_32
|
||||
psapi
|
||||
shlwapi
|
||||
)
|
||||
|
||||
IF (MINGW)
|
||||
|
||||
@@ -21,6 +21,8 @@ endif()
|
||||
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES "cstddef")
|
||||
CHECK_TYPE_SIZE("std::max_align_t" STD_MAX_ALIGN_T LANGUAGE CXX)
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES "type_traits")
|
||||
CHECK_TYPE_SIZE("std::is_trivially_copyable<int>" STD_IS_TRIVIALLY_COPYABLE LANGUAGE CXX)
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES)
|
||||
|
||||
CHECK_TYPE_SIZE("char32_t" CHAR32_T LANGUAGE CXX)
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#cmakedefine SCP_HAVE_MAX_ALIGN_T
|
||||
#cmakedefine SCP_HAVE_STD_MAX_ALIGN_T
|
||||
|
||||
#cmakedefine01 HAVE_STD_IS_TRIVIALLY_COPYABLE
|
||||
|
||||
#cmakedefine01 IS_64BIT
|
||||
|
||||
#cmakedefine01 SCP_HAVE_STRCASECMP
|
||||
|
||||
@@ -100,10 +100,6 @@ set(CMAKE_C_FLAGS_DEBUG ${COMPILER_FLAGS_DEBUG})
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "")
|
||||
|
||||
if(DEFINED ENV{LDFLAGS})
|
||||
set(CMAKE_EXE_LINKER_FLAGS $ENV{LDFLAGS})
|
||||
endif()
|
||||
|
||||
if (CLANG_USE_LIBCXX)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lc++abi")
|
||||
endif()
|
||||
|
||||
@@ -27,11 +27,6 @@ endif()
|
||||
set(COMPILER_FLAGS "")
|
||||
set(LINKER_FLAGS "")
|
||||
|
||||
# Don't ignore user-set LDFLAGS
|
||||
if(DEFINED ENV{LDFLAGS})
|
||||
set(LINKER_FLAGS $ENV{LDFLAGS})
|
||||
endif()
|
||||
|
||||
if (GCC_USE_GOLD)
|
||||
OPTION(GCC_INCREMENTAL_LINKING "Use incremental linking" OFF)
|
||||
set(LINKER_FLAGS "${LINKER_FLAGS} -fuse-ld=gold")
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ if (EXISTS "${PROJECT_SOURCE_DIR}/version_override.cmake")
|
||||
include("${PROJECT_SOURCE_DIR}/version_override.cmake")
|
||||
endif()
|
||||
|
||||
set_if_not_defined(FSO_VERSION_MAJOR 22)
|
||||
set_if_not_defined(FSO_VERSION_MINOR 3)
|
||||
set_if_not_defined(FSO_VERSION_MAJOR 20)
|
||||
set_if_not_defined(FSO_VERSION_MINOR 1)
|
||||
set_if_not_defined(FSO_VERSION_BUILD 0)
|
||||
|
||||
# A numeric revision e.g. the CI build number
|
||||
|
||||
@@ -26,7 +26,6 @@ TARGET_LINK_LIBRARIES(code PUBLIC openal)
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${LUA_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${PNG_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${JPEG_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC lz4)
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC sdl2)
|
||||
|
||||
@@ -66,12 +65,6 @@ TARGET_LINK_LIBRARIES(code PUBLIC jansson)
|
||||
|
||||
target_link_libraries(code PUBLIC variant)
|
||||
|
||||
target_link_libraries(code PUBLIC any)
|
||||
|
||||
target_link_libraries(code PUBLIC optional)
|
||||
|
||||
target_link_libraries(code PUBLIC imgui)
|
||||
|
||||
# Headers for standalone "gui" on UNIX
|
||||
IF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${MONGOOSE_LIBS})
|
||||
@@ -110,6 +103,3 @@ set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYM
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_COPY_PHASE_STRIP[variant=Debug] "NO")
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_STRIP_INSTALLED_PRODUCT[variant=Debug] "NO")
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN[variant=Debug] "NO")
|
||||
|
||||
# Prevent GCC warnings in third-party BitOp... see GitHub #4366
|
||||
suppress_file_warnings(scripting/lua/bitop/bit.c)
|
||||
@@ -7,7 +7,6 @@ ActionDefinition::ActionDefinition(SCP_string name, SCP_vector<ActionParameter>
|
||||
: m_name(std::move(name)), m_parameters(std::move(parameters))
|
||||
{
|
||||
}
|
||||
ActionDefinition::~ActionDefinition() = default;
|
||||
|
||||
const SCP_string& ActionDefinition::getName() const
|
||||
{
|
||||
|
||||
@@ -15,7 +15,6 @@ struct ActionParameter {
|
||||
class ActionDefinition {
|
||||
public:
|
||||
ActionDefinition(SCP_string name, SCP_vector<ActionParameter> parameters);
|
||||
virtual ~ActionDefinition();
|
||||
|
||||
const SCP_string& getName() const;
|
||||
const SCP_vector<actions::ActionParameter>& getParameters() const;
|
||||
|
||||
@@ -47,7 +47,24 @@ void parseActionParameters(const SCP_vector<ActionParameter>& params,
|
||||
|
||||
const ActionDefinitionManager& ActionDefinitionManager::instance()
|
||||
{
|
||||
static const ActionDefinitionManager manager;
|
||||
static const ActionDefinitionManager manager = []() {
|
||||
ActionDefinitionManager newManager;
|
||||
|
||||
newManager.addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::MoveToSubmodel>("Move To Subobject")));
|
||||
newManager.addDefinition(std::unique_ptr<ActionDefinition>(
|
||||
new BuiltinActionDefinition<types::ParticleEffectAction>("Start Particle Effect")));
|
||||
newManager.addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::PlaySoundAction>("Play 3D Sound")));
|
||||
newManager.addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::SetDirectionAction>("Set Direction")));
|
||||
newManager.addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::SetPositionAction>("Set Position")));
|
||||
newManager.addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::WaitAction>("Wait")));
|
||||
|
||||
return newManager;
|
||||
}();
|
||||
return manager;
|
||||
}
|
||||
void ActionDefinitionManager::addDefinition(std::unique_ptr<ActionDefinition> def)
|
||||
@@ -127,19 +144,5 @@ std::unique_ptr<Action> ActionDefinitionManager::parseAction(const flagset<Progr
|
||||
|
||||
return std::unique_ptr<Action>();
|
||||
}
|
||||
ActionDefinitionManager::ActionDefinitionManager()
|
||||
{
|
||||
addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::MoveToSubmodel>("Move To Subobject")));
|
||||
addDefinition(std::unique_ptr<ActionDefinition>(
|
||||
new BuiltinActionDefinition<types::ParticleEffectAction>("Start Particle Effect")));
|
||||
addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::PlaySoundAction>("Play 3D Sound")));
|
||||
addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::SetDirectionAction>("Set Direction")));
|
||||
addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::SetPositionAction>("Set Position")));
|
||||
addDefinition(std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::WaitAction>("Wait")));
|
||||
}
|
||||
|
||||
} // namespace actions
|
||||
@@ -19,7 +19,7 @@ class ActionDefinitionManager {
|
||||
static const ActionDefinitionManager& instance();
|
||||
|
||||
private:
|
||||
ActionDefinitionManager();
|
||||
ActionDefinitionManager() = default;
|
||||
|
||||
void addDefinition(std::unique_ptr<ActionDefinition> def);
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
#include "BuiltinActionDefinition.h"
|
||||
@@ -15,7 +15,6 @@ class BuiltinActionDefinition : public ActionDefinition {
|
||||
: ActionDefinition(std::move(name), {{"parameter", expression::ValueTypeTraits<ActionValueType>::type}})
|
||||
{
|
||||
}
|
||||
~BuiltinActionDefinition() override = default;
|
||||
|
||||
flagset<ProgramContextFlags> getRequiredContext() const override
|
||||
{
|
||||
|
||||
@@ -123,9 +123,9 @@ Program& Program::operator=(const Program& other)
|
||||
Program& Program::operator=(Program&&) noexcept = default;
|
||||
Program::Program(Program&&) noexcept = default;
|
||||
|
||||
bool ProgramSet::isEmpty() const
|
||||
const SCP_vector<actions::Program>& ProgramSet::getPrograms() const
|
||||
{
|
||||
return _programs.empty();
|
||||
return _programs;
|
||||
}
|
||||
|
||||
ProgramSet::ProgramSet() = default;
|
||||
|
||||
@@ -84,7 +84,7 @@ class ProgramSet {
|
||||
ProgramSet();
|
||||
explicit ProgramSet(const flagset<ProgramContextFlags>& context_flags);
|
||||
|
||||
bool isEmpty() const;
|
||||
const SCP_vector<actions::Program>& getPrograms() const;
|
||||
|
||||
/**
|
||||
* @brief Starts the execution of all programs within this set with the specified parameters
|
||||
|
||||
@@ -18,7 +18,7 @@ struct ProgramLocals {
|
||||
matrix localOrient = vmd_identity_matrix;
|
||||
int hostSubobject = -1;
|
||||
|
||||
TIMESTAMP waitTimestamp = TIMESTAMP::invalid();
|
||||
int waitTimestamp = 0;
|
||||
|
||||
expression::ProgramVariables variables;
|
||||
};
|
||||
|
||||
@@ -14,12 +14,6 @@ class AbstractExpression;
|
||||
template <typename T>
|
||||
class TypedActionExpression;
|
||||
|
||||
/**
|
||||
* @brief An expression that can be executed to compute a value at runtime
|
||||
*
|
||||
* This is the basis of having dynamic values in actions by allowing mods to specify complex expressions that make use
|
||||
* of variables and functions for computing their values at runtime.
|
||||
*/
|
||||
class ActionExpression {
|
||||
public:
|
||||
ActionExpression() = default;
|
||||
@@ -42,13 +36,6 @@ class ActionExpression {
|
||||
std::shared_ptr<nodes::AbstractExpression> m_expression;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An action expression with a specific type specified at compile time
|
||||
*
|
||||
* This is most useful for built-in actions that have the required type fixed at compile time.
|
||||
*
|
||||
* @tparam T The type of the expression value.
|
||||
*/
|
||||
template <typename T>
|
||||
class TypedActionExpression {
|
||||
public:
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "actions/expression/nodes/AbstractExpression.h"
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
#include "ParseContext.h"
|
||||
|
||||
#include "actions/expression/nodes/AbstractExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
|
||||
@@ -22,7 +22,19 @@ Value value_minus(const SCP_vector<Value>& parameters)
|
||||
|
||||
const FunctionManager& FunctionManager::instance()
|
||||
{
|
||||
static const FunctionManager manager;
|
||||
static const FunctionManager manager = []() {
|
||||
FunctionManager newManager;
|
||||
|
||||
newManager.addOperator("+", {ValueType::Integer, ValueType::Integer}, ValueType::Integer, value_plus<int>);
|
||||
newManager.addOperator("+", {ValueType::Float, ValueType::Float}, ValueType::Float, value_plus<float>);
|
||||
newManager.addOperator("+", {ValueType::Vector, ValueType::Vector}, ValueType::Vector, value_plus<vec3d>);
|
||||
|
||||
newManager.addOperator("-", {ValueType::Integer, ValueType::Integer}, ValueType::Integer, value_minus<int>);
|
||||
newManager.addOperator("-", {ValueType::Float, ValueType::Float}, ValueType::Float, value_minus<float>);
|
||||
newManager.addOperator("-", {ValueType::Vector, ValueType::Vector}, ValueType::Vector, value_minus<vec3d>);
|
||||
|
||||
return newManager;
|
||||
}();
|
||||
return manager;
|
||||
}
|
||||
void FunctionManager::addOperator(const SCP_string& name,
|
||||
@@ -75,16 +87,7 @@ const FunctionDefinition* FunctionManager::findOperator(const SCP_string& name,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FunctionManager::FunctionManager()
|
||||
{
|
||||
addOperator("+", {ValueType::Integer, ValueType::Integer}, ValueType::Integer, value_plus<int>);
|
||||
addOperator("+", {ValueType::Float, ValueType::Float}, ValueType::Float, value_plus<float>);
|
||||
addOperator("+", {ValueType::Vector, ValueType::Vector}, ValueType::Vector, value_plus<vec3d>);
|
||||
|
||||
addOperator("-", {ValueType::Integer, ValueType::Integer}, ValueType::Integer, value_minus<int>);
|
||||
addOperator("-", {ValueType::Float, ValueType::Float}, ValueType::Float, value_minus<float>);
|
||||
addOperator("-", {ValueType::Vector, ValueType::Vector}, ValueType::Vector, value_minus<vec3d>);
|
||||
}
|
||||
FunctionManager::FunctionManager() = default;
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <Parser.h>
|
||||
#include <Token.h>
|
||||
|
||||
#include "actions/expression/ParseContext.h"
|
||||
#include "actions/expression/Value.h"
|
||||
|
||||
#include <Parser.h>
|
||||
#include <Token.h>
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
@@ -43,13 +43,13 @@ ActionResult ParticleEffectAction::execute(ProgramLocals& locals) const
|
||||
auto pmi = model_get_instance(instance);
|
||||
auto pm = model_get(pmi->model_num);
|
||||
|
||||
model_instance_local_to_global_point_orient(&local_pos,
|
||||
find_submodel_instance_point_orient(&local_pos,
|
||||
&local_orient,
|
||||
&locals.localPosition,
|
||||
&locals.localOrient,
|
||||
pm,
|
||||
pmi,
|
||||
locals.hostSubobject);
|
||||
locals.hostSubobject,
|
||||
&locals.localPosition,
|
||||
&locals.localOrient);
|
||||
} else {
|
||||
local_pos = locals.localPosition;
|
||||
local_orient = locals.localOrient;
|
||||
|
||||
@@ -32,13 +32,13 @@ ActionResult PlaySoundAction::execute(ProgramLocals& locals) const
|
||||
auto pmi = model_get_instance(instance);
|
||||
auto pm = model_get(pmi->model_num);
|
||||
|
||||
model_instance_local_to_global_point_orient(&local_pos,
|
||||
find_submodel_instance_point_orient(&local_pos,
|
||||
&local_orient,
|
||||
&locals.localPosition,
|
||||
&locals.localOrient,
|
||||
pm,
|
||||
pmi,
|
||||
locals.hostSubobject);
|
||||
locals.hostSubobject,
|
||||
&locals.localPosition,
|
||||
&locals.localOrient);
|
||||
} else {
|
||||
local_pos = locals.localPosition;
|
||||
local_orient = locals.localOrient;
|
||||
|
||||
@@ -20,7 +20,7 @@ WaitAction::~WaitAction() = default;
|
||||
|
||||
ActionResult WaitAction::execute(actions::ProgramLocals& locals) const
|
||||
{
|
||||
if (!locals.waitTimestamp.isValid()) {
|
||||
if (!timestamp_valid(locals.waitTimestamp)) {
|
||||
auto waitTime = _waitTimeExpression.execute(locals.variables);
|
||||
|
||||
// Catch errors in the expression
|
||||
@@ -28,7 +28,7 @@ ActionResult WaitAction::execute(actions::ProgramLocals& locals) const
|
||||
waitTime = 0.001f;
|
||||
}
|
||||
|
||||
locals.waitTimestamp = _timestamp(fl2i(waitTime * MILLISECONDS_PER_SECOND));
|
||||
locals.waitTimestamp = timestamp(fl2i(waitTime * TIMESTAMP_FREQUENCY));
|
||||
}
|
||||
if (!timestamp_elapsed(locals.waitTimestamp)) {
|
||||
// Wait until the timestamp is elapsed
|
||||
@@ -36,7 +36,7 @@ ActionResult WaitAction::execute(actions::ProgramLocals& locals) const
|
||||
}
|
||||
// Timestamp is elapsed. The timestamp is elapsed so that timestamp_valid above returns false for the next wait
|
||||
// action
|
||||
locals.waitTimestamp = TIMESTAMP::invalid();
|
||||
locals.waitTimestamp = 0;
|
||||
return ActionResult::Finished;
|
||||
}
|
||||
std::unique_ptr<Action> WaitAction::clone() const
|
||||
|
||||
@@ -52,6 +52,34 @@ void ai_free_slot(int ai_index)
|
||||
Ai_info[ai_index].shipnum = -1;
|
||||
}
|
||||
|
||||
int get_wingnum(int objnum)
|
||||
{
|
||||
int shipnum, ai_index;
|
||||
|
||||
shipnum = Objects[objnum].instance;
|
||||
|
||||
ai_index = Ships[shipnum].ai_index;
|
||||
|
||||
return Ai_info[ai_index].wing;
|
||||
}
|
||||
|
||||
void set_wingnum(int objnum, int wingnum)
|
||||
{
|
||||
int shipnum, ai_index;
|
||||
|
||||
Assert(Objects[objnum].type == OBJ_SHIP);
|
||||
|
||||
shipnum = Objects[objnum].instance;
|
||||
|
||||
Assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
|
||||
|
||||
ai_index = Ships[shipnum].ai_index;
|
||||
|
||||
Assert( (ai_index >= 0) && (ai_index < MAX_AI_INFO) );
|
||||
|
||||
Ai_info[ai_index].wing = wingnum;
|
||||
}
|
||||
|
||||
const char *ai_get_goal_target_name(const char *name, int *index)
|
||||
{
|
||||
Assertion(name != nullptr && index != nullptr, "Arguments cannot be null!");
|
||||
|
||||
+64
-32
@@ -12,13 +12,11 @@
|
||||
#ifndef _AI_H
|
||||
#define _AI_H
|
||||
|
||||
#include "ai/aigoals.h"
|
||||
#include "ai/ai_profiles.h"
|
||||
#include "globalincs/globals.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "object/waypoint.h"
|
||||
#include "parse/sexp.h"
|
||||
#include "physics/physics.h"
|
||||
#include "ship/ship_flags.h"
|
||||
|
||||
@@ -34,8 +32,8 @@ typedef struct ai_flag_name {
|
||||
char flag_name[TOKEN_LENGTH];
|
||||
} ai_flag_name;
|
||||
|
||||
#define MAX_AI_FLAG_NAMES 2
|
||||
extern ai_flag_name Ai_flag_names[];
|
||||
extern const int Num_ai_flag_names;
|
||||
|
||||
// dock_orient_and_approach() modes.
|
||||
#define DOA_APPROACH 1 // Approach the current point on the path (aip->path_cur)
|
||||
@@ -56,19 +54,60 @@ extern const int Num_ai_flag_names;
|
||||
#define AISS_3 43 // Gotten near spot, fly about there.
|
||||
#define AISS_1a 44 // Pick a new nearby spot because we are endangered, then go to AISS_2
|
||||
|
||||
#define MAX_AI_GOALS 5
|
||||
|
||||
// types of ai goals -- tyese types will help us to determination on which goals should
|
||||
// have priority over others (i.e. when a player issues a goal to a wing, then a seperate
|
||||
// goal to a ship in that wing). We would probably use this type in conjunction with
|
||||
// goal priority to establish which goal to follow
|
||||
#define AIG_TYPE_EVENT_SHIP 1 // from mission event direct to ship
|
||||
#define AIG_TYPE_EVENT_WING 2 // from mission event direct to wing
|
||||
#define AIG_TYPE_PLAYER_SHIP 3 // from player direct to ship
|
||||
#define AIG_TYPE_PLAYER_WING 4 // from player direct to wing
|
||||
#define AIG_TYPE_DYNAMIC 5 // created on the fly
|
||||
|
||||
// Flags to ai_turn_towards_vector().
|
||||
#define AITTV_FAST (1<<0) // Turn fast, not slowed down based on skill level.
|
||||
#define AITTV_VIA_SEXP (1<<1) // Goober5000 - via sexp
|
||||
#define AITTV_IGNORE_BANK (1<<2) // Goober5000 - ignore bank when turning
|
||||
#define AITTV_SLOW_BANK_ACCEL (1<<3) // Asteroth - used by formation flying
|
||||
#define AITTV_FORCE_DELTA_BANK (1<<4) // Lafiel - Always use the provided delta bank and override calculated values
|
||||
|
||||
#define KAMIKAZE_HULL_ON_DEATH -1000.0f // Hull strength ship gets set to if it crash-dies.
|
||||
|
||||
// individual AI modes aka AI behaviors
|
||||
// structure for AI goals
|
||||
typedef struct ai_goal {
|
||||
int signature; // Unique identifier. All goals ever created (per mission) have a unique signature.
|
||||
int ai_mode; // one of the AIM_* modes for this goal
|
||||
int ai_submode; // maybe need a submode
|
||||
int type; // one of the AIG_TYPE_* values above
|
||||
flagset<AI::Goal_Flags> flags; // one of the AIGF_* values above
|
||||
fix time; // time at which this goal was issued.
|
||||
int priority; // how important is this goal -- number 0 - 100
|
||||
|
||||
const char *target_name; // name of the thing that this goal acts upon
|
||||
int target_name_index; // index of goal_target_name in Goal_target_names[][]
|
||||
waypoint_list *wp_list; // waypoints that this ship might fly.
|
||||
int target_instance; // instance of thing this ship might be chasing (currently only used for weapons; note, not the same as objnum!)
|
||||
int target_signature; // signature of object this ship might be chasing (currently only used for weapons; paired with above value to confirm target)
|
||||
|
||||
// unions for docking stuff.
|
||||
// (AIGF_DOCKER_INDEX_VALID and AIGF_DOCKEE_INDEX_VALID tell us to use indexes; otherwise we use names)
|
||||
// these are the dockpoints used on the docker and dockee ships, not the ships themselves
|
||||
union {
|
||||
const char *name;
|
||||
int index;
|
||||
} docker;
|
||||
|
||||
union {
|
||||
const char *name;
|
||||
int index;
|
||||
} dockee;
|
||||
|
||||
} ai_goal;
|
||||
|
||||
#define AIM_CHASE 0
|
||||
#define AIM_EVADE 1
|
||||
#define AIM_GET_BEHIND 2 // This mode is not actually implemented.
|
||||
#define AIM_GET_BEHIND 2
|
||||
#define AIM_STAY_NEAR 3 // Stay near another ship.
|
||||
#define AIM_STILL 4 // Sit still. Don't move. Hold your breath. Don't blink.
|
||||
#define AIM_GUARD 5 // Guard an object
|
||||
@@ -88,14 +127,14 @@ extern const int Num_ai_flag_names;
|
||||
#define AIM_SENTRYGUN 19 // AI mode for sentry guns only (floating turrets)
|
||||
#define AIM_WARP_OUT 20 // Commence warp out sequence. Point in legal direction. Then call John's code.
|
||||
#define AIM_FLY_TO_SHIP 21 // [Kazan] Fly to a ship, doesn't matter if it's hostile or friendly -- for Autopilot usage
|
||||
#define AIM_LUA 22 // Generic Lua-based AI mode
|
||||
|
||||
#define MAX_AI_BEHAVIORS 23 // Number of AIM_xxxx types
|
||||
#define MAX_AI_BEHAVIORS 22 // Number of AIM_xxxx types
|
||||
|
||||
#define MAX_WAYPOINTS_PER_LIST 20
|
||||
#define MAX_ENEMY_DISTANCE 2500.0f // Maximum distance from which a ship will pursue an enemy.
|
||||
|
||||
#define MAX_AI_GOALS 5
|
||||
#define AI_GOAL_NONE -1
|
||||
|
||||
#define AI_ACTIVE_GOAL_DYNAMIC 999
|
||||
|
||||
typedef struct ai_class {
|
||||
@@ -228,11 +267,11 @@ extern pnode *Ppfp; // Free pointer in path points.
|
||||
// Goober5000 (based on the "you can only remember 7 things in short-term memory" assumption)
|
||||
#define MAX_IGNORE_NEW_OBJECTS 7
|
||||
|
||||
#define AI_DYNAMIC_PATH_RECHECK_DELAY 1*1000
|
||||
|
||||
typedef struct ai_info {
|
||||
flagset<AI::AI_Flags> ai_flags; // Special flags for AI behavior.
|
||||
int shipnum; // Ship using this slot, -1 means none.
|
||||
int type; //
|
||||
int wing; // Member of what wing? -1 means none.
|
||||
|
||||
int behavior; // AI behavior; vestigial field from early development of FS1
|
||||
int mode;
|
||||
@@ -291,7 +330,7 @@ typedef struct ai_info {
|
||||
fix path_next_check_time; // Last time checked to see if would collide with model.
|
||||
int path_goal_dist; // minimum distance to first path point to consider path reached
|
||||
int path_subsystem_next_check; // timestamp to next check if subsystem is still visible
|
||||
vec3d path_depart_rvec; //Rotational orientation associated with the path
|
||||
vec3d path_depart_orient; //Rotational orientation associated with the path
|
||||
|
||||
int submode;
|
||||
int previous_submode; // previous submode, get it?
|
||||
@@ -304,9 +343,6 @@ typedef struct ai_info {
|
||||
int submode_parm1; // SUSHI: Another optional parameter
|
||||
fix next_predict_pos_time; // Next time to predict position.
|
||||
|
||||
int next_dynamic_path_check_time; //ETP: Time of next path check in ai_new_maybe_reposition_attack_subsys
|
||||
vec3d last_dynamic_path_goal; //ETP: Remebers last target location in ai_new_maybe_reposition_attack_subsys
|
||||
|
||||
//SUSHI: like last_predicted_enemy_pos, but for aiming (which currently ignores predicted position)
|
||||
//Unlike the predicted position stuff, also takes into account velocity
|
||||
//Only used against small ships
|
||||
@@ -369,6 +405,7 @@ typedef struct ai_info {
|
||||
|
||||
|
||||
union {
|
||||
float lead_scale; // Amount to lead current opponent by.
|
||||
float stay_near_distance; // Distance to stay within for AIM_STAY_NEAR mode.
|
||||
};
|
||||
|
||||
@@ -454,16 +491,8 @@ typedef struct ai_info {
|
||||
int ai_override_rot_timestamp; // mark for when to end the current rotational maneuver override
|
||||
|
||||
int form_obj_slotnum; // for flying in formation object mode, the position in the formation
|
||||
|
||||
int multilock_check_timestamp; // when to check for multilock next
|
||||
SCP_vector<std::pair<int, ship_subsys*>> ai_missile_locks_firing; // a list of missile locks (locked objnum, locked subsys) the ai is currently firing
|
||||
|
||||
ai_lua_parameters lua_ai_target;
|
||||
} ai_info;
|
||||
|
||||
int ai_maybe_autoscale(int absolute_index = -1);
|
||||
int ai_get_autoscale_index(int absolute_index = -1);
|
||||
|
||||
// Goober5000
|
||||
typedef struct {
|
||||
vec3d docker_point;
|
||||
@@ -488,6 +517,11 @@ typedef struct {
|
||||
|
||||
extern int Mission_all_attack; // !0 means all teams attack all teams.
|
||||
|
||||
extern void update_ai_info_for_hit(int hitter_obj, int hit_obj);
|
||||
extern void ai_frame_all(void);
|
||||
|
||||
extern int find_guard_obj(void);
|
||||
|
||||
extern ai_info Ai_info[];
|
||||
extern ai_info *Player_ai;
|
||||
|
||||
@@ -498,8 +532,7 @@ extern int Num_ai_classes;
|
||||
extern int Ai_firing_enabled;
|
||||
|
||||
extern const char *Skill_level_names(int skill_level, int translate = 1);
|
||||
|
||||
extern control_info AI_ci;
|
||||
extern int Ai_goal_signature;
|
||||
|
||||
// need access to following data in AiBig.cpp
|
||||
extern object *Pl_objp;
|
||||
@@ -521,11 +554,14 @@ void ai_object_init(object * obj, int ai_index);
|
||||
// Called once a frame
|
||||
void ai_process( object * obj, int ai_index, float frametime );
|
||||
|
||||
int get_wingnum(int objnum);
|
||||
|
||||
void set_wingnum(int objnum, int wingnum);
|
||||
const char *ai_get_goal_target_name(const char *name, int *index);
|
||||
void ai_clear_goal_target_names();
|
||||
|
||||
extern void init_ai_system(void);
|
||||
extern void ai_attack_object(object *attacker, object *attacked, int ship_info_index = -1);
|
||||
extern void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp, int ship_info_index = -1);
|
||||
extern void ai_evade_object(object *evader, object *evaded);
|
||||
extern void ai_ignore_object(object *ignorer, object *ignored, int ignore_new);
|
||||
extern void ai_ignore_wing(object *ignorer, int wingnum);
|
||||
@@ -535,7 +571,6 @@ extern void ai_do_default_behavior(object *obj);
|
||||
extern void ai_start_waypoints(object *objp, waypoint_list *wp_list, int wp_flags);
|
||||
extern void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hit_normal);
|
||||
extern void ai_ship_destroy(int shipnum);
|
||||
extern vec3d ai_get_acc_limit(vec3d* vel_limit, const object* objp);
|
||||
extern void ai_turn_towards_vector(vec3d *dest, object *objp, vec3d *slide_vec, vec3d *rel_pos, float bank_override, int flags, vec3d *rvec = nullptr, vec3d* turnrate_mod = nullptr);
|
||||
extern void init_ai_object(int objnum);
|
||||
extern void ai_init(void); // Call this one to parse ai.tbl.
|
||||
@@ -570,7 +605,7 @@ extern int ai_find_goal_index( ai_goal* aigp, int mode, int submode = -1, int pr
|
||||
extern void ai_do_safety(object *objp);
|
||||
|
||||
// used to get path info for fighter bay emerging and departing
|
||||
int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, int path_mask);
|
||||
int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, int path_mask, vec3d *pos, vec3d *fvec);
|
||||
int ai_acquire_depart_path(object *pl_objp, int parent_objnum, int path_mask);
|
||||
|
||||
// used by AiBig.cpp
|
||||
@@ -622,7 +657,7 @@ void ai_start_fly_to_ship(object *objp, int shipnum);
|
||||
void ai_fly_to_ship();
|
||||
|
||||
//Moved declaration here for player ship -WMC
|
||||
void ai_process_subobjects(int objnum);
|
||||
void process_subobjects(int objnum);
|
||||
|
||||
//SUSHI: Setting ai_info stuff from both ai class and ai profile
|
||||
void init_aip_from_class_and_profile(ai_info *aip, ai_class *aicp, ai_profile_t *profile);
|
||||
@@ -634,7 +669,4 @@ void do_random_sidethrust(ai_info *aip, ship_info *sip);
|
||||
|
||||
void ai_formation_object_recalculate_slotnums(int form_objnum, int exiting_objnum = -1);
|
||||
|
||||
|
||||
bool test_line_of_sight(vec3d* from, vec3d* to, std::unordered_set<const object*>&& excluded_objects = {}, float threshold = 10.0f, bool test_for_shields = false, bool test_for_hull = true, float* first_intersect_dist = nullptr, object** first_intersect_obj = nullptr);
|
||||
|
||||
#endif
|
||||
+2
-19
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace AI {
|
||||
FLAG_LIST(AI_Flags) {
|
||||
Formation_wing, // Fly in formation as part of wing. Also used when flying waypoints.
|
||||
Formation_wing, // Fly in formation as part of wing.
|
||||
Awaiting_repair, // Awaiting a repair ship.
|
||||
Being_repaired, // Currently docked with repair ship.
|
||||
Repairing, // Repairing a ship (or going to repair a ship)
|
||||
@@ -130,26 +130,9 @@ namespace AI {
|
||||
Use_only_single_fov_for_turrets,
|
||||
No_turning_directional_bias,
|
||||
Use_axial_turnrate_differences,
|
||||
all_nonshielded_ships_can_manage_ets,
|
||||
fightercraft_nonshielded_ships_can_manage_ets,
|
||||
nonshielded_ships_can_manage_ets,
|
||||
Better_collision_avoidance,
|
||||
Require_exact_los,
|
||||
Improved_missile_avoidance,
|
||||
Friendlies_use_countermeasure_firechance,
|
||||
Improved_subsystem_attack_pathing,
|
||||
Fixed_ship_weapon_collision,
|
||||
No_shield_damage_from_ship_collisions,
|
||||
Reset_last_hit_target_time_for_player_hits,
|
||||
Fighterbay_arrivals_use_carrier_orient,
|
||||
Fighterbay_departures_use_carrier_orient,
|
||||
Prevent_negative_turret_ammo,
|
||||
Fix_keep_safe_distance,
|
||||
Ignore_aspect_when_leading,
|
||||
Fix_good_rearm_time_bug,
|
||||
No_continuous_turn_on_attack,
|
||||
Fixed_removing_play_dead_order,
|
||||
Disable_bay_emerge_timeout,
|
||||
Adjusted_AI_class_autoscale,
|
||||
|
||||
NUM_VALUES
|
||||
};
|
||||
|
||||
+9
-110
@@ -58,26 +58,6 @@ int ai_path_type_match(char *p)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
const char* AI_secondary_range_aware_select_modes[] = {
|
||||
"retail",
|
||||
"aware",
|
||||
};
|
||||
|
||||
int Num_ai_secondary_range_aware_select_modes = sizeof(AI_secondary_range_aware_select_modes) / sizeof(char*);
|
||||
|
||||
int ai_secondary_range_select_mode_match(char* p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < Num_ai_secondary_range_aware_select_modes; i++)
|
||||
{
|
||||
if (!stricmp(AI_secondary_range_aware_select_modes[i], p))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void parse_ai_profiles_tbl(const char *filename)
|
||||
{
|
||||
int i;
|
||||
@@ -468,10 +448,7 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
|
||||
set_flag(profile, "$firing requires exact los:", AI::Profile_Flags::Require_exact_los);
|
||||
|
||||
set_flag(profile, "$fighterbay arrivals use carrier orientation:", AI::Profile_Flags::Fighterbay_arrivals_use_carrier_orient);
|
||||
|
||||
set_flag(profile, "$fighterbay departures use carrier orientation:", AI::Profile_Flags::Fighterbay_departures_use_carrier_orient);
|
||||
|
||||
profile->ai_path_mode = AI_PATH_MODE_NORMAL;
|
||||
if (optional_string("$ai path mode:"))
|
||||
{
|
||||
stuff_string(buf, F_NAME, NAME_LENGTH);
|
||||
@@ -504,12 +481,14 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
if (path_radii >= Minimum_subsystem_path_pt_dist) {
|
||||
profile->subsystem_path_radii = path_radii;
|
||||
} else {
|
||||
mprintf(("Warning: \"$override radius for subsystem path points:\" should be >= %i (read %i). Value will not be used.\n", Minimum_subsystem_path_pt_dist, path_radii));
|
||||
mprintf(("Warning: \"$override radius for subsystem path points:\" should be >= %i (read %i). Value will not be used. ", Minimum_subsystem_path_pt_dist, path_radii));
|
||||
}
|
||||
}
|
||||
|
||||
set_flag(profile, "$use POF radius for subsystem path points:", AI::Profile_Flags::Use_subsystem_path_point_radii);
|
||||
|
||||
profile->bay_arrive_speed_mult = 1.0f;
|
||||
profile->bay_depart_speed_mult = 1.0f;
|
||||
if (optional_string("$bay arrive speed multiplier:")) {
|
||||
stuff_float(&profile->bay_arrive_speed_mult);
|
||||
}
|
||||
@@ -541,76 +520,15 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
profile->second_order_lead_predict_factor = 0;
|
||||
}
|
||||
}
|
||||
if (optional_string("$ships with no shields can manage ETS:"))
|
||||
{
|
||||
mprintf(("Warning: \"$ships with no shields can manage ETS\" flag is deprecated in favor of \"$any ship with no shields can manage ETS\"\n"));
|
||||
bool temp;
|
||||
stuff_boolean(&temp);
|
||||
profile->flags.set(AI::Profile_Flags::all_nonshielded_ships_can_manage_ets, temp);
|
||||
}
|
||||
|
||||
set_flag(profile, "$no directional bias for missile and ship turning:", AI::Profile_Flags::No_turning_directional_bias);
|
||||
|
||||
set_flag(profile, "$respect ship axial turnrate differences:", AI::Profile_Flags::Use_axial_turnrate_differences);
|
||||
|
||||
set_flag(profile, "$any ship with no shields can manage ETS:", AI::Profile_Flags::all_nonshielded_ships_can_manage_ets);
|
||||
|
||||
set_flag(profile, "$fighters/bombers with no shields can manage ETS:", AI::Profile_Flags::fightercraft_nonshielded_ships_can_manage_ets);
|
||||
set_flag(profile, "$ships with no shields can manage ETS:", AI::Profile_Flags::nonshielded_ships_can_manage_ets);
|
||||
|
||||
set_flag(profile, "$better combat collision avoidance for fightercraft:", AI::Profile_Flags::Better_collision_avoidance);
|
||||
|
||||
set_flag(profile, "$improved missile avoidance for fightercraft:", AI::Profile_Flags::Improved_missile_avoidance);
|
||||
|
||||
set_flag(profile, "$friendly ships use AI profile countermeasure chance:", AI::Profile_Flags::Friendlies_use_countermeasure_firechance);
|
||||
|
||||
set_flag(profile, "$improved subsystem attack pathing:", AI::Profile_Flags::Improved_subsystem_attack_pathing);
|
||||
|
||||
set_flag(profile, "$fixed ship-weapon collisions:", AI::Profile_Flags::Fixed_ship_weapon_collision);
|
||||
|
||||
//Intention is to expand this feature to include a preference for close or long range weapons
|
||||
//hence using something besides a simple flag.
|
||||
if (optional_string("$AI secondary range awareness:"))
|
||||
{
|
||||
stuff_string(buf, F_NAME, NAME_LENGTH);
|
||||
int j = ai_secondary_range_select_mode_match(buf);
|
||||
if (j >= 0) {
|
||||
profile->ai_range_aware_secondary_select_mode = j;
|
||||
}
|
||||
else {
|
||||
Warning(LOCATION, "Invalid ai secondary range awareness mode '%s' specified", buf);
|
||||
}
|
||||
}
|
||||
|
||||
set_flag(profile, "$no shield damage from ship collisions:", AI::Profile_Flags::No_shield_damage_from_ship_collisions);
|
||||
|
||||
set_flag(profile, "$reset last_hit_target_time for player hits:", AI::Profile_Flags::Reset_last_hit_target_time_for_player_hits);
|
||||
|
||||
if (optional_string("$turret target recheck time:"))
|
||||
{
|
||||
stuff_float(&profile->turret_target_recheck_time);
|
||||
if (profile->turret_target_recheck_time < 0) {
|
||||
Warning(LOCATION, "Turret target recheck time must be positive.");
|
||||
profile->turret_target_recheck_time = 2000.0f;
|
||||
}
|
||||
}
|
||||
|
||||
set_flag(profile, "$prevent negative turret ammo:", AI::Profile_Flags::Prevent_negative_turret_ammo);
|
||||
|
||||
set_flag(profile, "$fix keep-safe-distance:", AI::Profile_Flags::Fix_keep_safe_distance);
|
||||
|
||||
set_flag(profile, "$AI ignores aspect lock for leading:", AI::Profile_Flags::Ignore_aspect_when_leading);
|
||||
|
||||
set_flag(profile, "$fix good-rearm-time bug:", AI::Profile_Flags::Fix_good_rearm_time_bug);
|
||||
|
||||
set_flag(profile, "$no continuous turn on attack:", AI::Profile_Flags::No_continuous_turn_on_attack);
|
||||
|
||||
set_flag(profile, "$remove-goal properly removes play-dead order:", AI::Profile_Flags::Fixed_removing_play_dead_order);
|
||||
|
||||
set_flag(profile, "$bay arrivals not time limited:", AI::Profile_Flags::Disable_bay_emerge_timeout);
|
||||
|
||||
set_flag(profile, "$use adjusted AI class autoscale:", AI::Profile_Flags::Adjusted_AI_class_autoscale);
|
||||
|
||||
|
||||
// if we've been through once already and are at the same place, force a move
|
||||
if (saved_Mp && (saved_Mp == Mp))
|
||||
{
|
||||
@@ -682,13 +600,11 @@ void ai_profile_t::reset()
|
||||
|
||||
flags.reset();
|
||||
|
||||
ai_path_mode = AI_PATH_MODE_NORMAL;
|
||||
ai_path_mode = 0;
|
||||
subsystem_path_radii = 0;
|
||||
bay_arrive_speed_mult = 1.0f;
|
||||
bay_depart_speed_mult = 1.0f;
|
||||
bay_arrive_speed_mult = 0;
|
||||
bay_depart_speed_mult = 0;
|
||||
second_order_lead_predict_factor = 0;
|
||||
ai_range_aware_secondary_select_mode = AI_RANGE_AWARE_SEC_SEL_MODE_RETAIL;
|
||||
turret_target_recheck_time = 2000.0f;
|
||||
|
||||
for (int i = 0; i < NUM_SKILL_LEVELS; ++i) {
|
||||
max_incoming_asteroids[i] = 0;
|
||||
@@ -760,27 +676,10 @@ void ai_profile_t::reset()
|
||||
flags.set(AI::Profile_Flags::Fix_ai_path_order_bug);
|
||||
flags.set(AI::Profile_Flags::Aspect_invulnerability_fix);
|
||||
flags.set(AI::Profile_Flags::Use_actual_primary_range);
|
||||
flags.set(AI::Profile_Flags::fightercraft_nonshielded_ships_can_manage_ets);
|
||||
flags.set(AI::Profile_Flags::nonshielded_ships_can_manage_ets);
|
||||
}
|
||||
// this flag has been enabled ever since 3.7.2
|
||||
if (mod_supports_version(3, 7, 2)) {
|
||||
flags.set(AI::Profile_Flags::Fix_ramming_stationary_targets_bug);
|
||||
}
|
||||
// and this flag has been enabled ever since 3.6.10
|
||||
if (mod_supports_version(3, 6, 10)) {
|
||||
flags.set(AI::Profile_Flags::Reset_last_hit_target_time_for_player_hits);
|
||||
}
|
||||
if (mod_supports_version(21, 4, 0)) {
|
||||
flags.set(AI::Profile_Flags::Fixed_ship_weapon_collision);
|
||||
}
|
||||
if (mod_supports_version(22, 0, 0)) {
|
||||
flags.set(AI::Profile_Flags::Fighterbay_arrivals_use_carrier_orient);
|
||||
flags.set(AI::Profile_Flags::Prevent_negative_turret_ammo);
|
||||
flags.set(AI::Profile_Flags::Fix_keep_safe_distance);
|
||||
}
|
||||
if (mod_supports_version(23, 0, 0)) {
|
||||
flags.set(AI::Profile_Flags::Fix_good_rearm_time_bug);
|
||||
flags.set(AI::Profile_Flags::No_continuous_turn_on_attack);
|
||||
flags.set(AI::Profile_Flags::Fixed_removing_play_dead_order);
|
||||
}
|
||||
}
|
||||
+1
-10
@@ -17,11 +17,8 @@
|
||||
// AI Path types
|
||||
#define AI_PATH_MODE_NORMAL 0
|
||||
#define AI_PATH_MODE_ALT1 1
|
||||
|
||||
#define AI_RANGE_AWARE_SEC_SEL_MODE_RETAIL 0
|
||||
#define AI_RANGE_AWARE_SEC_SEL_MODE_AWARE 1
|
||||
|
||||
#define MAX_AI_PROFILES 8
|
||||
#define MAX_AI_PROFILES 5
|
||||
|
||||
class ai_profile_t {
|
||||
public:
|
||||
@@ -110,12 +107,6 @@ public:
|
||||
|
||||
// How much 0-1 of a second-order lead prediction factor to add to lead indicators. Affects only the HUD indicator, and autoaim.
|
||||
float second_order_lead_predict_factor;
|
||||
|
||||
//Controls if the AI is dumb enough to keep trying to use out-of-range secondaries.
|
||||
int ai_range_aware_secondary_select_mode;
|
||||
|
||||
// how often turrets shoulds check for new targets, milliseconds
|
||||
float turret_target_recheck_time;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
+40
-216
@@ -17,13 +17,12 @@
|
||||
#include "io/timer.h"
|
||||
#include "math/staticrand.h"
|
||||
#include "mission/missionparse.h"
|
||||
#include "object/objcollide.h"
|
||||
#include "object/object.h"
|
||||
#include "ship/afterburner.h"
|
||||
#include "ship/ship.h"
|
||||
#include "utils/Random.h"
|
||||
#include "weapon/weapon.h"
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma optimize("", off)
|
||||
#pragma auto_inline(off)
|
||||
@@ -407,7 +406,7 @@ int ai_big_maybe_follow_subsys_path(int do_dot_check)
|
||||
|
||||
// get world pos of eye (stored in geye)
|
||||
ep = &(pm->view_positions[shipp->current_viewpoint]);
|
||||
model_instance_local_to_global_point( &geye, &ep->pnt, pm, pmi, 0, &Pl_objp->orient, &Pl_objp->pos );
|
||||
model_instance_find_world_point( &geye, &ep->pnt, pm, pmi, 0, &Pl_objp->orient, &Pl_objp->pos );
|
||||
|
||||
// get world pos of subsystem
|
||||
vm_vec_unrotate(&gsubpos, &aip->targeted_subsys->system_info->pnt, &En_objp->orient);
|
||||
@@ -530,120 +529,6 @@ int ai_big_maybe_follow_subsys_path(int do_dot_check)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ai_new_maybe_reposition_attack_subsys() {
|
||||
|
||||
ai_info* aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
|
||||
|
||||
if (aip->target_objnum < 0 || aip->targeted_subsys == nullptr)
|
||||
return false;
|
||||
|
||||
object* target_objp = &Objects[aip->target_objnum];
|
||||
|
||||
// dont bother with this if you're still far away
|
||||
if (vm_vec_dist(&Pl_objp->pos, &target_objp->pos) > target_objp->radius * 2.0f + 2000.0f)
|
||||
return false;
|
||||
|
||||
vec3d geye, gsubpos;
|
||||
eye* ep;
|
||||
polymodel* pm = model_get(Ship_info[Ships[Pl_objp->instance].ship_info_index].model_num);
|
||||
|
||||
// get world pos of eye (stored in geye)
|
||||
ep = &(pm->view_positions[Ships[Pl_objp->instance].current_viewpoint]);
|
||||
model_local_to_global_point(&geye, &ep->pnt, pm->id, 0, &Pl_objp->orient, &Pl_objp->pos);
|
||||
|
||||
// get world pos of subsystem
|
||||
vm_vec_unrotate(&gsubpos, &aip->targeted_subsys->system_info->pnt, &En_objp->orient);
|
||||
vm_vec_add2(&gsubpos, &En_objp->pos);
|
||||
|
||||
// you're in sight! shoot it!
|
||||
if (ship_subsystem_in_sight(En_objp, aip->targeted_subsys, &geye, &gsubpos, 0))
|
||||
return false;
|
||||
|
||||
// not in sight, gotta get there
|
||||
vec3d* good_pos = nullptr;
|
||||
// For performance reasons we only recheck once a second, otherwise we reuse the last found target.
|
||||
if (timestamp_elapsed(aip->next_dynamic_path_check_time)) {
|
||||
vec3d tgt2pl = Pl_objp->pos - target_objp->pos;
|
||||
vec3d tgt2subsys = gsubpos - target_objp->pos;
|
||||
float pl_dist = vm_vec_mag(&tgt2pl);
|
||||
float subsys_dist = vm_vec_mag(&tgt2subsys) * 1.3f; // add a bit to this, we don't want to go directly at the subsys, we want to go above it
|
||||
|
||||
float angle2subsys = vm_vec_delta_ang(&tgt2pl, &tgt2subsys, nullptr);
|
||||
float angle2goal = angle2subsys;
|
||||
if (angle2goal > 1.2f) // let's not try to go more than about 70 degrees around the ship
|
||||
angle2goal = 1.2f;
|
||||
|
||||
// we'll start by travelling along the sphere at our current distance around the ship
|
||||
// remember that goal_pos will be relative to our target_ship (so we can scale in and out easily)
|
||||
vec3d goal_pos, cross;
|
||||
vm_vec_cross(&cross, &tgt2pl, &tgt2subsys);
|
||||
vm_vec_normalize(&cross);
|
||||
vm_vec_negate(&cross);
|
||||
vm_rot_point_around_line(&goal_pos, &tgt2pl, angle2goal, &vmd_zero_vector, &cross);
|
||||
|
||||
// then scale in (or out) towards our subsys
|
||||
vec3d world_goal_pos;
|
||||
float new_radius = ((subsys_dist - pl_dist) * (angle2goal / angle2subsys)) + pl_dist;
|
||||
vm_vec_copy_scale(&world_goal_pos, &goal_pos, new_radius / pl_dist);
|
||||
world_goal_pos += target_objp->pos;
|
||||
|
||||
if (pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f)) {
|
||||
// ok best case won't work
|
||||
if (pl_dist > subsys_dist) {
|
||||
//we're farther away than the subsys, let's try at our current distance
|
||||
world_goal_pos = goal_pos + target_objp->pos;
|
||||
if (!pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f))
|
||||
good_pos = &world_goal_pos;
|
||||
}
|
||||
|
||||
// ok, let's start moving it away and see if that's better
|
||||
for (float i = 1.0f; i < 4.0f; i += 1.0f) {
|
||||
if (good_pos)
|
||||
break;
|
||||
|
||||
vm_vec_copy_scale(&world_goal_pos, &goal_pos, (new_radius / pl_dist) * powf(1.3f,i));
|
||||
world_goal_pos += target_objp->pos;
|
||||
if (!pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f))
|
||||
good_pos = &world_goal_pos;
|
||||
}
|
||||
|
||||
// welp this sucks, just run away i guess
|
||||
if (!good_pos) {
|
||||
vm_vec_copy_normalize(&world_goal_pos, &tgt2pl);
|
||||
world_goal_pos *= target_objp->radius + pl_dist;
|
||||
world_goal_pos += target_objp->pos;
|
||||
good_pos = &world_goal_pos;
|
||||
}
|
||||
}
|
||||
else
|
||||
good_pos = &world_goal_pos;
|
||||
aip->next_dynamic_path_check_time = timestamp( AI_DYNAMIC_PATH_RECHECK_DELAY );
|
||||
|
||||
}
|
||||
else{ //Not enough time has passed till recheck.
|
||||
good_pos = &aip->last_dynamic_path_goal;
|
||||
}
|
||||
|
||||
ai_turn_towards_vector(good_pos, Pl_objp, nullptr, nullptr, 0.0f, 0);
|
||||
float subsys_distance = vm_vec_dist(&Pl_objp->pos, &gsubpos);
|
||||
|
||||
// full speed until you're close, then slow down a bit
|
||||
accelerate_ship(aip, subsys_distance > 200.0f ? 1.0f : subsys_distance / 200.0f);
|
||||
|
||||
if (!(Ships[Pl_objp->instance].flags[Ship::Ship_Flags::Afterburner_locked])) {
|
||||
if (subsys_distance > 300.0f) {
|
||||
if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
|
||||
afterburners_start(Pl_objp);
|
||||
aip->afterburner_stop_time = Missiontime + 2 * F1_0;
|
||||
}
|
||||
}
|
||||
else
|
||||
afterburners_stop(Pl_objp);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// This function is only called from ai_big_chase_attack() when a ship is flying very slowly and
|
||||
// attacking a big ship. The ship should scan for enemy fighter/bombers... if any are close, then
|
||||
// return 1, otherwise return 0;
|
||||
@@ -661,17 +546,16 @@ int ai_big_maybe_start_strafe(ai_info *aip, ship_info *sip)
|
||||
// are near
|
||||
if ( sip->is_small_ship() ) {
|
||||
if ( timestamp_elapsed(aip->scan_for_enemy_timestamp) ) {
|
||||
ship_obj *so;
|
||||
object *test_objp;
|
||||
ship *test_sp;
|
||||
float dist_squared;
|
||||
|
||||
aip->scan_for_enemy_timestamp = timestamp(SCAN_FIGHTERS_INTERVAL);
|
||||
// iterate through ships, and see if any fighter/bomber from opposite team are near
|
||||
for (auto so: list_range(&Ship_obj_list)) {
|
||||
so = GET_FIRST(&Ship_obj_list);
|
||||
while( so != END_OF_LIST(&Ship_obj_list) ) {
|
||||
test_objp = &Objects[so->objnum];
|
||||
if (test_objp->flags[Object::Object_Flags::Should_be_dead])
|
||||
continue;
|
||||
|
||||
test_sp = &Ships[test_objp->instance];
|
||||
|
||||
if ( iff_x_attacks_y(Ships[Pl_objp->instance].team, test_sp->team) ) {
|
||||
@@ -682,6 +566,7 @@ int ai_big_maybe_start_strafe(ai_info *aip, ship_info *sip)
|
||||
}
|
||||
}
|
||||
}
|
||||
so = GET_NEXT(so);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -706,9 +591,6 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
|
||||
if (Pl_objp->phys_info.speed < 3.0f) {
|
||||
object *objp;
|
||||
for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
|
||||
if (objp->flags[Object::Object_Flags::Should_be_dead])
|
||||
continue;
|
||||
|
||||
if ((objp->type == OBJ_WEAPON) && (iff_x_attacks_y(Ships[Pl_objp->instance].team, Weapons[objp->instance].team)))
|
||||
if (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_LASER) {
|
||||
vec3d in_vec;
|
||||
@@ -755,10 +637,7 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
|
||||
}
|
||||
|
||||
// see if Pl_objp needs to reposition to get a good shot at subsystem which is being attacked
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing]) {
|
||||
if (ai_new_maybe_reposition_attack_subsys())
|
||||
return;
|
||||
} else if ( ai_big_maybe_follow_subsys_path() ) {
|
||||
if ( ai_big_maybe_follow_subsys_path() ) {
|
||||
if ((Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && !(aip->ai_flags[AI::AI_Flags::Kamikaze])) {
|
||||
afterburners_stop(Pl_objp);
|
||||
}
|
||||
@@ -788,28 +667,14 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
|
||||
|
||||
ai_turn_towards_vector(enemy_pos, Pl_objp, nullptr, rel_pos, 0.0f, 0, rvec);
|
||||
|
||||
// set an optimal range if applicable
|
||||
float optimal_range = 0.0f;
|
||||
ship_weapon* weapons = &Ships[Pl_objp->instance].weapons;
|
||||
weapon_info* wip = nullptr;
|
||||
ship* shipp = &Ships[Pl_objp->instance];
|
||||
|
||||
if (weapons->num_primary_banks >= 1 && weapons->current_primary_bank >= 0 && !shipp->flags[Ship::Ship_Flags::Primaries_locked]) {
|
||||
wip = &Weapon_info[weapons->primary_bank_weapons[weapons->current_primary_bank]];
|
||||
} else if (weapons->num_secondary_banks >= 1 && weapons->current_secondary_bank >= 0 && !shipp->flags[Ship::Ship_Flags::Secondaries_locked]) {
|
||||
wip = &Weapon_info[weapons->secondary_bank_weapons[weapons->current_secondary_bank]];
|
||||
}
|
||||
|
||||
if (wip != nullptr && wip->optimum_range > 0)
|
||||
optimal_range = wip->optimum_range;
|
||||
|
||||
// calc range of primary weapon
|
||||
weapon_travel_dist = ai_get_weapon_dist(&Ships[Pl_objp->instance].weapons);
|
||||
|
||||
if ( aip->targeted_subsys != NULL ) {
|
||||
if (dist_to_enemy > (optimal_range > 0.0f ? optimal_range : (weapon_travel_dist-20))) {
|
||||
if (dist_to_enemy > (weapon_travel_dist-20)) {
|
||||
accelerate_ship(aip, 1.0f);
|
||||
|
||||
ship *shipp = &Ships[Pl_objp->instance];
|
||||
if ((aip->ai_flags[AI::AI_Flags::Free_afterburner_use] || aip->ai_profile_flags[AI::Profile_Flags::Free_afterburner_use]) && !(shipp->flags[Ship::Ship_Flags::Afterburner_locked]) && (dot_to_enemy > 0.75f)) {
|
||||
if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
|
||||
afterburners_start(Pl_objp);
|
||||
@@ -834,7 +699,7 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
|
||||
} else if (dot_to_enemy < 0.75f) {
|
||||
accel = 0.75f;
|
||||
} else {
|
||||
if (dist_to_enemy > (optimal_range > 0.0f ? optimal_range : (weapon_travel_dist/2.0f))) {
|
||||
if (dist_to_enemy > weapon_travel_dist/2.0f) {
|
||||
accel = 1.0f;
|
||||
} else {
|
||||
accel = 1.0f - dot_to_enemy;
|
||||
@@ -851,6 +716,7 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
|
||||
} else {
|
||||
accelerate_ship(aip, accel);
|
||||
|
||||
ship *shipp = &Ships[Pl_objp->instance];
|
||||
if ((aip->ai_flags[AI::AI_Flags::Free_afterburner_use] || aip->ai_profile_flags[AI::Profile_Flags::Free_afterburner_use]) && !(shipp->flags[Ship::Ship_Flags::Afterburner_locked]) && (accel > 0.95f)) {
|
||||
if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
|
||||
afterburners_start(Pl_objp);
|
||||
@@ -870,9 +736,9 @@ void ai_big_chase_ct()
|
||||
ai_chase_ct();
|
||||
}
|
||||
|
||||
extern bool ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1);
|
||||
extern void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1);
|
||||
extern float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip, bool burst);
|
||||
extern bool ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp);
|
||||
extern void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp);
|
||||
extern int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vec3d *goal_point, float delta_time, float time_scale = 1.f);
|
||||
|
||||
extern void maybe_cheat_fire_synaptic(object *objp);
|
||||
@@ -891,19 +757,12 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
|
||||
swp = &Ships[Pl_objp->instance].weapons;
|
||||
|
||||
float target_radius = En_objp->radius;
|
||||
|
||||
// normal subsystem attacking uses whether or not its on a path and skips this function entirely to stop "over-eager" firing
|
||||
// since the improved method doesnt use paths, refine the target radius to stop over-eager shooting
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing] && aip->targeted_subsys != nullptr)
|
||||
target_radius = aip->targeted_subsys->system_info->radius;
|
||||
|
||||
if (dot_to_enemy > 0.95f - 0.5f * target_radius/MAX(1.0f, target_radius + dist_to_enemy)) {
|
||||
if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/MAX(1.0f, En_objp->radius + dist_to_enemy)) {
|
||||
aip->time_enemy_in_range += flFrametime;
|
||||
|
||||
// Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
|
||||
// and also the size of the target relative to distance to target.
|
||||
if (dot_to_enemy > std::max(0.5f, 0.90f + aip->ai_accuracy / 10.0f - En_objp->radius / std::max(1.0f, dist_to_enemy))) {
|
||||
if (dot_to_enemy > MAX(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/MAX(1.0f,dist_to_enemy))) {
|
||||
|
||||
ship *temp_shipp;
|
||||
temp_shipp = &Ships[Pl_objp->instance];
|
||||
@@ -924,9 +783,9 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
|
||||
if (tswp->num_secondary_banks > 0) {
|
||||
if (!(En_objp->flags[Object::Object_Flags::Protected]) || (aip->goals[0].ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP))) {
|
||||
bool valid_secondary = ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
|
||||
ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
|
||||
int current_bank = tswp->current_secondary_bank;
|
||||
if (current_bank > -1 && valid_secondary) {
|
||||
if (current_bank > -1) {
|
||||
weapon_info *swip = &Weapon_info[tswp->secondary_bank_weapons[current_bank]];
|
||||
|
||||
if(!(En_objp->flags[Object::Object_Flags::Protected]) || ((aip->goals[0].ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP)) && swip->wi_flags[Weapon::Info_Flags::Puncture] )) { //override lockdown on protected ships when using anti subsystem weapons - Valathil
|
||||
@@ -938,7 +797,7 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
}
|
||||
}
|
||||
|
||||
if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank]) && weapon_target_satisfies_lock_restrictions(swip, En_objp)) {
|
||||
if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
|
||||
float firing_range;
|
||||
if (swip->wi_flags[Weapon::Info_Flags::Local_ssm])
|
||||
firing_range=swip->lssm_lock_range;
|
||||
@@ -968,11 +827,10 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
} else {
|
||||
t = swip->fire_wait;
|
||||
if ((swip->burst_shots > 0) && (swip->burst_flags[Weapon::Burst_Flags::Random_length])) {
|
||||
swp->burst_counter[current_bank_adjusted] = Random::next(swip->burst_shots);
|
||||
swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
|
||||
} else {
|
||||
swp->burst_counter[current_bank_adjusted] = 0;
|
||||
}
|
||||
swp->burst_seed[current_bank_adjusted] = Random::next();
|
||||
}
|
||||
} else {
|
||||
if (swip->burst_shots > swp->burst_counter[current_bank_adjusted]) {
|
||||
@@ -981,11 +839,10 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
} else {
|
||||
t = set_secondary_fire_delay(aip, temp_shipp, swip, false);
|
||||
if ((swip->burst_shots > 0) && (swip->burst_flags[Weapon::Burst_Flags::Random_length])) {
|
||||
swp->burst_counter[current_bank_adjusted] = Random::next(swip->burst_shots);
|
||||
swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
|
||||
} else {
|
||||
swp->burst_counter[current_bank_adjusted] = 0;
|
||||
}
|
||||
swp->burst_seed[current_bank_adjusted] = Random::next();
|
||||
}
|
||||
}
|
||||
swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
|
||||
@@ -1022,8 +879,6 @@ void ai_big_switch_to_chase_mode(ai_info *aip)
|
||||
aip->submode_start_time = Missiontime;
|
||||
}
|
||||
|
||||
extern weapon_info* ai_get_weapon(ship_weapon* swp);
|
||||
|
||||
// Make object Pl_objp chase object En_objp, which is a big ship, not a small ship.
|
||||
void ai_big_chase()
|
||||
{
|
||||
@@ -1043,38 +898,16 @@ void ai_big_chase()
|
||||
player_pos = Pl_objp->pos;
|
||||
ai_big_pick_attack_point(En_objp, Pl_objp, &enemy_pos, 0.8f);
|
||||
|
||||
weapon_info* wip = ai_get_weapon(&shipp->weapons);
|
||||
bool ballistic_aiming = !IS_VEC_NULL(&The_mission.gravity) && wip->gravity_const != 0.0f;
|
||||
bool valid_ballistic_trajectory = true;
|
||||
// Compute the predicted position of the center of the ship, then add the delta to the goal pos.
|
||||
if (En_objp->phys_info.speed > 3.0f) {
|
||||
set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &En_objp->pos, &En_objp->phys_info.vel, aip);
|
||||
vm_vec_add2(&enemy_pos, &predicted_enemy_pos);
|
||||
vm_vec_sub2(&enemy_pos, &En_objp->pos);
|
||||
} else
|
||||
predicted_enemy_pos = En_objp->pos;
|
||||
|
||||
if (!ballistic_aiming) {
|
||||
// Compute the predicted position of the center of the ship, then add the delta to the goal pos.
|
||||
if (En_objp->phys_info.speed > 3.0f) {
|
||||
set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &En_objp->pos, &En_objp->phys_info.vel, aip);
|
||||
vm_vec_add2(&enemy_pos, &predicted_enemy_pos);
|
||||
vm_vec_sub2(&enemy_pos, &En_objp->pos);
|
||||
} else
|
||||
predicted_enemy_pos = En_objp->pos;
|
||||
|
||||
if (aip->targeted_subsys != NULL) {
|
||||
get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
|
||||
}
|
||||
} else {
|
||||
if (aip->targeted_subsys != NULL) {
|
||||
get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
|
||||
}
|
||||
|
||||
vec3d gravity = The_mission.gravity * wip->gravity_const;
|
||||
vec3d target;
|
||||
valid_ballistic_trajectory =
|
||||
physics_lead_ballistic_trajectory(&player_pos, &enemy_pos, &En_objp->phys_info.vel, wip->max_speed, &gravity, &target);
|
||||
|
||||
if (valid_ballistic_trajectory) {
|
||||
enemy_pos = Pl_objp->pos + target * vm_vec_dist(&Pl_objp->pos, &enemy_pos);
|
||||
predicted_enemy_pos = enemy_pos;
|
||||
} else {
|
||||
set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &En_objp->pos, &En_objp->phys_info.vel, aip);
|
||||
}
|
||||
if (aip->targeted_subsys != NULL) {
|
||||
get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
|
||||
}
|
||||
|
||||
dist_to_enemy = vm_vec_normalized_dir(&vec_to_enemy, &enemy_pos, &player_pos); // - En_objp->radius;
|
||||
@@ -1105,7 +938,7 @@ void ai_big_chase()
|
||||
// If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
|
||||
if ((dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags[AI::AI_Flags::Seek_lock])) {
|
||||
update_aspect_lock_information(aip, &vec_to_enemy, dist_to_enemy - En_objp->radius, En_objp->radius);
|
||||
} else if (aip->targeted_subsys != NULL && !ballistic_aiming) {
|
||||
} else if (aip->targeted_subsys != NULL) {
|
||||
Assert(aip->targeted_subsys != NULL);
|
||||
get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
|
||||
vm_vec_add2(&enemy_pos, &predicted_enemy_pos);
|
||||
@@ -1137,7 +970,7 @@ void ai_big_chase()
|
||||
}
|
||||
}
|
||||
|
||||
if (!aip->ai_profile_flags[AI::Profile_Flags::No_continuous_turn_on_attack] && aip->target_time < 2.0f)
|
||||
if (aip->target_time < 2.0f)
|
||||
if ((dot_to_enemy < 0.9f) || (dist_to_enemy > 300.0f)) {
|
||||
aip->submode = SM_CONTINUOUS_TURN;
|
||||
aip->submode_start_time = Missiontime - fl2f(2.75f); // This backdated start time allows immediate switchout.
|
||||
@@ -1206,10 +1039,7 @@ void ai_big_chase()
|
||||
float dist_normal_to_enemy;
|
||||
|
||||
if (vm_vec_mag_squared(&aip->big_attack_surface_normal) > 0.9) {
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing])
|
||||
dist_normal_to_enemy = fl_abs((dist_to_enemy / vm_vec_dot(&vec_to_enemy, &aip->big_attack_surface_normal)));
|
||||
else // I'm fairly sure this retail code VVV meant to use / instead of *
|
||||
dist_normal_to_enemy = fl_abs((dist_to_enemy * vm_vec_dot(&vec_to_enemy, &aip->big_attack_surface_normal)));
|
||||
dist_normal_to_enemy = fl_abs((dist_to_enemy * vm_vec_dot(&vec_to_enemy, &aip->big_attack_surface_normal)));
|
||||
} else {
|
||||
// don;t have normal so use a conservative value here
|
||||
dist_normal_to_enemy = 0.3f * dist_to_enemy;
|
||||
@@ -1345,7 +1175,7 @@ void ai_big_chase()
|
||||
// I noticed AI ships firing inappropriately when following a path near a big ship.
|
||||
// TODO: investigate why ships fire (and aren't close to hitting ship) when following a path near
|
||||
// a big ship
|
||||
if (aip->mode != AIM_EVADE && aip->path_start == -1 && (!ballistic_aiming || valid_ballistic_trajectory)) {
|
||||
if (aip->mode != AIM_EVADE && aip->path_start == -1 ) {
|
||||
ai_big_maybe_fire_weapons(dist_to_enemy, dot_to_enemy);
|
||||
} else {
|
||||
if (flFrametime < 1.0f)
|
||||
@@ -1486,17 +1316,13 @@ void ai_big_strafe_attack()
|
||||
ai_info *aip;
|
||||
vec3d target_pos;
|
||||
vec3d rand_vec;
|
||||
float accel, t;
|
||||
float target_dist = 0.0f;
|
||||
float target_dot = 0.0f;
|
||||
float target_dist, target_dot, accel, t;
|
||||
|
||||
aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
|
||||
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing]) {
|
||||
if (ai_new_maybe_reposition_attack_subsys())
|
||||
return;
|
||||
} else if (ai_big_maybe_follow_subsys_path(0))
|
||||
if ( ai_big_maybe_follow_subsys_path(0) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
|
||||
if ( ai_big_strafe_maybe_retreat(&target_pos) )
|
||||
@@ -1519,7 +1345,7 @@ void ai_big_strafe_attack()
|
||||
if ( t > 0.0f && t < 1.5f ) {
|
||||
// set up goal_point for avoid path to turn towards
|
||||
aip->goal_point = Pl_objp->pos;
|
||||
switch(Random::next(4)) {
|
||||
switch(rand()%4) {
|
||||
case 0:
|
||||
vm_vec_scale_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.uvec, 2.0f);
|
||||
break;
|
||||
@@ -1613,11 +1439,9 @@ void ai_big_strafe_glide_attack()
|
||||
flight_dot_to_enemy = vm_vec_dot(&target_objp->pos, &norm_vel_vec); //Angle between flight path and target ship
|
||||
|
||||
//If we're following a path, then we shouldn't be doing glide strafe
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing]) {
|
||||
if (ai_new_maybe_reposition_attack_subsys())
|
||||
return;
|
||||
} else if (ai_big_maybe_follow_subsys_path(0))
|
||||
if ( ai_big_maybe_follow_subsys_path(0) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Gets a point on the target ship to attack, as well as distance and the angle between the nose of the attacker and that point.
|
||||
ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
|
||||
@@ -1751,7 +1575,7 @@ void ai_big_strafe_retreat1()
|
||||
if (Missiontime - aip->submode_start_time > fl2f(1.50f)) {
|
||||
// set up goal_point for avoid path to turn towards
|
||||
aip->prev_goal_point = Pl_objp->pos;
|
||||
switch(Random::next(4)) {
|
||||
switch(rand()%4) {
|
||||
case 0:
|
||||
vm_vec_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.uvec);
|
||||
break;
|
||||
|
||||
+851
-1521
File diff suppressed because it is too large.
Load diff
+182
-284
File diff suppressed because it is too large.
Load diff
+5
-71
@@ -12,30 +12,15 @@
|
||||
#ifndef _AIGOALS_H
|
||||
#define _AIGOALS_H
|
||||
|
||||
#include "ai/ai_flags.h"
|
||||
#include "globalincs/globals.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "parse/sexp.h"
|
||||
#include "scripting/lua/LuaTypes.h"
|
||||
#include "scripting/lua/LuaValue.h"
|
||||
|
||||
struct wing;
|
||||
struct ai_info;
|
||||
struct ai_goal;
|
||||
|
||||
// macros for goals which get set via sexpressions in the mission code
|
||||
|
||||
// types of ai goals -- tyese types will help us to determination on which goals should
|
||||
// have priority over others (i.e. when a player issues a goal to a wing, then a seperate
|
||||
// goal to a ship in that wing). We would probably use this type in conjunction with
|
||||
// goal priority to establish which goal to follow
|
||||
#define AIG_TYPE_EVENT_SHIP 1 // from mission event direct to ship
|
||||
#define AIG_TYPE_EVENT_WING 2 // from mission event direct to wing
|
||||
#define AIG_TYPE_PLAYER_SHIP 3 // from player direct to ship
|
||||
#define AIG_TYPE_PLAYER_WING 4 // from player direct to wing
|
||||
#define AIG_TYPE_DYNAMIC 5 // created on the fly
|
||||
|
||||
#define AI_GOAL_NONE -1
|
||||
|
||||
// IMPORTANT! If you add a new AI_GOAL_x define, be sure to update the functions
|
||||
// ai_update_goal_references() and query_referenced_in_ai_goals() or else risk breaking
|
||||
// Fred. If the goal you add doesn't have a target (such as chase_any), then you don't have
|
||||
@@ -74,7 +59,6 @@ struct ai_info;
|
||||
#define AI_GOAL_IGNORE_NEW (1<<24) // 0x01000000
|
||||
#define AI_GOAL_CHASE_SHIP_CLASS (1<<25) // 0x02000000
|
||||
#define AI_GOAL_PLAY_DEAD_PERSISTENT (1<<26) // 0x03000000
|
||||
#define AI_GOAL_LUA (1<<27) // 0x04000000
|
||||
|
||||
// now the masks for ship types
|
||||
|
||||
@@ -98,53 +82,6 @@ struct ai_info;
|
||||
|
||||
#define MAX_AI_DOCK_NAMES 25
|
||||
|
||||
enum class ai_achievability { ACHIEVABLE, NOT_ACHIEVABLE, NOT_KNOWN, SATISFIED };
|
||||
|
||||
struct ai_lua_parameters {
|
||||
object_ship_wing_point_team target;
|
||||
luacpp::LuaValueList arguments;
|
||||
};
|
||||
|
||||
// structure for AI goals
|
||||
typedef struct ai_goal {
|
||||
int signature; // Unique identifier. All goals ever created (per mission) have a unique signature.
|
||||
int ai_mode; // one of the AIM_* modes for this goal
|
||||
int ai_submode; // maybe need a submode
|
||||
int type; // one of the AIG_TYPE_* values above
|
||||
flagset<AI::Goal_Flags> flags; // one of the AIGF_* values above
|
||||
fix time; // time at which this goal was issued.
|
||||
int priority; // how important is this goal -- number 0 - 100
|
||||
|
||||
const char *target_name; // name of the thing that this goal acts upon
|
||||
int target_name_index; // index of goal_target_name in Goal_target_names[][]
|
||||
waypoint_list *wp_list; // waypoints that this ship might fly.
|
||||
int target_instance; // instance of thing this ship might be chasing (currently only used for weapons; note, not the same as objnum!)
|
||||
int target_signature; // signature of object this ship might be chasing (currently only used for weapons; paired with above value to confirm target)
|
||||
|
||||
// extra goal-specific data
|
||||
int int_data;
|
||||
float float_data;
|
||||
|
||||
// unions for docking stuff.
|
||||
// (AIGF_DOCKER_INDEX_VALID and AIGF_DOCKEE_INDEX_VALID tell us to use indexes; otherwise we use names)
|
||||
// these are the dockpoints used on the docker and dockee ships, not the ships themselves
|
||||
union {
|
||||
const char *name;
|
||||
int index;
|
||||
} docker;
|
||||
|
||||
union {
|
||||
const char *name;
|
||||
int index;
|
||||
} dockee;
|
||||
|
||||
ai_lua_parameters lua_ai_target;
|
||||
|
||||
} ai_goal;
|
||||
|
||||
extern void ai_goal_reset(ai_goal *aigp, bool adding_goal = false, int ai_mode = AI_GOAL_NONE, int ai_submode = -1, int type = -1);
|
||||
|
||||
|
||||
typedef flag_def_list ai_goal_list;
|
||||
|
||||
extern ai_goal_list Ai_goal_names[];
|
||||
@@ -153,10 +90,7 @@ extern int Num_ai_goals;
|
||||
extern int Num_ai_dock_names;
|
||||
extern char Ai_dock_names[MAX_AI_DOCK_NAMES][NAME_LENGTH];
|
||||
|
||||
extern const char *Ai_goal_text(int goal, int submode);
|
||||
|
||||
// every goal in a mission gets a unique signature
|
||||
extern int Ai_goal_signature;
|
||||
extern const char *Ai_goal_text(int goal);
|
||||
|
||||
// extern function definitions
|
||||
extern void ai_post_process_mission();
|
||||
@@ -169,14 +103,14 @@ extern int ai_goal_num(ai_goal *goals);
|
||||
extern void ai_add_ship_goal_scripting(int mode, int submode, int priority, char *shipname, ai_info *aip);
|
||||
extern void ai_add_ship_goal_sexp( int sexp, int type, ai_info *aip );
|
||||
extern void ai_add_wing_goal_sexp( int sexp, int type, wing *wingp );
|
||||
extern void ai_add_goal_sub_sexp( int sexp, int type, ai_info *aip, ai_goal *aigp, char *actor_name);
|
||||
extern void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp, char *actor_name );
|
||||
|
||||
extern int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp );
|
||||
extern void ai_remove_wing_goal_sexp( int sexp, wing *wingp );
|
||||
|
||||
// adds goals to ships/sings through player orders
|
||||
extern void ai_add_ship_goal_player(int type, int mode, int submode, char* shipname, ai_info* aip, const ai_lua_parameters& lua_target = { object_ship_wing_point_team(), luacpp::LuaValueList{} });
|
||||
extern void ai_add_wing_goal_player(int type, int mode, int submode, char* shipname, int wingnum, const ai_lua_parameters& lua_target = { object_ship_wing_point_team(), luacpp::LuaValueList{} });
|
||||
extern void ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, ai_info *aip );
|
||||
extern void ai_add_wing_goal_player( int type, int mode, int submode, char *shipname, int wingnum );
|
||||
|
||||
extern void ai_remove_ship_goal( ai_info *aip, int index );
|
||||
extern void ai_clear_ship_goals( ai_info *aip );
|
||||
|
||||
@@ -32,6 +32,6 @@ int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int second
|
||||
bool check_los(int objnum, int target_objnum, float threshold, int primary_bank, int secondary_bank, ship_subsys* turret);
|
||||
|
||||
//Does all the stuff needed to aim and fire a turret.
|
||||
void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss);
|
||||
void ai_fire_from_turret(ship *shipp, ship_subsys *ss);
|
||||
|
||||
#endif
|
||||
@@ -1,233 +0,0 @@
|
||||
#include "ai/ailua.h"
|
||||
|
||||
#include "hud/hudsquadmsg.h"
|
||||
#include "iff_defs/iff_defs.h"
|
||||
#include "parse/sexp/sexp_lookup.h"
|
||||
#include "parse/sexp/LuaAISEXP.h"
|
||||
#include "scripting/api/objs/ai_helper.h"
|
||||
#include "scripting/api/objs/enums.h"
|
||||
#include "scripting/api/objs/oswpt.h"
|
||||
#include "scripting/api/objs/ship.h"
|
||||
#include "scripting/scripting.h"
|
||||
|
||||
static std::unordered_map<int, ai_mode_lua> Lua_ai_modes;
|
||||
static std::unordered_map<int, player_order_lua> Lua_player_orders;
|
||||
|
||||
void ai_lua_add_mode(int sexp_op, const ai_mode_lua& mode) {
|
||||
Lua_ai_modes.emplace(sexp_op, mode);
|
||||
}
|
||||
|
||||
bool ai_lua_add_order(int sexp_op, player_order_lua order) {
|
||||
Player_orders.emplace_back(vm_strdup(order.parseText.c_str()), vm_strdup(order.displayText.c_str()), -1, sexp_op);
|
||||
Player_orders.back().localize();
|
||||
|
||||
Lua_player_orders.emplace(sexp_op, std::move(order));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ai_lua_has_mode(int sexp_op){
|
||||
return Lua_ai_modes.count(sexp_op) > 0;
|
||||
}
|
||||
|
||||
const ai_mode_lua* ai_lua_find_mode(int sexp_op){
|
||||
auto aiLuaMode = Lua_ai_modes.find(sexp_op);
|
||||
|
||||
if(aiLuaMode == Lua_ai_modes.end())
|
||||
return nullptr;
|
||||
else
|
||||
return &aiLuaMode->second;
|
||||
}
|
||||
|
||||
const player_order_lua* ai_lua_find_player_order(int sexp_op) {
|
||||
auto aiLuaOrder = Lua_player_orders.find(sexp_op);
|
||||
|
||||
if (aiLuaOrder == Lua_player_orders.end())
|
||||
return nullptr;
|
||||
else
|
||||
return &aiLuaOrder->second;
|
||||
}
|
||||
|
||||
void run_ai_lua_action(const luacpp::LuaFunction& action, const ai_mode_lua& lua_ai, ai_info* aip) {
|
||||
if (!action.isValid()) {
|
||||
Error(LOCATION,
|
||||
"Lua AI SEXP called without a valid action function! A script probably failed to set the action for some reason.");
|
||||
return;
|
||||
}
|
||||
|
||||
luacpp::LuaValueList luaParameters;
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_AI_Helper.Set(object_h(&Objects[Ships[aip->shipnum].objnum]))));
|
||||
if (lua_ai.needsTarget) {
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_OSWPT.Set(aip->lua_ai_target.target)));
|
||||
}
|
||||
for (const auto& additionalParam : aip->lua_ai_target.arguments) {
|
||||
luaParameters.push_back(additionalParam);
|
||||
}
|
||||
|
||||
auto retVals = action.call(Script_system.GetLuaSession(), luaParameters);
|
||||
|
||||
if (!retVals.empty() && retVals[0].getValueType() == luacpp::ValueType::BOOLEAN) {
|
||||
if (retVals[0].getValue<bool>())
|
||||
ai_mission_goal_complete(aip);
|
||||
}
|
||||
}
|
||||
|
||||
void ai_lua(ai_info* aip){
|
||||
Assertion(aip->mode == AIM_LUA, "Tried to invoke LuaAI on a ship not in LuaAI-Mode");
|
||||
|
||||
const auto& lua_ai = Lua_ai_modes.at(aip->submode);
|
||||
|
||||
const auto& lua_ai_sexp = lua_ai.sexp;
|
||||
const auto& action = lua_ai_sexp.getAction();
|
||||
|
||||
run_ai_lua_action(action, lua_ai, aip);
|
||||
}
|
||||
|
||||
void ai_lua_start(ai_goal* aigp, object* objp){
|
||||
|
||||
Assert(objp->type == OBJ_SHIP);
|
||||
Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
|
||||
|
||||
ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
|
||||
|
||||
aip->mode = AIM_LUA;
|
||||
aip->submode = aigp->ai_submode;
|
||||
aip->lua_ai_target = aigp->lua_ai_target;
|
||||
aip->submode_start_time = Missiontime;
|
||||
|
||||
const auto& lua_ai = Lua_ai_modes.at(aigp->ai_submode);
|
||||
|
||||
const auto& lua_ai_sexp = lua_ai.sexp;
|
||||
const auto& action = lua_ai_sexp.getActionEnter();
|
||||
|
||||
run_ai_lua_action(action, lua_ai, aip);
|
||||
}
|
||||
|
||||
bool ai_lua_is_valid_target_intrinsic(int sexp_op, int target_objnum, ship* self) {
|
||||
ship* target = &Ships[Objects[target_objnum].instance];
|
||||
|
||||
const player_order_lua& order = *ai_lua_find_player_order(sexp_op);
|
||||
switch (order.targetRestrictions) {
|
||||
case player_order_lua::target_restrictions::TARGET_ALL:
|
||||
return true;
|
||||
case player_order_lua::target_restrictions::TARGET_OWN:
|
||||
return target->team == self->team;
|
||||
case player_order_lua::target_restrictions::TARGET_ALLIES:
|
||||
return !iff_x_attacks_y(self->team, target->team);
|
||||
case player_order_lua::target_restrictions::TARGET_ENEMIES:
|
||||
return iff_x_attacks_y(self->team, target->team);
|
||||
case player_order_lua::target_restrictions::TARGET_SAME_WING:
|
||||
return target->wingnum != -1 && self->wingnum == target->wingnum;
|
||||
case player_order_lua::target_restrictions::TARGET_PLAYER_WING:
|
||||
return target->wingnum != -1 && Ships[Player_obj->instance].wingnum == target->wingnum;
|
||||
case player_order_lua::target_restrictions::TARGET_ALL_CAPS:
|
||||
return Ship_info[target->ship_info_index].is_big_ship();
|
||||
case player_order_lua::target_restrictions::TARGET_ALLIED_CAPS:
|
||||
return !iff_x_attacks_y(self->team, target->team) && Ship_info[target->ship_info_index].is_big_ship();
|
||||
case player_order_lua::target_restrictions::TARGET_ENEMY_CAPS:
|
||||
return iff_x_attacks_y(self->team, target->team) && Ship_info[target->ship_info_index].is_big_ship();
|
||||
case player_order_lua::target_restrictions::TARGET_NOT_SELF:
|
||||
return self->objnum != target_objnum;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ai_lua_is_valid_target_lua(const ai_mode_lua& mode, int target_objnum, ship* self) {
|
||||
|
||||
const auto& action = mode.sexp.getTargetRestrict();
|
||||
|
||||
if (!action.isValid())
|
||||
return true;
|
||||
|
||||
luacpp::LuaValueList luaParameters;
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_Ship.Set(object_h(&Objects[self->objnum]))));
|
||||
if (mode.needsTarget) {
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_OSWPT.Set(object_ship_wing_point_team(&Ships[Objects[target_objnum].instance]))));
|
||||
}
|
||||
|
||||
auto retVals = action.call(Script_system.GetLuaSession(), luaParameters);
|
||||
|
||||
if (!retVals.empty() && retVals[0].getValueType() == luacpp::ValueType::BOOLEAN) {
|
||||
return retVals[0].getValue<bool>();
|
||||
}
|
||||
else {
|
||||
Error(LOCATION, "LuaAI target restriction function did not return a valid boolean!");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ai_lua_is_valid_target(int sexp_op, int target_objnum, ship* self, size_t order) {
|
||||
const ai_mode_lua& mode = *ai_lua_find_mode(sexp_op);
|
||||
|
||||
//All targetless AI modes are fine
|
||||
if (mode.needsTarget) {
|
||||
|
||||
//No target is then not valid
|
||||
if (target_objnum == -1)
|
||||
return false;
|
||||
|
||||
//As of now, only accept ships
|
||||
if (Objects[target_objnum].type != OBJ_SHIP)
|
||||
return false;
|
||||
|
||||
if (!ai_lua_is_valid_target_intrinsic(sexp_op, target_objnum, self))
|
||||
return false;
|
||||
|
||||
// check if this order can be issued against the target
|
||||
ship *shipp = &Ships[Objects[target_objnum].instance];
|
||||
if (shipp->orders_allowed_against.find(order) == shipp->orders_allowed_against.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//If we haven't bailed yet, query the custom callback
|
||||
return ai_lua_is_valid_target_lua(mode, target_objnum, self);
|
||||
}
|
||||
|
||||
ai_achievability ai_lua_is_achievable(const ai_goal* aigp, int objnum){
|
||||
const auto& lua_ai = Lua_ai_modes.at(aigp->ai_submode);
|
||||
|
||||
const auto& action = lua_ai.sexp.getAchievable();
|
||||
|
||||
if (!action.isValid())
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
|
||||
luacpp::LuaValueList luaParameters;
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_Ship.Set(object_h(&Objects[objnum]))));
|
||||
if (lua_ai.needsTarget) {
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_OSWPT.Set(aigp->lua_ai_target.target)));
|
||||
}
|
||||
for (const auto& additionalParam : aigp->lua_ai_target.arguments) {
|
||||
luaParameters.push_back(additionalParam);
|
||||
}
|
||||
|
||||
auto retVals = action.call(Script_system.GetLuaSession(), luaParameters);
|
||||
|
||||
if (!retVals.empty() && retVals[0].getValueType() == luacpp::ValueType::USERDATA) {
|
||||
scripting::api::enum_h enumData;
|
||||
retVals[0].getValue(scripting::api::l_Enum.Get(&enumData));
|
||||
if (!enumData.IsValid()) {
|
||||
Error(LOCATION, "LuaAI SEXP achievability hook returned an invalid avilability enum.");
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
|
||||
switch (enumData.index) {
|
||||
case scripting::api::LE_LUAAI_ACHIEVABLE:
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
case scripting::api::LE_LUAAI_NOT_YET_ACHIEVABLE:
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
case scripting::api::LE_LUAAI_UNACHIEVABLE:
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
default:
|
||||
Error(LOCATION, "LuaAI SEXP achievability hook returned an invalid avilability enum.");
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Error(LOCATION, "LuaAI SEXP achievability hook did not return any avilability enum!");
|
||||
}
|
||||
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ai/ai.h"
|
||||
#include "ai/aigoals.h"
|
||||
#include "mission/missionmessage.h"
|
||||
#include "ship/ship.h"
|
||||
#include "parse/sexp.h"
|
||||
#include "globalincs/globals.h"
|
||||
|
||||
namespace sexp { class LuaAISEXP; }
|
||||
|
||||
struct ai_mode_lua {
|
||||
const sexp::LuaAISEXP& sexp;
|
||||
bool needsTarget;
|
||||
const char* hudText;
|
||||
};
|
||||
|
||||
struct player_order_lua {
|
||||
int ai_message = MESSAGE_YESSIR;
|
||||
SCP_string parseText = "";
|
||||
SCP_string displayText = "";
|
||||
enum class target_restrictions : int { TARGET_ALLIES, TARGET_ALL, TARGET_OWN, TARGET_ENEMIES, TARGET_SAME_WING, TARGET_PLAYER_WING, TARGET_ALL_CAPS, TARGET_ALLIED_CAPS, TARGET_ENEMY_CAPS, TARGET_NOT_SELF } targetRestrictions = target_restrictions::TARGET_ALL;
|
||||
};
|
||||
|
||||
|
||||
void ai_lua_add_mode(int sexp_op, const ai_mode_lua& mode);
|
||||
bool ai_lua_add_order(int sexp_op, player_order_lua order);
|
||||
bool ai_lua_has_mode(int sexp_op);
|
||||
const ai_mode_lua* ai_lua_find_mode(int sexp_op);
|
||||
const player_order_lua* ai_lua_find_player_order(int sexp_op);
|
||||
void ai_lua(ai_info* aip);
|
||||
void ai_lua_start(ai_goal* aigp, object* objp);
|
||||
bool ai_lua_is_valid_target(int sexp_op, int target_objnum, ship* self, size_t order);
|
||||
ai_achievability ai_lua_is_achievable(const ai_goal* aigp, int objnum);
|
||||
+234
-309
File diff suppressed because it is too large.
Load diff
@@ -397,6 +397,8 @@ int anim_show_next_frame(anim_instance *instance, float frametime)
|
||||
|
||||
t1 = timer_get_fixed_seconds();
|
||||
for ( i = 0; i < frame_diff; i++ ) {
|
||||
anim_check_for_palette_change(instance);
|
||||
|
||||
// if we're playing backwards, every frame must be a keyframe and we set the data ptr here
|
||||
if(instance->direction == ANIM_DIRECT_REVERSE){
|
||||
if ( anim_instance_is_streamed(instance) ) {
|
||||
@@ -641,7 +643,7 @@ void anim_read_header(anim *ptr, CFILE *fp)
|
||||
|
||||
if (diff != 0) {
|
||||
if (ptr->height > 16) {
|
||||
nprintf(("ANI","ANI %s with size %dx%d (%.1f%% wasted)\n", ptr->name, ptr->width, ptr->height, waste));
|
||||
mprintf(("ANI %s with size %dx%d (%.1f%% wasted)\n", ptr->name, ptr->width, ptr->height, waste));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -788,6 +790,9 @@ anim *anim_load(const char *real_filename, int cf_dir_type, int file_mapped)
|
||||
|
||||
cfclose(fp);
|
||||
|
||||
// store screen signature, so we can tell if palette changes
|
||||
ptr->screen_sig = gr_screen.signature;
|
||||
|
||||
anim_set_palette(ptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,13 @@
|
||||
const int packer_code = PACKER_CODE;
|
||||
const int transparent_code = 254;
|
||||
|
||||
void anim_check_for_palette_change(anim_instance *instance) {
|
||||
if ( instance->parent->screen_sig != gr_screen.signature ) {
|
||||
instance->parent->screen_sig = gr_screen.signature;
|
||||
anim_set_palette(instance->parent);
|
||||
}
|
||||
}
|
||||
|
||||
anim_instance *init_anim_instance(anim *ptr, int bpp)
|
||||
{
|
||||
anim_instance *inst;
|
||||
@@ -90,6 +97,8 @@ ubyte *anim_get_next_raw_buffer(anim_instance *inst, int xlate_pal, int aabitmap
|
||||
return NULL;
|
||||
}
|
||||
|
||||
anim_check_for_palette_change(inst);
|
||||
|
||||
if ( anim_instance_is_streamed(inst) ) {
|
||||
if ( xlate_pal ){
|
||||
inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation, aabitmap, bpp);
|
||||
|
||||
@@ -58,6 +58,7 @@ typedef struct anim {
|
||||
ubyte xparent_g; // green component for the transparent color in source image
|
||||
ubyte xparent_b; // blue component for the transparent color in source image
|
||||
int flags;
|
||||
uint screen_sig;
|
||||
int file_offset; // file offset to start of frame data
|
||||
int cache_file_offset;
|
||||
ubyte *cache;
|
||||
@@ -104,6 +105,7 @@ anim_instance *init_anim_instance(anim *ptr, int bpp);
|
||||
void free_anim_instance(anim_instance *inst);
|
||||
ubyte *anim_get_next_raw_buffer(anim_instance *inst, int xlate_pal, int aabitmap, int bpp);
|
||||
void anim_set_palette(anim *a);
|
||||
void anim_check_for_palette_change(anim_instance *inst);
|
||||
|
||||
|
||||
#endif /* __PACKUNPACK_H__ */
|
||||
+387
-828
File diff suppressed because it is too large.
Load diff
+22
-35
@@ -14,29 +14,29 @@
|
||||
|
||||
#include "globalincs/globals.h" // for NAME_LENGTH
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "io/timer.h"
|
||||
|
||||
|
||||
class object;
|
||||
class polymodel;
|
||||
struct collision_info_struct;
|
||||
class model_draw_list;
|
||||
|
||||
#define MAX_ASTEROIDS 2000 //Increased from 512 to 2000 in 2022
|
||||
#define MAX_ASTEROIDS 512
|
||||
|
||||
#define NUM_ASTEROID_SIZES 3
|
||||
#define NUM_ASTEROID_POFS 3 // Number of POFs per debris size
|
||||
#define NUM_DEBRIS_SIZES 3
|
||||
#define NUM_DEBRIS_POFS 3 // Number of POFs per debris size
|
||||
|
||||
#define ASTEROID_TYPE_DEBRIS -1
|
||||
#define ASTEROID_TYPE_SMALL 0
|
||||
#define ASTEROID_TYPE_MEDIUM 1
|
||||
#define ASTEROID_TYPE_LARGE 2
|
||||
|
||||
// these should always be equal for the benefit of generic asteroids (c.f. asteroid_page_in)
|
||||
#define MAX_ACTIVE_DEBRIS_TYPES NUM_ASTEROID_SIZES
|
||||
#define MAX_ACTIVE_DEBRIS_TYPES NUM_DEBRIS_SIZES
|
||||
|
||||
// Goober5000 - currently same as MAX_SHIP_DETAIL_LEVELS (put here to avoid an #include)
|
||||
#define MAX_ASTEROID_DETAIL_LEVELS 5
|
||||
|
||||
|
||||
// Data structure to track the active asteroids
|
||||
typedef struct asteroid_obj {
|
||||
asteroid_obj *next, *prev;
|
||||
@@ -48,18 +48,16 @@ extern asteroid_obj Asteroid_obj_list;
|
||||
// Data structure for determining a type and amount of other asteroids an
|
||||
// asteroid will split to when destroyed.
|
||||
typedef struct asteroid_split_info {
|
||||
int asteroid_type; //index position
|
||||
char name[NAME_LENGTH]; //asteroid name
|
||||
int min; //minimum asteroids to spawn
|
||||
int max; //maximum asteroids to spawn
|
||||
int asteroid_type;
|
||||
int min;
|
||||
int max;
|
||||
} asteroid_split_info;
|
||||
|
||||
class asteroid_info
|
||||
{
|
||||
public:
|
||||
char name[NAME_LENGTH]; // name for the asteroid
|
||||
int type; // type of asteroid, 0 = small, 1 = medium, 2 = large, -1 = debris
|
||||
char pof_files[NUM_ASTEROID_POFS][MAX_FILENAME_LEN]; // POF files to load/associate with ship
|
||||
char pof_files[NUM_DEBRIS_POFS][MAX_FILENAME_LEN]; // POF files to load/associate with ship
|
||||
int num_detail_levels; // number of detail levels for this ship
|
||||
int detail_distance[MAX_ASTEROID_DETAIL_LEVELS]; // distance to change detail levels at
|
||||
float max_speed; // cap on speed for asteroid
|
||||
@@ -71,29 +69,25 @@ public:
|
||||
float blast; // maximum blast impulse from area effect explosion
|
||||
float initial_asteroid_strength; // starting strength of asteroid
|
||||
SCP_vector< asteroid_split_info > split_info;
|
||||
polymodel *modelp[NUM_ASTEROID_POFS];
|
||||
int model_num[NUM_ASTEROID_POFS];
|
||||
polymodel *modelp[NUM_DEBRIS_POFS];
|
||||
int model_num[NUM_DEBRIS_POFS];
|
||||
SCP_vector<int> explosion_bitmap_anims;
|
||||
float fireball_radius_multiplier; // the model radius is multiplied by this to determine the fireball size
|
||||
SCP_string display_name; // only used for hud targeting display and for 'ship' asteroids
|
||||
float spawn_weight; // ship asteroids only, relative proportion to spawn compared to other types in its asteroid field
|
||||
float gravity_const; // multiplier for mission gravity
|
||||
|
||||
asteroid_info( )
|
||||
: type(ASTEROID_TYPE_DEBRIS), num_detail_levels(0), max_speed(0), damage_type_idx(0),
|
||||
: num_detail_levels( 0 ), max_speed( 0 ), damage_type_idx( 0 ),
|
||||
damage_type_idx_sav( -1 ), inner_rad( 0 ), outer_rad( 0 ),
|
||||
damage( 0 ), blast( 0 ), initial_asteroid_strength( 0 ),
|
||||
fireball_radius_multiplier( -1 ), spawn_weight( 1 ), gravity_const( 0 )
|
||||
fireball_radius_multiplier( -1 )
|
||||
{
|
||||
name[ 0 ] = 0;
|
||||
display_name = "";
|
||||
memset( detail_distance, 0, sizeof( detail_distance ) );
|
||||
|
||||
for (int i = 0; i < NUM_ASTEROID_POFS; i++)
|
||||
for (int i = 0; i < NUM_DEBRIS_POFS; i++)
|
||||
{
|
||||
modelp[i] = NULL;
|
||||
model_num[i] = -1;
|
||||
pof_files[i][0] = '\0';
|
||||
pof_files[i][0] = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -107,9 +101,9 @@ typedef struct asteroid {
|
||||
int model_instance_num;
|
||||
int asteroid_type; // 0..MAX_DEBRIS_TYPES
|
||||
int asteroid_subtype; // Index in asteroid_info for modelnum and modelp
|
||||
TIMESTAMP check_for_wrap; // timestamp to check for asteroid wrapping around field
|
||||
TIMESTAMP check_for_collide; // timestamp to check for asteroid colliding with escort ships
|
||||
TIMESTAMP final_death_time; // timestamp to swap in new models after explosion starts
|
||||
int check_for_wrap; // timestamp to check for asteroid wrapping around field
|
||||
int check_for_collide; // timestamp to check for asteroid colliding with escort ships
|
||||
int final_death_time; // timestamp to swap in new models after explosion starts
|
||||
int collide_objnum; // set to objnum that asteroid will be impacting soon
|
||||
int collide_objsig; // object signature corresponding to collide_objnum
|
||||
vec3d death_hit_pos; // hit pos that caused death
|
||||
@@ -120,7 +114,7 @@ typedef struct asteroid {
|
||||
// TYPEDEF FOR DEBRIS TYPE
|
||||
typedef enum {
|
||||
DG_ASTEROID,
|
||||
DG_DEBRIS
|
||||
DG_SHIP
|
||||
} debris_genre_t;
|
||||
|
||||
// TYPEDEF FOR FIELD TYPE
|
||||
@@ -133,7 +127,7 @@ typedef struct asteroid_field {
|
||||
vec3d min_bound; // Minimum range of field.
|
||||
vec3d max_bound; // Maximum range of field.
|
||||
float bound_rad;
|
||||
bool has_inner_bound;
|
||||
int has_inner_bound;
|
||||
vec3d inner_min_bound;
|
||||
vec3d inner_max_bound;
|
||||
vec3d vel; // Average asteroid moves at this velocity.
|
||||
@@ -142,9 +136,6 @@ typedef struct asteroid_field {
|
||||
field_type_t field_type; // active throws and wraps, passive does not
|
||||
debris_genre_t debris_genre; // type of debris (ship or asteroid) [generic type]
|
||||
int field_debris_type[MAX_ACTIVE_DEBRIS_TYPES]; // one of the debris type defines above
|
||||
int num_used_field_debris_types; // how many of the above are used
|
||||
|
||||
SCP_vector<SCP_string> target_names; // default retail behavior is to just throw at the first big ship in the field
|
||||
} asteroid_field;
|
||||
|
||||
extern SCP_vector< asteroid_info > Asteroid_info;
|
||||
@@ -161,9 +152,7 @@ void asteroid_init();
|
||||
void asteroid_level_init();
|
||||
void asteroid_level_close();
|
||||
void asteroid_create_all();
|
||||
void asteroid_create_asteroid_field(int num_asteroids, int field_type, int asteroid_speed, bool brown, bool blue, bool orange, vec3d o_min, vec3d o_max, bool inner_box, vec3d i_min, vec3d i_max, SCP_vector<SCP_string> targets);
|
||||
void asteroid_create_debris_field(int num_asteroids, int asteroid_speed, int debris1, int debris2, int debris3, vec3d o_min, vec3d o_max);
|
||||
void asteroid_render(object* obj, model_draw_list* scene);
|
||||
void asteroid_render(object * obj, model_draw_list *scene);
|
||||
void asteroid_delete( object *asteroid_objp );
|
||||
void asteroid_process_pre( object *asteroid_objp );
|
||||
void asteroid_process_post( object *asteroid_objp);
|
||||
@@ -174,8 +163,6 @@ int asteroid_collide_objnum(object *asteroid_objp);
|
||||
float asteroid_time_to_impact(object *asteroid_objp);
|
||||
void asteroid_show_brackets();
|
||||
void asteroid_target_closest_danger();
|
||||
void asteroid_add_target(object* objp);
|
||||
int get_asteroid_position(const char* name);
|
||||
|
||||
// need to extern for multiplayer
|
||||
void asteroid_sub_create(object *parent_objp, int asteroid_type, vec3d *relvec);
|
||||
|
||||
@@ -47,9 +47,10 @@ NavMessage NavMsgs[NP_NUM_MESSAGES];
|
||||
int audio_handle;
|
||||
int NavLinkDistance;
|
||||
// time offsets for autonav events
|
||||
TIMESTAMP LockAPConv;
|
||||
TIMESTAMP EndAPCinematic;
|
||||
TIMESTAMP MoveCamera;
|
||||
int LockAPConv;
|
||||
int EndAPCinematic;
|
||||
int MoveCamera;
|
||||
int camMovingTime;
|
||||
//float CameraSpeed;
|
||||
bool CinematicStarted, CameraMoving;
|
||||
vec3d cameraPos, cameraTarget;
|
||||
@@ -150,9 +151,6 @@ bool CanAutopilot(const vec3d *targetPos, bool send_msg)
|
||||
for (ship_obj *so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so))
|
||||
{
|
||||
object *other_objp = &Objects[so->objnum];
|
||||
if (other_objp->flags[Object::Object_Flags::Should_be_dead])
|
||||
continue;
|
||||
|
||||
// attacks player?
|
||||
if (iff_x_attacks_y(obj_team(other_objp), obj_team(Player_obj))
|
||||
&& !(Ship_info[Ships[other_objp->instance].ship_info_index].flags[Ship::Info_Flags::Cargo])) // ignore cargo
|
||||
@@ -208,9 +206,8 @@ bool StartAutopilot()
|
||||
{
|
||||
// Check for support ship and dismiss it if it is not doing anything.
|
||||
// If the support ship is doing something then tell the user such.
|
||||
for (auto so: list_range(&Ship_obj_list))
|
||||
for ( object *objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) )
|
||||
{
|
||||
auto objp = &Objects[so->objnum];
|
||||
if ((objp->type == OBJ_SHIP) && !(objp->flags[Object::Object_Flags::Should_be_dead]))
|
||||
{
|
||||
Assertion((objp->instance >= 0) && (objp->instance < MAX_SHIPS),
|
||||
@@ -265,9 +262,9 @@ bool StartAutopilot()
|
||||
}
|
||||
|
||||
if (The_mission.flags[Mission::Mission_Flags::Use_ap_cinematics])
|
||||
LockAPConv = _timestamp(); // lock convergence instantly
|
||||
LockAPConv = timestamp(); // lock convergence instantly
|
||||
else
|
||||
LockAPConv = _timestamp(3 * MILLISECONDS_PER_SECOND); // 3 seconds before we lock convergence
|
||||
LockAPConv = timestamp(3000); // 3 seconds before we lock convergence
|
||||
Player_use_ai = 1;
|
||||
set_time_compression(1);
|
||||
lock_time_compression(true);
|
||||
@@ -405,7 +402,7 @@ bool StartAutopilot()
|
||||
&& Autopilot_flight_leader != &Objects[Ships[i].objnum]) //only if not flight leader's object
|
||||
{
|
||||
ai_info *aip = &Ai_info[Ships[i].ai_index];
|
||||
int wingnum = Ships[i].wingnum, wing_index = get_wing_index(&Objects[Ships[i].objnum], wingnum);
|
||||
int wingnum = aip->wing, wing_index = get_wing_index(&Objects[Ships[i].objnum], wingnum);
|
||||
vec3d goal_point;
|
||||
object *leader_objp = get_wing_leader(wingnum);
|
||||
|
||||
@@ -719,7 +716,7 @@ bool StartAutopilot()
|
||||
vm_vec_scale(&pos, 5*speed_cap*tc_factor); // pos is now scaled by 5 times the speed (5 seconds ahead)
|
||||
vm_vec_add(&pos, &pos, &Autopilot_flight_leader->pos); // pos is now 5*speed cap in front of player ship
|
||||
|
||||
switch (Random::next(24))
|
||||
switch (myrand()%24)
|
||||
// 8 different ways of getting perp points
|
||||
// 4 of which will not be used when capships are present (anything below, or straight above)
|
||||
{
|
||||
@@ -798,8 +795,7 @@ bool StartAutopilot()
|
||||
vm_vec_scale(&perp, Autopilot_flight_leader->radius+radius);
|
||||
|
||||
// randomly scale up/down by up to 20%
|
||||
// jg18 - original comment said [-20,20] but preserving existing behavior
|
||||
j = Random::next(-19, 20);
|
||||
j = 20-myrand()%40; // [-20,20]
|
||||
|
||||
vm_vec_scale(&perp, 1.0f+(float(j)/100.0f));
|
||||
vm_vec_add(&cameraPos, &pos, &perp);
|
||||
@@ -831,8 +827,9 @@ bool StartAutopilot()
|
||||
//CameraMoving = false;
|
||||
|
||||
|
||||
EndAPCinematic = _timestamp((10 * MILLISECONDS_PER_SECOND * tc_factor) + NPS_TICKRATE); // 10 objective seconds before end of cinematic
|
||||
MoveCamera = _timestamp(fl2i(5.5 * MILLISECONDS_PER_SECOND * tc_factor) + NPS_TICKRATE);
|
||||
EndAPCinematic = timestamp((10000*tc_factor)+NPS_TICKRATE); // 10 objective seconds before end of cinematic
|
||||
MoveCamera = timestamp((5500*tc_factor)+NPS_TICKRATE);
|
||||
camMovingTime = int(4.5*float(tc_factor));
|
||||
set_time_compression((float)tc_factor);
|
||||
}
|
||||
|
||||
@@ -925,7 +922,10 @@ void EndAutoPilot()
|
||||
if ( ((aigp->target_name != NULL) && !stricmp(aigp->target_name, goal_name))
|
||||
&& (aigp->ai_mode == goal) )
|
||||
{
|
||||
ai_goal_reset(aigp);
|
||||
aigp->ai_mode = AI_GOAL_NONE;
|
||||
aigp->signature = -1;
|
||||
aigp->priority = -1;
|
||||
aigp->flags.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -946,7 +946,10 @@ void EndAutoPilot()
|
||||
if ( ((aigp->target_name != NULL) && !stricmp(aigp->target_name, goal_name))
|
||||
&& (aigp->ai_mode == goal) )
|
||||
{
|
||||
ai_goal_reset(aigp);
|
||||
aigp->ai_mode = AI_GOAL_NONE;
|
||||
aigp->signature = -1;
|
||||
aigp->priority = -1;
|
||||
aigp->flags.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1049,13 +1052,15 @@ void NavSystem_Do()
|
||||
if(cam != NULL)
|
||||
cam->set_rotation_facing(&Player_obj->pos);
|
||||
|
||||
if (timestamp_elapsed(MoveCamera) && !CameraMoving && vm_vec_mag(&cameraTarget) > 0.0f)
|
||||
if (timestamp() >= MoveCamera && !CameraMoving && vm_vec_mag(&cameraTarget) > 0.0f)
|
||||
{
|
||||
//Free_camera->set_position(&cameraTarget, float(camMovingTime), float(camMovingTime)/2.0f);
|
||||
//Free_camera->set_translation_velocity(&cameraTarget);
|
||||
CameraMoving = true;
|
||||
}
|
||||
|
||||
|
||||
if (timestamp_elapsed(EndAPCinematic) || !CanAutopilot())
|
||||
if (timestamp() >= EndAPCinematic || !CanAutopilot())
|
||||
{
|
||||
nav_warp();
|
||||
EndAutoPilot();
|
||||
@@ -1212,7 +1217,7 @@ void send_autopilot_msg(const char *msg, const char *snd)
|
||||
}
|
||||
|
||||
if (msg[0] != '\0' && strcmp(msg, "none") != 0)
|
||||
message_training_queue("autopilot builtin message", TIMESTAMP::immediate(), 5); // display message for five seconds
|
||||
message_training_queue("autopilot builtin message", timestamp(0), 5); // display message for five seconds
|
||||
}
|
||||
|
||||
// ********************************************************************************************
|
||||
@@ -1575,18 +1580,3 @@ bool IsVisited(int nav)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
void Nav_SetColor(const char *nav, bool visited, ubyte r, ubyte g, ubyte b)
|
||||
{
|
||||
int n = FindNav(nav);
|
||||
|
||||
if (n >= 0 && n < MAX_NAVPOINTS)
|
||||
{
|
||||
auto rgb = visited ? Navs[n].visited_color : Navs[n].normal_color;
|
||||
rgb[0] = r;
|
||||
rgb[1] = g;
|
||||
rgb[2] = b;
|
||||
}
|
||||
}
|
||||
@@ -29,30 +29,19 @@
|
||||
class NavPoint
|
||||
{
|
||||
public:
|
||||
char m_NavName[32] = { 0 };
|
||||
int flags = 0;
|
||||
ubyte normal_color[3] = { 0x80, 0x80, 0xFF };
|
||||
ubyte visited_color[3] = { 0xFF, 0xFF, 0x00 };
|
||||
char m_NavName[32];
|
||||
int flags;
|
||||
|
||||
const void *target_obj = nullptr;
|
||||
int waypoint_num = -1; //only used when flags & NP_WAYPOINT
|
||||
const void *target_obj;
|
||||
int waypoint_num; //only used when flags & NP_WAYPOINT
|
||||
|
||||
const vec3d *GetPosition();
|
||||
|
||||
// these assignments should match the initialization
|
||||
void clear()
|
||||
{
|
||||
m_NavName[0] = 0;
|
||||
flags = 0;
|
||||
|
||||
normal_color[0] = 0x80;
|
||||
normal_color[1] = 0x80;
|
||||
normal_color[2] = 0xFF;
|
||||
|
||||
visited_color[0] = 0xFF;
|
||||
visited_color[1] = 0xFF;
|
||||
visited_color[2] = 0x00;
|
||||
|
||||
target_obj = nullptr;
|
||||
waypoint_num = -1;
|
||||
}
|
||||
@@ -79,7 +68,7 @@ extern bool AutoPilotEngaged;
|
||||
extern int CurrentNav;
|
||||
extern NavPoint Navs[MAX_NAVPOINTS];
|
||||
extern NavMessage NavMsgs[NP_NUM_MESSAGES];
|
||||
extern TIMESTAMP LockAPConv;
|
||||
extern int LockAPConv;
|
||||
extern SCP_map<int,int> autopilot_wings;
|
||||
|
||||
// Cycles through the NavPoint List
|
||||
@@ -166,8 +155,6 @@ unsigned int DistanceTo(int nav);
|
||||
bool IsVisited(const char *nav);
|
||||
bool IsVisited(int nav);
|
||||
|
||||
void Nav_SetColor(const char *nav, bool visited, ubyte r, ubyte g, ubyte b);
|
||||
|
||||
void send_autopilot_msg(const char *msg, const char *snd=nullptr);
|
||||
void send_autopilot_msgID(int msgid);
|
||||
#endif
|
||||
|
||||
+22
-23
@@ -53,7 +53,6 @@ MONITOR(SizeBitmapPage)
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// Definition of public variables (declared as extern in bmpman.h).
|
||||
int ENVMAP = -1;
|
||||
int IRRMAP = -1;
|
||||
|
||||
size_t bm_texture_ram = 0;
|
||||
int Bm_paging = 0;
|
||||
@@ -593,9 +592,12 @@ void bm_convert_format(bitmap *bmp, ushort flags) {
|
||||
return;
|
||||
|
||||
if (Is_standalone) {
|
||||
Assert(bmp->bpp == 8);
|
||||
return;
|
||||
} else {
|
||||
if(!(flags & BMP_AABITMAP))
|
||||
if (flags & BMP_AABITMAP)
|
||||
Assert(bmp->bpp == 8);
|
||||
else
|
||||
Assert((bmp->bpp == 16) || (bmp->bpp == 32));
|
||||
}
|
||||
|
||||
@@ -1118,7 +1120,7 @@ static int bm_load_info(BM_TYPE type, const char *filename, CFILE *img_cfp, int
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bm_load(const char *real_filename, int dir_type) {
|
||||
int bm_load(const char *real_filename) {
|
||||
int free_slot = -1;
|
||||
int w, h, bpp = 8;
|
||||
int rc = 0;
|
||||
@@ -1150,7 +1152,6 @@ int bm_load(const char *real_filename, int dir_type) {
|
||||
if (Is_standalone) {
|
||||
char standalone_filename[MAX_FILENAME_LEN] = "right_bracket";
|
||||
strcpy_s(filename, standalone_filename);
|
||||
dir_type = CF_TYPE_INTERFACE;
|
||||
}
|
||||
|
||||
// safety catch for strcat...
|
||||
@@ -1163,11 +1164,11 @@ int bm_load(const char *real_filename, int dir_type) {
|
||||
// Lets find out what type it is
|
||||
{
|
||||
// see if it's already loaded (checks for any type with filename)
|
||||
if (bm_load_sub_fast(filename, &handle, dir_type))
|
||||
if (bm_load_sub_fast(filename, &handle))
|
||||
return handle;
|
||||
|
||||
// if we are still here then we need to fall back to a file-based search
|
||||
int rval = bm_load_sub_slow(filename, BM_NUM_TYPES, bm_ext_list, &img_cfp, dir_type);
|
||||
int rval = bm_load_sub_slow(filename, BM_NUM_TYPES, bm_ext_list, &img_cfp);
|
||||
|
||||
if (rval < 0)
|
||||
return -1;
|
||||
@@ -1223,7 +1224,7 @@ int bm_load(const char *real_filename, int dir_type) {
|
||||
entry->bm.palette = nullptr;
|
||||
entry->num_mipmaps = mm_lvl;
|
||||
entry->mem_taken = (size_t)bm_size;
|
||||
entry->dir_type = dir_type;
|
||||
entry->dir_type = CF_TYPE_ANY;
|
||||
entry->handle = handle;
|
||||
|
||||
entry->load_count++;
|
||||
@@ -1234,8 +1235,8 @@ int bm_load(const char *real_filename, int dir_type) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
int bm_load(const SCP_string& filename, int dir_type) {
|
||||
return bm_load(filename.c_str(), dir_type);
|
||||
int bm_load(const SCP_string& filename) {
|
||||
return bm_load(filename.c_str());
|
||||
}
|
||||
|
||||
bool bm_load_and_parse_eff(const char *filename, int dir_type, int *nframes, int *nfps, int *key, BM_TYPE *type) {
|
||||
@@ -1423,7 +1424,7 @@ static int bm_load_image_data(int handle, int bpp, ushort flags, bool nodebug)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *keyframe, float *total_time, bool can_drop_frames, int dir_type, bool rethrow_exceptions) {
|
||||
int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *keyframe, float *total_time, bool can_drop_frames, int dir_type) {
|
||||
int i, n;
|
||||
anim the_anim;
|
||||
CFILE *img_cfp = nullptr;
|
||||
@@ -1465,7 +1466,6 @@ int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *ke
|
||||
if (Is_standalone) {
|
||||
char standalone_filename[MAX_FILENAME_LEN] = "cursorweb";
|
||||
strcpy_s(filename, standalone_filename);
|
||||
dir_type = CF_TYPE_INTERFACE;
|
||||
}
|
||||
|
||||
// safety catch for strcat...
|
||||
@@ -1531,7 +1531,7 @@ int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *ke
|
||||
cfclose(img_cfp);
|
||||
return -1;
|
||||
} else {
|
||||
nprintf(("BmpMan","BMPMAN: Found EFF (%s) with %d frames at %d fps.\n", filename, anim_frames, anim_fps));
|
||||
mprintf(("BMPMAN: Found EFF (%s) with %d frames at %d fps.\n", filename, anim_frames, anim_fps));
|
||||
}
|
||||
if (anim_fps == 0) {
|
||||
Error(LOCATION, "animation (%s) has invalid fps of 0, fix this!", filename);
|
||||
@@ -1594,15 +1594,10 @@ int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *ke
|
||||
img_size = the_apng.imgsize();
|
||||
}
|
||||
catch (const apng::ApngException& e) {
|
||||
mprintf(("Failed to load apng: %s\n", e.what() ));
|
||||
if (img_cfp != nullptr)
|
||||
cfclose(img_cfp);
|
||||
if (rethrow_exceptions) {
|
||||
throw e;
|
||||
}
|
||||
else {
|
||||
nprintf(("apng","Failed to load apng: %s\n", e.what()));
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1794,7 +1789,7 @@ int bm_load_either(const char *filename, int *nframes, int *fps, int *keyframe,
|
||||
*fps = 0;
|
||||
int tidx = bm_load_animation(filename, nframes, fps, keyframe, nullptr, can_drop_frames, dir_type);
|
||||
if (tidx == -1) {
|
||||
tidx = bm_load(filename, dir_type);
|
||||
tidx = bm_load(filename);
|
||||
if (tidx != -1 && nframes != NULL)
|
||||
*nframes = 1;
|
||||
}
|
||||
@@ -1835,7 +1830,7 @@ int bm_load_sub_fast(const char *real_filename, int *handle, int dir_type, bool
|
||||
}
|
||||
|
||||
int bm_load_sub_slow(const char *real_filename, const int num_ext, const char **ext_list, CFILE **img_cfp, int dir_type) {
|
||||
auto res = cf_find_file_location_ext(real_filename, num_ext, ext_list, dir_type);
|
||||
auto res = cf_find_file_location_ext(real_filename, num_ext, ext_list, dir_type, false);
|
||||
|
||||
// could not be found, or is invalid for some reason
|
||||
if (!res.found)
|
||||
@@ -1873,7 +1868,9 @@ bitmap * bm_lock(int handle, int bpp, ushort flags, bool nodebug) {
|
||||
}
|
||||
// otherwise do it as normal
|
||||
else {
|
||||
if ((flags & BMP_TEX_NONCOMP) && (!(flags & BMP_TEX_COMP))) {
|
||||
if (flags & BMP_AABITMAP) {
|
||||
Assert(bpp == 8);
|
||||
} else if ((flags & BMP_TEX_NONCOMP) && (!(flags & BMP_TEX_COMP))) {
|
||||
Assert(bpp >= 16); // cheating but bpp passed isn't what we normally end up with
|
||||
} else if ((flags & BMP_TEX_DXT1) || (flags & BMP_TEX_DXT3) || (flags & BMP_TEX_DXT5) || (flags & BMP_TEX_BC7)) {
|
||||
Assert(bpp >= 16); // cheating but bpp passed isn't what we normally end up with
|
||||
@@ -1883,6 +1880,8 @@ bitmap * bm_lock(int handle, int bpp, ushort flags, bool nodebug) {
|
||||
(be->type == BM_TYPE_CUBEMAP_DXT3) ||
|
||||
(be->type == BM_TYPE_CUBEMAP_DXT5));
|
||||
Assert(bpp >= 16);
|
||||
} else {
|
||||
Assert(0); //?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2623,7 +2622,7 @@ void bm_page_in_stop() {
|
||||
}
|
||||
}
|
||||
|
||||
nprintf(("BmpMan","BMPMAN: %d/%d bitmap slots in use.\n", total_bitmaps, total_slots));
|
||||
mprintf(("Bmpman: %d/%d bitmap slots in use.\n", total_bitmaps, total_slots));
|
||||
#endif
|
||||
|
||||
Bm_paging = 0;
|
||||
|
||||
+4
-11
@@ -116,7 +116,6 @@ extern const char *bm_ani_ext_list[]; //!< List of extensions for those types
|
||||
extern const int BM_ANI_NUM_TYPES; //!< Calculated number of bitmap animation types
|
||||
|
||||
extern int ENVMAP; //!< References a map that is for environment mapping -Bobboau
|
||||
extern int IRRMAP; //!< References a filtered envmap for diffuse environment lighting - qazwsxal
|
||||
|
||||
/**
|
||||
* @brief Initilizes the bitmap manager
|
||||
@@ -178,7 +177,7 @@ void clear_bm_lookup_cache();
|
||||
* @returns The bitmap number if successful, else
|
||||
* @returns a negative value if not
|
||||
*/
|
||||
int bm_load(const char* filename, int dir_type = CF_TYPE_ANY);
|
||||
int bm_load(const char* filename);
|
||||
|
||||
/**
|
||||
* @brief Loads a bitmap so we can draw with it later. (Preferred version)
|
||||
@@ -188,7 +187,7 @@ int bm_load(const char* filename, int dir_type = CF_TYPE_ANY);
|
||||
* @returns The bitmap number if successful, else
|
||||
* @returns a negative value if not
|
||||
*/
|
||||
int bm_load(const SCP_string& filename, int dir_type = CF_TYPE_ANY);
|
||||
int bm_load(const SCP_string& filename);
|
||||
|
||||
/**
|
||||
* @brief Reloads a bitmap as a duplicate.
|
||||
@@ -290,14 +289,7 @@ bool bm_release_rendertarget(int handle);
|
||||
* @returns The bm number of the first bitmap in the sequence if successful, or
|
||||
* @returns A negative value if unsuccessful
|
||||
*/
|
||||
int bm_load_animation(const char *filename,
|
||||
int *nframes = nullptr,
|
||||
int *fps = nullptr,
|
||||
int *keyframe = nullptr,
|
||||
float *total_time = nullptr,
|
||||
bool can_drop_frames = false,
|
||||
int dir_type = CF_TYPE_ANY,
|
||||
bool rethrow_exceptions = false);
|
||||
int bm_load_animation(const char *filename, int *nframes = nullptr, int *fps = nullptr, int *keyframe = nullptr, float *total_time = nullptr, bool can_drop_frames = 0, int dir_type = CF_TYPE_ANY);
|
||||
|
||||
/**
|
||||
* @brief Loads either animation (bm_load_animation) or still image (bm_load)
|
||||
@@ -547,6 +539,7 @@ void bm_set_components_argb_32_tex(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, u
|
||||
*/
|
||||
void bm_get_components(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a);
|
||||
|
||||
extern int ENVMAP; //this holds a reference to a map that is for environment mapping -Bobboau
|
||||
|
||||
/**
|
||||
* @brief Returns the compression type of the bitmap indexed by handle
|
||||
|
||||
+62
-175
@@ -9,13 +9,11 @@
|
||||
#include "model/model.h" //polymodel, model_get
|
||||
#include "options/Option.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "parse/sexp_container.h"
|
||||
#include "playerman/player.h" //player_get_padlock_orient
|
||||
#include "ship/ship.h" //compute_slew_matrix
|
||||
|
||||
//*************************IMPORTANT GLOBALS*************************
|
||||
float VIEWER_ZOOM_DEFAULT = 0.75f; // Default viewer zoom, 0.625 as per multi-lateral agreement on 3/24/97
|
||||
float COCKPIT_ZOOM_DEFAULT = 0.75f;
|
||||
float Sexp_fov = 0.0f;
|
||||
warp_camera Warp_camera;
|
||||
|
||||
@@ -45,7 +43,7 @@ auto FovOption = options::OptionBuilder<float>("Graphics.FOV", "Field Of View",
|
||||
.finish();
|
||||
|
||||
//*************************CLASS: camera*************************
|
||||
//This is where the camera class begins! :D
|
||||
//This is where the camera class beings! :D
|
||||
camera::camera(const char *in_name, int in_signature)
|
||||
{
|
||||
set_name(in_name);
|
||||
@@ -239,24 +237,12 @@ void camera::set_rotation_facing(vec3d *in_target, float in_rotation_time, float
|
||||
{
|
||||
matrix temp_matrix = IDENTITY_MATRIX;
|
||||
|
||||
if (in_target != nullptr)
|
||||
if(in_target != NULL)
|
||||
{
|
||||
vec3d position;
|
||||
matrix orient_buf;
|
||||
matrix *orient = nullptr;
|
||||
vec3d position = vmd_zero_vector;
|
||||
this->get_info(&position, NULL);
|
||||
|
||||
if (Use_host_orientation_for_set_camera_facing)
|
||||
{
|
||||
// we want to retrieve the camera's orientation, so provide a buffer for it
|
||||
orient = &orient_buf;
|
||||
|
||||
// reset the camera's orientation state because if we have no host object, that is what the camera will default to
|
||||
c_ori = vmd_identity_matrix;
|
||||
}
|
||||
|
||||
this->get_info(&position, orient, false);
|
||||
|
||||
if (vm_vec_same(in_target, &position))
|
||||
if(in_target->xyz.x == position.xyz.x && in_target->xyz.y == position.xyz.y && in_target->xyz.z == position.xyz.z)
|
||||
{
|
||||
Warning(LOCATION, "Camera tried to point to self");
|
||||
return;
|
||||
@@ -264,25 +250,7 @@ void camera::set_rotation_facing(vec3d *in_target, float in_rotation_time, float
|
||||
|
||||
vec3d targetvec;
|
||||
vm_vec_normalized_dir(&targetvec, in_target, &position);
|
||||
|
||||
if (Use_host_orientation_for_set_camera_facing)
|
||||
{
|
||||
// point along the target vector, but using the host orient's roll
|
||||
vm_vector_2_matrix(&temp_matrix, &targetvec, &orient->vec.uvec, nullptr);
|
||||
|
||||
// if we have a host, we need the difference between the camera's current orient and the orient we want
|
||||
// if not, we will later set the absolute orientation, rather than the orientation relative to the host
|
||||
if (object_host.IsValid())
|
||||
{
|
||||
vm_transpose(orient);
|
||||
temp_matrix = temp_matrix * *orient;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// point directly along the target vector
|
||||
vm_vector_2_matrix(&temp_matrix, &targetvec, nullptr, nullptr);
|
||||
}
|
||||
vm_vector_2_matrix(&temp_matrix, &targetvec, NULL, NULL);
|
||||
}
|
||||
|
||||
set_rotation(&temp_matrix, in_rotation_time, in_rotation_acceleration_time, in_rotation_deceleration_time);
|
||||
@@ -330,16 +298,13 @@ float camera::get_fov()
|
||||
}
|
||||
|
||||
eye* get_submodel_eye(polymodel *pm, int submodel_num);
|
||||
void camera::get_info(vec3d *position, matrix *orientation, bool apply_camera_orientation)
|
||||
void camera::get_info(vec3d *position, matrix *orientation)
|
||||
{
|
||||
if(position == NULL && orientation == NULL)
|
||||
return;
|
||||
|
||||
eye* eyep = NULL;
|
||||
vec3d host_normal;
|
||||
matrix host_orient = vmd_identity_matrix;
|
||||
bool use_host_orient = false;
|
||||
|
||||
//POSITION
|
||||
if(!(flags & CAM_STATIONARY_POS) || object_host.IsValid())
|
||||
{
|
||||
@@ -377,19 +342,16 @@ void camera::get_info(vec3d *position, matrix *orientation, bool apply_camera_or
|
||||
Assertion(objp->type == OBJ_SHIP, "This part of the code expects the object to be a ship");
|
||||
|
||||
vec3d c_pos_in;
|
||||
model_instance_local_to_global_point_dir(&c_pos_in, &host_normal, &eyep->pnt, &eyep->norm, pm, pmi, eyep->parent);
|
||||
find_submodel_instance_point_normal(&c_pos_in, &host_normal, pm, pmi, eyep->parent, &eyep->pnt, &eyep->norm);
|
||||
vm_vec_unrotate(&c_pos, &c_pos_in, &objp->orient);
|
||||
vm_vec_add2(&c_pos, &objp->pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pmi != nullptr)
|
||||
{
|
||||
model_instance_local_to_global_point_orient(&c_pos, &host_orient, &pt, &vmd_identity_matrix, pm, pmi, object_host_submodel, &objp->orient, &objp->pos);
|
||||
use_host_orient = true;
|
||||
}
|
||||
model_instance_find_world_point(&c_pos, &pt, pm, pmi, object_host_submodel, &objp->orient, &objp->pos);
|
||||
else
|
||||
model_local_to_global_point( &c_pos, &pt, pm, object_host_submodel, &objp->orient, &objp->pos );
|
||||
model_find_world_point( &c_pos, &pt, pm, object_host_submodel, &objp->orient, &objp->pos );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,7 +367,7 @@ void camera::get_info(vec3d *position, matrix *orientation, bool apply_camera_or
|
||||
}
|
||||
}
|
||||
|
||||
if (position != nullptr)
|
||||
if(position != NULL)
|
||||
*position = c_pos;
|
||||
|
||||
//ORIENTATION
|
||||
@@ -416,32 +378,32 @@ void camera::get_info(vec3d *position, matrix *orientation, bool apply_camera_or
|
||||
{
|
||||
if(object_target.IsValid())
|
||||
{
|
||||
object *target_objp = object_target.objp;
|
||||
int model_num = object_get_model(target_objp);
|
||||
polymodel *target_pm = nullptr;
|
||||
polymodel_instance *target_pmi = nullptr;
|
||||
object *objp = object_target.objp;
|
||||
int model_num = object_get_model(objp);
|
||||
polymodel *pm = nullptr;
|
||||
polymodel_instance *pmi = nullptr;
|
||||
vec3d target_pos = vmd_zero_vector;
|
||||
|
||||
//See if we can get the model
|
||||
if(model_num >= 0)
|
||||
{
|
||||
target_pm = model_get(model_num);
|
||||
if (target_objp->type == OBJ_SHIP)
|
||||
target_pmi = model_get_instance(Ships[target_objp->instance].model_instance_num);
|
||||
pm = model_get(model_num);
|
||||
if (objp->type == OBJ_SHIP)
|
||||
pmi = model_get_instance(Ships[objp->instance].model_instance_num);
|
||||
}
|
||||
|
||||
//If we don't have a submodel or don't have the model use object pos
|
||||
//Otherwise, find the submodel pos as it is rotated
|
||||
if (object_target_submodel < 0 || target_pm == nullptr)
|
||||
if(object_target_submodel < 0 || pm == NULL)
|
||||
{
|
||||
target_pos = target_objp->pos;
|
||||
target_pos = objp->pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target_pmi != nullptr)
|
||||
model_instance_local_to_global_point(&target_pos, &vmd_zero_vector, target_pm, target_pmi, object_target_submodel, &target_objp->orient, &target_objp->pos);
|
||||
if (pmi != nullptr)
|
||||
model_instance_find_world_point(&target_pos, &vmd_zero_vector, pm, pmi, object_target_submodel, &objp->orient, &objp->pos);
|
||||
else
|
||||
model_local_to_global_point( &target_pos, &vmd_zero_vector, target_pm, object_target_submodel, &target_objp->orient, &target_objp->pos );
|
||||
model_find_world_point( &target_pos, &vmd_zero_vector, pm, object_target_submodel, &objp->orient, &objp->pos );
|
||||
}
|
||||
|
||||
vec3d targetvec;
|
||||
@@ -456,10 +418,6 @@ void camera::get_info(vec3d *position, matrix *orientation, bool apply_camera_or
|
||||
vm_vector_2_matrix(&c_ori, &host_normal, vm_vec_same(&host_normal, &object_host.objp->orient.vec.uvec)?NULL:&object_host.objp->orient.vec.uvec, NULL);
|
||||
target_set = true;
|
||||
}
|
||||
else if (use_host_orient)
|
||||
{
|
||||
c_ori = host_orient;
|
||||
}
|
||||
else
|
||||
{
|
||||
c_ori = object_host.objp->orient;
|
||||
@@ -470,30 +428,23 @@ void camera::get_info(vec3d *position, matrix *orientation, bool apply_camera_or
|
||||
c_ori = vmd_identity_matrix;
|
||||
}
|
||||
|
||||
//Do custom orientation stuff, if needed
|
||||
if (func_custom_orientation != nullptr && !target_set)
|
||||
matrix mtxA = c_ori;
|
||||
matrix mtxB = IDENTITY_MATRIX;
|
||||
float pos = 0.0f;
|
||||
for(int i = 0; i < 9; i++)
|
||||
{
|
||||
func_custom_orientation(this, &c_ori);
|
||||
ori[i].get(&pos, NULL);
|
||||
mtxB.a1d[i] = pos;
|
||||
}
|
||||
vm_matrix_x_matrix(&c_ori, &mtxA, &mtxB);
|
||||
|
||||
// only do this if we want to find where the camera is actually pointing;
|
||||
// skip this if we just want the orientation of the host
|
||||
if (apply_camera_orientation)
|
||||
{
|
||||
matrix mtxA = c_ori;
|
||||
matrix mtxB = IDENTITY_MATRIX;
|
||||
float pos = 0.0f;
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
ori[i].get(&pos, nullptr);
|
||||
mtxB.a1d[i] = pos;
|
||||
}
|
||||
vm_matrix_x_matrix(&c_ori, &mtxA, &mtxB);
|
||||
|
||||
vm_orthogonalize_matrix(&c_ori);
|
||||
}
|
||||
vm_orthogonalize_matrix(&c_ori);
|
||||
}
|
||||
//Do custom orientation stuff, if needed
|
||||
if(func_custom_orientation != NULL && !target_set)
|
||||
{
|
||||
func_custom_orientation(this, &c_ori);
|
||||
}
|
||||
|
||||
*orientation = c_ori;
|
||||
}
|
||||
}
|
||||
@@ -608,46 +559,11 @@ void warp_camera::get_info(vec3d *position, matrix *orientation)
|
||||
|
||||
#define MAX_SUBTITLE_LINES 64
|
||||
subtitle::subtitle(int in_x_pos, int in_y_pos, const char* in_text, const char* in_imageanim, float in_display_time,
|
||||
float in_fade_time, const color* in_text_color, int in_text_fontnum, bool center_x, bool center_y, int in_width,
|
||||
int in_height, bool in_post_shaded, int in_line_height_modifier, bool in_adjust_wh)
|
||||
: display_time(-1.0f), fade_time(-1.0f), text_fontnum(-1), line_height_modifier(0),
|
||||
image_id(-1), time_displayed(-1.0f), time_displayed_end(-1.0f), post_shaded(false), do_screen_scaling(false)
|
||||
float in_fade_time, const color *in_text_color, int in_text_fontnum, bool center_x, bool center_y, int in_width,
|
||||
int in_height, bool in_post_shaded)
|
||||
:display_time(-1.0f), fade_time(-1.0f), text_fontnum(-1), time_displayed(-1.0f), time_displayed_end(-1.0f),
|
||||
post_shaded(false)
|
||||
{
|
||||
if ((in_text == nullptr || *in_text == '\0') && (in_imageanim == nullptr || *in_imageanim == '\0'))
|
||||
return;
|
||||
|
||||
auto screen_w = gr_screen.center_w;
|
||||
auto screen_h = gr_screen.center_h;
|
||||
|
||||
// we might be doing screen scaling
|
||||
if (Show_subtitle_screen_base_res[0] > 0 && Show_subtitle_screen_base_res[1] > 0)
|
||||
{
|
||||
// The way this works is we specify our subtitle coordinates relative to a base resolution, e.g. 1024x768.
|
||||
// The base resolution is adjusted behind the scenes to match the aspect ratio of the current resolution
|
||||
// (and the subtitle coordinates are adjusted as well). Then the adjusted subtitles are resized by the
|
||||
// graphics code when they are drawn to the screen.
|
||||
do_screen_scaling = true;
|
||||
|
||||
// we might need to adjust the coordinates for aspect ratio
|
||||
if (in_adjust_wh)
|
||||
{
|
||||
if (Show_subtitle_screen_base_res[0] != Show_subtitle_screen_adjusted_res[0])
|
||||
{
|
||||
in_x_pos = in_x_pos * Show_subtitle_screen_adjusted_res[0] / Show_subtitle_screen_base_res[0];
|
||||
in_width = in_width * Show_subtitle_screen_adjusted_res[0] / Show_subtitle_screen_base_res[0];
|
||||
}
|
||||
if (Show_subtitle_screen_base_res[1] != Show_subtitle_screen_adjusted_res[1])
|
||||
{
|
||||
in_y_pos = in_y_pos * Show_subtitle_screen_adjusted_res[1] / Show_subtitle_screen_base_res[1];
|
||||
in_height = in_height * Show_subtitle_screen_adjusted_res[1] / Show_subtitle_screen_base_res[1];
|
||||
}
|
||||
}
|
||||
|
||||
// use these because we will be scaling the subtitle when it is displayed
|
||||
screen_w = Show_subtitle_screen_adjusted_res[0];
|
||||
screen_h = Show_subtitle_screen_adjusted_res[1];
|
||||
}
|
||||
|
||||
// Initialize color
|
||||
gr_init_color(&text_color, 0, 0, 0);
|
||||
|
||||
@@ -657,15 +573,19 @@ subtitle::subtitle(int in_x_pos, int in_y_pos, const char* in_text, const char*
|
||||
memset( imageanim, 0, sizeof(imageanim) );
|
||||
memset( &text_pos, 0, 2*sizeof(int) );
|
||||
memset( &image_pos, 0, 4*sizeof(int) );
|
||||
image_id = -1;
|
||||
|
||||
if ( ((in_text != NULL) && (strlen(in_text) <= 0)) && ((in_imageanim != NULL) && (strlen(in_imageanim) <= 0)) )
|
||||
return;
|
||||
|
||||
if (in_text != NULL && in_text[0] != '\0')
|
||||
{
|
||||
text_buf = in_text;
|
||||
sexp_replace_variable_names_with_values(text_buf);
|
||||
sexp_container_replace_refs_with_values(text_buf);
|
||||
in_text = text_buf.c_str();
|
||||
}
|
||||
|
||||
|
||||
int num_text_lines = 0;
|
||||
const char *text_line_ptrs[MAX_SUBTITLE_LINES];
|
||||
int text_line_lens[MAX_SUBTITLE_LINES];
|
||||
@@ -688,7 +608,6 @@ subtitle::subtitle(int in_x_pos, int in_y_pos, const char* in_text, const char*
|
||||
else
|
||||
gr_init_alphacolor(&text_color, 255, 255, 255, 255);
|
||||
text_fontnum = in_text_fontnum;
|
||||
line_height_modifier = in_line_height_modifier;
|
||||
|
||||
//Setup display and fade time
|
||||
display_time = fl_abs(in_display_time);
|
||||
@@ -727,14 +646,6 @@ subtitle::subtitle(int in_x_pos, int in_y_pos, const char* in_text, const char*
|
||||
if(w > tw)
|
||||
tw = w;
|
||||
|
||||
if (line_height_modifier != 0.0f)
|
||||
{
|
||||
if (Show_subtitle_uses_pixels)
|
||||
h = line_height_modifier;
|
||||
else
|
||||
h = fl2i(h * line_height_modifier / 100.0f);
|
||||
}
|
||||
|
||||
th += h;
|
||||
}
|
||||
|
||||
@@ -760,20 +671,20 @@ subtitle::subtitle(int in_x_pos, int in_y_pos, const char* in_text, const char*
|
||||
|
||||
//Center it?
|
||||
if(center_x)
|
||||
image_pos.x = (screen_w - tw)/2;
|
||||
image_pos.x = (gr_screen.center_w - tw)/2 + gr_screen.center_offset_x;
|
||||
if(center_y)
|
||||
image_pos.y = (screen_h - th)/2;
|
||||
image_pos.y = (gr_screen.center_h - th)/2 + gr_screen.center_offset_y;
|
||||
}
|
||||
|
||||
if(in_x_pos < 0 && !center_x)
|
||||
image_pos.x += screen_w + in_x_pos;
|
||||
image_pos.x += gr_screen.center_offset_x + gr_screen.center_w + in_x_pos;
|
||||
else if(!center_x)
|
||||
image_pos.x += in_x_pos;
|
||||
image_pos.x += gr_screen.center_offset_x + in_x_pos;
|
||||
|
||||
if(in_y_pos < 0 && !center_y)
|
||||
image_pos.y += screen_h + in_y_pos;
|
||||
image_pos.y += gr_screen.center_offset_y + gr_screen.center_h + in_y_pos;
|
||||
else if(!center_y)
|
||||
image_pos.y += in_y_pos;
|
||||
image_pos.y += gr_screen.center_offset_y + in_y_pos;
|
||||
|
||||
image_pos.w = in_width;
|
||||
image_pos.h = in_height;
|
||||
@@ -829,35 +740,24 @@ void subtitle::do_frame(float frametime)
|
||||
int x = text_pos.x;
|
||||
int y = text_pos.y;
|
||||
|
||||
auto sizing_mode = do_screen_scaling ? GR_RESIZE_FULL : GR_RESIZE_NONE;
|
||||
if (do_screen_scaling)
|
||||
{
|
||||
gr_set_screen_scale(Show_subtitle_screen_adjusted_res[0], Show_subtitle_screen_adjusted_res[1]);
|
||||
gr_set_clip(0, 0, Show_subtitle_screen_adjusted_res[0], Show_subtitle_screen_adjusted_res[1]);
|
||||
}
|
||||
|
||||
// do the actual drawing ---------------------
|
||||
|
||||
for(SCP_vector<SCP_string>::iterator line = text_lines.begin(); line != text_lines.end(); ++line)
|
||||
{
|
||||
gr_string(x, y, (char*)line->c_str(), sizing_mode);
|
||||
|
||||
if (line_height_modifier != 0.0f)
|
||||
{
|
||||
if (Show_subtitle_uses_pixels)
|
||||
font_height = line_height_modifier;
|
||||
else
|
||||
font_height = fl2i(font_height * line_height_modifier / 100.0f);
|
||||
}
|
||||
|
||||
gr_string(x, y, (char*)line->c_str(), GR_RESIZE_NONE);
|
||||
y += font_height;
|
||||
}
|
||||
|
||||
// restore old font
|
||||
if (old_fontnum >= 0)
|
||||
{
|
||||
font::set_font(old_fontnum);
|
||||
}
|
||||
|
||||
if(image_id >= 0)
|
||||
{
|
||||
gr_set_bitmap(image_id, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, text_color.alpha/255.0f);
|
||||
|
||||
// image scaling?
|
||||
// scaling?
|
||||
if (image_pos.w > 0 || image_pos.h > 0)
|
||||
{
|
||||
int orig_w, orig_h;
|
||||
@@ -869,28 +769,16 @@ void subtitle::do_frame(float frametime)
|
||||
scale.xyz.z = 1.0f;
|
||||
|
||||
gr_push_scale_matrix(&scale);
|
||||
gr_bitmap(fl2i(image_pos.x / scale.xyz.x), fl2i(image_pos.y / scale.xyz.y), sizing_mode);
|
||||
gr_bitmap(fl2i(image_pos.x / scale.xyz.x), fl2i(image_pos.y / scale.xyz.y), GR_RESIZE_NONE);
|
||||
gr_pop_scale_matrix();
|
||||
}
|
||||
// no scaling
|
||||
else
|
||||
{
|
||||
gr_bitmap(image_pos.x, image_pos.y, sizing_mode);
|
||||
gr_bitmap(image_pos.x, image_pos.y, GR_RESIZE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
// finished the actual drawing ---------------
|
||||
|
||||
if (do_screen_scaling)
|
||||
{
|
||||
gr_reset_screen_scale();
|
||||
gr_reset_clip();
|
||||
}
|
||||
|
||||
// restore old font
|
||||
if (old_fontnum >= 0)
|
||||
font::set_font(old_fontnum);
|
||||
|
||||
time_displayed += frametime;
|
||||
}
|
||||
|
||||
@@ -905,9 +793,9 @@ subtitle::~subtitle()
|
||||
|
||||
void subtitle::clone(const subtitle &sub)
|
||||
{
|
||||
|
||||
text_lines = sub.text_lines;
|
||||
text_fontnum = sub.text_fontnum;
|
||||
line_height_modifier = sub.line_height_modifier;
|
||||
|
||||
// copy the structs
|
||||
text_pos = sub.text_pos;
|
||||
@@ -930,7 +818,6 @@ void subtitle::clone(const subtitle &sub)
|
||||
time_displayed_end = sub.time_displayed_end;
|
||||
|
||||
post_shaded = sub.post_shaded;
|
||||
do_screen_scaling = sub.do_screen_scaling;
|
||||
}
|
||||
|
||||
subtitle& subtitle::operator=(const subtitle &sub)
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
object *get_object_target();
|
||||
int get_object_target_submodel();
|
||||
float get_fov();
|
||||
void get_info(vec3d *position, matrix *orientation, bool apply_camera_orientation = true);
|
||||
void get_info(vec3d *position, matrix *orientation);
|
||||
|
||||
//Is
|
||||
bool is_empty(){return sig < 0;}
|
||||
@@ -123,7 +123,6 @@ private:
|
||||
float fade_time;
|
||||
color text_color;
|
||||
int text_fontnum;
|
||||
int line_height_modifier;
|
||||
|
||||
//Done with set
|
||||
char imageanim[MAX_FILENAME_LEN];
|
||||
@@ -137,13 +136,10 @@ private:
|
||||
float time_displayed_end;
|
||||
|
||||
bool post_shaded;
|
||||
bool do_screen_scaling;
|
||||
|
||||
public:
|
||||
subtitle(int in_x_pos, int in_y_pos, const char* in_text = NULL, const char* in_imageanim = NULL,
|
||||
float in_display_time = 0, float in_fade_time = 0.0f, const color *in_text_color = NULL, int in_text_fontnum = -1,
|
||||
bool center_x = false, bool center_y = false, int in_width = 0, int in_height = 0, bool post_shaded = false,
|
||||
int in_line_height_modifier = 0, bool in_adjust_wh = true);
|
||||
bool center_x = false, bool center_y = false, int in_width = 0, int in_height = 0, bool post_shaded=false);
|
||||
~subtitle();
|
||||
|
||||
subtitle(const subtitle &sub) { clone(sub); }
|
||||
@@ -156,7 +152,6 @@ public:
|
||||
//Some global stuff
|
||||
extern SCP_vector<subtitle> Subtitles;
|
||||
extern float VIEWER_ZOOM_DEFAULT;
|
||||
extern float COCKPIT_ZOOM_DEFAULT;
|
||||
extern float Sexp_fov;
|
||||
|
||||
//Helpful functions
|
||||
|
||||
+68
-92
@@ -59,6 +59,8 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
|
||||
{ CF_TYPE_MODELS, "data" DIR_SEPARATOR_STR "models", ".pof", CF_TYPE_DATA },
|
||||
{ CF_TYPE_TABLES, "data" DIR_SEPARATOR_STR "tables", ".tbl .tbm .lua", CF_TYPE_DATA },
|
||||
{ CF_TYPE_SOUNDS, "data" DIR_SEPARATOR_STR "sounds", ".wav .ogg", CF_TYPE_DATA },
|
||||
{ CF_TYPE_SOUNDS_8B22K, "data" DIR_SEPARATOR_STR "sounds" DIR_SEPARATOR_STR "8b22k", ".wav .ogg", CF_TYPE_SOUNDS },
|
||||
{ CF_TYPE_SOUNDS_16B11K, "data" DIR_SEPARATOR_STR "sounds" DIR_SEPARATOR_STR "16b11k", ".wav .ogg", CF_TYPE_SOUNDS },
|
||||
{ CF_TYPE_VOICE, "data" DIR_SEPARATOR_STR "voice", "", CF_TYPE_DATA },
|
||||
{ CF_TYPE_VOICE_BRIEFINGS, "data" DIR_SEPARATOR_STR "voice" DIR_SEPARATOR_STR "briefing", ".wav .ogg", CF_TYPE_VOICE },
|
||||
{ CF_TYPE_VOICE_CMD_BRIEF, "data" DIR_SEPARATOR_STR "voice" DIR_SEPARATOR_STR "command_briefings", ".wav .ogg", CF_TYPE_VOICE },
|
||||
@@ -68,26 +70,27 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
|
||||
{ CF_TYPE_VOICE_TRAINING, "data" DIR_SEPARATOR_STR "voice" DIR_SEPARATOR_STR "training", ".wav .ogg", CF_TYPE_VOICE },
|
||||
{ CF_TYPE_MUSIC, "data" DIR_SEPARATOR_STR "music", ".wav .ogg", CF_TYPE_DATA },
|
||||
{ CF_TYPE_MOVIES, "data" DIR_SEPARATOR_STR "movies", ".mve .msb .ogg .mp4 .srt .webm .png",CF_TYPE_DATA },
|
||||
{ CF_TYPE_INTERFACE, "data" DIR_SEPARATOR_STR "interface", ".pcx .ani .dds .tga .eff .png .jpg .rml .rcss", CF_TYPE_DATA },
|
||||
{ CF_TYPE_INTERFACE, "data" DIR_SEPARATOR_STR "interface", ".pcx .ani .dds .tga .eff .png .jpg", CF_TYPE_DATA },
|
||||
{ CF_TYPE_FONT, "data" DIR_SEPARATOR_STR "fonts", ".vf .ttf .otf", CF_TYPE_DATA },
|
||||
{ CF_TYPE_EFFECTS, "data" DIR_SEPARATOR_STR "effects", ".ani .eff .pcx .neb .tga .jpg .png .dds .sdr", CF_TYPE_DATA },
|
||||
{ CF_TYPE_HUD, "data" DIR_SEPARATOR_STR "hud", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
|
||||
{ CF_TYPE_PLAYERS, "data" DIR_SEPARATOR_STR "players", ".hcf", /* DON'T add pilot files here!! */ CF_TYPE_DATA },
|
||||
{ CF_TYPE_PLAYERS, "data" DIR_SEPARATOR_STR "players", ".hcf", CF_TYPE_DATA },
|
||||
{ CF_TYPE_PLAYER_IMAGES, "data" DIR_SEPARATOR_STR "players" DIR_SEPARATOR_STR "images", ".pcx .png .dds", CF_TYPE_PLAYERS },
|
||||
{ CF_TYPE_SQUAD_IMAGES, "data" DIR_SEPARATOR_STR "players" DIR_SEPARATOR_STR "squads", ".pcx .png .dds", CF_TYPE_PLAYERS },
|
||||
{ CF_TYPE_SINGLE_PLAYERS, "data" DIR_SEPARATOR_STR "players" DIR_SEPARATOR_STR "single", ".pl2 .cs2 .plr .csg .css .json", CF_TYPE_PLAYERS },
|
||||
{ CF_TYPE_MULTI_PLAYERS, "data" DIR_SEPARATOR_STR "players" DIR_SEPARATOR_STR "multi", ".plr .json", CF_TYPE_PLAYERS },
|
||||
{ CF_TYPE_PLAYER_BINDS, "data" DIR_SEPARATOR_STR "players" DIR_SEPARATOR_STR "presets", ".json", CF_TYPE_PLAYERS },
|
||||
{ CF_TYPE_CACHE, "data" DIR_SEPARATOR_STR "cache", ".clr .tmp .bx", CF_TYPE_DATA }, //clr=cached color
|
||||
{ CF_TYPE_MULTI_CACHE, "data" DIR_SEPARATOR_STR "multidata", ".pcx .png .jpg .dds .fs2 .txt", CF_TYPE_DATA },
|
||||
{ CF_TYPE_MULTI_CACHE, "data" DIR_SEPARATOR_STR "multidata", ".pcx .png .dds .fs2 .txt", CF_TYPE_DATA },
|
||||
{ CF_TYPE_MISSIONS, "data" DIR_SEPARATOR_STR "missions", ".fs2 .fc2 .ntl .ssv", CF_TYPE_DATA },
|
||||
{ CF_TYPE_CONFIG, "data" DIR_SEPARATOR_STR "config", ".cfg .tbl .tbm .xml .csv", CF_TYPE_DATA },
|
||||
{ CF_TYPE_DEMOS, "data" DIR_SEPARATOR_STR "demos", ".fsd", CF_TYPE_DATA },
|
||||
{ CF_TYPE_CBANIMS, "data" DIR_SEPARATOR_STR "cbanims", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
|
||||
{ CF_TYPE_INTEL_ANIMS, "data" DIR_SEPARATOR_STR "intelanims", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
|
||||
{ CF_TYPE_SCRIPTS, "data" DIR_SEPARATOR_STR "scripts", ".lua .lc .fnl", CF_TYPE_DATA },
|
||||
{ CF_TYPE_SCRIPTS, "data" DIR_SEPARATOR_STR "scripts", ".lua .lc", CF_TYPE_DATA },
|
||||
{ CF_TYPE_FICTION, "data" DIR_SEPARATOR_STR "fiction", ".txt", CF_TYPE_DATA },
|
||||
{ CF_TYPE_FREDDOCS, "data" DIR_SEPARATOR_STR "freddocs", ".html", CF_TYPE_DATA }
|
||||
{ CF_TYPE_FREDDOCS, "data" DIR_SEPARATOR_STR "freddocs", ".html", CF_TYPE_DATA },
|
||||
{ CF_TYPE_INTERFACE_MARKUP, "data" DIR_SEPARATOR_STR "interface" DIR_SEPARATOR_STR "markup", ".rml", CF_TYPE_INTERFACE },
|
||||
{ CF_TYPE_INTERFACE_CSS, "data" DIR_SEPARATOR_STR "interface" DIR_SEPARATOR_STR "css", ".rcss", CF_TYPE_INTERFACE },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -284,7 +287,7 @@ static int _cfile_chdir(const char *new_dir, const char *cur_dir __UNUSED)
|
||||
const char *colon = strchr(new_dir, ':');
|
||||
|
||||
if (colon) {
|
||||
if (!cfile_chdrive(SCP_tolower(*(colon - 1)) - 'a' + 1, 1))
|
||||
if (!cfile_chdrive(tolower(*(colon - 1)) - 'a' + 1, 1))
|
||||
return 1;
|
||||
|
||||
path = colon + 1;
|
||||
@@ -302,7 +305,7 @@ static int _cfile_chdir(const char *new_dir, const char *cur_dir __UNUSED)
|
||||
status = _chdir(path);
|
||||
if (status != 0) {
|
||||
#ifdef _WIN32
|
||||
cfile_chdrive(SCP_tolower(cur_dir[0]) - 'a' + 1, 1);
|
||||
cfile_chdrive(tolower(cur_dir[0]) - 'a' + 1, 1);
|
||||
#endif /* _WIN32 */
|
||||
return 2;
|
||||
}
|
||||
@@ -325,7 +328,7 @@ static int _cfile_chdir(const char *new_dir, const char *cur_dir __UNUSED)
|
||||
*/
|
||||
int cfile_push_chdir(int type)
|
||||
{
|
||||
SCP_string dir;
|
||||
char dir[CFILE_ROOT_DIRECTORY_LEN];
|
||||
char OriginalDirectory[CFILE_ROOT_DIRECTORY_LEN];
|
||||
|
||||
_getcwd(OriginalDirectory, CFILE_ROOT_DIRECTORY_LEN - 1);
|
||||
@@ -338,9 +341,9 @@ int cfile_push_chdir(int type)
|
||||
|
||||
strcpy_s(Cfile_stack[Cfile_stack_pos++], OriginalDirectory);
|
||||
|
||||
cf_create_default_path_string(dir, type, NULL);
|
||||
cf_create_default_path_string(dir, sizeof(dir) - 1, type, NULL);
|
||||
|
||||
return _cfile_chdir(dir.c_str(), OriginalDirectory);
|
||||
return _cfile_chdir(dir, OriginalDirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,26 +468,26 @@ char *cf_add_ext(const char *filename, const char *ext)
|
||||
*/
|
||||
int cf_delete(const char *filename, int path_type, uint32_t location_flags)
|
||||
{
|
||||
SCP_string longname;
|
||||
char longname[MAX_PATH_LEN];
|
||||
|
||||
Assert(CF_TYPE_SPECIFIED(path_type));
|
||||
|
||||
cf_create_default_path_string(longname, path_type, filename, location_flags);
|
||||
cf_create_default_path_string(longname, sizeof(longname) - 1, path_type, filename, false, location_flags);
|
||||
|
||||
return (_unlink(longname.c_str()) != -1);
|
||||
return (_unlink(longname) != -1);
|
||||
}
|
||||
|
||||
|
||||
// Same as _access function to read a file's access bits
|
||||
int cf_access(const char *filename, int dir_type, int mode)
|
||||
{
|
||||
SCP_string longname;
|
||||
char longname[MAX_PATH_LEN];
|
||||
|
||||
Assert( CF_TYPE_SPECIFIED(dir_type) );
|
||||
|
||||
cf_create_default_path_string(longname, dir_type, filename);
|
||||
cf_create_default_path_string( longname, sizeof(longname)-1, dir_type, filename );
|
||||
|
||||
return access(longname.c_str(), mode);
|
||||
return access(longname,mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -529,18 +532,18 @@ int cf_exists_full_ext(const char *filename, int dir_type, const int num_ext, co
|
||||
#ifdef _WIN32
|
||||
void cf_attrib(const char *filename, int set, int clear, int dir_type)
|
||||
{
|
||||
SCP_string longname;
|
||||
char longname[MAX_PATH_LEN];
|
||||
|
||||
Assert( CF_TYPE_SPECIFIED(dir_type) );
|
||||
|
||||
cf_create_default_path_string(longname, dir_type, filename);
|
||||
cf_create_default_path_string( longname, sizeof(longname)-1, dir_type, filename );
|
||||
|
||||
FILE *fp = fopen(longname.c_str(), "rb");
|
||||
FILE *fp = fopen(longname, "rb");
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
|
||||
DWORD z = GetFileAttributes(longname.c_str());
|
||||
SetFileAttributes(longname.c_str(), z | (set & ~clear));
|
||||
DWORD z = GetFileAttributes(longname);
|
||||
SetFileAttributes(longname, z | (set & ~clear));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -551,13 +554,13 @@ int cf_rename(const char *old_name, const char *name, int dir_type)
|
||||
Assert( CF_TYPE_SPECIFIED(dir_type) );
|
||||
|
||||
int ret_code;
|
||||
SCP_string old_longname;
|
||||
SCP_string new_longname;
|
||||
char old_longname[_MAX_PATH];
|
||||
char new_longname[_MAX_PATH];
|
||||
|
||||
cf_create_default_path_string(old_longname, dir_type, old_name);
|
||||
cf_create_default_path_string(new_longname, dir_type, name);
|
||||
cf_create_default_path_string( old_longname, sizeof(old_longname)-1, dir_type, old_name );
|
||||
cf_create_default_path_string( new_longname, sizeof(old_longname)-1, dir_type, name );
|
||||
|
||||
ret_code = rename(old_longname.c_str(), new_longname.c_str());
|
||||
ret_code = rename(old_longname, new_longname );
|
||||
if(ret_code != 0){
|
||||
switch(errno){
|
||||
case EACCES :
|
||||
@@ -601,7 +604,7 @@ void cf_create_directory(int dir_type, uint32_t location_flags)
|
||||
{
|
||||
int num_dirs = 0;
|
||||
int dir_tree[CF_MAX_PATH_TYPES];
|
||||
SCP_string longname;
|
||||
char longname[MAX_PATH_LEN];
|
||||
struct stat statbuf;
|
||||
|
||||
Assertion( CF_TYPE_SPECIFIED(dir_type), "Invalid dir_type passed to cf_create_directory." );
|
||||
@@ -619,10 +622,10 @@ void cf_create_directory(int dir_type, uint32_t location_flags)
|
||||
int i;
|
||||
|
||||
for (i=num_dirs-1; i>=0; i-- ) {
|
||||
cf_create_default_path_string(longname, dir_tree[i], nullptr, location_flags);
|
||||
if (stat(longname.c_str(), &statbuf) != 0) {
|
||||
mprintf(( "CFILE: Creating new directory '%s'\n", longname.c_str() ));
|
||||
mkdir_recursive(longname.c_str());
|
||||
cf_create_default_path_string(longname, sizeof(longname) - 1, dir_tree[i], nullptr, false, location_flags);
|
||||
if (stat(longname, &statbuf) != 0) {
|
||||
mprintf(( "CFILE: Creating new directory '%s'\n", longname ));
|
||||
mkdir_recursive(longname);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -631,7 +634,7 @@ void cf_create_directory(int dir_type, uint32_t location_flags)
|
||||
//
|
||||
// parameters: *filepath ==> name of file to open (may be path+name)
|
||||
// *mode ==> specifies how file should be opened (eg "rb" for read binary)
|
||||
// passing NULL to mode triggers an assert and returns NULL
|
||||
// passing NULL to mode deletes the file if it exists and returns NULL
|
||||
// type ==> one of: CFILE_NORMAL
|
||||
// CFILE_MEMORY_MAPPED
|
||||
// dir_type => override extension check, value is one of CF_TYPE* #defines
|
||||
@@ -644,7 +647,7 @@ void cf_create_directory(int dir_type, uint32_t location_flags)
|
||||
//
|
||||
|
||||
CFILE* _cfopen(const char* source, int line, const char* file_path, const char* mode, int type, int dir_type,
|
||||
bool /* localize */, uint32_t location_flags)
|
||||
bool localize, uint32_t location_flags)
|
||||
{
|
||||
/* Bobboau, what is this doing here? 31 is way too short... - Goober5000
|
||||
if( strlen(file_path) > 31 )
|
||||
@@ -671,13 +674,13 @@ CFILE* _cfopen(const char* source, int line, const char* file_path, const char*
|
||||
// the harddisk. No fancy packfile stuff here!
|
||||
|
||||
if ( strchr(mode,'w') || strchr(mode,'+') || strchr(mode,'a') ) {
|
||||
SCP_string longname;
|
||||
char longname[_MAX_PATH];
|
||||
auto last_separator = strrchr(file_path, DIR_SEPARATOR_CHAR);
|
||||
|
||||
// For write-only files, require a full path or a path type
|
||||
if ( last_separator ) {
|
||||
// Full path given?
|
||||
longname = file_path;
|
||||
strcpy_s(longname, file_path );
|
||||
} else {
|
||||
// Path type given?
|
||||
Assert( dir_type != CF_TYPE_ANY );
|
||||
@@ -685,7 +688,7 @@ CFILE* _cfopen(const char* source, int line, const char* file_path, const char*
|
||||
// Create the directory if necessary
|
||||
cf_create_directory(dir_type, location_flags);
|
||||
|
||||
cf_create_default_path_string(longname, dir_type, file_path, location_flags);
|
||||
cf_create_default_path_string(longname, sizeof(longname) - 1, dir_type, file_path, false, location_flags);
|
||||
}
|
||||
Assert( !(type & CFILE_MEMORY_MAPPED) );
|
||||
|
||||
@@ -724,7 +727,7 @@ CFILE* _cfopen(const char* source, int line, const char* file_path, const char*
|
||||
strcpy_s(happy_mode, mode);
|
||||
}
|
||||
|
||||
FILE *fp = fopen(longname.c_str(), happy_mode);
|
||||
FILE *fp = fopen(longname, happy_mode);
|
||||
if (fp) {
|
||||
return cf_open_fill_cfblock(source, line, last_separator ? (last_separator + 1) : file_path, fp, dir_type);
|
||||
}
|
||||
@@ -735,8 +738,10 @@ CFILE* _cfopen(const char* source, int line, const char* file_path, const char*
|
||||
//================================================
|
||||
// Search for file on disk, on cdrom, or in a packfile
|
||||
|
||||
auto find_res = cf_find_file_location(file_path, dir_type, location_flags);
|
||||
char copy_file_path[MAX_PATH_LEN]; // FIX change in memory from cf_find_file_location
|
||||
strcpy_s(copy_file_path, file_path);
|
||||
|
||||
auto find_res = cf_find_file_location( copy_file_path, dir_type, localize, location_flags );
|
||||
if ( find_res.found ) {
|
||||
|
||||
// Fount it, now create a cfile out of it
|
||||
@@ -776,7 +781,7 @@ CFILE* _cfopen(const char* source, int line, const char* file_path, const char*
|
||||
//
|
||||
// parameters: *filepath ==> name of file to open (may be path+name)
|
||||
// *mode ==> specifies how file should be opened (eg "rb" for read binary)
|
||||
// passing NULL to mode triggers an assert and returns NULL
|
||||
// passing NULL to mode deletes the file if it exists and returns NULL
|
||||
|
||||
// dir_type => override extension check, value is one of CF_TYPE* #defines
|
||||
//
|
||||
@@ -861,7 +866,6 @@ static int cfget_cfile_block()
|
||||
cfile->data = nullptr;
|
||||
cfile->fp = nullptr;
|
||||
cfile->type = CFILE_BLOCK_USED;
|
||||
cf_clear_compression_info(cfile);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -917,7 +921,7 @@ int cfclose( CFILE * cfile )
|
||||
} else {
|
||||
// VP do nothing
|
||||
}
|
||||
cf_clear_compression_info(cfile);
|
||||
|
||||
cfile->type = CFILE_BLOCK_UNUSED;
|
||||
return result;
|
||||
}
|
||||
@@ -969,7 +973,7 @@ static CFILE *cf_open_fill_cfblock(const char* source, int line, const char* ori
|
||||
if(pos == -1L)
|
||||
pos = 0;
|
||||
cf_init_lowlevel_read_code(cfp,0,filelength(fileno(fp)), 0 );
|
||||
cf_check_compression(cfp);
|
||||
|
||||
return cfp;
|
||||
}
|
||||
}
|
||||
@@ -1004,7 +1008,7 @@ static CFILE *cf_open_packed_cfblock(const char* source, int line, const char* o
|
||||
cfp->line_num = line;
|
||||
|
||||
cf_init_lowlevel_read_code(cfp,offset, size, 0 );
|
||||
cf_check_compression(cfp);
|
||||
|
||||
return cfp;
|
||||
}
|
||||
|
||||
@@ -1048,7 +1052,6 @@ static CFILE *cf_open_mapped_fill_cfblock(const char* source, int line, const ch
|
||||
cfp->line_num = line;
|
||||
|
||||
cf_init_lowlevel_read_code(cfp, 0, 0, 0 );
|
||||
|
||||
#if defined _WIN32
|
||||
cfp->hMapFile = CreateFileMapping(cfp->hInFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (cfp->hMapFile == NULL) {
|
||||
@@ -1233,17 +1236,15 @@ void cfread_string(char *buf, int n, CFILE *file)
|
||||
} while (c != 0 );
|
||||
}
|
||||
|
||||
void cfread_string_len(char *buf, int buffer_size, CFILE *file)
|
||||
void cfread_string_len(char *buf,int n, CFILE *file)
|
||||
{
|
||||
int len;
|
||||
len = cfread_int(file);
|
||||
Assertion( (len <= buffer_size), "String cannot fit in buffer, len: %i, buffer: %i", len, buffer_size);
|
||||
if (len) {
|
||||
Assertion( (len < n), "len: %i, n: %i", len, n );
|
||||
if (len)
|
||||
cfread(buf, len, 1, file);
|
||||
Assertion(len != buffer_size || buf[len - 1] == 0, "Unterminated string does not fit in buffer");
|
||||
}
|
||||
|
||||
buf[MIN(len, buffer_size - 1)] = 0;
|
||||
buf[len] = 0;
|
||||
}
|
||||
|
||||
SCP_string cfread_string_len(CFILE *file)
|
||||
@@ -1842,53 +1843,28 @@ int cfile_get_path_type(const SCP_string& dir)
|
||||
{
|
||||
SCP_string buf = dir;
|
||||
|
||||
// remove leading and trailing slashes
|
||||
if ( !buf.empty() ) {
|
||||
auto start = buf.find_first_not_of("\\/");
|
||||
auto end = buf.find_last_not_of("\\/");
|
||||
|
||||
if (start == SCP_string::npos) {
|
||||
buf = "";
|
||||
} else if ( (start > 0) || (end < buf.length()-1) ) {
|
||||
buf = buf.substr(start, end-start+1);
|
||||
}
|
||||
// Remove trailing slashes; avoid buffer overflow on 1-char strings
|
||||
while (buf.size() > 0 && (buf[buf.size() - 1] == '\\' || buf[buf.size() - 1] == '/')) {
|
||||
buf.resize(buf.size() - 1);
|
||||
}
|
||||
|
||||
if (buf.empty()) {
|
||||
return CF_TYPE_ROOT;
|
||||
// Remove leading slashes
|
||||
while (buf.size() > 0 && (buf[0] == '\\' || buf[0] == '/')) {
|
||||
buf = buf.substr(1);
|
||||
}
|
||||
|
||||
// Use official DIR_SEPARATOR_CHAR
|
||||
extern void normalize_directory_separators(SCP_string &str);
|
||||
normalize_directory_separators(buf);
|
||||
|
||||
// identify path type
|
||||
auto best_match = CF_TYPE_INVALID;
|
||||
|
||||
for (auto& Pathtype : Pathtypes) {
|
||||
if ( !Pathtype.path ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip root, it should have been detected before getting here
|
||||
if (Pathtype.index == CF_TYPE_ROOT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't allow unknown subdirs directly under data
|
||||
if (Pathtype.index == CF_TYPE_DATA) {
|
||||
if (buf == Pathtype.path) {
|
||||
best_match = CF_TYPE_DATA;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// for everything else just find closest match, allowing for unknown subdirectories
|
||||
if ( !strnicmp(buf.c_str(), Pathtype.path, strlen(Pathtype.path)) ) {
|
||||
best_match = Pathtype.index;
|
||||
for (char& c : buf) {
|
||||
if (c == '\\' || c == '/') {
|
||||
c = DIR_SEPARATOR_CHAR;
|
||||
}
|
||||
}
|
||||
|
||||
return best_match;
|
||||
for (auto& Pathtype : Pathtypes) {
|
||||
if (Pathtype.path != nullptr && buf == Pathtype.path) {
|
||||
return Pathtype.index;
|
||||
}
|
||||
}
|
||||
|
||||
return CF_TYPE_INVALID;
|
||||
}
|
||||
+39
-37
@@ -49,38 +49,41 @@ typedef struct {
|
||||
#define CF_TYPE_MODELS 5
|
||||
#define CF_TYPE_TABLES 6
|
||||
#define CF_TYPE_SOUNDS 7
|
||||
#define CF_TYPE_VOICE 8
|
||||
#define CF_TYPE_VOICE_BRIEFINGS 9
|
||||
#define CF_TYPE_VOICE_CMD_BRIEF 10
|
||||
#define CF_TYPE_VOICE_DEBRIEFINGS 11
|
||||
#define CF_TYPE_VOICE_PERSONAS 12
|
||||
#define CF_TYPE_VOICE_SPECIAL 13
|
||||
#define CF_TYPE_VOICE_TRAINING 14
|
||||
#define CF_TYPE_MUSIC 15
|
||||
#define CF_TYPE_MOVIES 16
|
||||
#define CF_TYPE_INTERFACE 17
|
||||
#define CF_TYPE_FONT 18
|
||||
#define CF_TYPE_EFFECTS 19
|
||||
#define CF_TYPE_HUD 20
|
||||
#define CF_TYPE_PLAYERS 21
|
||||
#define CF_TYPE_PLAYER_IMAGES 22
|
||||
#define CF_TYPE_SQUAD_IMAGES 23
|
||||
#define CF_TYPE_SINGLE_PLAYERS 24
|
||||
#define CF_TYPE_MULTI_PLAYERS 25
|
||||
#define CF_TYPE_PLAYER_BINDS 26
|
||||
#define CF_TYPE_CACHE 27
|
||||
#define CF_TYPE_MULTI_CACHE 28
|
||||
#define CF_TYPE_MISSIONS 29
|
||||
#define CF_TYPE_CONFIG 30
|
||||
#define CF_TYPE_DEMOS 31
|
||||
#define CF_TYPE_CBANIMS 32
|
||||
#define CF_TYPE_INTEL_ANIMS 33
|
||||
#define CF_TYPE_SCRIPTS 34
|
||||
#define CF_TYPE_FICTION 35
|
||||
#define CF_TYPE_FREDDOCS 36
|
||||
#define CF_TYPE_SOUNDS_8B22K 8
|
||||
#define CF_TYPE_SOUNDS_16B11K 9
|
||||
#define CF_TYPE_VOICE 10
|
||||
#define CF_TYPE_VOICE_BRIEFINGS 11
|
||||
#define CF_TYPE_VOICE_CMD_BRIEF 12
|
||||
#define CF_TYPE_VOICE_DEBRIEFINGS 13
|
||||
#define CF_TYPE_VOICE_PERSONAS 14
|
||||
#define CF_TYPE_VOICE_SPECIAL 15
|
||||
#define CF_TYPE_VOICE_TRAINING 16
|
||||
#define CF_TYPE_MUSIC 17
|
||||
#define CF_TYPE_MOVIES 18
|
||||
#define CF_TYPE_INTERFACE 19
|
||||
#define CF_TYPE_FONT 20
|
||||
#define CF_TYPE_EFFECTS 21
|
||||
#define CF_TYPE_HUD 22
|
||||
#define CF_TYPE_PLAYERS 23
|
||||
#define CF_TYPE_PLAYER_IMAGES 24
|
||||
#define CF_TYPE_SQUAD_IMAGES 25
|
||||
#define CF_TYPE_SINGLE_PLAYERS 26
|
||||
#define CF_TYPE_MULTI_PLAYERS 27
|
||||
#define CF_TYPE_CACHE 28
|
||||
#define CF_TYPE_MULTI_CACHE 29
|
||||
#define CF_TYPE_MISSIONS 30
|
||||
#define CF_TYPE_CONFIG 31
|
||||
#define CF_TYPE_DEMOS 32
|
||||
#define CF_TYPE_CBANIMS 33
|
||||
#define CF_TYPE_INTEL_ANIMS 34
|
||||
#define CF_TYPE_SCRIPTS 35
|
||||
#define CF_TYPE_FICTION 36
|
||||
#define CF_TYPE_FREDDOCS 37
|
||||
#define CF_TYPE_INTERFACE_MARKUP 38
|
||||
#define CF_TYPE_INTERFACE_CSS 39
|
||||
|
||||
#define CF_MAX_PATH_TYPES \
|
||||
37 // Can be as high as you'd like //DTP; yeah but beware alot of things uses CF_MAX_PATH_TYPES
|
||||
40 // Can be as high as you'd like //DTP; yeah but beware alot of things uses CF_MAX_PATH_TYPES
|
||||
|
||||
// TRUE if type is specified and valid
|
||||
#define CF_TYPE_SPECIFIED(path_type) (((path_type)>CF_TYPE_INVALID) && ((path_type)<CF_MAX_PATH_TYPES))
|
||||
@@ -322,17 +325,15 @@ void cfread_vector(vec3d* vec, CFILE* file, vec3d* deflt = nullptr);
|
||||
// to n characters.
|
||||
void cfread_string(char *buf,int n, CFILE *file);
|
||||
/**
|
||||
* @brief Read a fixed length string that is not null-terminated, with the length stored in file.
|
||||
* @brief Read a fixed length string that is not null-terminated, with the length stored in file
|
||||
*
|
||||
* If it is not null-terminated, the length of the string *must* be less than the size of the buffer (to make room for one)
|
||||
*
|
||||
* @param buf Pre-allocated array to store string
|
||||
* @param buffer_size Size of pre-allocated array
|
||||
* @param n Size of pre-allocated array
|
||||
* @param file File to read from
|
||||
*
|
||||
* @note Appends NULL character to string (buf)
|
||||
*/
|
||||
void cfread_string_len(char *buf,int buffer_size, CFILE *file);
|
||||
void cfread_string_len(char *buf,int n, CFILE *file);
|
||||
|
||||
/**
|
||||
* @brief Read a string from the file where the length is stored in the file
|
||||
@@ -391,7 +392,7 @@ struct CFileLocation {
|
||||
// size - File size
|
||||
// offset - Offset into pack file. 0 if not a packfile.
|
||||
// Returns: If not found returns 0.
|
||||
CFileLocation cf_find_file_location(const char* filespec, int pathtype,
|
||||
CFileLocation cf_find_file_location(const char* filespec, int pathtype, bool localize = false,
|
||||
uint32_t location_flags = CF_LOCATION_ALL);
|
||||
|
||||
struct CFileLocationExt : public CFileLocation {
|
||||
@@ -415,7 +416,8 @@ struct CFileLocationExt : public CFileLocation {
|
||||
// offset - Offset into pack file. 0 if not a packfile.
|
||||
// Returns: If not found returns -1, else returns offset into ext_list.
|
||||
// (NOTE: This function is exponentially slow, so don't use it unless truely needed!!)
|
||||
CFileLocationExt cf_find_file_location_ext(const char* filename, const int ext_num, const char** ext_list, int pathtype);
|
||||
CFileLocationExt cf_find_file_location_ext(const char* filename, const int ext_num, const char** ext_list, int pathtype,
|
||||
bool localize = false);
|
||||
|
||||
// Functions to change directories
|
||||
int cfile_chdir(const char *dir);
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "cfile/cfile.h"
|
||||
#include "cfile/cfilearchive.h"
|
||||
#include "cfile/cfilecompression.h"
|
||||
#include "luaconf.h"
|
||||
|
||||
#include <sstream>
|
||||
@@ -34,6 +33,7 @@
|
||||
#define CHECK_POSITION
|
||||
|
||||
// Called once to setup the low-level reading code.
|
||||
|
||||
void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size, size_t pos )
|
||||
{
|
||||
Assert(cfile != nullptr);
|
||||
@@ -54,33 +54,6 @@ void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size,
|
||||
}
|
||||
}
|
||||
|
||||
//This function checks the file header to see if the file is compressed, if so it fills the correct compression info.
|
||||
void cf_check_compression(CFILE* cfile)
|
||||
{
|
||||
if (cfile->size <= 16)
|
||||
return;
|
||||
int header=cfread_int(cfile);
|
||||
cfseek(cfile, 0, SEEK_SET);
|
||||
if (comp_check_header(header) == COMP_HEADER_MATCH)
|
||||
comp_create_ci(cfile, header);
|
||||
}
|
||||
|
||||
//This function is called to cleanup compression info when the cfile handle is reused.
|
||||
void cf_clear_compression_info(CFILE* cfile)
|
||||
{
|
||||
if (cfile->compression_info.header != 0)
|
||||
{
|
||||
free(cfile->compression_info.offsets);
|
||||
free(cfile->compression_info.decoder_buffer);
|
||||
cfile->compression_info.offsets = nullptr;
|
||||
cfile->compression_info.decoder_buffer = nullptr;
|
||||
cfile->compression_info.header = 0;
|
||||
cfile->compression_info.block_size = 0;
|
||||
cfile->compression_info.last_decoded_block_pos = 0;
|
||||
cfile->compression_info.last_decoded_block_bytes = 0;
|
||||
cfile->compression_info.num_offsets = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// cfeof() Tests for end-of-file on a stream
|
||||
@@ -88,11 +61,11 @@ void cf_clear_compression_info(CFILE* cfile)
|
||||
// returns a nonzero value after the first read operation that attempts to read
|
||||
// past the end of the file. It returns 0 if the current position is not end of file.
|
||||
// There is no error return.
|
||||
|
||||
int cfeof(CFILE *cfile)
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
if (cfile->compression_info.header != 0)
|
||||
return comp_feof(cfile);
|
||||
|
||||
int result = 0;
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
@@ -119,8 +92,6 @@ int cfeof(CFILE *cfile)
|
||||
int cftell( CFILE * cfile )
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
if (cfile->compression_info.header != 0)
|
||||
return (int)comp_ftell(cfile);
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
if (cfile->fp) {
|
||||
@@ -146,9 +117,6 @@ int cfseek( CFILE *cfile, int offset, int where )
|
||||
|
||||
Assert(cfile != NULL);
|
||||
|
||||
if (cfile->compression_info.header != 0)
|
||||
return comp_fseek(cfile, offset, where);
|
||||
|
||||
// TODO: seek to offset in memory mapped file
|
||||
Assert( !cfile->mem_mapped );
|
||||
|
||||
@@ -234,31 +202,23 @@ int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
// This is a file from memory
|
||||
bytes_read = size;
|
||||
memcpy(buf, reinterpret_cast<const char*>(cfile->data) + cfile->raw_position, size);
|
||||
} else if (cfile->compression_info.header != 0) {
|
||||
bytes_read = comp_fread(cfile, reinterpret_cast<char*>(buf),size);
|
||||
if (bytes_read != size)
|
||||
{
|
||||
mprintf(("\nFile: %s decompression error. Result was: %d expected: %d\n", cfile->original_filename.c_str(), (int)bytes_read, (int)size));
|
||||
Assertion(bytes_read == size, "File decompression error!");
|
||||
}
|
||||
} else {
|
||||
bytes_read = fread(buf, 1, size, cfile->fp);
|
||||
bytes_read = fread( buf, 1, size, cfile->fp );
|
||||
}
|
||||
|
||||
if ( bytes_read > 0 ) {
|
||||
cfile->raw_position += bytes_read;
|
||||
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
|
||||
}
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
//Raw position is not the fp position with compressed files
|
||||
if (cfile->fp && cfile->compression_info.header == 0) {
|
||||
if (cfile->fp) {
|
||||
auto tmp_offset = ftell(cfile->fp) - cfile->lib_offset;
|
||||
Assert(tmp_offset == cfile->raw_position);
|
||||
Assert(tmp_offset==cfile->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (int)(bytes_read / elsize);
|
||||
|
||||
}
|
||||
|
||||
int cfread_lua_number(double *buf, CFILE *cfile)
|
||||
@@ -306,3 +266,4 @@ int cfread_lua_number(double *buf, CFILE *cfile)
|
||||
return items_read;
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _CFILEARCHIVE_H
|
||||
#define _CFILEARCHIVE_H
|
||||
|
||||
@@ -22,17 +23,6 @@
|
||||
#define CFILE_BLOCK_UNUSED 0
|
||||
#define CFILE_BLOCK_USED 1
|
||||
|
||||
struct COMPRESSION_INFO {
|
||||
int header = 0;
|
||||
size_t compressed_size = 0;
|
||||
int block_size = 0;
|
||||
int num_offsets = 0;
|
||||
int* offsets = nullptr;
|
||||
char* decoder_buffer = nullptr;
|
||||
int last_decoded_block_pos = 0;
|
||||
int last_decoded_block_bytes = 0;
|
||||
};
|
||||
|
||||
struct CFILE {
|
||||
int type = CFILE_BLOCK_UNUSED; // CFILE_BLOCK_UNUSED, CFILE_BLOCK_USED
|
||||
int dir_type; // directory location
|
||||
@@ -54,19 +44,12 @@ struct CFILE {
|
||||
SCP_string original_filename;
|
||||
const char* source_file;
|
||||
int line_num;
|
||||
COMPRESSION_INFO compression_info;
|
||||
};
|
||||
|
||||
#define MAX_CFILE_BLOCKS 64
|
||||
extern std::array<CFILE, MAX_CFILE_BLOCKS> Cfile_block_list;
|
||||
|
||||
// Called once to setup the low-level reading code.
|
||||
void cf_init_lowlevel_read_code(CFILE* cfile, size_t lib_offset, size_t size, size_t pos);
|
||||
|
||||
// This checks if the file is compressed or not, and creates the proper compression info if so.
|
||||
void cf_check_compression(CFILE* cfile);
|
||||
|
||||
// Used to clear compression info data and free dynamic memory used by compression
|
||||
void cf_clear_compression_info(CFILE* cfile);
|
||||
void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size, size_t pos );
|
||||
|
||||
#endif
|
||||
@@ -1,206 +0,0 @@
|
||||
#define _CFILE_INTERNAL
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
#endif
|
||||
|
||||
#include "lz4.h"
|
||||
#include "cfilecompression.h"
|
||||
#include "cfilearchive.h"
|
||||
|
||||
/*INTERNAL FUNCTIONS*/
|
||||
/*LZ41*/
|
||||
void lz41_load_offsets(CFILE* cf);
|
||||
size_t lz41_stream_random_access(CFILE* cf, char* bytes_out, size_t offset, size_t length);
|
||||
void lz41_create_ci(CFILE* cf, int header);
|
||||
/*MISC*/
|
||||
int fso_fseek(CFILE* cfile, int offset, int where);
|
||||
/*END OF INTERNAL FUNCTIONS*/
|
||||
|
||||
int comp_check_header(int header)
|
||||
{
|
||||
if (LZ41_FILE_HEADER == header)
|
||||
return COMP_HEADER_MATCH;
|
||||
|
||||
return COMP_HEADER_MISMATCH;
|
||||
}
|
||||
|
||||
void comp_create_ci(CFILE* cf, int header)
|
||||
{
|
||||
//mprintf(("(CI)Compressed File Opened: %s \n", cf->original_filename.c_str()));
|
||||
|
||||
if (LZ41_FILE_HEADER == header)
|
||||
lz41_create_ci(cf, header);
|
||||
|
||||
//mprintf(("(CI)Uncompressed FileSize: %d \n", cf->size));
|
||||
//mprintf(("(CI)Compressed FileSize: %d \n", cf->compression_info.compressed_size));
|
||||
//mprintf(("(CI)Block Size: %d \n", cf->compression_info.block_size));
|
||||
}
|
||||
|
||||
size_t comp_fread(CFILE* cf, char* buffer, size_t length)
|
||||
{
|
||||
/* Check the request to be at least 1 byte */
|
||||
Assertion(length > 0, "Invalid length requested.");
|
||||
|
||||
/* Check that we are not requesting to read beyond end of file */
|
||||
Assertion(cf->raw_position + length <= cf->size, "Invalid length requested.");
|
||||
|
||||
if (LZ41_FILE_HEADER == cf->compression_info.header)
|
||||
return lz41_stream_random_access(cf, buffer, cf->raw_position, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t comp_ftell(CFILE* cf)
|
||||
{
|
||||
return cf->raw_position;
|
||||
}
|
||||
|
||||
int comp_feof(CFILE* cf)
|
||||
{
|
||||
if (cf->raw_position >= cf->size)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int comp_fseek(CFILE* cf, int offset, int where)
|
||||
{
|
||||
size_t goal_position;
|
||||
switch (where) {
|
||||
case SEEK_SET: goal_position = offset; break;
|
||||
case SEEK_CUR: goal_position = cf->raw_position + offset; break;
|
||||
case SEEK_END: goal_position = cf->size + offset; break;
|
||||
default: return 1;
|
||||
}
|
||||
|
||||
// Make sure we don't seek beyond the end of the file
|
||||
CAP(goal_position,(size_t)0, cf->size);
|
||||
|
||||
cf->raw_position = goal_position;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Special fseek for compressed files handled by FSO, only SEEK_SET and SEEK_END is supported.
|
||||
int fso_fseek(CFILE* cfile, int offset, int where)
|
||||
{
|
||||
size_t goal_position;
|
||||
|
||||
switch (where) {
|
||||
case SEEK_SET: goal_position = offset + cfile->lib_offset; break;
|
||||
case SEEK_END: goal_position = cfile->compression_info.compressed_size + offset + cfile->lib_offset; break;
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
// Make sure we don't seek beyond the end of the file
|
||||
CAP(goal_position, cfile->lib_offset, cfile->lib_offset + cfile->compression_info.compressed_size);
|
||||
|
||||
int result = 0;
|
||||
if (cfile->fp)
|
||||
{
|
||||
result = fseek(cfile->fp, (long)goal_position, SEEK_SET);
|
||||
Assertion(goal_position >= cfile->lib_offset, "Invalid offset values detected while seeking! Goal was " SIZE_T_ARG ", lib_offset is " SIZE_T_ARG ".", goal_position, cfile->lib_offset);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void lz41_create_ci(CFILE* cf, int header)
|
||||
{
|
||||
cf->compression_info.header = header;
|
||||
cf->compression_info.compressed_size = cf->size;
|
||||
fso_fseek(cf, (int)sizeof(int)*-3, SEEK_END);
|
||||
auto fNumoffsets = fread(&cf->compression_info.num_offsets, sizeof(int), 1, cf->fp);
|
||||
auto fSize = fread(&cf->size, sizeof(int), 1, cf->fp);
|
||||
auto fBsize = fread(&cf->compression_info.block_size, sizeof(int), 1, cf->fp);
|
||||
|
||||
Assertion(cf->compression_info.num_offsets > 0, "Invalid number of offsets, compressed file is possibly in the wrong format or corrupted.");
|
||||
#if !defined(NDEBUG)
|
||||
Assertion(cf->size > 4, "Invalid filesize, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(cf->compression_info.block_size > 16, "Invalid block size, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(fNumoffsets == 1, "Error while reading the number of offsets, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(fSize == 1, "Error while reading original filesize, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(fBsize == 1, "Error while reading block size, compressed file is possibly in the wrong format or corrupted.");
|
||||
#endif
|
||||
|
||||
cf->compression_info.decoder_buffer = (char*)malloc(LZ4_compressBound(cf->compression_info.block_size));
|
||||
cf->compression_info.last_decoded_block_pos = 0;
|
||||
cf->compression_info.last_decoded_block_bytes = 0;
|
||||
lz41_load_offsets(cf);
|
||||
}
|
||||
|
||||
void lz41_load_offsets(CFILE* cf)
|
||||
{
|
||||
cf->compression_info.offsets = (int*)malloc(cf->compression_info.num_offsets * sizeof(int));
|
||||
int block;
|
||||
int* offsets_ptr = cf->compression_info.offsets;
|
||||
|
||||
/* Seek to the first offset position, remember to consider the trailing ints */
|
||||
fso_fseek(cf, ( ( sizeof(int) * cf->compression_info.num_offsets ) * -1 ) - (sizeof(int)*3 ), SEEK_END);
|
||||
for (block = 0; block < cf->compression_info.num_offsets; ++block)
|
||||
{
|
||||
auto bytes_read = fread(offsets_ptr++, sizeof(int), 1, cf->fp);
|
||||
Assertion(bytes_read == 1, "Error reading offset list.");
|
||||
}
|
||||
}
|
||||
|
||||
size_t lz41_stream_random_access(CFILE* cf, char* bytes_out, size_t offset, size_t length)
|
||||
{
|
||||
LZ4_streamDecode_t lz4_stream_decode_body;
|
||||
LZ4_streamDecode_t* lz4_stream_decode = &lz4_stream_decode_body;
|
||||
/* The blocks (current_block to end_block) contain the data we want */
|
||||
size_t current_block = offset / cf->compression_info.block_size;
|
||||
size_t end_block = ((offset + length - 1) / cf->compression_info.block_size) + 1;
|
||||
size_t written_bytes = 0;
|
||||
|
||||
if (cf->compression_info.num_offsets <= (int)end_block)
|
||||
return (size_t)LZ41_OFFSETS_MISMATCH;
|
||||
|
||||
/* Seek to the first block to read, if it matches the cached block, search the next one instead */
|
||||
if (cf->compression_info.last_decoded_block_pos == cf->compression_info.offsets[current_block] && current_block + 1 <= end_block)
|
||||
fso_fseek(cf, cf->compression_info.offsets[current_block+1], SEEK_SET);
|
||||
else
|
||||
fso_fseek(cf, cf->compression_info.offsets[current_block], SEEK_SET);
|
||||
|
||||
offset = offset % cf->compression_info.block_size;
|
||||
char* cmp_buf = (char*)malloc(LZ4_compressBound(cf->compression_info.block_size));
|
||||
|
||||
/* Start decoding */
|
||||
for (; current_block < end_block; ++current_block)
|
||||
{
|
||||
/* Only read and decode if the requested block is not the cached one */
|
||||
if (cf->compression_info.last_decoded_block_pos != cf->compression_info.offsets[current_block])
|
||||
{
|
||||
cf->compression_info.last_decoded_block_pos = cf->compression_info.offsets[current_block];
|
||||
|
||||
/* The difference in offsets is the size of the block */
|
||||
int cmp_bytes = cf->compression_info.offsets[current_block + 1] - cf->compression_info.offsets[current_block];
|
||||
auto bytes_read = fread(cmp_buf, cmp_bytes, 1, cf->fp);
|
||||
Assertion(bytes_read == 1, "Error reading from compressed file.");
|
||||
|
||||
cf->compression_info.last_decoded_block_bytes = LZ4_decompress_safe_continue(lz4_stream_decode, cmp_buf, cf->compression_info.decoder_buffer, cmp_bytes, cf->compression_info.block_size);
|
||||
if (cf->compression_info.last_decoded_block_bytes <= 0)
|
||||
{
|
||||
free(cmp_buf);
|
||||
return (size_t)LZ41_DECOMPRESSION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write out the part of the data we care about from buffer */
|
||||
size_t block_length = (length < (cf->compression_info.last_decoded_block_bytes - offset) ? length : (cf->compression_info.last_decoded_block_bytes - offset));
|
||||
memcpy(bytes_out + written_bytes, cf->compression_info.decoder_buffer + offset, (size_t)block_length);
|
||||
written_bytes += block_length;
|
||||
offset = 0;
|
||||
length -= block_length;
|
||||
}
|
||||
|
||||
free(cmp_buf);
|
||||
return written_bytes;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
ShivanSpS - Compressed files support for FSO. Many thanks to ngld, taylor and everyone elsewho helped me in getting this done.
|
||||
|
||||
-The file header is a version, this is used to tell FSO how to decompress that file, always use 4 chars to mantain alignment.
|
||||
-The header ID can be used to add diferent revisions to LZ41 decompression system or to add other compression format supports whiout breaking compatibility.
|
||||
-The system uses a offset list to record the position of every block in file, this list, along with the number of offsets, original filesize,
|
||||
and block size, must be written by the compressor app.
|
||||
-A decoder cache is used to store the last decoded block, this ensures each block is read and decoded only once in sequential reads. This cache
|
||||
is a little bit bigger than the block size, a higher block size means less overhead added to the file, but it also means a little more ram will
|
||||
be used during decompression.
|
||||
-All this dynamic memory is assigned at cfopen() and it is cleared on cfclose().
|
||||
|
||||
................................char[4]..........(n ints)...(int)..........(int)..........(int)
|
||||
-COMPRESSED FILE DATA STUCTURE: HEADER|N BLOCKS|N OFFSETS|NUM_OFFSETS|ORIGINAL_FILESIZE|BLOCK_SIZE
|
||||
*/
|
||||
|
||||
#ifndef _CFILECOMPRESSION_H
|
||||
#define _CFILECOMPRESSION_H
|
||||
struct CFILE;
|
||||
|
||||
/*LZ41*/
|
||||
#define LZ41_FILE_HEADER 0x31345A4C // "14ZL" -> "LZ41", inverted
|
||||
#define LZ41_DECOMPRESSION_ERROR -1
|
||||
#define LZ41_MAX_BLOCKS_OVERFLOW -2
|
||||
#define LZ41_HEADER_MISMATCH -3
|
||||
#define LZ41_OFFSETS_MISMATCH -4
|
||||
/******/
|
||||
|
||||
#define COMP_HEADER_MATCH 1
|
||||
#define COMP_HEADER_MISMATCH 0
|
||||
|
||||
/*
|
||||
Returns COMP_HEADER_MATCH if header is a valid compressed file header,
|
||||
returns COMP_HEADER_MISMATCH if it dosent.
|
||||
*/
|
||||
int comp_check_header(int header);
|
||||
|
||||
/*
|
||||
This is called to generate the correct compression_info data
|
||||
after the file has been indentified as a compressed file.
|
||||
This must be done before calling any other function.
|
||||
*/
|
||||
void comp_create_ci(CFILE* cf, int header);
|
||||
|
||||
/*
|
||||
Read X bytes from the uncompressed file starting from X offset.
|
||||
Returns the amount of bytes read, and 0 or lower to indicate errors.
|
||||
*/
|
||||
size_t comp_fread(CFILE* cf, char* buffer, size_t length);
|
||||
|
||||
/*
|
||||
Returns the current uncompressed file position.
|
||||
*/
|
||||
size_t comp_ftell(CFILE* cf);
|
||||
|
||||
/*
|
||||
Returns 1 of the uncompressed file has been completely read, otherwise it returns a 0.
|
||||
*/
|
||||
int comp_feof(CFILE* cf);
|
||||
|
||||
/*
|
||||
Used to move the uncompressed file current position.
|
||||
*/
|
||||
int comp_fseek(CFILE* cf, int offset, int where);
|
||||
|
||||
#endif
|
||||
+993
-994
File diff suppressed because it is too large.
Load diff
@@ -42,8 +42,8 @@ bool cf_check_location_flags(uint32_t check_flags, uint32_t desired_flags);
|
||||
// Output: path - Fully qualified pathname.
|
||||
//Returns 0 if result would be too long (invalid result)
|
||||
int cf_create_default_path_string(char* path, uint path_max, int pathtype, const char* filename = nullptr,
|
||||
uint32_t location_flags = CF_LOCATION_ALL);
|
||||
int cf_create_default_path_string(SCP_string& path, int pathtype, const char* filename = nullptr,
|
||||
bool localize = false, uint32_t location_flags = CF_LOCATION_ALL);
|
||||
int cf_create_default_path_string(SCP_string& path, int pathtype, const char* filename = nullptr, bool localize = false,
|
||||
uint32_t location_flags = CF_LOCATION_ALL);
|
||||
|
||||
#endif //_CFILESYSTEM_H
|
||||
@@ -1,181 +0,0 @@
|
||||
#include "cheats_table.h"
|
||||
#include "parse/parselo.h"
|
||||
|
||||
void cheat_table_init() {
|
||||
customCheats.clear();
|
||||
|
||||
if (cf_exists_full("cheats.tbl", CF_TYPE_TABLES)) {
|
||||
parse_cheat_table("cheats.tbl");
|
||||
}
|
||||
parse_modular_table("*-cht.tbm", parse_cheat_table);
|
||||
}
|
||||
|
||||
bool CustomCheat::canUseCheat() {
|
||||
return !requireCheatsEnabled || Cheats_enabled;
|
||||
}
|
||||
|
||||
void CustomCheat::runCheat() {
|
||||
if (!canUseCheat()) return;
|
||||
HUD_printf("%s", cheatMsg.c_str());
|
||||
scripting::hooks::OnCheat->run(scripting::hook_param_list(scripting::hook_param("Cheat", 's', cheatCode)));
|
||||
CheatUsed = cheatCode;
|
||||
}
|
||||
|
||||
void SpawnShipCheat::runCheat() {
|
||||
CustomCheat::runCheat();
|
||||
|
||||
if (canUseCheat() && (Game_mode & GM_IN_MISSION) && Player_obj != nullptr) {
|
||||
extern void prevent_spawning_collision(object *new_obj);
|
||||
ship_subsys *ptr;
|
||||
char name[NAME_LENGTH];
|
||||
int ship_idx, ship_class;
|
||||
|
||||
// if not found, then don't create it :(
|
||||
ship_class = ship_info_lookup(shipClassName.c_str());
|
||||
if (ship_class < 0)
|
||||
return;
|
||||
|
||||
vec3d pos = Player_obj->pos;
|
||||
matrix orient = Player_obj->orient;
|
||||
pos.xyz.x += frand_range(-700.0f, 700.0f);
|
||||
pos.xyz.y += frand_range(-700.0f, 700.0f);
|
||||
pos.xyz.z += frand_range(-700.0f, 700.0f);
|
||||
|
||||
int objnum = ship_create(&orient, &pos, ship_class);
|
||||
if (objnum < 0)
|
||||
return;
|
||||
|
||||
ship *shipp = &Ships[Objects[objnum].instance];
|
||||
shipp->ship_name[0] = '\0';
|
||||
shipp->display_name.clear();
|
||||
for (size_t j = 0; j < Player_orders.size(); j++)
|
||||
shipp->orders_accepted.insert(j);
|
||||
|
||||
// Goober5000 - stolen from support ship creation
|
||||
// create a name for the ship with a number. look for collisions until one isn't found anymore
|
||||
ship_idx = 1;
|
||||
do {
|
||||
sprintf(name, "%s %d", shipName.c_str(), ship_idx);
|
||||
if ( (ship_name_lookup(name) == -1) && (ship_find_exited_ship_by_name(name) == -1) )
|
||||
{
|
||||
strcpy_s(shipp->ship_name, name);
|
||||
break;
|
||||
}
|
||||
|
||||
ship_idx++;
|
||||
} while(1);
|
||||
|
||||
shipp->flags.set(Ship::Ship_Flags::Escort);
|
||||
shipp->escort_priority = 1000 - ship_idx;
|
||||
|
||||
// now make sure we're not colliding with anyone
|
||||
prevent_spawning_collision(&Objects[objnum]);
|
||||
|
||||
// Goober5000 - beam free
|
||||
for (ptr = GET_FIRST(&shipp->subsys_list); ptr != END_OF_LIST(&shipp->subsys_list); ptr = GET_NEXT(ptr))
|
||||
{
|
||||
// mark all turrets as beam free
|
||||
if (ptr->system_info->type == SUBSYSTEM_TURRET)
|
||||
{
|
||||
ptr->weapons.flags.set(Ship::Weapon_Flags::Beam_Free);
|
||||
ptr->turret_next_fire_stamp = timestamp((int)frand_range(50.0f, 4000.0f));
|
||||
}
|
||||
}
|
||||
|
||||
// Cyborg17 to prevent a nullptr...
|
||||
ship_set_warp_effects(&Objects[objnum]);
|
||||
// warpin
|
||||
shipfx_warpin_start(&Objects[objnum]);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_cheat_table(const char* filename) {
|
||||
try {
|
||||
read_file_text(filename, CF_TYPE_TABLES);
|
||||
reset_parse();
|
||||
optional_string("#CUSTOM CHEATS");
|
||||
|
||||
while (optional_string("$Cheat:")) {
|
||||
SCP_string code, msg, shipClass, shipName;
|
||||
bool requireCheats = false;
|
||||
bool shipSpawn = false;
|
||||
required_string("+Code:");
|
||||
stuff_string(code, F_RAW);
|
||||
|
||||
//We are limited to the buffer size used for the traditional cheats. We can still use short cheats, though.
|
||||
if (code.length() > CHEAT_BUFFER_LEN) {
|
||||
Warning(LOCATION, "Cheat code %s is too long. It will be cut off to maximum length (%i characters).", code.c_str(), CHEAT_BUFFER_LEN);
|
||||
code = code.substr(0, CHEAT_BUFFER_LEN);
|
||||
}
|
||||
|
||||
required_string("+Message:");
|
||||
stuff_string(msg, F_MESSAGE);
|
||||
|
||||
if (optional_string("+RequireCheats:")) {
|
||||
stuff_boolean(&requireCheats);
|
||||
}
|
||||
|
||||
if (optional_string("+SpawnShip:")) {
|
||||
shipSpawn = true;
|
||||
required_string("+Name:");
|
||||
stuff_string(shipName, F_NAME);
|
||||
required_string("+Class:");
|
||||
stuff_string(shipClass, F_NAME);
|
||||
|
||||
int shipClassInfo = ship_info_lookup(shipClass.c_str());
|
||||
if (shipClassInfo < 0) {
|
||||
shipSpawn = false;
|
||||
Warning(LOCATION, "There is no ship class named '%s'. The '%s' cheat won't summon a ship.", shipClass.c_str(), code.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (shipSpawn) {
|
||||
std::unique_ptr<CustomCheat> shipCheat(new SpawnShipCheat(code, msg, requireCheats, shipClass, shipName));
|
||||
|
||||
if(customCheats.count(code) == 1) {
|
||||
Warning(LOCATION, "A cheat for code '%s' already exists. It will be replaced.", code.c_str());
|
||||
customCheats[code] = std::move(shipCheat);
|
||||
} else {
|
||||
customCheats.emplace(code, std::move(shipCheat));
|
||||
}
|
||||
} else {
|
||||
std::unique_ptr<CustomCheat> cheat(new CustomCheat(code, msg, requireCheats));
|
||||
|
||||
if(customCheats.count(code) == 1) {
|
||||
Warning(LOCATION, "A cheat for code '%s' already exists. It will be replaced.", code.c_str());
|
||||
customCheats[code] = std::move(cheat);
|
||||
} else {
|
||||
customCheats.emplace(code, std::move(cheat));
|
||||
}
|
||||
}
|
||||
}
|
||||
required_string("#END");
|
||||
}
|
||||
catch (const parse::ParseException& e) {
|
||||
mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", filename, e.what()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool checkForCustomCheats(const char* buffer, int buffer_length) {
|
||||
const char* check_buffer = buffer;
|
||||
for (int i = 0; i < buffer_length; i++) {
|
||||
if (*check_buffer == '\0') {
|
||||
check_buffer++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through our map of custom cheats and check if the code is within the buffer.
|
||||
// We don't just check for equality, as the player may have typed other characters before the cheat.
|
||||
for (auto &ccheat : customCheats) {
|
||||
auto found = std::strstr(check_buffer, ccheat.first.c_str());
|
||||
if (found != nullptr) {
|
||||
ccheat.second->runCheat();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#include "globalincs/globals.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "io/key.h"
|
||||
#include "hud/hudmessage.h"
|
||||
#include "hud/hudsquadmsg.h"
|
||||
#include "scripting/global_hooks.h"
|
||||
#include "scripting/hook_api.h"
|
||||
#include "ship/ship.h"
|
||||
#include "ship/shipfx.h"
|
||||
|
||||
#define CHEAT_BUFFER_LEN 17
|
||||
|
||||
class CustomCheat {
|
||||
public:
|
||||
SCP_string cheatCode;
|
||||
SCP_string cheatMsg;
|
||||
bool requireCheatsEnabled;
|
||||
|
||||
CustomCheat(SCP_string cheat_code, SCP_string cheat_msg, bool require_cheats_enabled) :
|
||||
cheatCode(std::move(cheat_code)),
|
||||
cheatMsg(std::move(cheat_msg)),
|
||||
requireCheatsEnabled(require_cheats_enabled) { }
|
||||
|
||||
virtual ~CustomCheat() = default;
|
||||
|
||||
virtual void runCheat();
|
||||
|
||||
bool canUseCheat();
|
||||
};
|
||||
|
||||
class SpawnShipCheat : public CustomCheat {
|
||||
protected:
|
||||
SCP_string shipClassName;
|
||||
SCP_string shipName;
|
||||
public:
|
||||
|
||||
SpawnShipCheat(SCP_string cheat_code, SCP_string cheat_msg, bool require_cheats_enabled, SCP_string class_name, SCP_string ship_name) : CustomCheat(cheat_code, cheat_msg, require_cheats_enabled),
|
||||
shipClassName(std::move(class_name)),
|
||||
shipName(std::move(ship_name)) { }
|
||||
|
||||
void runCheat() override;
|
||||
};
|
||||
|
||||
static SCP_map<SCP_string, std::unique_ptr<CustomCheat>> customCheats;
|
||||
|
||||
void cheat_table_init();
|
||||
void parse_cheat_table(const char* filename);
|
||||
bool checkForCustomCheats(const char* converted_buffer, int buffer_length);
|
||||
+200
-214
@@ -19,10 +19,8 @@
|
||||
#include "hud/hudconfig.h"
|
||||
#include "io/joy.h"
|
||||
#include "network/multi.h"
|
||||
#include "network/multi_log.h"
|
||||
#include "options/OptionsManager.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "osapi/dialogs.h"
|
||||
#include "parse/sexp.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "sound/openal.h"
|
||||
@@ -44,7 +42,6 @@
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
@@ -156,7 +153,6 @@ typedef struct
|
||||
// Please group them by type, ie graphics, gameplay etc, maximum 20 different types
|
||||
Flag exe_params[] =
|
||||
{
|
||||
//flag launcher text FSO on_flags off_flags category reference URL
|
||||
{ "-nospec", "Disable specular", true, EASY_DEFAULT | EASY_HI_MEM_OFF, EASY_ALL_ON | EASY_HI_MEM_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nospec", },
|
||||
{ "-noglow", "Disable glow maps", true, EASY_DEFAULT | EASY_HI_MEM_OFF, EASY_ALL_ON | EASY_HI_MEM_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noglow", },
|
||||
{ "-noenv", "Disable environment maps", true, EASY_DEFAULT | EASY_HI_MEM_OFF, EASY_ALL_ON | EASY_HI_MEM_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noenv", },
|
||||
@@ -172,12 +168,9 @@ Flag exe_params[] =
|
||||
{ "-fb_thrusters", "Enable Framebuffer Thrusters", true, EASY_ALL_ON, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fb_thrusters", },
|
||||
{ "-no_deferred", "Disable Deferred Lighting", true, EASY_DEFAULT | EASY_HI_MEM_OFF, EASY_ALL_ON | EASY_HI_MEM_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_deferred"},
|
||||
{ "-enable_shadows", "Enable Shadows", true, EASY_ALL_ON | EASY_HI_MEM_ON, EASY_DEFAULT | EASY_HI_MEM_OFF, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-enable_shadows"},
|
||||
{ "-deferred_cockpit", "Enable Deferred Lighting for Cockpits", true, EASY_ALL_ON | EASY_HI_MEM_ON, EASY_DEFAULT | EASY_HI_MEM_OFF, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-deferred_cockpit"},
|
||||
|
||||
//flag launcher text FSO on_flags off_flags category reference URL
|
||||
{ "-no_vsync", "Disable vertical sync", true, 0, EASY_DEFAULT, "Game Speed", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_vsync", },
|
||||
|
||||
//flag launcher text FSO on_flags off_flags category reference URL
|
||||
{ "-fps", "Show frames per second on HUD", false, 0, EASY_DEFAULT, "HUD", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fps", },
|
||||
{ "-dualscanlines", "Add another pair of scanning lines", true, 0, EASY_DEFAULT, "HUD", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-dualscanlines", },
|
||||
{ "-targetinfo", "Enable info next to target", true, 0, EASY_DEFAULT, "HUD", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-targetinfo", },
|
||||
@@ -185,27 +178,24 @@ Flag exe_params[] =
|
||||
{ "-rearm_timer", "Enable rearm/repair completion timer", true, 0, EASY_DEFAULT, "HUD", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-rearm_timer", },
|
||||
{ "-ballistic_gauge", "Enable analog ballistic ammo gauge", true, 0, EASY_DEFAULT, "HUD", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-ballistic_gauge", },
|
||||
|
||||
//flag launcher text FSO on_flags off_flags category reference URL
|
||||
{ "-window", "Run in window", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-window", },
|
||||
{ "-fullscreen_window", "Run in fullscreen window", false, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fullscreen_window", },
|
||||
{ "-stretch_menu", "Stretch interface to fill screen", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-stretch_menu", },
|
||||
{ "-noscalevid", "Disable scale-to-window for movies", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noscalevid", },
|
||||
{ "-nomotiondebris", "Disable motion debris", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nomotiondebris",},
|
||||
{ "-ship_choice_3d", "Use 3D models for ship selection", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-ship_choice_3d", },
|
||||
{ "-weapon_choice_3d", "Use 3D models for weapon selection", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-weapon_choice_3d", },
|
||||
{ "-3dwarp", "Enable 3D warp", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-3dwarp", },
|
||||
{ "-warp_flash", "Enable flash upon warp", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-warp_flash", },
|
||||
{ "-no_ap_interrupt", "Disable interrupting autopilot", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_ap_interrupt", },
|
||||
{ "-no_screenshake", "Disable screen shaking", true, 0, EASY_DEFAULT, "Gameplay", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_screenshake", },
|
||||
|
||||
//flag launcher text FSO on_flags off_flags category reference URL
|
||||
{ "-nosound", "Disable all sound", false, 0, EASY_DEFAULT, "Audio", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nosound", },
|
||||
{ "-nomusic", "Disable music", false, 0, EASY_DEFAULT, "Audio", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nomusic", },
|
||||
{ "-no_enhanced_sound", "Disable enhanced sound", false, 0, EASY_DEFAULT, "Audio", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_enhanced_sound", },
|
||||
|
||||
//flag launcher text FSO on_flags off_flags category reference URL
|
||||
{ "-portable_mode", "Store config in portable location", false, 0, EASY_DEFAULT, "Launcher", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-portable_mode", },
|
||||
{ "-joy_info", "Outputs SDL joystick info", true, 0, EASY_DEFAULT, "Launcher", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-joy_info",},
|
||||
|
||||
//flag launcher text FSO on_flags off_flags category reference URL
|
||||
{ "-standalone", "Run as standalone server", false, 0, EASY_DEFAULT, "Multiplayer", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-standalone", },
|
||||
{ "-startgame", "Skip mainhall and start hosting", false, 0, EASY_DEFAULT, "Multiplayer", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-startgame", },
|
||||
{ "-closed", "Start hosted server as closed", false, 0, EASY_DEFAULT, "Multiplayer", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-closed", },
|
||||
@@ -214,9 +204,7 @@ Flag exe_params[] =
|
||||
{ "-clientdamage", "", false, 0, EASY_DEFAULT, "Multiplayer", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-clientdamage", },
|
||||
{ "-mpnoreturn", "Disable flight deck option", true, 0, EASY_DEFAULT, "Multiplayer", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-mpnoreturn", },
|
||||
{ "-gateway_ip", "Set gateway IP address", false, 0, EASY_DEFAULT, "Multiplayer", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-gateway_ip", },
|
||||
{ "-ingame_join", "Disable in-game joining", true, 0, EASY_DEFAULT, "Multiplayer", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-ingame_join", },
|
||||
|
||||
//flag launcher text FSO on_flags off_flags category reference URL
|
||||
{ "-no_set_gamma", "Disable setting of gamma", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_set_gamma", },
|
||||
{ "-nomovies", "Disable video playback", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nomovies", },
|
||||
{ "-noparseerrors", "Disable parsing errors", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noparseerrors", },
|
||||
@@ -232,18 +220,13 @@ Flag exe_params[] =
|
||||
{ "-set_cpu_affinity", "Sets processor affinity to config value", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-set_cpu_affinity", },
|
||||
{ "-nograb", "Disables mouse grabbing", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nograb", },
|
||||
{ "-noshadercache", "Disables the shader cache", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noshadercache", },
|
||||
{ "-prefer_ipv4", "Prefer IPv4 DNS lookups", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-prefer_ipv4", },
|
||||
{ "-prefer_ipv6", "Prefer IPv6 DNS lookups", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-prefer_ipv6", },
|
||||
{ "-log_multi_packet", "Log multi packet types ", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-log_multi_packet",},
|
||||
#ifdef WIN32
|
||||
{ "-fix_registry", "Use a different registry path", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fix_registry", },
|
||||
#endif
|
||||
|
||||
//flag launcher text FSO on_flags off_flags category reference URL
|
||||
{ "-ingame_join", "Allow in-game joining", true, 0, EASY_DEFAULT, "Experimental", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-ingame_join", },
|
||||
{ "-voicer", "Enable voice recognition", true, 0, EASY_DEFAULT, "Experimental", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-voicer", },
|
||||
|
||||
//flag launcher text FSO on_flags off_flags category reference URL
|
||||
{ "-override_data", "Enable override directory", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-override_data", },
|
||||
{ "-bmpmanusage", "Show how many BMPMAN slots are in use", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-bmpmanusage", },
|
||||
{ "-pos", "Show position of camera", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-pos", },
|
||||
{ "-stats", "Show statistics", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-stats", },
|
||||
@@ -257,22 +240,17 @@ Flag exe_params[] =
|
||||
{ "-output_sexps", "Output SEXPs to sexps.html", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_sexps", },
|
||||
{ "-output_scripting", "Output scripting to scripting.html", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_scripting", },
|
||||
{ "-output_script_json", "Output scripting doc to scripting.json", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_script_json", },
|
||||
{ "-controlconfig_tbl", "Save control presets to table", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-controlconfig_tbl", },
|
||||
{ "-save_render_target", "Save render targets to file", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-save_render_target", },
|
||||
{ "-verify_vps", "Spew VP CRCs to vp_crcs.txt", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-verify_vps", },
|
||||
{ "-reparse_mainhall", "Reparse mainhall.tbl when loading halls", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-reparse_mainhall", },
|
||||
{ "-noninteractive", "Disables interactive dialogs", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noninteractive", },
|
||||
{ "-no_unfocused_pause","Don't pause if the window isn't focused", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_unfocused_pause", },
|
||||
{ "-benchmark_mode", "Puts the game into benchmark mode", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-benchmark_mode", },
|
||||
{ "-profile_frame_time","Profile frame time", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_frame_time", },
|
||||
{ "-profile_frame_time","Profile frame time", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_frame_time", },
|
||||
{ "-profile_write_file", "Write profiling information to file", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_write_file", },
|
||||
{ "-json_profiling", "Generate JSON profiling output", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-json_profiling", },
|
||||
{ "-debug_window", "Enable the debug window", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-debug_window", },
|
||||
{ "-gr_debug", "Output graphics debug information", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-gr_debug", },
|
||||
{ "-stdout_log", "Output log file to stdout", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-stdout_log", },
|
||||
{ "-slow_frames_ok", "Don't adjust timestamps for slow frames", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-slow_frames_ok", },
|
||||
{ "-imgui_debug", "Show imgui debug/demo window in the lab", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-imgui_debug", },
|
||||
{ "-luadev", "Make lua errors non-fatal", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-luadev", },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -296,6 +274,7 @@ cmdline_parm gameclosed_arg("-closed", NULL, AT_NONE); // Cmdline_closed_game
|
||||
cmdline_parm gamerestricted_arg("-restricted", NULL, AT_NONE); // Cmdline_restricted_game
|
||||
cmdline_parm port_arg("-port", "Multiplayer network port", AT_INT);
|
||||
cmdline_parm multilog_arg("-multilog", NULL, AT_NONE); // Cmdline_multi_log
|
||||
cmdline_parm client_dodamage("-clientdamage", NULL, AT_NONE); // Cmdline_client_dodamage
|
||||
cmdline_parm pof_spew("-pofspew", NULL, AT_NONE); // Cmdline_spew_pof_info
|
||||
cmdline_parm weapon_spew("-weaponspew", nullptr, AT_STRING); // Cmdline_spew_weapon_stats
|
||||
cmdline_parm mouse_coords("-coords", NULL, AT_NONE); // Cmdline_mouse_coords
|
||||
@@ -308,6 +287,7 @@ char *Cmdline_game_password = NULL;
|
||||
char *Cmdline_rank_above = NULL;
|
||||
char *Cmdline_rank_below = NULL;
|
||||
int Cmdline_cd_check = 1;
|
||||
int Cmdline_client_dodamage = 0;
|
||||
int Cmdline_closed_game = 0;
|
||||
int Cmdline_freespace_no_music = 0;
|
||||
int Cmdline_freespace_no_sound = 0;
|
||||
@@ -328,17 +308,17 @@ int Cmdline_use_last_pilot = 0;
|
||||
|
||||
// Graphics related
|
||||
cmdline_parm fov_arg("-fov", "Vertical field-of-view factor", AT_FLOAT); // Cmdline_fov -- comand line FOV -Bobboau
|
||||
cmdline_parm fov_cockpit_arg("-fov_cockpit", "Vertical field-of-view factor for Cockpits", AT_FLOAT);
|
||||
cmdline_parm clip_dist_arg("-clipdist", "Changes the distance from the viewpoint for the near-clipping plane", AT_FLOAT); // Cmdline_clip_dist
|
||||
cmdline_parm ambient_power_arg("-ambient", "Multiplies the brightness of all ambient light", AT_FLOAT);
|
||||
cmdline_parm light_power_arg("-light", "Multiplies the brightness of all light", AT_FLOAT);
|
||||
cmdline_parm emissive_power_arg("-emissive", "Multiplies the brightness of all ambient light", AT_FLOAT);
|
||||
cmdline_parm emissive_arg("-emissive_light", "Enable emissive light from ships", AT_NONE); // semi-deprecated but still functional
|
||||
cmdline_parm spec_static_arg("-spec_static", "Adjusts suns contribution to specular highlights", AT_FLOAT);
|
||||
cmdline_parm spec_point_arg("-spec_point", "Adjusts laser weapons contribution to specular highlights", AT_FLOAT);
|
||||
cmdline_parm spec_tube_arg("-spec_tube", "Adjusts beam weapons contribution to specular highlights", AT_FLOAT);
|
||||
cmdline_parm ambient_factor_arg("-ambient_factor", "Adjusts ambient light applied to all parts of a ship", AT_INT); // Cmdline_ambient_factor
|
||||
cmdline_parm env("-noenv", NULL, AT_NONE); // Cmdline_env
|
||||
cmdline_parm glow_arg("-noglow", NULL, AT_NONE); // Cmdline_glow -- use Bobs glow code
|
||||
cmdline_parm nomotiondebris_arg("-nomotiondebris", NULL, AT_NONE); // Cmdline_nomotiondebris -- Removes those ugly floating rocks -C
|
||||
cmdline_parm noscalevid_arg("-noscalevid", NULL, AT_NONE); // Cmdline_noscalevid -- disable video scaling that fits to window
|
||||
cmdline_parm spec_arg("-nospec", NULL, AT_NONE); // Cmdline_spec --
|
||||
cmdline_parm emissive_arg("-emissive_light", "Enable emissive light from ships", AT_NONE); // Cmdline_no_emissive -- don't use emissive light in OGL
|
||||
cmdline_parm normal_arg("-nonormal", NULL, AT_NONE); // Cmdline_normal -- disable normal mapping
|
||||
cmdline_parm height_arg("-noheight", NULL, AT_NONE); // Cmdline_height -- enable support for parallax mapping
|
||||
cmdline_parm enable_3d_shockwave_arg("-3dshockwave", NULL, AT_NONE);
|
||||
@@ -357,13 +337,10 @@ cmdline_parm flightshaftsoff_arg("-nolightshafts", NULL, AT_NONE);
|
||||
cmdline_parm shadow_quality_arg("-shadow_quality", NULL, AT_INT);
|
||||
cmdline_parm enable_shadows_arg("-enable_shadows", NULL, AT_NONE);
|
||||
cmdline_parm no_deferred_lighting_arg("-no_deferred", NULL, AT_NONE); // Cmdline_no_deferred
|
||||
cmdline_parm deferred_lighting_cockpit_arg("-deferred_cockpit", nullptr, AT_NONE);
|
||||
cmdline_parm anisotropy_level_arg("-anisotropic_filter", NULL, AT_INT);
|
||||
|
||||
float Cmdline_clip_dist = Default_min_draw_distance;
|
||||
float Cmdline_ambient_power = 1.0f;
|
||||
float Cmdline_emissive_power = 0.0f;
|
||||
float Cmdline_light_power = 1.0f;
|
||||
int Cmdline_ambient_factor = 128;
|
||||
int Cmdline_env = 1;
|
||||
int Cmdline_mipmap = 0;
|
||||
int Cmdline_glow = 1;
|
||||
@@ -377,7 +354,6 @@ int Cmdline_softparticles = 0;
|
||||
int Cmdline_bloom_intensity = 25;
|
||||
bool Cmdline_force_lightshaft_off = false;
|
||||
int Cmdline_no_deferred_lighting = 0;
|
||||
bool Cmdline_deferred_lighting_cockpit = false;
|
||||
int Cmdline_aniso_level = 0;
|
||||
|
||||
// Game Speed related
|
||||
@@ -389,31 +365,33 @@ int Cmdline_no_vsync = 0;
|
||||
|
||||
// HUD related
|
||||
cmdline_parm ballistic_gauge("-ballistic_gauge", NULL, AT_NONE); // Cmdline_ballistic_gauge
|
||||
cmdline_parm dualscanlines_arg("-dualscanlines", NULL, AT_NONE); // Cmdline_dualscanlines -- Change to phreaks options including new targeting code; semi-deprecated but still functional
|
||||
cmdline_parm dualscanlines_arg("-dualscanlines", NULL, AT_NONE); // Cmdline_dualscanlines -- Change to phreaks options including new targeting code
|
||||
cmdline_parm orb_radar("-orbradar", NULL, AT_NONE); // Cmdline_orb_radar
|
||||
cmdline_parm rearm_timer_arg("-rearm_timer", NULL, AT_NONE); // Cmdline_rearm_timer; semi-deprecated but still functional-Mjn
|
||||
cmdline_parm targetinfo_arg("-targetinfo", NULL, AT_NONE); // Cmdline_targetinfo -- Adds ship name/class to right of target box -C; semi-deprecated but still functional-Mjn
|
||||
cmdline_parm rearm_timer_arg("-rearm_timer", NULL, AT_NONE); // Cmdline_rearm_timer
|
||||
cmdline_parm targetinfo_arg("-targetinfo", NULL, AT_NONE); // Cmdline_targetinfo -- Adds ship name/class to right of target box -C
|
||||
|
||||
int Cmdline_ballistic_gauge = 0; // WMCoolmon's gauge thingy
|
||||
int Cmdline_dualscanlines = 0;
|
||||
int Cmdline_orb_radar = 0;
|
||||
int Cmdline_rearm_timer = 0;
|
||||
int Cmdline_targetinfo = 0;
|
||||
|
||||
// Gameplay related
|
||||
cmdline_parm use_3dwarp("-3dwarp", nullptr, AT_NONE); // Is now Fireball_use_3d_warp
|
||||
cmdline_parm use_warp_flash("-warp_flash", nullptr, AT_NONE); // Cmdline_warp_flash
|
||||
cmdline_parm allow_autpilot_interrupt("-no_ap_interrupt", nullptr, AT_NONE);
|
||||
cmdline_parm stretch_menu("-stretch_menu", nullptr, AT_NONE); // Cmdline_stretch_menu
|
||||
cmdline_parm use_3dwarp("-3dwarp", NULL, AT_NONE); // Cmdline_3dwarp
|
||||
cmdline_parm ship_choice_3d_arg("-ship_choice_3d", NULL, AT_NONE); // Cmdline_ship_choice_3d
|
||||
cmdline_parm weapon_choice_3d_arg("-weapon_choice_3d", NULL, AT_NONE); // Cmdline_weapon_choice_3d
|
||||
cmdline_parm use_warp_flash("-warp_flash", NULL, AT_NONE); // Cmdline_warp_flash
|
||||
cmdline_parm allow_autpilot_interrupt("-no_ap_interrupt", NULL, AT_NONE);
|
||||
cmdline_parm stretch_menu("-stretch_menu", NULL, AT_NONE); // Cmdline_stretch_menu
|
||||
cmdline_parm no_screenshake("-no_screenshake", nullptr, AT_NONE); // Cmdline_no_screenshake
|
||||
cmdline_parm deadzone("-deadzone",
|
||||
"Sets the joystick deadzone. Integer value from 0 to 100 as a percentage of the joystick's range (100% would make the stick do nothing). Disables deadzone slider in the in-game Options menu.", AT_INT); //Cmdline_deadzone
|
||||
|
||||
int Cmdline_3dwarp = 0;
|
||||
int Cmdline_ship_choice_3d = 0;
|
||||
int Cmdline_weapon_choice_3d = 0;
|
||||
int Cmdline_warp_flash = 0;
|
||||
int Cmdline_autopilot_interruptable = 1;
|
||||
int Cmdline_stretch_menu = 0;
|
||||
int Cmdline_no_screenshake = 0;
|
||||
int Cmdline_deadzone = -1;
|
||||
|
||||
// Audio related
|
||||
cmdline_parm voice_recognition_arg("-voicer", NULL, AT_NONE); // Cmdline_voice_recognition
|
||||
@@ -433,15 +411,14 @@ cmdline_parm mpnoreturn_arg("-mpnoreturn", NULL, AT_NONE); // Cmdline_mpnoreturn
|
||||
cmdline_parm objupd_arg("-cap_object_update", "Multiplayer object update cap (0-3)", AT_INT);
|
||||
cmdline_parm gateway_ip_arg("-gateway_ip", "Set gateway IP address", AT_STRING);
|
||||
|
||||
char *Cmdline_almission = nullptr; //DTP for autoload multi mission.
|
||||
int Cmdline_ingamejoin = 1;
|
||||
char *Cmdline_almission = NULL; //DTP for autoload multi mission.
|
||||
int Cmdline_ingamejoin = 0;
|
||||
int Cmdline_mpnoreturn = 0;
|
||||
int Cmdline_objupd = 3; // client object updates on LAN by default
|
||||
char *Cmdline_gateway_ip = nullptr;
|
||||
|
||||
// Launcher related options
|
||||
cmdline_parm portable_mode("-portable_mode", NULL, AT_NONE);
|
||||
cmdline_parm joy_info("-joy_info", "Outputs SDL joystick info", AT_NONE);
|
||||
|
||||
bool Cmdline_portable_mode = false;
|
||||
|
||||
@@ -461,9 +438,6 @@ cmdline_parm no_geo_sdr_effects("-no_geo_effects", NULL, AT_NONE);
|
||||
cmdline_parm set_cpu_affinity("-set_cpu_affinity", NULL, AT_NONE);
|
||||
cmdline_parm nograb_arg("-nograb", NULL, AT_NONE);
|
||||
cmdline_parm noshadercache_arg("-noshadercache", NULL, AT_NONE);
|
||||
cmdline_parm prefer_ipv4_arg("-prefer_ipv4", nullptr, AT_NONE);
|
||||
cmdline_parm prefer_ipv6_arg("-prefer_ipv6", nullptr, AT_NONE);
|
||||
cmdline_parm log_multi_packet_arg("-log_multi_packet", nullptr, AT_NONE);
|
||||
#ifdef WIN32
|
||||
cmdline_parm fix_registry("-fix_registry", NULL, AT_NONE);
|
||||
#endif
|
||||
@@ -482,14 +456,9 @@ bool Cmdline_no_geo_sdr_effects = false;
|
||||
bool Cmdline_set_cpu_affinity = false;
|
||||
bool Cmdline_nograb = false;
|
||||
bool Cmdline_noshadercache = false;
|
||||
bool Cmdline_prefer_ipv4 = false;
|
||||
bool Cmdline_prefer_ipv6 = false;
|
||||
bool Cmdline_dump_packet_type = false;
|
||||
#ifdef WIN32
|
||||
bool Cmdline_alternate_registry_path = false;
|
||||
#endif
|
||||
uint Cmdline_rng_seed = 0;
|
||||
bool Cmdline_reuse_rng_seed = false;
|
||||
|
||||
// Developer/Testing related
|
||||
cmdline_parm start_mission_arg("-start_mission", "Skip mainhall and run this mission", AT_STRING); // Cmdline_start_mission
|
||||
@@ -512,7 +481,6 @@ cmdline_parm reparse_mainhall_arg("-reparse_mainhall", NULL, AT_NONE); //Cmdline
|
||||
cmdline_parm frame_profile_write_file("-profile_write_file", NULL, AT_NONE); // Cmdline_profile_write_file
|
||||
cmdline_parm no_unfocused_pause_arg("-no_unfocused_pause", NULL, AT_NONE); //Cmdline_no_unfocus_pause
|
||||
cmdline_parm benchmark_mode_arg("-benchmark_mode", NULL, AT_NONE); //Cmdline_benchmark_mode
|
||||
cmdline_parm pilot_arg("-pilot", nullptr, AT_STRING); //Cmdline_pilot
|
||||
cmdline_parm noninteractive_arg("-noninteractive", NULL, AT_NONE); //Cmdline_noninteractive
|
||||
cmdline_parm json_profiling("-json_profiling", NULL, AT_NONE); //Cmdline_json_profiling
|
||||
cmdline_parm show_video_info("-show_video_info", NULL, AT_NONE); //Cmdline_show_video_info
|
||||
@@ -520,11 +488,7 @@ cmdline_parm frame_profile_arg("-profile_frame_time", NULL, AT_NONE); //Cmdline_
|
||||
cmdline_parm debug_window_arg("-debug_window", NULL, AT_NONE); // Cmdline_debug_window
|
||||
cmdline_parm graphics_debug_output_arg("-gr_debug", nullptr, AT_NONE); // Cmdline_graphics_debug_output
|
||||
cmdline_parm log_to_stdout_arg("-stdout_log", nullptr, AT_NONE); // Cmdline_log_to_stdout
|
||||
cmdline_parm slow_frames_ok_arg("-slow_frames_ok", nullptr, AT_NONE); // Cmdline_slow_frames_ok
|
||||
cmdline_parm fixed_seed_rand("-seed", nullptr, AT_INT); // Cmdline_rng_seed,Cmdline_reuse_rng_seed;
|
||||
cmdline_parm luadev_arg("-luadev", "Make lua errors non-fatal", AT_NONE); // Cmdline_lua_devmode
|
||||
cmdline_parm override_arg("-override_data", "Enable override directory", AT_NONE); // Cmdline_override_data
|
||||
cmdline_parm imgui_debug_arg("-imgui_debug", nullptr, AT_NONE);
|
||||
|
||||
|
||||
char *Cmdline_start_mission = NULL;
|
||||
int Cmdline_dis_collisions = 0;
|
||||
@@ -548,7 +512,6 @@ int Cmdline_reparse_mainhall = 0;
|
||||
bool Cmdline_profile_write_file = false;
|
||||
bool Cmdline_no_unfocus_pause = false;
|
||||
bool Cmdline_benchmark_mode = false;
|
||||
const char *Cmdline_pilot = nullptr;
|
||||
bool Cmdline_noninteractive = false;
|
||||
bool Cmdline_json_profiling = false;
|
||||
bool Cmdline_frame_profile = false;
|
||||
@@ -556,17 +519,12 @@ bool Cmdline_show_video_info = false;
|
||||
bool Cmdline_debug_window = false;
|
||||
bool Cmdline_graphics_debug_output = false;
|
||||
bool Cmdline_log_to_stdout = false;
|
||||
bool Cmdline_slow_frames_ok = false;
|
||||
bool Cmdline_lua_devmode = false;
|
||||
bool Cmdline_override_data = false;
|
||||
bool Cmdline_show_imgui_debug = false;
|
||||
|
||||
// Other
|
||||
cmdline_parm get_flags_arg(GET_FLAGS_STRING, "Output the launcher flags file", AT_STRING);
|
||||
cmdline_parm output_sexp_arg("-output_sexps", NULL, AT_NONE); //WMC - outputs all SEXPs to sexps.html
|
||||
cmdline_parm output_scripting_arg("-output_scripting", NULL, AT_NONE); //WMC
|
||||
cmdline_parm output_script_json_arg("-output_script_json", nullptr, AT_NONE); // m!m
|
||||
cmdline_parm generate_controlconfig_arg("-controlconfig_tbl", nullptr, AT_NONE);
|
||||
|
||||
// Deprecated flags - CommanderDJ
|
||||
cmdline_parm deprecated_spec_arg("-spec", "Deprecated", AT_NONE);
|
||||
@@ -582,12 +540,18 @@ cmdline_parm deprecated_missile_lighting_arg("-missile_lighting", "Deprecated",
|
||||
cmdline_parm deprecated_cache_bitmaps_arg("-cache_bitmaps", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_no_emissive_arg("-no_emissive_light", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_postprocess_arg("-post_process", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_spec_static_arg("-spec_static", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_spec_point_arg("-spec_point", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_spec_tube_arg("-spec_tube", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_ambient_factor_arg("-ambient_factor", "Deprecated", AT_NONE); //
|
||||
cmdline_parm deprecated_ship_choice_3d_arg("-ship_choice_3d", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_weapon_choice_3d_arg("-weapon_choice_3d", "Deprecated", AT_NONE);
|
||||
|
||||
int Cmdline_deprecated_spec = 0;
|
||||
int Cmdline_deprecated_glow = 0;
|
||||
int Cmdline_deprecated_normal = 0;
|
||||
int Cmdline_deprecated_env = 0;
|
||||
int Cmdline_deprecated_tbp = 0;
|
||||
int Cmdline_deprecated_jpgtga = 0;
|
||||
int Cmdline_deprecated_nohtl = 0;
|
||||
bool Cmdline_deprecated_brief_lighting = 0;
|
||||
bool Cmdline_deprecated_missile_lighting = false;
|
||||
bool Cmdline_deprecated_cache_bitmaps = false;
|
||||
bool Cmdline_deprecated_postprocess = false;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// NOTE: this assumes that os_init() has already been called but isn't a fatal error if it hasn't
|
||||
@@ -614,41 +578,61 @@ void cmdline_debug_print_cmdline()
|
||||
mprintf(("\n"));
|
||||
|
||||
//Print log messages about any deprecated flags we found - CommanderDJ
|
||||
//Do it programmatically, rather than enumerating them by hand - MageKing17
|
||||
for (parmp = GET_FIRST(&Parm_list); parmp != END_OF_LIST(&Parm_list); parmp = GET_NEXT(parmp)) {
|
||||
if (parmp->name_found && !stricmp("deprecated", parmp->help)) {
|
||||
mprintf(("Deprecated flag '%s' found. Please remove from your cmdline.\n", parmp->name));
|
||||
}
|
||||
if(Cmdline_deprecated_spec == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-spec' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_glow == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-glow' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_normal == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-normal' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_env == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-env' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_tbp == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-tbp' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_jpgtga == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-jpgtga' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_nohtl == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-nohtl' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_brief_lighting == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-brief_lighting' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if (Cmdline_deprecated_missile_lighting)
|
||||
{
|
||||
mprintf(("Deprecated flag '-missile_lighting' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if (Cmdline_deprecated_cache_bitmaps) {
|
||||
mprintf(("Deprecated flag '-cache_bitmaps' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if (Cmdline_deprecated_postprocess) {
|
||||
mprintf(("Deprecated flag '-post_process' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// prints simple cmdline to multi.log
|
||||
void cmdline_print_cmdline_multi()
|
||||
{
|
||||
cmdline_parm *parmp;
|
||||
int found = 0;
|
||||
std::ostringstream cmdline;
|
||||
|
||||
for (parmp = GET_FIRST(&Parm_list); parmp !=END_OF_LIST(&Parm_list); parmp = GET_NEXT(parmp) ) {
|
||||
if ( parmp->name_found ) {
|
||||
cmdline << " " << parmp->name;
|
||||
|
||||
if (parmp->args) {
|
||||
cmdline << " " << parmp->args;
|
||||
}
|
||||
|
||||
found++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !found ) {
|
||||
cmdline << " <none>";
|
||||
}
|
||||
|
||||
ml_printf("Command line:%s", cmdline.str().c_str());
|
||||
}
|
||||
|
||||
// Return true if this character is an extra char (white space and quotes)
|
||||
int is_extra_space(char ch)
|
||||
{
|
||||
@@ -828,7 +812,7 @@ void os_validate_parms(int argc, char *argv[])
|
||||
if (!stricmp(token, "-help") || !stricmp(token, "--help") || !stricmp(token, "-h") || !stricmp(token, "-?")) {
|
||||
printf("FreeSpace 2 Open, version %s\n", FS_VERSION_FULL);
|
||||
printf("Website: http://scp.indiegames.us\n");
|
||||
printf("Github (bug reporting): https://github.com/scp-fs2open/fs2open.github.com/issues\n\n");
|
||||
printf("Mantis (bug reporting): http://scp.indiegames.us/mantis/\n\n");
|
||||
printf("Usage: fs2_open [options]\n");
|
||||
|
||||
// not the prettiest thing but the job gets done
|
||||
@@ -1335,7 +1319,6 @@ static json_t* json_get_v1() {
|
||||
json_array_append_new(caps_array, json_string("No D3D"));
|
||||
json_array_append_new(caps_array, json_string("New Sound"));
|
||||
json_array_append_new(caps_array, json_string("SDL"));
|
||||
json_array_append_new(caps_array, json_string("Multijoy"));
|
||||
|
||||
json_object_set_new(root, "caps", caps_array);
|
||||
}
|
||||
@@ -1427,7 +1410,24 @@ static json_t* json_get_v1() {
|
||||
json_object_set_new(root, "openal", openal_obj);
|
||||
}
|
||||
{
|
||||
auto joystick_array = io::joystick::getJsonArray();
|
||||
auto joystick_array = json_array();
|
||||
|
||||
auto joysticks = io::joystick::getJoystickInformations();
|
||||
for (auto& info : joysticks) {
|
||||
auto joystick_obj = json_object();
|
||||
|
||||
json_object_set_new(joystick_obj, "name", json_string(info.name.c_str()));
|
||||
json_object_set_new(joystick_obj, "guid", json_string(info.guid.c_str()));
|
||||
|
||||
json_object_set_new(joystick_obj, "num_axes", json_integer(info.num_axes));
|
||||
json_object_set_new(joystick_obj, "num_balls", json_integer(info.num_balls));
|
||||
json_object_set_new(joystick_obj, "num_buttons", json_integer(info.num_buttons));
|
||||
json_object_set_new(joystick_obj, "num_hats", json_integer(info.num_hats));
|
||||
|
||||
json_object_set_new(joystick_obj, "is_haptic", json_boolean(info.is_haptic));
|
||||
|
||||
json_array_append_new(joystick_array, joystick_obj);
|
||||
}
|
||||
|
||||
json_object_set_new(root, "joysticks", joystick_array);
|
||||
}
|
||||
@@ -1530,11 +1530,6 @@ bool SetCmdlineParams()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (joy_info.found()) {
|
||||
io::joystick::printJoyJSON();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (no_fpscap.found())
|
||||
{
|
||||
Cmdline_NoFPSCap = 1;
|
||||
@@ -1566,9 +1561,6 @@ bool SetCmdlineParams()
|
||||
// is this a standalone server??
|
||||
if (standalone_arg.found()) {
|
||||
Is_standalone = 1;
|
||||
|
||||
// also enable non-interactive by default here
|
||||
Cmdline_noninteractive = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1627,7 +1619,7 @@ bool SetCmdlineParams()
|
||||
Cmdline_game_name = gamename_arg.str();
|
||||
|
||||
// be sure that this string fits in our limits
|
||||
if ( strlen(Cmdline_game_name) >= MAX_GAMENAME_LEN ) {
|
||||
if ( strlen(Cmdline_game_name) > MAX_GAMENAME_LEN ) {
|
||||
Cmdline_game_name[MAX_GAMENAME_LEN-1] = '\0';
|
||||
}
|
||||
}
|
||||
@@ -1637,9 +1629,8 @@ bool SetCmdlineParams()
|
||||
Cmdline_game_password = gamepassword_arg.str();
|
||||
|
||||
// be sure that this string fits in our limits
|
||||
if ( strlen(Cmdline_game_password) >= MAX_PASSWD_LEN ) {
|
||||
ReleaseWarning(LOCATION, "Multi game password is longer than max of %d charaters and will be trimmed to fit!", MAX_PASSWD_LEN-1);
|
||||
Cmdline_game_password[MAX_PASSWD_LEN-1] = '\0';
|
||||
if ( strlen(Cmdline_game_name) > MAX_PASSWD_LEN ) {
|
||||
Cmdline_game_name[MAX_PASSWD_LEN-1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1672,6 +1663,12 @@ bool SetCmdlineParams()
|
||||
Cmdline_multi_log = 1;
|
||||
}
|
||||
|
||||
|
||||
// maybe use old-school client damage
|
||||
if(client_dodamage.found()){
|
||||
Cmdline_client_dodamage = 1;
|
||||
}
|
||||
|
||||
// spew pof info
|
||||
if(pof_spew.found()){
|
||||
Cmdline_spew_pof_info = 1;
|
||||
@@ -1724,7 +1721,7 @@ bool SetCmdlineParams()
|
||||
sprintf(override, "{\"width\":%d,\"height\":%d}", width, height);
|
||||
options::OptionsManager::instance()->setOverride("Graphics.Resolution", override);
|
||||
} else {
|
||||
mprintf(("Failed to parse -res parameter \"%s\". Must be in format \"<width>x<height>\".\n", Cmdline_res));
|
||||
mprintf(("Failed to parse -res parameter \"%s\". Must be in format \"<width>x<height>\".", Cmdline_res));
|
||||
}
|
||||
}
|
||||
if(center_res_arg.found()){
|
||||
@@ -1742,7 +1739,7 @@ bool SetCmdlineParams()
|
||||
|
||||
if(targetinfo_arg.found())
|
||||
{
|
||||
Extra_target_info = true;
|
||||
Cmdline_targetinfo = 1;
|
||||
}
|
||||
|
||||
if(nomovies_arg.found() ) {
|
||||
@@ -1825,16 +1822,6 @@ bool SetCmdlineParams()
|
||||
}
|
||||
}
|
||||
|
||||
if ( fov_cockpit_arg.found() ) {
|
||||
auto val = fov_cockpit_arg.get_float();
|
||||
if (val > 0.1) {
|
||||
COCKPIT_ZOOM_DEFAULT = val;
|
||||
}
|
||||
else {
|
||||
COCKPIT_ZOOM_DEFAULT = VIEWER_ZOOM_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
if( clip_dist_arg.found() ) {
|
||||
Min_draw_distance = Cmdline_clip_dist = clip_dist_arg.get_float();
|
||||
}
|
||||
@@ -1849,7 +1836,7 @@ bool SetCmdlineParams()
|
||||
}
|
||||
|
||||
if ( use_warp_flash.found() ) {
|
||||
Fireball_warp_flash = true;
|
||||
Cmdline_warp_flash = 1;
|
||||
}
|
||||
|
||||
if ( allow_autpilot_interrupt.found() ) {
|
||||
@@ -1860,28 +1847,25 @@ bool SetCmdlineParams()
|
||||
Cmdline_no_screenshake = 1;
|
||||
}
|
||||
|
||||
if ( deadzone.found() ) {
|
||||
Cmdline_deadzone = deadzone.get_int();
|
||||
}
|
||||
|
||||
if ( stretch_menu.found() ) {
|
||||
Cmdline_stretch_menu = 1;
|
||||
}
|
||||
// new lighting lines
|
||||
if ( ambient_power_arg.found() )
|
||||
Cmdline_ambient_power = ambient_power_arg.get_float();
|
||||
|
||||
if (emissive_power_arg.found() && emissive_power_arg.has_param()) {
|
||||
Cmdline_emissive_power = emissive_power_arg.get_float();
|
||||
} else if (emissive_arg.found() || emissive_power_arg.found()) {
|
||||
// for legacy support no argument param defaults to the old emissive value
|
||||
Cmdline_emissive_power = 0.30f;
|
||||
// specular comand lines
|
||||
if ( spec_point_arg.found() ) {
|
||||
static_point_factor = spec_point_arg.get_float();
|
||||
}
|
||||
|
||||
if (light_power_arg.found())
|
||||
Cmdline_light_power = light_power_arg.get_float();
|
||||
if ( spec_static_arg.found() ) {
|
||||
static_light_factor = spec_static_arg.get_float();
|
||||
}
|
||||
|
||||
if (spec_arg.found()) {
|
||||
if ( spec_tube_arg.found() ) {
|
||||
static_tube_factor = spec_tube_arg.get_float();
|
||||
}
|
||||
|
||||
if ( spec_arg.found() )
|
||||
{
|
||||
Cmdline_spec = 0;
|
||||
}
|
||||
|
||||
@@ -1936,13 +1920,22 @@ bool SetCmdlineParams()
|
||||
if ( glow_arg.found() )
|
||||
Cmdline_glow = 0;
|
||||
|
||||
if ( ship_choice_3d_arg.found() )
|
||||
Cmdline_ship_choice_3d = 1;
|
||||
|
||||
if ( weapon_choice_3d_arg.found() )
|
||||
Cmdline_weapon_choice_3d = 1;
|
||||
|
||||
if (ingamejoin_arg.found() )
|
||||
Cmdline_ingamejoin = 0;
|
||||
Cmdline_ingamejoin = 1;
|
||||
|
||||
if ( start_mission_arg.found() ) {
|
||||
Cmdline_start_mission = start_mission_arg.str();
|
||||
}
|
||||
|
||||
if ( ambient_factor_arg.found() )
|
||||
Cmdline_ambient_factor = ambient_factor_arg.get_int();
|
||||
|
||||
if ( output_scripting_arg.found() )
|
||||
Output_scripting_meta = true;
|
||||
|
||||
@@ -1953,9 +1946,6 @@ bool SetCmdlineParams()
|
||||
Cmdline_output_sexp_info = true;
|
||||
}
|
||||
|
||||
if (generate_controlconfig_arg.found())
|
||||
Generate_controlconfig_table = true;
|
||||
|
||||
if ( no_pbo_arg.found() )
|
||||
{
|
||||
Cmdline_no_pbo = 1;
|
||||
@@ -2025,6 +2015,10 @@ bool SetCmdlineParams()
|
||||
Cmdline_no_fbo = 1;
|
||||
}
|
||||
|
||||
if ( emissive_arg.found() ) {
|
||||
Cmdline_emissive = 1;
|
||||
}
|
||||
|
||||
if ( rearm_timer_arg.found() )
|
||||
Cmdline_rearm_timer = 1;
|
||||
|
||||
@@ -2084,17 +2078,11 @@ bool SetCmdlineParams()
|
||||
|
||||
if( enable_shadows_arg.found() )
|
||||
{
|
||||
// if only using `enable_shadows` then default quality level can be overriden in mod_settings.tbl --wookieejedi
|
||||
Shadow_quality_uses_mod_option = true;
|
||||
|
||||
Shadow_quality = ShadowQuality::Medium;
|
||||
}
|
||||
|
||||
if( shadow_quality_arg.found() )
|
||||
{
|
||||
// set that we are not using default shadow quality level --wookieejedi
|
||||
Shadow_quality_uses_mod_option = false;
|
||||
|
||||
switch (shadow_quality_arg.get_int()) {
|
||||
case 0:
|
||||
Shadow_quality = ShadowQuality::Disabled;
|
||||
@@ -2123,11 +2111,6 @@ bool SetCmdlineParams()
|
||||
Cmdline_no_deferred_lighting = 1;
|
||||
}
|
||||
|
||||
if( deferred_lighting_cockpit_arg.found() )
|
||||
{
|
||||
Cmdline_deferred_lighting_cockpit = true;
|
||||
}
|
||||
|
||||
if (anisotropy_level_arg.found())
|
||||
{
|
||||
Cmdline_aniso_level = anisotropy_level_arg.get_int();
|
||||
@@ -2148,11 +2131,6 @@ bool SetCmdlineParams()
|
||||
Cmdline_benchmark_mode = true;
|
||||
}
|
||||
|
||||
if (pilot_arg.found())
|
||||
{
|
||||
Cmdline_pilot = pilot_arg.str();
|
||||
}
|
||||
|
||||
if (noninteractive_arg.found())
|
||||
{
|
||||
Cmdline_noninteractive = true;
|
||||
@@ -2180,31 +2158,69 @@ bool SetCmdlineParams()
|
||||
Cmdline_log_to_stdout = true;
|
||||
}
|
||||
|
||||
if (slow_frames_ok_arg.found()) {
|
||||
Cmdline_slow_frames_ok = true;
|
||||
}
|
||||
if ( luadev_arg.found()) {
|
||||
Cmdline_lua_devmode = true;
|
||||
}
|
||||
|
||||
if ( override_arg.found()) {
|
||||
Cmdline_override_data = true;
|
||||
}
|
||||
|
||||
if (imgui_debug_arg.found()) {
|
||||
Cmdline_show_imgui_debug = true;
|
||||
}
|
||||
|
||||
if (show_video_info.found())
|
||||
{
|
||||
Cmdline_show_video_info = true;
|
||||
}
|
||||
|
||||
//Deprecated flags - CommanderDJ
|
||||
if( deprecated_spec_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_spec = 1;
|
||||
}
|
||||
|
||||
if( deprecated_glow_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_glow = 1;
|
||||
}
|
||||
|
||||
if( deprecated_normal_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_normal = 1;
|
||||
}
|
||||
|
||||
if( deprecated_env_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_env = 1;
|
||||
}
|
||||
|
||||
if( deprecated_tbp_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_tbp = 1;
|
||||
}
|
||||
|
||||
if( deprecated_jpgtga_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_jpgtga = 1;
|
||||
}
|
||||
|
||||
if ( deprecated_htl_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_nohtl = 1;
|
||||
}
|
||||
|
||||
if ( deprecated_brieflighting_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_brief_lighting = 1;
|
||||
}
|
||||
|
||||
if (deprecated_missile_lighting_arg.found())
|
||||
{
|
||||
Cmdline_deprecated_missile_lighting = true;
|
||||
}
|
||||
|
||||
if (deprecated_cache_bitmaps_arg.found()) {
|
||||
Cmdline_deprecated_cache_bitmaps = true;
|
||||
}
|
||||
|
||||
if (deprecated_no_emissive_arg.found()) {
|
||||
Cmdline_emissive = 0;
|
||||
}
|
||||
|
||||
if (deprecated_postprocess_arg.found()) {
|
||||
Cmdline_deprecated_postprocess = true;
|
||||
}
|
||||
|
||||
if (deprecated_fxaa_arg.found() ) {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
|
||||
|
||||
@@ -2243,37 +2259,7 @@ bool SetCmdlineParams()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prefer_ipv4_arg.found()) {
|
||||
Cmdline_prefer_ipv4 = true;
|
||||
}
|
||||
|
||||
if (prefer_ipv6_arg.found()) {
|
||||
Cmdline_prefer_ipv6 = true;
|
||||
|
||||
// Rule 2: Not both
|
||||
if (Cmdline_prefer_ipv4) {
|
||||
ReleaseWarning(LOCATION, "Cannot set preference for both IPv4 and IPv6! Reverting to default behavior...\n");
|
||||
|
||||
Cmdline_prefer_ipv4 = false;
|
||||
Cmdline_prefer_ipv6 = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (log_multi_packet_arg.found()) {
|
||||
Cmdline_dump_packet_type = true;
|
||||
}
|
||||
|
||||
if (fixed_seed_rand.found()) {
|
||||
Cmdline_rng_seed = abs(fixed_seed_rand.get_int());
|
||||
if (Cmdline_rng_seed>0) {
|
||||
Cmdline_reuse_rng_seed = true;
|
||||
}
|
||||
else {
|
||||
Warning(LOCATION,"-seed must be an integer greater than 0. Invalid input seed will be disregarded.");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+8
-13
@@ -30,6 +30,7 @@ extern char *Cmdline_game_password;
|
||||
extern char *Cmdline_rank_above;
|
||||
extern char *Cmdline_rank_below;
|
||||
extern int Cmdline_cd_check;
|
||||
extern int Cmdline_client_dodamage;
|
||||
extern int Cmdline_closed_game;
|
||||
extern int Cmdline_freespace_no_music;
|
||||
extern int Cmdline_freespace_no_sound;
|
||||
@@ -53,9 +54,10 @@ extern char *Cmdline_center_res;
|
||||
|
||||
// Graphics related
|
||||
extern float Cmdline_clip_dist;
|
||||
extern float Cmdline_ambient_power;
|
||||
extern float Cmdline_emissive_power;
|
||||
extern float Cmdline_light_power;
|
||||
extern float static_light_factor;
|
||||
extern float static_point_factor;
|
||||
extern float static_tube_factor;
|
||||
extern int Cmdline_ambient_factor;
|
||||
extern int Cmdline_env;
|
||||
extern int Cmdline_glow;
|
||||
extern int Cmdline_noscalevid; // disables fit-to-window for movies - taylor
|
||||
@@ -67,7 +69,6 @@ extern int Cmdline_softparticles;
|
||||
extern int Cmdline_bloom_intensity;
|
||||
extern bool Cmdline_force_lightshaft_off;
|
||||
extern int Cmdline_no_deferred_lighting;
|
||||
extern bool Cmdline_deferred_lighting_cockpit;
|
||||
extern int Cmdline_emissive;
|
||||
extern int Cmdline_aniso_level;
|
||||
|
||||
@@ -80,14 +81,16 @@ extern int Cmdline_ballistic_gauge;
|
||||
extern int Cmdline_dualscanlines;
|
||||
extern int Cmdline_orb_radar;
|
||||
extern int Cmdline_rearm_timer;
|
||||
extern int Cmdline_targetinfo;
|
||||
|
||||
// Gameplay related
|
||||
extern int Cmdline_3dwarp;
|
||||
extern int Cmdline_ship_choice_3d;
|
||||
extern int Cmdline_weapon_choice_3d;
|
||||
extern int Cmdline_warp_flash;
|
||||
extern int Cmdline_autopilot_interruptable;
|
||||
extern int Cmdline_stretch_menu;
|
||||
extern int Cmdline_no_screenshake;
|
||||
extern int Cmdline_deadzone;
|
||||
|
||||
// Audio related
|
||||
extern int Cmdline_voice_recognition;
|
||||
@@ -121,9 +124,6 @@ extern bool Cmdline_no_geo_sdr_effects;
|
||||
extern bool Cmdline_set_cpu_affinity;
|
||||
extern bool Cmdline_nograb;
|
||||
extern bool Cmdline_noshadercache;
|
||||
extern bool Cmdline_prefer_ipv4;
|
||||
extern bool Cmdline_prefer_ipv6;
|
||||
extern bool Cmdline_dump_packet_type;
|
||||
#ifdef WIN32
|
||||
extern bool Cmdline_alternate_registry_path;
|
||||
#endif
|
||||
@@ -144,7 +144,6 @@ extern int Cmdline_reparse_mainhall;
|
||||
extern bool Cmdline_profile_write_file;
|
||||
extern bool Cmdline_no_unfocus_pause;
|
||||
extern bool Cmdline_benchmark_mode;
|
||||
extern const char *Cmdline_pilot;
|
||||
extern bool Cmdline_noninteractive;
|
||||
extern bool Cmdline_json_profiling;
|
||||
extern bool Cmdline_frame_profile;
|
||||
@@ -152,10 +151,6 @@ extern bool Cmdline_show_video_info;
|
||||
extern bool Cmdline_debug_window;
|
||||
extern bool Cmdline_graphics_debug_output;
|
||||
extern bool Cmdline_log_to_stdout;
|
||||
extern bool Cmdline_slow_frames_ok;
|
||||
extern bool Cmdline_lua_devmode;
|
||||
extern bool Cmdline_override_data;
|
||||
extern bool Cmdline_show_imgui_debug;
|
||||
|
||||
enum class WeaponSpewType { NONE = 0, STANDARD, ALL };
|
||||
extern WeaponSpewType Cmdline_spew_weapon_stats;
|
||||
|
||||
+1068
-1489
File diff suppressed because it is too large.
Load diff
@@ -12,78 +12,72 @@
|
||||
#define CONTROLS_CONFIG_H
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "scripting/scripting.h"
|
||||
|
||||
#define CONTROL_CONFIG_XSTR 507
|
||||
|
||||
#define CCF_AXIS_BTN 0x40 //!< axis is a button on positive side. If also inverted, is on negative side instead
|
||||
#define CCF_RELATIVE 0x20 //!< axis is relative
|
||||
#define CCF_INVERTED 0x10 //!< axis is inverted
|
||||
#define CCF_AXIS 0x08 //!< btn is an axis
|
||||
#define CCF_HAT 0x04 //!< btn is a hat
|
||||
#define CCF_BALL 0x02 //!< btn is a ball
|
||||
#define CCF_BUTTON 0x01 //!< btn is actually a button
|
||||
#define CCF_NONE 0x00 //!< No flags. Alias is "NONE"
|
||||
|
||||
/*!
|
||||
* These are used to index a corresponding axis value from an array.
|
||||
* These are used to index a corresponding joystick axis value from an array.
|
||||
* Currently only used by ::Axis_map_to[] and ::Axis_map_to_defaults[]
|
||||
*/
|
||||
enum Joy_axis_index : short {
|
||||
enum Joy_axis_index {
|
||||
JOY_X_AXIS =0,
|
||||
JOY_Y_AXIS,
|
||||
JOY_Z_AXIS,
|
||||
JOY_RX_AXIS,
|
||||
JOY_RY_AXIS,
|
||||
JOY_RZ_AXIS,
|
||||
|
||||
JOY_NUM_AXES // Number of axes a joystick may have. Must be last enum in Joy_axis_index.
|
||||
JOY_RZ_AXIS
|
||||
};
|
||||
|
||||
// Aliases for mouse axes. Really should unify this...
|
||||
const short MOUSE_X_AXIS = JOY_X_AXIS;
|
||||
const short MOUSE_Y_AXIS = JOY_Y_AXIS;
|
||||
const short MOUSE_Z_AXIS = JOY_Z_AXIS;
|
||||
const short MOUSE_NUM_AXES = 3;
|
||||
const short MOUSE_NUM_BUTTONS = 9; // Keep this up to date with mouse.h until z64555 stops being lazy and does it right
|
||||
|
||||
/*!
|
||||
* Controller index enumeration
|
||||
* @details For use with hardcoded bindings and scripting API to allow human-readable translation
|
||||
* @note These enums are hardcoded so that an int value of 0 would be JOY0
|
||||
* These are used to index a corresponding (analog) action, namely controlling the orientation angles and throttle.
|
||||
*/
|
||||
enum CID : int8_t {
|
||||
CID_NONE = -3, //!< No device bound
|
||||
CID_KEYBOARD = -2, //!< belongs to keyboard
|
||||
CID_MOUSE = -1, //!< to mouse
|
||||
CID_JOY0 = 0, //!< to Joy0
|
||||
CID_JOY1 = 1, //!< to Joy1 (Throttle?)
|
||||
CID_JOY2 = 2, //!< to Joy2 (Pedals?)
|
||||
CID_JOY3 = 3, //!< to Joy3 (Head tracker?)
|
||||
|
||||
CID_JOY_MAX //!< Maximum supported joysticks, must be last in the enum definition
|
||||
enum Joy_axis_action_index {
|
||||
JOY_HEADING_AXIS =0,
|
||||
JOY_PITCH_AXIS,
|
||||
JOY_BANK_AXIS,
|
||||
JOY_ABS_THROTTLE_AXIS,
|
||||
JOY_REL_THROTTLE_AXIS,
|
||||
|
||||
/*!
|
||||
* This must always be below the last defined item
|
||||
*/
|
||||
NUM_JOY_AXIS_ACTIONS //!< The total number of actions an axis may map to
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
* Control Configuration Types. Namely differ in how the control is activated
|
||||
*/
|
||||
enum CC_type {
|
||||
CC_TYPE_TRIGGER = 0, //!< A normal, one-shot type control that is activated when a key is or button is pressed
|
||||
CC_TYPE_CONTINUOUS, //!< A continous control that is activated as long as the key or button is held down
|
||||
CC_TYPE_AXIS_ABS, //!< Absolute Axis mode. Axis position = output value
|
||||
CC_TYPE_AXIS_REL, //!< Relative Axis mode. Axis postion away from its center adds or subtracts an output value register
|
||||
CC_TYPE_AXIS_BTN_NEG, //!< Axis Button mode, negative side. Axis position in the negative side will trigger a button action
|
||||
CC_TYPE_AXIS_BTN_POS //!< Axis Button mode, positive side. Axis position in the positive side will trigger a button action
|
||||
CC_TYPE_TRIGGER =0, //!< A normal, one-shot type control that is activated when a key is or button is pressed
|
||||
CC_TYPE_CONTINUOUS //!< A continous control that is activated as long as the key or button is held down
|
||||
};
|
||||
|
||||
/*!
|
||||
* Control configuration item type.
|
||||
*/
|
||||
typedef struct config_item {
|
||||
short key_default; //!< default key bound to action
|
||||
short joy_default; //!< default joystick button bound to action
|
||||
char tab; //!< what tab (category) it belongs in
|
||||
int indexXSTR; //!< what string index we should use to translate this with an XSTR
|
||||
const char *text; //!< describes the action in the config screen
|
||||
char type; //!< manner control should be checked in
|
||||
short key_id; //!< actual key bound to action
|
||||
short joy_id; //!< joystick button bound to action
|
||||
int used; //!< has control been used yet in mission? If so, this is the timestamp
|
||||
bool disabled; //!< whether this action should be available at all
|
||||
bool continuous_ongoing;//!< whether this action is a continuous one and is currently ongoing
|
||||
} config_item;
|
||||
|
||||
/*!
|
||||
* All available actions
|
||||
* This is the value of the id field in config_item
|
||||
* The first group of items are ship targeting.
|
||||
*
|
||||
* Note: Do not adjust the order or numeric value
|
||||
*/
|
||||
enum IoActionId : int {
|
||||
enum IoActionId {
|
||||
TARGET_NEXT =0, //!< target next
|
||||
TARGET_PREV =1, //!< target previous
|
||||
TARGET_NEXT_CLOSEST_HOSTILE =2, //!< target the next hostile target
|
||||
@@ -258,7 +252,7 @@ enum IoActionId : int {
|
||||
DOWN_SLIDE_THRUST =111, //!< DOWN_SLIDE_THRUST
|
||||
HUD_TARGETBOX_TOGGLE_WIREFRAME =112, //!< HUD_TARGETBOX_TOGGLE_WIREFRAME
|
||||
VIEW_TOPDOWN =113, //!< VIEW_TOPDOWN
|
||||
VIEW_TRACK_TARGET =114, //!< VIEW_TRACK_TARGET (Switfty) Toggle for VM_Track
|
||||
VIEW_TRACK_TARGET =114, //!< VIEW_TRACK_TARGET
|
||||
|
||||
//!< @n
|
||||
//!< AutoPilot - Kazan
|
||||
@@ -285,747 +279,69 @@ enum IoActionId : int {
|
||||
CUSTOM_CONTROL_4 = 122,
|
||||
CUSTOM_CONTROL_5 = 123,
|
||||
|
||||
//!< @n
|
||||
//!< Analog controls
|
||||
//!<-----------------------------
|
||||
//!< All axis actions must be kept together
|
||||
JOY_HEADING_AXIS = 124,
|
||||
JOY_PITCH_AXIS = 125,
|
||||
JOY_BANK_AXIS = 126,
|
||||
JOY_ABS_THROTTLE_AXIS = 127,
|
||||
JOY_REL_THROTTLE_AXIS = 128,
|
||||
|
||||
TOGGLE_HUD_SHADOWS,
|
||||
|
||||
/*!
|
||||
* This must always be below the last defined item
|
||||
*/
|
||||
CCFG_MAX //!< The total number of defined control actions (or last define + 1)
|
||||
};
|
||||
|
||||
// Constants for array-based checking (within Control_config) for the axis actions.
|
||||
// z64: Yes, this is hackish. No I don't really like it.
|
||||
const int JOY_AXIS_BEGIN = JOY_HEADING_AXIS;
|
||||
const int JOY_AXIS_END = JOY_REL_THROTTLE_AXIS + 1;
|
||||
const int NUM_JOY_AXIS_ACTIONS = JOY_AXIS_END - JOY_AXIS_BEGIN;
|
||||
|
||||
/*!
|
||||
* @brief Enum for indexing into axis action arrays.
|
||||
* @note Keep this up to date with JOY_HEADING_AXIS and friends. Pay attention to their order and copy to here
|
||||
*/
|
||||
namespace Action {
|
||||
enum {
|
||||
HEADING = 0,
|
||||
PITCH,
|
||||
BANK,
|
||||
ABS_THROTTLE,
|
||||
REL_THROTTLE,
|
||||
|
||||
NUM_VALUES
|
||||
};
|
||||
}
|
||||
|
||||
/*!
|
||||
* Where the preset is located in memory
|
||||
*/
|
||||
enum class Preset_t {
|
||||
hardcode, // Preset is defined hardcode
|
||||
tbl, // Preset is defined in controlconfigdefaults.tbl
|
||||
pst // Preset is defined in a .json
|
||||
};
|
||||
|
||||
class CCB;
|
||||
|
||||
/*!
|
||||
* A singular button binding
|
||||
*/
|
||||
class CC_bind {
|
||||
public:
|
||||
CC_bind() = default;
|
||||
CC_bind(CID _cid, short _btn) : cid(_cid), btn(_btn) { validate(); };
|
||||
CC_bind(CID _cid, short _btn, char _flags) : cid(_cid), flags(_flags), btn(_btn) { validate(); };
|
||||
CC_bind(const CC_bind &A) = default;
|
||||
|
||||
CC_bind& operator=(const CC_bind &A) = default;
|
||||
|
||||
/*!
|
||||
* Checks if this CC_bind is equal to the given CC_bind
|
||||
*
|
||||
* @note EQUAL means EQUAL, this will return FALSE if the flags differ by a single bit
|
||||
*/
|
||||
bool operator==(const CC_bind &B) const;
|
||||
|
||||
/*!
|
||||
* Checks if this CC_bind is not equal to the given CC_bind
|
||||
*/
|
||||
bool operator!=(const CC_bind &B) const;
|
||||
|
||||
/*!
|
||||
* Checks if this CC_bind is equal to the given CC_bind, disregarding the inversion flag
|
||||
*/
|
||||
bool invert_agnostic_equals(const CC_bind &B) const;
|
||||
|
||||
/*!
|
||||
* Clears the binding and flags. Some flags are retained.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/*!
|
||||
* True if not bound
|
||||
*/
|
||||
bool empty() const;
|
||||
|
||||
/**
|
||||
* Takes the given binding.
|
||||
*/
|
||||
void take(CID _cid, short _btn, char _flags = 0);
|
||||
|
||||
/**
|
||||
* Validates the binding, clearing it if necassary
|
||||
*/
|
||||
void validate();
|
||||
|
||||
/*!
|
||||
* Returns a copy of ::btn
|
||||
*/
|
||||
short get_btn() const;
|
||||
|
||||
/*!
|
||||
* Returns a copy of ::cid
|
||||
*/
|
||||
CID get_cid() const;
|
||||
|
||||
/*!
|
||||
* Returns a copy of ::flags
|
||||
*/
|
||||
char get_flags() const;
|
||||
|
||||
/*!
|
||||
* Returns a human-readable string of this binding
|
||||
*/
|
||||
SCP_string textify() const;
|
||||
|
||||
/*!
|
||||
* Sets the inversion state of this binding according to the passed bool. True = inverted, false = normal
|
||||
*/
|
||||
void invert(bool);
|
||||
|
||||
/*!
|
||||
* Toggles the inversion state
|
||||
*/
|
||||
void invert_toggle();
|
||||
|
||||
/*!
|
||||
* Is true if inverted, false otherwise
|
||||
*/
|
||||
bool is_inverted() const;
|
||||
|
||||
/*!
|
||||
* Checks if this CC_bind conflicts with the given argument
|
||||
*
|
||||
* @note Similar to operator==, but ignores certain combinations of flags.
|
||||
*/
|
||||
bool conflicts_with(const CC_bind&) const;
|
||||
|
||||
private:
|
||||
CID cid = CID_NONE; //!< Which controller this belongs to
|
||||
char flags = 0; //!< mask to determine various additional attributes of btn
|
||||
short btn = -1; //!< The button, key combo, or axis that's bound.
|
||||
};
|
||||
|
||||
/*!
|
||||
* A pair of bindings.
|
||||
* @note Please don't set the bindings directly, use ::take() instead.
|
||||
*/
|
||||
class CCB {
|
||||
public:
|
||||
CC_bind first; // The primary binding
|
||||
CC_bind second; // The secondary binding
|
||||
|
||||
public:
|
||||
CCB() = default;
|
||||
CCB(const CCB& A) = default;
|
||||
|
||||
/*!
|
||||
* Returns true if nothing is bound
|
||||
*/
|
||||
bool empty() const;
|
||||
|
||||
/*!
|
||||
* Takes a given binding
|
||||
*
|
||||
* @param[in] A The bind to take
|
||||
* @param[in] order 0 is primary, 1 is secondary, -1 is overwrite existing bind (if any)
|
||||
*
|
||||
* @note Should there be a binding to the same controller as the given binding, it is cleared
|
||||
*/
|
||||
void take(CC_bind A, int order);
|
||||
|
||||
/*!
|
||||
* Clears both bindings
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/*!
|
||||
* Gets the value of btn if either CC_bind has the given CID
|
||||
*
|
||||
* @returns the btn of the bound controller, or
|
||||
* @returns -1 if that controller is not bound
|
||||
*/
|
||||
short get_btn(CID) const;
|
||||
|
||||
/*!
|
||||
* Assigns the contents of the given CCB to this CCB
|
||||
*/
|
||||
CCB& operator=(const CCB&) = default;
|
||||
|
||||
/*!
|
||||
* Checks if the given CCB is exactly equal to this
|
||||
*/
|
||||
bool operator==(const CCB&) const;
|
||||
|
||||
/*!
|
||||
* Checks if the given CCB differs from this
|
||||
*/
|
||||
bool operator!=(const CCB&) const;
|
||||
|
||||
/*!
|
||||
* Returns True if this CCB's first isn't empty and the given CCB has a binding that can conflict with it
|
||||
*/
|
||||
bool has_first_conflict(const CCB&) const;
|
||||
|
||||
/*!
|
||||
* Returns True if this CCB's second isn't empty and the given CCB has a binding that can conflict with it
|
||||
*/
|
||||
bool has_second_conflict(const CCB&) const;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to first, or second, whichever has a binding equal to the given CC_bind, or nullptr if neither
|
||||
*/
|
||||
CC_bind* find(const CC_bind&);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to first, or second, whichever has a CID equal to the given CID, or nullptr if neither
|
||||
*/
|
||||
CC_bind* find(CID);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to first, or second, whichever has *all* flags in the given mask
|
||||
*/
|
||||
CC_bind* find_flags(const char);
|
||||
|
||||
/*!
|
||||
* Sets the inversion state of both bindings
|
||||
*/
|
||||
void invert(bool);
|
||||
|
||||
/*!
|
||||
* Toggles the inversion state of Primary and copies it to Secondary
|
||||
*/
|
||||
void invert_toggle();
|
||||
|
||||
/*!
|
||||
* Is true if both bindings are inverted, false otherwise
|
||||
*/
|
||||
bool is_inverted() const;
|
||||
};
|
||||
|
||||
/*!
|
||||
* A preset, a collection of bindings for use in Control_config with an associated name
|
||||
*/
|
||||
class CC_preset {
|
||||
public:
|
||||
SCP_vector<CCB> bindings;
|
||||
SCP_string name;
|
||||
Preset_t type;
|
||||
|
||||
public:
|
||||
CC_preset() = default;
|
||||
CC_preset(const CC_preset& A) = default;
|
||||
|
||||
CC_preset& operator=(const CC_preset&);
|
||||
/*!
|
||||
* Checks if the given preset is a duplicate of this one
|
||||
*/
|
||||
bool is_duplicate_of(CC_preset&);
|
||||
};
|
||||
|
||||
/*!
|
||||
* Control configuration item type.
|
||||
* @detail Contains binding info, documentation, behavior, etc. for a single control
|
||||
*/
|
||||
class CCI : public CCB {
|
||||
public:
|
||||
// Inherited from CCB
|
||||
// CC_bind first;
|
||||
// CC_bind second;
|
||||
|
||||
// Items Set in menu
|
||||
char tab; //!< what tab (category) it belongs in
|
||||
int indexXSTR; //!< what string index we should use to translate this with an XSTR 0 = None, 1= Use item index + CONTROL_CONFIG_XSTR, 2 <= use CCI::indexXSTR directly
|
||||
SCP_string text; //!< describes the action in the config screen
|
||||
|
||||
CC_type type; //!< manner control should be checked in
|
||||
|
||||
// Items used during gameplay
|
||||
TIMESTAMP digital_used; //!< has control been used yet in mission? If so, this is the timestamp.
|
||||
// Note this is not a UI timestamp because this is purely for mission timing purposes.
|
||||
int analog_value; // For axes, this denotes the last frame's value
|
||||
bool disabled = true; //!< whether this action should be available at all
|
||||
bool locked = false; //!< whether this action will be triggered by the respectively bound key
|
||||
bool scriptEnabledByDefault = false; //!< whether this binding will execute it's registered hooks if triggered. Resets each mission
|
||||
bool continuous_ongoing; //!< whether this action is a continuous one and is currently ongoing
|
||||
|
||||
public:
|
||||
CCI() = default;
|
||||
CCI(const CCI& A) = default;
|
||||
|
||||
/*!
|
||||
* Assigns the contents of the given CCI to this CCI
|
||||
*/
|
||||
CCI& operator=(const CCI&);
|
||||
|
||||
/*!
|
||||
* @brief Takes the bindings of the given CCB, but leaves all other members alone
|
||||
*/
|
||||
CCI& operator=(const CCB&);
|
||||
|
||||
/*!
|
||||
* Returns true if this item is analogue or an axis
|
||||
*/
|
||||
bool is_axis();
|
||||
};
|
||||
|
||||
/*!
|
||||
* Builder predicate to populate a ControlConfig vector with hardcoded default bindings.
|
||||
*/
|
||||
class CCI_builder {
|
||||
public:
|
||||
/*!
|
||||
* Initilizes the given ControlConfig vector
|
||||
*/
|
||||
CCI_builder(SCP_vector<CCI>& _ControlConfig);
|
||||
|
||||
/*!
|
||||
* Start a chain of factory methods. If there are any pre-init work to be done, that's done here.
|
||||
*/
|
||||
CCI_builder& start();
|
||||
|
||||
/*!
|
||||
* End a chain of factory methods. If there any post-init work to be done, that's done here.
|
||||
*/
|
||||
void end();
|
||||
|
||||
/*!
|
||||
* Assigns the hardcoded binding to the given action
|
||||
*
|
||||
* @note This differs from the original hardcode from :V: in the hopes of it being more intuitive to future IoAction additions
|
||||
*
|
||||
* param[in] action_id The IoActionId enum associated with the control
|
||||
* param[in] primary The btn, key combo, or axis for the default primary binding
|
||||
* param[in] secondary The btn, key combo, or axis for the default secondary binding
|
||||
* param[in] tab Category tab this control should show up under on the config menu
|
||||
* param[in] indexXSTR ID for the XSTR localization utility. 0 for no localization, 1 for automatic lookup, all other values are index for item in strings.tbl
|
||||
* param[in] text Label text displayed in the config menu.
|
||||
* param[in] type How the control should be treated. see doxy on CC_type for details
|
||||
* param[in] disabled If true, this control will not be available in the game. Controlsconfigdefaults.tbl may override this. Don't use this to temporarily disable controls in mission.
|
||||
*
|
||||
* @details The CID for the default primary and secondary is determined by the type parameter.
|
||||
* If the type is CC_TYPE_TRIGGER or CC_TYPE_CONTINOUS, the primary is assumed to be a key combo and secondary is assumed to be a joy button.
|
||||
* If the type is CC_TYPE_AXIS_ABS, CC_TYPE_AXIS_REL, CC_TYPE_AXIS_BTN_POS, or CC_TYPE_AXIS_BTN_NEG, the primary is a Joy0 axis and the secondary is a Mouse axis
|
||||
*/
|
||||
CCI_builder& operator()(IoActionId action_id, short primary, short secondary, char tab, int indexXSTR, const char *text, CC_type type, bool disabled = false);
|
||||
|
||||
private:
|
||||
CCI_builder(); // Only one builder per Control Config, so a default constructor is useless
|
||||
SCP_vector<CCI>& ControlConfig;
|
||||
};
|
||||
|
||||
|
||||
extern int Failed_key_index;
|
||||
|
||||
extern int Axis_map_to[];
|
||||
extern int Invert_axis[];
|
||||
extern int Invert_axis_defaults[];
|
||||
|
||||
extern int Joy_dead_zone_size;
|
||||
extern int Joy_sensitivity;
|
||||
|
||||
extern int Control_config_overlay_id;
|
||||
|
||||
extern SCP_vector<CCI> Control_config; //!< Stores the keyboard configuration
|
||||
extern SCP_vector<CC_preset> Control_config_presets; // tabled control presets; pointers to config_item arrays
|
||||
extern const char **Joy_button_text; // String table of button labels. XSTR'd on init.
|
||||
extern config_item Control_config[]; //!< Stores the keyboard configuration
|
||||
extern SCP_vector<config_item*> Control_config_presets; // tabled control presets; pointers to config_item arrays
|
||||
extern SCP_vector<SCP_string> Control_config_preset_names; // names for Control_config_presets (identical order of items)
|
||||
extern const char **Scan_code_text;
|
||||
extern const char **Joy_button_text;
|
||||
|
||||
extern bool Generate_controlconfig_table;
|
||||
void control_config_common_init(); //!< initialize common control config stuff - call at game startup after localization has been initialized
|
||||
void control_config_common_close(); //!< close common control config stuff - call at game shutdown
|
||||
|
||||
// string table constants for labels and stuff.
|
||||
#define NUM_AXIS_TEXT JOY_NUM_AXES
|
||||
#define NUM_MOUSE_TEXT MOUSE_NUM_BUTTONS
|
||||
|
||||
extern char *Axis_text[NUM_AXIS_TEXT]; // String table of axis labels (joystick and mice). XSTR'd on init.
|
||||
extern char *Mouse_button_text[NUM_MOUSE_TEXT]; // String table of mouse button labels. XSTR'd on init.
|
||||
|
||||
/*!
|
||||
* @brief Checks if either binding in the CCB has the given cid
|
||||
*
|
||||
* @returns 0 if its the first element, or
|
||||
* @returns 1 if its the second element, or
|
||||
* @returns -1 if neither
|
||||
*/
|
||||
int is_cid_either(CID, const CCB);
|
||||
|
||||
/*!
|
||||
* Allows CID to be assigned by a short. Has Assert to check values
|
||||
*/
|
||||
void cid_assign(CID&, const short);
|
||||
|
||||
/*!
|
||||
* @brief initialize common control config stuff - call at game startup after localization has been initialized
|
||||
*/
|
||||
void control_config_common_init();
|
||||
|
||||
/*!
|
||||
* @brief close common control config stuff - call at game shutdown
|
||||
*/
|
||||
void control_config_common_close();
|
||||
|
||||
/*!
|
||||
* @brief init config menu
|
||||
*/
|
||||
void control_config_init();
|
||||
|
||||
/*!
|
||||
* @brief do a frame of the config menu
|
||||
*/
|
||||
void control_config_do_frame(float frametime);
|
||||
|
||||
/*!
|
||||
* @brief close config menu
|
||||
*/
|
||||
void control_config_close();
|
||||
|
||||
/*!
|
||||
* @brief Cancel configuration of controls, revert any changes, return to previous menu/game state
|
||||
*/
|
||||
void control_config_cancel_exit();
|
||||
|
||||
/*!
|
||||
* @brief Copies all bindings within preset into Control_config
|
||||
*/
|
||||
void control_config_use_preset(CC_preset &preset);
|
||||
|
||||
/*!
|
||||
* @brief Search for a given preset by name and use it, if it exists.
|
||||
*
|
||||
* @returns TRUE if the preset was found and selected
|
||||
* @returns FALSE if the preset was not found, and the defaults were selected
|
||||
*
|
||||
* @details If the preset does not exist, then defaults are used instead.
|
||||
* @see control_config_use_preset()
|
||||
*/
|
||||
bool control_config_use_preset_by_name(const SCP_string &name);
|
||||
|
||||
/**
|
||||
* @brief Gets the currently used preset
|
||||
*
|
||||
* @param[in] invert_agnostic If true, check the bindings in Control_config against the preset, ignoring inversion flags
|
||||
*
|
||||
* @returns an iterator to the current preset, or
|
||||
* @returns ::iterator Control_config_presets.end() if current bindings are not in a preset
|
||||
*
|
||||
* @note Similar to preset_find_duplicate, this function has additional logic in its search to ignore disabled controls
|
||||
*/
|
||||
|
||||
SCP_vector<CC_preset>::iterator control_config_get_current_preset(bool invert_agnostic = false);
|
||||
|
||||
/*!
|
||||
* Returns the IoActionId (index within Control_config[]) of a control bound to the given key
|
||||
*
|
||||
* @param[in] key The key combo to look for
|
||||
* @param[in] find_override If true, return the IoActionId of a control that has this key as its default
|
||||
*
|
||||
* @return -1 if the given key is NULL or not bound.
|
||||
*
|
||||
* @details If find_override is set to true, then this returns the index of the action
|
||||
*/
|
||||
void control_config_reset_defaults(int presetnum=-1);
|
||||
int translate_key_to_index(const char *key, bool find_override=true);
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Given the system default key 'key', return the current control input(s) that is bound to that function.
|
||||
*
|
||||
* @param[in] key The default key combo (as cstring) to a certain control
|
||||
*
|
||||
* @return The key combo (as cstring) currently bound to the control, or
|
||||
* @return nullptr if the given key is not a system default key, or
|
||||
* @return "None" if there is nothing bound to the control
|
||||
*
|
||||
* @details Both the 'key' and the return value are descriptive strings that can be displayed
|
||||
* directly to the user. If 'key' isn't a real key, is not normally bound to anything,
|
||||
* or there is no key currently bound to the function, nullptr is returned.
|
||||
*
|
||||
* @note Not thread safe. Has an internal buffer for the return value which is overwritten on each call.
|
||||
*
|
||||
* @note Uses CC_bind::textify for translations
|
||||
*/
|
||||
const char *translate_key(char *key);
|
||||
char *translate_key(char *key);
|
||||
|
||||
/**
|
||||
* @brief Converts the specified key code to a human readable string, according to the selected locale and keyboard layout
|
||||
* @brief Converts the specified key code to a human readable string
|
||||
*
|
||||
* @param[in] code The key scancode to convert
|
||||
* @note The returned value is localized to the current language
|
||||
*
|
||||
* @return The text representation of the code.
|
||||
*
|
||||
* @note Not thread safe. Has an internal buffer for the return value which is overwritten on each call.
|
||||
*
|
||||
* @note The return value is translated according to localization settings
|
||||
* @param code The key code to convert
|
||||
* @return The text representation of the code. The returned value is stored in a temporary location, copy it to your
|
||||
* own buffer if you want to continue using it.
|
||||
*/
|
||||
const char *textify_scancode(int code);
|
||||
|
||||
/**
|
||||
* @brief Converts the specified key code to a human readable string. Uses the English locale and QWERTY layout.
|
||||
*
|
||||
* @param[in] code The key scancode to convert
|
||||
*
|
||||
* @return The text representation of the code
|
||||
*
|
||||
* @note Not thread safe. Has an internal buffer for the return value which is overwritten on each call.
|
||||
* @note Same as textify_scancode but always returns the same value regardless of current language
|
||||
* @param code The key code to convert
|
||||
* @return The name of the key
|
||||
* @see textify_scancode
|
||||
*/
|
||||
const char *textify_scancode_universal(int code);
|
||||
|
||||
/*!
|
||||
* @brief Checks how long a control has been active
|
||||
*
|
||||
* @param[in] id The IoActionId of the control to check
|
||||
*
|
||||
* @returns Time, in milliseconds, that the control has been active.
|
||||
*
|
||||
* @note This function is only to be used on CC_CONTINOUS type controls, or it will trigger an assertion
|
||||
*/
|
||||
float check_control_timef(int id);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for check_control_used. Allows the game to ignore the key if told to do so by the ignore-key SEXP.
|
||||
*
|
||||
* @param[in] id The IoActionId of the control to check
|
||||
* @param[in] key The key combo to check against the control. If -1, re-check the last key passed to this function
|
||||
*
|
||||
* @returns 0 If the control wasn't used, or
|
||||
* @returns 1 If the control was used
|
||||
*
|
||||
* @details If the given control is CC_CONTINOUS, param key is effectively ignored and instead keyd_press[] is checked.
|
||||
* This function also checks against joy and mouse presses.
|
||||
*/
|
||||
int check_control(int id, int key = -1);
|
||||
|
||||
/**
|
||||
* @brief Gets the scaled reading for all control axes.
|
||||
*
|
||||
* @param[in] frametime Frametime used to scale the mouse axes
|
||||
* @param[out] axis_v Output array of all control axes, must have size of JOY_NUM_AXIS_ACTIONS
|
||||
*
|
||||
* @details
|
||||
* * Clamps output to stay within +/-JOY_AXIS_RANGE.
|
||||
* * Should multiple axes be bound to a control, their values are blended with a simple sum. This is to save a bit of
|
||||
* processing for speed.
|
||||
*/
|
||||
void control_get_axes_readings(int *axis_v, float frame_time);
|
||||
|
||||
/**
|
||||
* @brief Markes the given control (by IoActionId) as used
|
||||
*
|
||||
* @details Updates the ::used timestamp, triggers a script hook, and marks ::continous_ongoing as true
|
||||
*/
|
||||
void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr);
|
||||
void control_used(int id);
|
||||
|
||||
/**
|
||||
* @brief Runs a given controlbindings lua hook added in ccd.tbl
|
||||
*
|
||||
* @return Whether the control should be overridden
|
||||
*/
|
||||
bool control_run_lua(IoActionId id, int value);
|
||||
|
||||
/**
|
||||
* @brief Resets the cache for script evaluations for continuous buttons. Should be called once per frame.
|
||||
*
|
||||
*/
|
||||
void control_reset_lua_cache();
|
||||
|
||||
/**
|
||||
* @brief Registers a new hook for the keybinding action system.
|
||||
* Parameters set if it is a normal or an override function, as well as if it is enabled by default or needs to be enabled manually each mission
|
||||
*/
|
||||
void control_register_hook(IoActionId id, const luacpp::LuaFunction& hook, bool is_override, bool enabledByDefault);
|
||||
|
||||
/**
|
||||
* @brief Enables or disables the respective Lua hook
|
||||
*/
|
||||
void control_enable_hook(IoActionId id, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Resets every hooks enable state to the tabled value. Call on Mission start
|
||||
*/
|
||||
void control_reset_hook();
|
||||
|
||||
/**
|
||||
* @brief Clears the bindings of all controls
|
||||
*/
|
||||
void control_config_clear();
|
||||
|
||||
/**
|
||||
* @brief debug function used to indicate number of controls checked
|
||||
*/
|
||||
void control_check_indicate();
|
||||
|
||||
/**
|
||||
* @brief Clears the used timestamp of all controls
|
||||
*/
|
||||
void control_config_clear_used_status();
|
||||
|
||||
/**
|
||||
* @brief Applies sensitivity multiplier and deadzone histerisis to the raw axis value
|
||||
*
|
||||
* @param[in] raw The raw axis value to transform
|
||||
*
|
||||
* @return The transformed value
|
||||
*/
|
||||
int joy_get_scaled_reading(int raw);
|
||||
|
||||
/**
|
||||
* Lookups the given stringified enum to find its value
|
||||
* @return -1 if not found, or
|
||||
* @return IoActionId if found
|
||||
*/
|
||||
int ActionToVal(const char * str);
|
||||
|
||||
/**
|
||||
* Reverse lookups the IoActionId to get its stringified name
|
||||
* @return Pointer to the stringified name of the action, or
|
||||
* @return nullptr if not found
|
||||
*/
|
||||
const char * ValToAction(IoActionId id);
|
||||
|
||||
/**
|
||||
* Reverse lookups the IoActionId to get its stringified name
|
||||
* @return Pointer to the stringified name of the action, or
|
||||
* @return nullptr if not found, or invalid id
|
||||
*/
|
||||
const char * ValToAction(int id);
|
||||
|
||||
/**
|
||||
* Lookups the given stringified enum to find its value
|
||||
*/
|
||||
CID CIDToVal(const char * str);
|
||||
|
||||
/**
|
||||
* Reverse lookups the CID to get its stringified name
|
||||
* @return The stringified name of the CID, or
|
||||
* @return "NONE" if not found
|
||||
*/
|
||||
SCP_string ValToCID(CID id);
|
||||
|
||||
/**
|
||||
* Reverse lookups the CID to get its stringified name
|
||||
* @return The stringified name of the CID, or
|
||||
* @return "NONE" if not found, or invalid id
|
||||
*/
|
||||
SCP_string ValToCID(int id);
|
||||
/**
|
||||
* Lookups the given stringified enum to find its value
|
||||
*/
|
||||
char CCFToVal(const char * str);
|
||||
|
||||
/**
|
||||
* Constructs a enum string from the CCF_FLAGS
|
||||
* @return The stringified name of the CCF, or
|
||||
* @return "NONE" if not found, or invalid id
|
||||
*/
|
||||
SCP_string ValToCCF(char id);
|
||||
|
||||
/**
|
||||
* Lookups the given stringified enum to find its value
|
||||
*/
|
||||
short InputToVal(CID cid, const char * str);
|
||||
|
||||
/**
|
||||
* Constructs a enumstring from the input binding, depending on the CID
|
||||
*
|
||||
* @return The stringified name of the input, or
|
||||
* @return "NONE" if not found, or invalid CC_bind
|
||||
*
|
||||
* @note This requires a CCB due to the way things are encoded
|
||||
*/
|
||||
SCP_string ValToInput(const CC_bind &bind);
|
||||
|
||||
/**
|
||||
* Lookups the given stringified enum to find its btn value
|
||||
*/
|
||||
short MouseToVal(const char * str);
|
||||
|
||||
/**
|
||||
* Constructs a enum string from the mouse input
|
||||
*
|
||||
* @return The stringified name of the input, or
|
||||
* @return "NONE" if not found, or invalid CC_bind
|
||||
* TODO XSTR
|
||||
*
|
||||
* @note This requires a CCB due to the way things are encoded
|
||||
*/
|
||||
SCP_string ValToMouse(const CC_bind &bind);
|
||||
|
||||
/**
|
||||
* Lookups the given stringified enum to find its btn value
|
||||
*/
|
||||
short KeyboardToVal(const char * str);
|
||||
|
||||
/**
|
||||
* Constructs an enum string from the key binding
|
||||
*
|
||||
* @return The stringified name of the input, or
|
||||
* @return "NONE" f not found, or invalid CC_bind
|
||||
*
|
||||
* @note This requires a CCB due to the way things are encoded
|
||||
* TODO XSTR
|
||||
*/
|
||||
SCP_string ValToKeyboard(const CC_bind &bind);
|
||||
|
||||
/**
|
||||
* Lookups the given stringified enum to find its value
|
||||
*/
|
||||
short JoyToVal(const char * str);
|
||||
|
||||
/**
|
||||
* Constructs a enum string from the Joystick input
|
||||
*
|
||||
* @return The stringified name of the input, or
|
||||
* @return "NONE" if not found, or invalid CC_bind
|
||||
*
|
||||
* @note This requires a CCB due to the way things are encoded
|
||||
* TODO XSTR
|
||||
*/
|
||||
SCP_string ValToJoy(const CC_bind &bind);
|
||||
|
||||
/**
|
||||
* Lookups the given string to find its tab value
|
||||
*/
|
||||
char CCTabToVal(const char *str);
|
||||
|
||||
/**
|
||||
* Reverse lookups the given tab in mCCTabToVal to retrieve the string name
|
||||
*
|
||||
* @return The stringified name of the input, or
|
||||
* @return nullptr if not found, or invalid tab
|
||||
*
|
||||
*/
|
||||
SCP_string ValToCCTab(char tab);
|
||||
|
||||
/**
|
||||
* Lookups the given string to find its CC_type value. =
|
||||
*/
|
||||
CC_type CCTypeToVal(const char *str);
|
||||
|
||||
/**
|
||||
* Reverse lookups the given CC_type to retrieve the string name
|
||||
|
||||
*/
|
||||
SCP_string ValToCCType(CC_type type);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,499 +0,0 @@
|
||||
#include "controlconfig/controlsconfig.h"
|
||||
#include "controlconfig/presets.h"
|
||||
#include "libs/jansson.h"
|
||||
#include "parse/parselo.h"
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
using namespace io::presets;
|
||||
|
||||
const SCP_vector<std::pair<Section, const char*>> SectionMapping {
|
||||
std::pair<Section, const char*>(Section::Unnamed, nullptr),
|
||||
std::pair<Section, const char*>(Section::Actions, "actions"),
|
||||
std::pair<Section, const char*>(Section::Primary, "primary"),
|
||||
std::pair<Section, const char*>(Section::Secondary, "secondary"),
|
||||
};
|
||||
|
||||
const char * lookupSectionName(Section id) {
|
||||
for (const auto& pair: SectionMapping) {
|
||||
if (pair.first == id) {
|
||||
return pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Section lookupSectionValue(const char* name) {
|
||||
Assertion(name != nullptr, "Key name must be a valid pointer!");
|
||||
|
||||
for (auto& pair : SectionMapping) {
|
||||
if (!strcmp(pair.second, name)) {
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
return Section::Invalid;
|
||||
}
|
||||
|
||||
void load_preset_files() {
|
||||
SCP_vector<SCP_string> filelist;
|
||||
cf_get_file_list(filelist, CF_TYPE_PLAYER_BINDS, NOX("*.json"), CF_SORT_NAME, nullptr,
|
||||
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
||||
|
||||
std::unique_ptr<PresetFileHandler> handler = nullptr;
|
||||
|
||||
for (const auto &file : filelist) {
|
||||
CFILE* fp = cfopen((file + ".json").c_str(), "r", CFILE_NORMAL, CF_TYPE_PLAYER_BINDS, false,
|
||||
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
||||
|
||||
if (!fp) {
|
||||
mprintf(("PST => Unable to open '%s' for loading!\n", file.c_str()));
|
||||
// try next
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
handler.reset(new PresetFileHandler(fp, true));
|
||||
} catch (const std::exception& e) {
|
||||
mprintf(("PST => Failed to open JSON: `%s`\n", e.what()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check the signature
|
||||
int pst_id = handler->readInt("signature");
|
||||
if (pst_id != PST_FILE_ID) {
|
||||
mprintf(("PST => Invalid header id for `%s`, skipping...\n", file.c_str()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Version
|
||||
int version = handler->readInt("version");
|
||||
mprintf(("PST => Loading `%s` with version %i\n", file.c_str(), version));
|
||||
|
||||
|
||||
// Start reading in data
|
||||
CC_preset preset;
|
||||
preset.name = file;
|
||||
preset.type = Preset_t::pst;
|
||||
preset.bindings.resize(Control_config.size());
|
||||
|
||||
size_t size;
|
||||
handler->beginArrayRead(Section::Actions, size);
|
||||
for (size_t i = 0; i < size; ++i, handler->nextArrayElement()) {
|
||||
SCP_string str;
|
||||
int id;
|
||||
|
||||
//handler->beginSectionRead(Section::Unnamed);
|
||||
str = handler->readString("bind");
|
||||
id = ActionToVal(str.c_str());
|
||||
|
||||
if (id < 0) {
|
||||
// Unknown bind, possibly custom. Ignore for now
|
||||
handler->endSectionRead(); // Unnamed
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &item = preset.bindings[id];
|
||||
|
||||
handler->beginSectionRead(Section::Primary);
|
||||
str = handler->readString("cid");
|
||||
auto cid = CIDToVal(str.c_str());
|
||||
|
||||
str = handler->readString("flags");
|
||||
auto flags = CCFToVal(str.c_str());
|
||||
|
||||
str = handler->readString("input");
|
||||
auto btn = InputToVal(cid, str.c_str());
|
||||
item.first.take(cid, btn, flags);
|
||||
|
||||
handler->endSectionRead(); // Primary
|
||||
|
||||
|
||||
handler->beginSectionRead(Section::Secondary);
|
||||
str = handler->readString("cid");
|
||||
cid = CIDToVal(str.c_str());
|
||||
|
||||
str = handler->readString("flags");
|
||||
flags = CCFToVal(str.c_str());
|
||||
|
||||
str = handler->readString("input");
|
||||
btn = InputToVal(cid, str.c_str());
|
||||
item.second.take(cid, btn, flags);
|
||||
handler->endSectionRead(); // Secondary
|
||||
|
||||
//handler->endSectionRead(); // Unnamed
|
||||
}
|
||||
handler->endArrayRead(); // Actions
|
||||
|
||||
// Done with the file
|
||||
auto it = preset_find_duplicate(preset);
|
||||
|
||||
if (it == Control_config_presets.end()) {
|
||||
Control_config_presets.push_back(preset);
|
||||
|
||||
} else if ((it->name != preset.name) || (it->type != Preset_t::pst)) {
|
||||
// Complain and ignore if the preset names or the type differs
|
||||
Warning(LOCATION, "PST => Preset '%s' is a duplicate of an existing preset, ignoring", preset.name.c_str());
|
||||
|
||||
} // else, silent ignore
|
||||
}
|
||||
}
|
||||
|
||||
bool preset_file_exists(SCP_string name) {
|
||||
if (name.find(".json") == std::string::npos) {
|
||||
name.append(".json");
|
||||
}
|
||||
|
||||
return cf_exists(name.c_str(), CF_TYPE_PLAYER_BINDS) != 0;
|
||||
}
|
||||
|
||||
bool save_preset_file(CC_preset preset, bool overwrite) {
|
||||
// Must have a name
|
||||
if (preset.name.empty()) {
|
||||
mprintf(("PST => Unable to save preset, missing name!\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
SCP_string filename = preset.name + ".json";
|
||||
std::unique_ptr<PresetFileHandler> handler = nullptr;
|
||||
|
||||
// Check if there's a file already
|
||||
CFILE* fp = cfopen(filename.c_str(), "r", CFILE_NORMAL, CF_TYPE_PLAYER_BINDS, false,
|
||||
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
||||
|
||||
if ((fp != nullptr) && !overwrite) {
|
||||
mprintf(("PST => Unable to save '%s', file already exists!\n", filename.c_str()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try opening file for write
|
||||
fp = cfopen(filename.c_str(), "w", CFILE_NORMAL, CF_TYPE_PLAYER_BINDS, false,
|
||||
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
||||
|
||||
if (!fp) {
|
||||
mprintf(("PST => Unable to save '%s', unknown error!\n", filename.c_str()));
|
||||
}
|
||||
|
||||
try {
|
||||
handler.reset(new PresetFileHandler(fp, false));
|
||||
} catch (const std::exception& e) {
|
||||
mprintf(("PST => Failed to open JSON: %s\n", e.what()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write header and version
|
||||
handler->writeInt("signature", PST_FILE_ID);
|
||||
handler->writeInt("version", PST_VERSION);
|
||||
|
||||
mprintf(("PST => Saving %s with version %d...\n", filename.c_str(), (int)PST_VERSION));
|
||||
|
||||
handler->beginArrayWrite(Section::Actions);
|
||||
for (int i = 0; static_cast<size_t>(i) < preset.bindings.size(); ++i) {
|
||||
const auto& item = preset.bindings[i];
|
||||
const auto& first = item.first;
|
||||
const auto& second = item.second;
|
||||
|
||||
handler->beginSectionWrite(Section::Unnamed);
|
||||
handler->writeString("bind", ValToAction(i));
|
||||
|
||||
handler->beginSectionWrite(Section::Primary);
|
||||
handler->writeString("cid", ValToCID(first.get_cid()));
|
||||
handler->writeString("flags", ValToCCF(first.get_flags()));
|
||||
handler->writeString("input", ValToInput(first));
|
||||
handler->endSectionWrite(); // Primary
|
||||
|
||||
handler->beginSectionWrite(Section::Secondary);
|
||||
handler->writeString("cid", ValToCID(second.get_cid()));
|
||||
handler->writeString("flags", ValToCCF(second.get_flags()));
|
||||
handler->writeString("input", ValToInput(second));
|
||||
handler->endSectionWrite(); // Secondary
|
||||
|
||||
handler->endSectionWrite(); // Control_config[i]
|
||||
}
|
||||
handler->endArrayWrite(); // Actions
|
||||
|
||||
handler->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SCP_vector<CC_preset>::iterator preset_find_duplicate(const CC_preset& new_preset) {
|
||||
SCP_vector<CC_preset>::iterator it = Control_config_presets.begin(); // NOLINT(modernize-use-auto)
|
||||
|
||||
for (; it != Control_config_presets.end(); ++it) {
|
||||
size_t i;
|
||||
for (i = 0; i < it->bindings.size(); ++i) {
|
||||
if (new_preset.bindings[i] != it->bindings[i]) {
|
||||
// These two differ, check the next in the vector
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == it->bindings.size()) {
|
||||
// These two are equal
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
PresetFileHandler::PresetFileHandler(CFILE* cfp, bool reading) : _cfp(cfp) {
|
||||
Assertion(cfp != nullptr, "File pointer must be valid!");
|
||||
|
||||
if (reading) {
|
||||
json_error_t error;
|
||||
_rootObj = json_load_cfile(cfp, 0, &error);
|
||||
|
||||
if (!_rootObj) {
|
||||
SCP_string errorStr;
|
||||
sprintf(errorStr, "Error while reading pilot file! %d: %s", error.line, error.text);
|
||||
throw std::runtime_error(errorStr);
|
||||
}
|
||||
} else {
|
||||
_rootObj = json_object();
|
||||
}
|
||||
_currentEl = _rootObj;
|
||||
_elementStack.push_back(_currentEl);
|
||||
};
|
||||
|
||||
PresetFileHandler::~PresetFileHandler() {
|
||||
json_decref(_rootObj);
|
||||
|
||||
cfclose(_cfp);
|
||||
_cfp = nullptr;
|
||||
};
|
||||
|
||||
bool PresetFileHandler::beginSectionRead(Section s) {
|
||||
const char * sectionName = lookupSectionName(s);
|
||||
ensureExists(sectionName);
|
||||
|
||||
auto section = json_object_get(_currentEl, sectionName);
|
||||
if (json_typeof(section) != JSON_OBJECT) {
|
||||
Error(LOCATION, "Section must be a JSON object!");
|
||||
return false;
|
||||
}
|
||||
|
||||
pushElement(section);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
void PresetFileHandler::beginSectionWrite(Section s) {
|
||||
auto key_name = lookupSectionName(s);
|
||||
|
||||
json_t* obj = json_object();
|
||||
|
||||
if (json_is_array(_currentEl)) {
|
||||
// Currently in an array, section must be unnamed
|
||||
Assertion(key_name == nullptr, "Elements of array may not be named sections!");
|
||||
json_array_append_new(_currentEl, obj);
|
||||
} else {
|
||||
Assertion(key_name != nullptr, "Section outside of arrays must be named!");
|
||||
json_object_set_new(_currentEl, key_name, obj);
|
||||
}
|
||||
pushElement(obj);
|
||||
};
|
||||
|
||||
bool PresetFileHandler::beginArrayRead(Section s, size_t &size) {
|
||||
Assertion(_arrayIndex == INVALID_SIZE, "Array nesting is not supported yet!");
|
||||
|
||||
const char * name = lookupSectionName(s);
|
||||
ensureExists(name);
|
||||
|
||||
auto array = json_object_get(_currentEl, name);
|
||||
if (json_typeof(array) != JSON_ARRAY) {
|
||||
Error(LOCATION, "Expected an array for '%s' but it was a different type!", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
pushElement(array);
|
||||
size = json_array_size(array);
|
||||
|
||||
if (size == 0) {
|
||||
// Nothing to do here, avoid calling nextArraySection since that assumes that there is at least one element
|
||||
return true;
|
||||
} // Else, advance to the first element in the array
|
||||
|
||||
_arrayIndex = 0;
|
||||
nextArrayElement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PresetFileHandler::beginArrayWrite(Section s) {
|
||||
const char * name = lookupSectionName(s);
|
||||
auto array = json_array();
|
||||
|
||||
if (json_is_array(_currentEl)) {
|
||||
// We are in an array, section must be unnamed
|
||||
Assertion(name == nullptr, "Inside an array there can be no named section!");
|
||||
json_array_append_new(_currentEl, array);
|
||||
} else {
|
||||
Assertion(name != nullptr, "Section outside of arrays must be named!");
|
||||
json_object_set_new(_currentEl, name, array);
|
||||
}
|
||||
pushElement(array);
|
||||
}
|
||||
|
||||
void PresetFileHandler::endSectionRead() {
|
||||
Assertion(json_typeof(_currentEl) == JSON_OBJECT, "Current element for section reading is not an object!");
|
||||
|
||||
popElement();
|
||||
};
|
||||
|
||||
void PresetFileHandler::endSectionWrite() {
|
||||
Assertion(json_is_object(_currentEl), "Section ended while not in a section!");
|
||||
|
||||
popElement();
|
||||
};
|
||||
|
||||
void PresetFileHandler::endArrayRead() {
|
||||
// First, remove the element we are currently in if it exists
|
||||
if (json_typeof(_currentEl) != JSON_ARRAY) {
|
||||
popElement();
|
||||
}
|
||||
|
||||
Assertion(json_typeof(_currentEl) == JSON_ARRAY, "Current element must be an array!");
|
||||
|
||||
// TODO Sections are straightforward, but Arrays may need special consideration re: index.
|
||||
popElement();
|
||||
_arrayIndex = INVALID_SIZE;
|
||||
};
|
||||
|
||||
void PresetFileHandler::endArrayWrite() {
|
||||
Assertion(json_is_array(_currentEl), "Array ended while not in an array!");
|
||||
|
||||
popElement();
|
||||
};
|
||||
|
||||
void PresetFileHandler::ensureExists(const char* name) {
|
||||
if (json_typeof(_currentEl) != JSON_OBJECT) {
|
||||
Error(LOCATION, "JSON reading requires a value with name '%s' but the current element is not an object!", name);
|
||||
}
|
||||
if (json_object_get(_currentEl, name) == nullptr) {
|
||||
Error(LOCATION, "JSON reading requires a value with name '%s' but there is no such value!", name);
|
||||
}
|
||||
}
|
||||
|
||||
void PresetFileHandler::ensureNotExists(const char* name) {
|
||||
// Stuff debug checks into booleans, otherwise clang will trigger a false positive for static method with just the asserts
|
||||
// Make sure we're in an element that can support keys
|
||||
bool supports_keys = json_is_object(_currentEl);
|
||||
|
||||
// Make sure we don't overwrite previous values
|
||||
bool element_is_unique = json_object_get(_currentEl, name) == nullptr;
|
||||
|
||||
Assertion(supports_keys, "Currently not in an element that supports keys!");
|
||||
Assertion(element_is_unique, "Entry with name %s already exists!", name);
|
||||
}
|
||||
|
||||
bool PresetFileHandler::exists(const char* name) {
|
||||
if (json_typeof(_currentEl) != JSON_OBJECT) {
|
||||
return false;
|
||||
}
|
||||
if (json_object_get(_currentEl, name) == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PresetFileHandler::flush() {
|
||||
Assertion(_elementStack.size() == 1, "Not all sections or arrays have been ended!");
|
||||
|
||||
json_dump_cfile(_rootObj, _cfp, JSON_INDENT(4) | JSON_PRESERVE_ORDER);
|
||||
}
|
||||
|
||||
void PresetFileHandler::nextArrayElement() {
|
||||
Assertion(_arrayIndex != INVALID_SIZE, "Array index must be valid for this function!");
|
||||
bool in_section = json_is_object(_currentEl);
|
||||
|
||||
if (in_section) {
|
||||
// We have to pop the previous section first
|
||||
popElement();
|
||||
|
||||
Assertion(json_typeof(_currentEl) == JSON_ARRAY, "The previous element should have been an array!");
|
||||
}
|
||||
|
||||
auto max = json_array_size(_currentEl);
|
||||
Assertion(_arrayIndex <= max, "Invalid array index detected!");
|
||||
|
||||
// Silently ignore if we are one past the last element since this function is used in a for loop where this function
|
||||
// is executed one time after the index is incremented past the last element
|
||||
if (_arrayIndex == max) {
|
||||
// Increment the index so we catch usage errors the next time someone tries to call this function
|
||||
++_arrayIndex;
|
||||
return;
|
||||
}
|
||||
|
||||
// We use the current array index and then increment it to avoid skipping the first element
|
||||
auto el = json_array_get(_currentEl, _arrayIndex);
|
||||
++_arrayIndex;
|
||||
|
||||
pushElement(el);
|
||||
}
|
||||
|
||||
json_t * PresetFileHandler::popElement() {
|
||||
Assertion(_elementStack.size() > 1, "Element stack may not get smaller than one element!");
|
||||
|
||||
_elementStack.pop_back();
|
||||
_currentEl = _elementStack.back();
|
||||
return _currentEl;
|
||||
}
|
||||
|
||||
void PresetFileHandler::pushElement(json_t* el) {
|
||||
Assertion(el != nullptr, "Invalid JSON element pointer passed!");
|
||||
|
||||
_currentEl = el;
|
||||
_elementStack.push_back(_currentEl);
|
||||
}
|
||||
|
||||
SCP_string PresetFileHandler::readString(const char* key) {
|
||||
auto el = json_object_get(_currentEl, key);
|
||||
|
||||
if (el == nullptr || json_typeof(el) != JSON_STRING) {
|
||||
Error(LOCATION, "JSON element %s must be a string but it is not valid!", key);
|
||||
return SCP_string();
|
||||
}
|
||||
auto json_str = json_string_value(el);
|
||||
SCP_string val;
|
||||
val.assign(json_str, json_str + json_string_length(el));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int PresetFileHandler::readInt(const char* key) {
|
||||
auto el = json_object_get(_currentEl, key);
|
||||
|
||||
if (el == nullptr || json_typeof(el) != JSON_INTEGER) {
|
||||
Error(LOCATION, "JSON element %s must be an integer but it is not valid!", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return static_cast<int>(json_integer_value(el));
|
||||
}
|
||||
|
||||
void PresetFileHandler::writeString(const char* key, SCP_string s) {
|
||||
ensureNotExists(key);
|
||||
json_t *jstr = nullptr;
|
||||
|
||||
if (!s.empty()) {
|
||||
// if this string isn't proper UTF-8, try to convert it
|
||||
if (utf8::find_invalid(s.begin(), s.end()) != s.end()) {
|
||||
SCP_string buffer;
|
||||
coerce_to_utf8(buffer, s.c_str());
|
||||
jstr = json_string(buffer.c_str());
|
||||
} else {
|
||||
jstr = json_string(s.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
json_object_set_new(_currentEl, key, jstr);
|
||||
};
|
||||
|
||||
void PresetFileHandler::writeInt(const char* key, int val) {
|
||||
ensureNotExists(key);
|
||||
|
||||
json_object_set_new(_currentEl, key, json_integer(val));
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "cfile/cfile.h"
|
||||
#include "controlconfig/controlsconfig.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/vmallocator.h"
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
/** API for saving and loading player custom bindings, known as Presets
|
||||
|
||||
Init. If the player/preset dir doesn't exist. Create it, and print a help.txt with all enums (as strings) to label
|
||||
the button names and action names.
|
||||
|
||||
Example output:
|
||||
{
|
||||
"actions": {
|
||||
"TARGET_NEXT": {
|
||||
"first": {
|
||||
"cid": 0,
|
||||
"flags": 0,
|
||||
"btn": 0
|
||||
},
|
||||
"second": {
|
||||
"cid": 0,
|
||||
"flags": 0,
|
||||
"btn": 0
|
||||
}
|
||||
},
|
||||
"TARGET_PREV" : {
|
||||
"first": {
|
||||
"cid": 0,
|
||||
"flags": 0,
|
||||
"btn": 0
|
||||
},
|
||||
"second": {
|
||||
"cid": 0,
|
||||
"flags": 0,
|
||||
"btn": 0
|
||||
}
|
||||
},
|
||||
},
|
||||
"version": 0
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
namespace io {
|
||||
namespace presets {
|
||||
|
||||
static const unsigned int PST_VERSION = 0;
|
||||
static const unsigned int PST_FILE_ID = 0x5f545350; // "PST_" in file
|
||||
|
||||
enum class Section {
|
||||
Invalid = -1,
|
||||
Unnamed = 0,
|
||||
|
||||
// arrays
|
||||
Actions,
|
||||
|
||||
// subsections
|
||||
Primary,
|
||||
Secondary
|
||||
};
|
||||
|
||||
class PresetFileHandler {
|
||||
public:
|
||||
PresetFileHandler(CFILE* cfp, bool reading);
|
||||
~PresetFileHandler();
|
||||
|
||||
/**
|
||||
* begins writing to a section using the presets::Section as its name
|
||||
* @param[in] s id of the section
|
||||
*/
|
||||
void beginSectionWrite(presets::Section s);
|
||||
|
||||
/**
|
||||
* begins reading a section, if it exists
|
||||
* @param[in] s id of the section
|
||||
*/
|
||||
bool beginSectionRead(presets::Section s);
|
||||
|
||||
/**
|
||||
* Begins writing to an array using the presets::Section as its name
|
||||
*
|
||||
* @param[in] s id of the array
|
||||
*/
|
||||
void beginArrayWrite(presets::Section s);
|
||||
|
||||
/**
|
||||
* Begins reading from an array
|
||||
* @param[int] s id of the array
|
||||
* @param[out] size the size of the array
|
||||
*/
|
||||
bool beginArrayRead(presets::Section s, size_t &size);
|
||||
|
||||
/**
|
||||
* ends writing to a section
|
||||
*/
|
||||
void endSectionWrite();
|
||||
|
||||
/**
|
||||
* ends reading a section
|
||||
*/
|
||||
void endSectionRead();
|
||||
|
||||
/**
|
||||
* ends writing to an array.
|
||||
*/
|
||||
void endArrayWrite();
|
||||
|
||||
/**
|
||||
* ends reading from an array
|
||||
*/
|
||||
void endArrayRead();
|
||||
|
||||
/**
|
||||
* Writes a string with the given key name
|
||||
*/
|
||||
void writeString(const char* key, SCP_string s);
|
||||
|
||||
/**
|
||||
* Reads a string with the given key name
|
||||
*/
|
||||
SCP_string readString(const char* key);
|
||||
|
||||
/**
|
||||
* Writes an int with the given key name
|
||||
*/
|
||||
void writeInt(const char* key, int val);
|
||||
|
||||
/**
|
||||
* Reads an int with the given key name
|
||||
*/
|
||||
int readInt(const char* key);
|
||||
|
||||
/**
|
||||
* For writing, Flushes the buffers and dumps to file
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* Advances the array parser to the next element
|
||||
*/
|
||||
void nextArrayElement();
|
||||
|
||||
private:
|
||||
CFILE* _cfp = nullptr;
|
||||
|
||||
json_t* _rootObj = nullptr;
|
||||
|
||||
json_t* _currentEl = nullptr;
|
||||
|
||||
/**
|
||||
* BufferStack of all json elements being read/written
|
||||
*/
|
||||
SCP_vector<json_t*> _elementStack;
|
||||
|
||||
/**
|
||||
* Stack to keep track of the heirachy/nesting. Back is immediate parent, Front is root
|
||||
*/
|
||||
SCP_vector<void*> _parentStack;
|
||||
|
||||
size_t _arrayIndex = INVALID_SIZE;
|
||||
|
||||
//void* _sectionIterator = nullptr; //!< Pointer to the current section/array we're in
|
||||
|
||||
private:
|
||||
/**
|
||||
* Pushes an element onto the elementStack
|
||||
*/
|
||||
void pushElement(json_t* el);
|
||||
|
||||
/**
|
||||
* Pops an element off the elementStack
|
||||
*/
|
||||
json_t * popElement();
|
||||
|
||||
/**
|
||||
* Used for optionals, checks if an element exists with the given name
|
||||
* @return True if the element exists
|
||||
* @return False otherwise
|
||||
*/
|
||||
bool exists(const char* name);
|
||||
|
||||
/**
|
||||
* Throws an error if an element with the given name does not exist
|
||||
*/
|
||||
void ensureExists(const char* name);
|
||||
|
||||
/**
|
||||
* Throws an error if an element with the given name exists
|
||||
*/
|
||||
void ensureNotExists(const char* name);
|
||||
};
|
||||
|
||||
} // namespace presets
|
||||
} // namespace io
|
||||
|
||||
/**
|
||||
* @brief Saves the given preset to file.
|
||||
* @param[in] preset The preset to save
|
||||
* @param[in] overwrite If true, overwrite existing preset file which have the same name as the given preset
|
||||
*
|
||||
* @returns True if successful, or
|
||||
* @returns False Preset does not have a name, or
|
||||
* @returns False Preset file exists and overwrite == false, or
|
||||
* @returns False Preset file .JSON could not be written
|
||||
*/
|
||||
bool save_preset_file(CC_preset preset, bool overwrite);
|
||||
|
||||
/**
|
||||
* @brief Loads in all preset .json files from 'players/presets'
|
||||
* @details If a preset file is unique, it is loaded into the game and available for use by the player. However, if
|
||||
* a preset is a duplicate of another preset, it is ignored, and the player is warned of it
|
||||
*/
|
||||
void load_preset_files();
|
||||
|
||||
/**
|
||||
* @brief Checks if the given preset is a duplicate within Control_config_presets vector
|
||||
* @returns iterator to the duplicate if found, or
|
||||
* @returns iterator to Control_config_presets.end() otherwise
|
||||
*/
|
||||
SCP_vector<CC_preset>::iterator preset_find_duplicate(const CC_preset& new_preset);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns true if a preset file with the given name exists.
|
||||
*/
|
||||
bool preset_file_exists(SCP_string name);
|
||||
@@ -93,12 +93,13 @@ class Decoder {
|
||||
bool m_decoding;
|
||||
size_t m_queueSize = 0;
|
||||
|
||||
Decoder(const Decoder&) SCP_DELETED_FUNCTION;
|
||||
|
||||
Decoder& operator=(const Decoder&) SCP_DELETED_FUNCTION;
|
||||
protected:
|
||||
Decoder();
|
||||
|
||||
public:
|
||||
Decoder(const Decoder&) = delete;
|
||||
Decoder& operator=(const Decoder&) = delete;
|
||||
|
||||
virtual ~Decoder();
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ void VideoPresenter::uploadVideoFrame(const VideoFramePtr& frame) {
|
||||
}
|
||||
}
|
||||
|
||||
void VideoPresenter::displayFrame(float x1, float y1, float x2, float y2, float alpha) {
|
||||
void VideoPresenter::displayFrame(float x1, float y1, float x2, float y2) {
|
||||
GR_DEBUG_SCOPE("Draw video frame");
|
||||
|
||||
movie_vertex vertex_data[4];
|
||||
@@ -127,26 +127,10 @@ void VideoPresenter::displayFrame(float x1, float y1, float x2, float y2, float
|
||||
layout.add_vertex_component(vertex_format_data::POSITION2, sizeof(vertex_data[0]), offsetof(movie_vertex, pos));
|
||||
layout.add_vertex_component(vertex_format_data::TEX_COORD2, sizeof(vertex_data[0]), offsetof(movie_vertex, uv));
|
||||
|
||||
switch (_properties.pixelFormat) {
|
||||
case FramePixelFormat::YUV420:
|
||||
material_set_movie(&_movie_material,
|
||||
_planeTextureHandles[0],
|
||||
_planeTextureHandles[1],
|
||||
_planeTextureHandles[2],
|
||||
alpha);
|
||||
if (_properties.pixelFormat == FramePixelFormat::YUV420) {
|
||||
gr_render_movie(&_movie_material, PRIM_TYPE_TRISTRIP, &layout, 4, gr_immediate_buffer_handle, offset);
|
||||
break;
|
||||
case FramePixelFormat::BGR:
|
||||
material_set_unlit(&_rgb_material, _planeTextureHandles[0], alpha, false, false);
|
||||
} else {
|
||||
gr_render_primitives(&_rgb_material, PRIM_TYPE_TRISTRIP, &layout, 0, 4, gr_immediate_buffer_handle, offset);
|
||||
break;
|
||||
case FramePixelFormat::BGRA:
|
||||
material_set_unlit(&_rgb_material, _planeTextureHandles[0], alpha, true, false);
|
||||
gr_render_primitives(&_rgb_material, PRIM_TYPE_TRISTRIP, &layout, 0, 4, gr_immediate_buffer_handle, offset);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Unhandled enum value!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 1153 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user