mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 17:52:08 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af41b5bf19 |
No files matched your search
@@ -65,7 +65,7 @@ jobs:
|
||||
name: Build Linux distribution zip
|
||||
needs: build_linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-748b19d
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-dd9d8cf
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
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
|
||||
run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh
|
||||
|
||||
build_windows:
|
||||
strategy:
|
||||
@@ -171,7 +171,7 @@ jobs:
|
||||
name: Build Windows distribution zip
|
||||
needs: build_windows
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-748b19d
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-dd9d8cf
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [Win32, x64]
|
||||
@@ -207,4 +207,4 @@ jobs:
|
||||
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
|
||||
run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
name: Build Linux distribution zip
|
||||
needs: build_linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-748b19d
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-dd9d8cf
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
@@ -126,7 +126,8 @@ jobs:
|
||||
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 }}
|
||||
# All builds are zips at the moment so this is fine for now
|
||||
asset_content_type: application/zip
|
||||
|
||||
build_windows:
|
||||
strategy:
|
||||
@@ -206,7 +207,7 @@ jobs:
|
||||
name: Build Windows distribution zip
|
||||
needs: build_windows
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-748b19d
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-dd9d8cf
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [Win32, x64]
|
||||
@@ -251,13 +252,5 @@ jobs:
|
||||
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:
|
||||
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 }}
|
||||
# All builds are zips at the moment so this is fine for now
|
||||
asset_content_type: application/zip
|
||||
@@ -1,224 +0,0 @@
|
||||
name: Build Test package
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'test/*'
|
||||
|
||||
env:
|
||||
QT_VERSION: 5.12.9
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [FastDebug, Release]
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
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 }}
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
- 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: gcc-5
|
||||
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
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: Generate AppImage
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/generate_appimage.sh $GITHUB_WORKSPACE/build/install
|
||||
- name: Upload build result
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: linux-${{ matrix.configuration }}
|
||||
path: ${{ github.workspace }}/build/install/*.AppImage
|
||||
linux_zip:
|
||||
name: Build Linux distribution zip
|
||||
needs: build_linux
|
||||
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@v1
|
||||
with:
|
||||
name: linux-Release
|
||||
path: builds
|
||||
- name: Download FastDebug builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: linux-FastDebug
|
||||
path: builds
|
||||
- name: Create Distribution package
|
||||
working-directory: ./builds
|
||||
run: $GITHUB_WORKSPACE/ci/linux/create_dist_pack.sh Linux
|
||||
- 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
|
||||
|
||||
build_windows:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [FastDebug, Release]
|
||||
arch: [Win32, x64]
|
||||
simd: [SSE2]
|
||||
name: Windows
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
- 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: 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:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
ARCHITECTURE: ${{ matrix.arch }}
|
||||
SIMD: ${{ matrix.simd }}
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_USE_SPEECH="ON" \
|
||||
-DFSO_USE_VOICEREC="ON" -DMSVC_SIMD_INSTRUCTIONS="$SIMD" \
|
||||
-DFSO_BUILD_QTFRED=ON -DFSO_BUILD_TESTS=ON \
|
||||
-DFSO_INSTALL_DEBUG_FILES="ON" -A "$ARCHITECTURE" \
|
||||
-G "Visual Studio 16 2019" -T "v142" ..
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --build . --config "$CONFIGURATION" --target INSTALL -- /verbosity:minimal
|
||||
ls -alR "$(pwd)/install"
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
shell: bash
|
||||
run: ./bin/$CONFIGURATION/unittests --gtest_shuffle
|
||||
- name: Upload build result
|
||||
uses: actions/upload-artifact@v2
|
||||
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:
|
||||
matrix:
|
||||
arch: [Win32, x64]
|
||||
simd: [SSE2]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: '0'
|
||||
ref: '${{ github.ref }}'
|
||||
- name: Download Release builds
|
||||
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
|
||||
working-directory: ./builds
|
||||
shell: bash
|
||||
env:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
SIMD: ${{ matrix.simd }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/create_dist_pack.sh Windows
|
||||
- name: Upload result package
|
||||
working-directory: ./builds
|
||||
shell: bash
|
||||
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
|
||||
@@ -8,7 +8,6 @@ env:
|
||||
jobs:
|
||||
build_linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
compiler: [gcc-5, gcc-10, clang-9]
|
||||
|
||||
+3
-8
@@ -44,14 +44,9 @@ PROJECT(FS2_Open)
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
# Currently only GCC support ccache properly without generating additional warnings
|
||||
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})
|
||||
endif()
|
||||
if(NOT CMAKE_CXX_COMPILER_LAUNCHER)
|
||||
message(STATUS "Using ccache for C++ compilation: ${CCACHE_PROGRAM}")
|
||||
SET(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
|
||||
endif()
|
||||
message(STATUS "Using ccache for compilation: ${CCACHE_PROGRAM}")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_PROGRAM}")
|
||||
endif()
|
||||
|
||||
IF(CMAKE_CROSSCOMPILING)
|
||||
|
||||
@@ -32,5 +32,4 @@ export LD_LIBRARY_PATH
|
||||
|
||||
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 ..
|
||||
-DFFMPEG_USE_PRECOMPILED=ON -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON -DFSO_BUILD_QTFRED=ON ..
|
||||
@@ -12,19 +12,13 @@ if [ "$OS" = "Linux" ]; then
|
||||
|
||||
echo "::set-output name=package_path::$(pwd)/$(get_package_name)-builds-Linux.tar.gz"
|
||||
echo "::set-output name=package_name::$(get_package_name)-builds-Linux.tar.gz"
|
||||
echo "::set-output name=package_mime::$(file -b --mime-type "$(pwd)/$(get_package_name)-builds-Linux.tar.gz")"
|
||||
elif [ "$OS" = "Windows" ]; then
|
||||
7z a -xr'!*.pdb' "$(get_package_name)-builds-$ARCH-$SIMD.zip" "*"
|
||||
|
||||
echo "::set-output name=package_path::$(pwd)/$(get_package_name)-builds-$ARCH-$SIMD.zip"
|
||||
echo "::set-output name=package_name::$(get_package_name)-builds-$ARCH-$SIMD.zip"
|
||||
echo "::set-output name=package_mime::$(file -b --mime-type "$(pwd)/$(get_package_name)-builds-$ARCH-$SIMD.zip")"
|
||||
|
||||
7z a "$(get_package_name)-debug-$ARCH-$SIMD.7z" "*.pdb"
|
||||
|
||||
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")"
|
||||
else
|
||||
echo "Invalid OS: $OS"
|
||||
fi
|
||||
@@ -1,24 +1,19 @@
|
||||
function get_package_name() {
|
||||
if git describe --match 'nightly_*' --exact-match 2>/dev/null >/dev/null; then
|
||||
if git describe --match 'nightly_*' --exact-match >/dev/null; then
|
||||
echo -n "$(git describe --match "nightly_*" --exact-match)"
|
||||
return
|
||||
fi
|
||||
|
||||
if git describe --match 'release_*' --exact-match 2>/dev/null >/dev/null; then
|
||||
if git describe --match 'release_*' --exact-match >/dev/null; then
|
||||
echo -n "$(git describe --match "release_*" --exact-match | sed 's/release_/fs2_open_/i')"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$(git branch --show-current)" =~ ^test\/(.*)$ ]]; then
|
||||
echo -n "test_${BASH_REMATCH[1]}"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "unknown_config"
|
||||
}
|
||||
|
||||
function get_version_name() {
|
||||
if git describe --match 'nightly_*' --exact-match 2>/dev/null >/dev/null; then
|
||||
if git describe --match 'nightly_*' --exact-match >/dev/null; then
|
||||
local tag_name=$(git describe --match "nightly_*" --exact-match)
|
||||
|
||||
# Use the bash regex matching for getting the relevant part of the tag name
|
||||
@@ -28,7 +23,7 @@ function get_version_name() {
|
||||
return
|
||||
fi
|
||||
|
||||
if git describe --match 'release_*' --exact-match 2>/dev/null >/dev/null; then
|
||||
if git describe --match 'release_*' --exact-match >/dev/null; then
|
||||
local tag_name=$(git describe --match "release_*" --exact-match)
|
||||
|
||||
# Use the bash regex matching for getting the relevant part of the tag name
|
||||
@@ -38,11 +33,6 @@ function get_version_name() {
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$(git branch --show-current)" =~ ^test\/(.*)$ ]]; then
|
||||
echo -n "${BASH_REMATCH[1]}"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "unknown_config"
|
||||
}
|
||||
|
||||
|
||||
@@ -2,20 +2,11 @@
|
||||
|
||||
set -e
|
||||
|
||||
UPLOAD_TYPE=$1
|
||||
|
||||
SCRIPT=$(readlink -f "$0")
|
||||
HERE=$(dirname "$SCRIPT")
|
||||
|
||||
source $HERE/dist_functions.sh
|
||||
|
||||
if [ "$UPLOAD_TYPE" = "nightly" ]; then
|
||||
SSHPASS=$INDIEGAMES_SSHPASS upload_files_to_sftp "$INDIEGAMES_USER@scp.indiegames.us" "public_html/builds/nightly"
|
||||
SSHPASS=$INDIEGAMES_SSHPASS upload_files_to_sftp "$INDIEGAMES_USER@scp.indiegames.us" "public_html/builds/nightly"
|
||||
|
||||
SSHPASS=$DATACORDER_SSHPASS upload_files_to_sftp "$DATACORDER_USER@porphyrion.feralhosting.com" "www/datacorder.porphyrion.feralhosting.com/public_html/builds/nightly"
|
||||
elif [ "$UPLOAD_TYPE" = "test" ]; then
|
||||
SSHPASS=$DATACORDER_SSHPASS upload_files_to_sftp "$DATACORDER_USER@porphyrion.feralhosting.com" "www/datacorder.porphyrion.feralhosting.com/public_html/builds/test"
|
||||
else
|
||||
echo "Unknown upload type $UPLOAD_TYPE"
|
||||
exit 1
|
||||
fi
|
||||
SSHPASS=$DATACORDER_SSHPASS upload_files_to_sftp "$DATACORDER_USER@porphyrion.feralhosting.com" "www/datacorder.porphyrion.feralhosting.com/public_html/builds/nightly"
|
||||
@@ -63,8 +63,6 @@ ENDIF(FSO_USE_SPEECH)
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC jansson)
|
||||
|
||||
target_link_libraries(code PUBLIC variant)
|
||||
|
||||
# Headers for standalone "gui" on UNIX
|
||||
IF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${MONGOOSE_LIBS})
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
#include "Action.h"
|
||||
|
||||
namespace actions {
|
||||
|
||||
Action::~Action() = default;
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
namespace actions {
|
||||
|
||||
enum class ActionResult
|
||||
{
|
||||
Finished,
|
||||
Errored,
|
||||
Wait
|
||||
};
|
||||
|
||||
class Action {
|
||||
public:
|
||||
virtual ~Action() = 0;
|
||||
|
||||
virtual ActionResult execute(ProgramLocals& locals) const = 0;
|
||||
|
||||
virtual std::unique_ptr<Action> clone() const = 0;
|
||||
};
|
||||
|
||||
} // namespace actions
|
||||
@@ -1,20 +0,0 @@
|
||||
|
||||
#include "ActionDefinition.h"
|
||||
|
||||
namespace actions {
|
||||
|
||||
ActionDefinition::ActionDefinition(SCP_string name, SCP_vector<ActionParameter> parameters)
|
||||
: m_name(std::move(name)), m_parameters(std::move(parameters))
|
||||
{
|
||||
}
|
||||
|
||||
const SCP_string& ActionDefinition::getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
const SCP_vector<ActionParameter>& ActionDefinition::getParameters() const
|
||||
{
|
||||
return m_parameters;
|
||||
}
|
||||
|
||||
} // namespace actions
|
||||
@@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Action.h"
|
||||
|
||||
#include "actions/expression/ActionExpression.h"
|
||||
#include "actions/expression/Value.h"
|
||||
|
||||
namespace actions {
|
||||
|
||||
struct ActionParameter {
|
||||
SCP_string name;
|
||||
expression::ValueType type;
|
||||
};
|
||||
|
||||
class ActionDefinition {
|
||||
public:
|
||||
ActionDefinition(SCP_string name, SCP_vector<ActionParameter> parameters);
|
||||
|
||||
const SCP_string& getName() const;
|
||||
const SCP_vector<actions::ActionParameter>& getParameters() const;
|
||||
|
||||
virtual flagset<ProgramContextFlags> getRequiredContext() const = 0;
|
||||
|
||||
virtual std::unique_ptr<Action> createAction(
|
||||
const SCP_unordered_map<SCP_string, expression::ActionExpression>& parameterExpressions) const = 0;
|
||||
|
||||
private:
|
||||
SCP_string m_name;
|
||||
SCP_vector<ActionParameter> m_parameters;
|
||||
};
|
||||
|
||||
} // namespace actions
|
||||
@@ -1,148 +0,0 @@
|
||||
|
||||
#include "ActionDefinitionManager.h"
|
||||
|
||||
#include "BuiltinActionDefinition.h"
|
||||
|
||||
#include "actions/types/MoveToSubmodel.h"
|
||||
#include "actions/types/ParticleEffectAction.h"
|
||||
#include "actions/types/PlaySoundAction.h"
|
||||
#include "actions/types/SetDirectionAction.h"
|
||||
#include "actions/types/SetPositionAction.h"
|
||||
#include "actions/types/WaitAction.h"
|
||||
#include "utils/join_string.h"
|
||||
|
||||
namespace actions {
|
||||
namespace {
|
||||
|
||||
void parseActionParameters(const SCP_vector<ActionParameter>& params,
|
||||
SCP_unordered_map<SCP_string, expression::ActionExpression>& parameterExpressions,
|
||||
const expression::ParseContext& context)
|
||||
{
|
||||
// We do not impose a set order of the parameters so we need to do something similar to the outer loop and
|
||||
// check for every possibility in a loop.
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
// We might be doing some empty loops here in case of errors but there is a bound so this will finish
|
||||
// eventually
|
||||
for (const auto& parameter : params) {
|
||||
// Do not check for parameters which we already found
|
||||
if (parameterExpressions.find(parameter.name) != parameterExpressions.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto paramString = "+" + parameter.name + ":";
|
||||
|
||||
if (!optional_string(paramString.c_str())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We found our parameter!
|
||||
parameterExpressions[parameter.name] =
|
||||
expression::ActionExpression::parseFromTable(parameter.type, context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const ActionDefinitionManager& ActionDefinitionManager::instance()
|
||||
{
|
||||
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)
|
||||
{
|
||||
m_definitions.push_back(std::move(def));
|
||||
}
|
||||
std::unique_ptr<Action> ActionDefinitionManager::parseAction(const flagset<ProgramContextFlags>& context_flags,
|
||||
const expression::ParseContext& context) const
|
||||
{
|
||||
// We search through all definitions for every time we want to check for an action. This is not the most efficient
|
||||
// way to handle this since it results in an O(n^2) runtime. A better alternative would be to either construct a
|
||||
// regex with all the accepted actions as alternatives (although runtime of this is also doubtful) or peek at the
|
||||
// next token, extract the relevant part (The stuff between "+" and ":") and then look that up in a map. Then we
|
||||
// would get down to O(n).
|
||||
// Since this only happens at parse time, this is good enough for now.
|
||||
for (const auto& actionDef : m_definitions) {
|
||||
const auto parseString = "+" + actionDef->getName() + ":";
|
||||
|
||||
if (!optional_string(parseString.c_str())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SCP_unordered_map<SCP_string, expression::ActionExpression> parameterExpressions;
|
||||
const auto& params = actionDef->getParameters();
|
||||
|
||||
if (params.size() == 1) {
|
||||
// Single parameters are expected to be placed directly after the action name
|
||||
parameterExpressions[params.front().name] =
|
||||
expression::ActionExpression::parseFromTable(params.front().type, context);
|
||||
} else {
|
||||
parseActionParameters(params, parameterExpressions, context);
|
||||
|
||||
if (parameterExpressions.size() != params.size()) {
|
||||
SCP_vector<ActionParameter> missingParameters;
|
||||
std::copy_if(params.cbegin(),
|
||||
params.cend(),
|
||||
std::back_inserter(missingParameters),
|
||||
[¶meterExpressions](const ActionParameter& param) {
|
||||
const auto iter = parameterExpressions.find(param.name);
|
||||
return iter == parameterExpressions.cend();
|
||||
});
|
||||
|
||||
SCP_vector<SCP_string> missingParameterNames;
|
||||
std::transform(missingParameters.cbegin(),
|
||||
missingParameters.cend(),
|
||||
std::back_inserter(missingParameterNames),
|
||||
[](const ActionParameter& param) { return "+" + param.name; });
|
||||
|
||||
const auto namesList = util::join_container(missingParameterNames, ", ");
|
||||
|
||||
error_display(0, "Missing parameters for action '%s': %s", parseString.c_str(), namesList.c_str());
|
||||
|
||||
// Fix up the parameter list so that we can continue parsing
|
||||
for (const auto& missingParam : missingParameters) {
|
||||
parameterExpressions[missingParam.name] = expression::ActionExpression();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto actionInstance = actionDef->createAction(parameterExpressions);
|
||||
|
||||
const auto requiredFlags = actionDef->getRequiredContext();
|
||||
|
||||
if ((context_flags & requiredFlags) != requiredFlags) {
|
||||
// We are missing at least one context flag so this action is not valid here. We still return the action
|
||||
// here though since that is the only way we can keep parsing the table without throwing random error
|
||||
// messages at the user. However, this will trigger an Assertion (in debug) or crashes when the action is
|
||||
// actually run later.
|
||||
error_display(0,
|
||||
"The action %s is not available in the current context. This will lead to an Assertion failure when "
|
||||
"this action is triggered.",
|
||||
parseString.c_str());
|
||||
}
|
||||
|
||||
return actionInstance;
|
||||
}
|
||||
|
||||
return std::unique_ptr<Action>();
|
||||
}
|
||||
|
||||
} // namespace actions
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ActionDefinition.h"
|
||||
|
||||
namespace actions {
|
||||
|
||||
class ActionDefinitionManager {
|
||||
public:
|
||||
// This type is supposed to be global so it may not be copied
|
||||
ActionDefinitionManager(const ActionDefinitionManager&) = delete;
|
||||
ActionDefinitionManager& operator=(const ActionDefinitionManager&) = delete;
|
||||
|
||||
ActionDefinitionManager(ActionDefinitionManager&&) noexcept = default;
|
||||
ActionDefinitionManager& operator=(ActionDefinitionManager&&) noexcept = default;
|
||||
|
||||
std::unique_ptr<Action> parseAction(const flagset<ProgramContextFlags>& context_flags,
|
||||
const expression::ParseContext& context) const;
|
||||
|
||||
static const ActionDefinitionManager& instance();
|
||||
|
||||
private:
|
||||
ActionDefinitionManager() = default;
|
||||
|
||||
void addDefinition(std::unique_ptr<ActionDefinition> def);
|
||||
|
||||
SCP_vector<std::unique_ptr<ActionDefinition>> m_definitions;
|
||||
};
|
||||
|
||||
} // namespace actions
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
#include "BuiltinActionDefinition.h"
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ActionDefinition.h"
|
||||
|
||||
#include "expression/Value.h"
|
||||
|
||||
namespace actions {
|
||||
|
||||
template <typename TAction>
|
||||
class BuiltinActionDefinition : public ActionDefinition {
|
||||
using ActionValueType = typename TAction::ValueType;
|
||||
|
||||
public:
|
||||
BuiltinActionDefinition(SCP_string name)
|
||||
: ActionDefinition(std::move(name), {{"parameter", expression::ValueTypeTraits<ActionValueType>::type}})
|
||||
{
|
||||
}
|
||||
|
||||
flagset<ProgramContextFlags> getRequiredContext() const override
|
||||
{
|
||||
return flagset<ProgramContextFlags>(TAction::getRequiredExecutionContextFlags());
|
||||
}
|
||||
|
||||
std::unique_ptr<Action> createAction(
|
||||
const SCP_unordered_map<SCP_string, expression::ActionExpression>& parameterExpressions) const override
|
||||
{
|
||||
const auto paramIter = parameterExpressions.find("parameter");
|
||||
|
||||
// This should have been caught earlier
|
||||
Assertion(paramIter != parameterExpressions.cend(), "Could not find built-in parameter!");
|
||||
|
||||
return std::unique_ptr<Action>(new TAction(paramIter->second.template asTyped<ActionValueType>()));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace actions
|
||||
@@ -1,174 +0,0 @@
|
||||
|
||||
#include "Program.h"
|
||||
|
||||
#include "Action.h"
|
||||
|
||||
#include "actions/ActionDefinitionManager.h"
|
||||
#include "executor/global_executors.h"
|
||||
#include "tracing/Monitor.h"
|
||||
#include "tracing/categories.h"
|
||||
#include "tracing/tracing.h"
|
||||
|
||||
namespace {
|
||||
using namespace actions;
|
||||
|
||||
tracing::Monitor<int> RunningProgramsMonitor("RunningPrograms", 0);
|
||||
|
||||
struct ProgramRunner {
|
||||
tracing::RunningCounter runningCounter{RunningProgramsMonitor};
|
||||
|
||||
ProgramInstance instance;
|
||||
|
||||
explicit ProgramRunner(const ProgramInstance& _instance) : instance(_instance) {}
|
||||
|
||||
executor::Executor::CallbackResult operator()()
|
||||
{
|
||||
TRACE_SCOPE(tracing::ProgramStepOne);
|
||||
|
||||
auto res = instance.step();
|
||||
|
||||
switch (res) {
|
||||
case ProgramState::Running:
|
||||
// If the program is still running then we need to reschedule our execution
|
||||
return executor::Executor::CallbackResult::Reschedule;
|
||||
case ProgramState::Done:
|
||||
case ProgramState::Errored:
|
||||
default:
|
||||
return executor::Executor::CallbackResult::Done;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace actions {
|
||||
|
||||
ProgramInstance::ProgramInstance(const actions::Program* program) : _program(program) {}
|
||||
|
||||
ProgramState ProgramInstance::step()
|
||||
{
|
||||
if (!_locals.host.IsValid()) {
|
||||
return ProgramState::Done;
|
||||
}
|
||||
|
||||
auto& actions = _program->getActions();
|
||||
|
||||
for (; _currentInstruction < actions.size(); ++_currentInstruction) {
|
||||
auto& action = actions[_currentInstruction];
|
||||
|
||||
auto result = action->execute(_locals);
|
||||
|
||||
if (result == ActionResult::Errored) {
|
||||
return ProgramState::Errored;
|
||||
}
|
||||
|
||||
if (result == ActionResult::Wait) {
|
||||
// Action is not done yet, call again next frame
|
||||
return ProgramState::Running;
|
||||
}
|
||||
|
||||
// Action finished, run next action in program
|
||||
}
|
||||
|
||||
// End of program reached, we are done now
|
||||
return ProgramState::Done;
|
||||
}
|
||||
ProgramLocals& ProgramInstance::locals()
|
||||
{
|
||||
return _locals;
|
||||
}
|
||||
const ProgramLocals& ProgramInstance::locals() const
|
||||
{
|
||||
return _locals;
|
||||
}
|
||||
|
||||
const SCP_vector<std::unique_ptr<actions::Action>>& Program::getActions() const
|
||||
{
|
||||
return _actions;
|
||||
}
|
||||
|
||||
Program::Program() = default;
|
||||
|
||||
ProgramInstance Program::newInstance() const
|
||||
{
|
||||
return ProgramInstance(this);
|
||||
}
|
||||
|
||||
void Program::parseActions(const flagset<ProgramContextFlags>& context_flags, const expression::ParseContext& context)
|
||||
{
|
||||
while (true) {
|
||||
auto action = ActionDefinitionManager::instance().parseAction(context_flags, context);
|
||||
|
||||
if (!action) {
|
||||
break;
|
||||
}
|
||||
|
||||
_actions.push_back(std::move(action));
|
||||
}
|
||||
}
|
||||
|
||||
Program::Program(const Program& other) { *this = other; }
|
||||
Program& Program::operator=(const Program& other)
|
||||
{
|
||||
_actions.clear();
|
||||
_actions.reserve(other._actions.size());
|
||||
|
||||
for (auto& action : other._actions) {
|
||||
_actions.emplace_back(action->clone());
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Program& Program::operator=(Program&&) noexcept = default;
|
||||
Program::Program(Program&&) noexcept = default;
|
||||
|
||||
const SCP_vector<actions::Program>& ProgramSet::getPrograms() const
|
||||
{
|
||||
return _programs;
|
||||
}
|
||||
|
||||
ProgramSet::ProgramSet() = default;
|
||||
|
||||
ProgramSet::ProgramSet(const flagset<ProgramContextFlags>& context_flags) : _contextFlags(context_flags) {}
|
||||
|
||||
void ProgramSet::start(object* objp, const vec3d* local_pos, const matrix* local_orient, int submodel) const
|
||||
{
|
||||
if (_contextFlags[ProgramContextFlags::HasObject]) {
|
||||
Assertion(objp != nullptr, "Invalid object pointer supplied.");
|
||||
}
|
||||
if (_contextFlags[ProgramContextFlags::HasSubobject]) {
|
||||
Assertion(submodel >= 0, "Invalid subobject supplied.");
|
||||
}
|
||||
|
||||
for (const auto& prog : _programs) {
|
||||
auto instance = prog.newInstance();
|
||||
|
||||
instance.locals().host = object_h(objp);
|
||||
instance.locals().localPosition = *local_pos;
|
||||
instance.locals().localOrient = *local_orient;
|
||||
instance.locals().hostSubobject = submodel;
|
||||
instance.locals().variables = getDefaultTableVariables();
|
||||
|
||||
// Just use the executor system to take care of running this program
|
||||
executor::OnSimulationExecutor->post(ProgramRunner(instance));
|
||||
}
|
||||
}
|
||||
|
||||
ProgramSet ProgramSet::parseProgramSet(const char* tag, const flagset<ProgramContextFlags>& context_flags)
|
||||
{
|
||||
ProgramSet set(context_flags);
|
||||
|
||||
const auto parseContext = getTableParseContext();
|
||||
|
||||
while (optional_string(tag)) {
|
||||
Program p;
|
||||
p.parseActions(context_flags, parseContext);
|
||||
|
||||
set._programs.push_back(std::move(p));
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
} // namespace actions
|
||||
@@ -1,112 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
#include "Action.h"
|
||||
#include "common.h"
|
||||
|
||||
namespace actions {
|
||||
|
||||
class Program;
|
||||
class ProgramSet;
|
||||
|
||||
enum class ProgramState
|
||||
{
|
||||
Running,
|
||||
Done,
|
||||
Errored
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An instance of a program execution
|
||||
*
|
||||
* A program instance contains all information related to a single execution of a program. A program instance is
|
||||
* independent from other instances. Local variables used by actions are stored in a specific structure which can be
|
||||
* used by actions to keep track of state in this specific instance.
|
||||
*/
|
||||
class ProgramInstance {
|
||||
size_t _currentInstruction = 0;
|
||||
ProgramLocals _locals;
|
||||
const Program* _program = nullptr;
|
||||
|
||||
public:
|
||||
explicit ProgramInstance(const Program* program);
|
||||
|
||||
ProgramState step();
|
||||
|
||||
ProgramLocals& locals();
|
||||
const ProgramLocals& locals() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A program specified in a table
|
||||
*
|
||||
* A program is a sequential collection of Actions which are executed in sequence.
|
||||
*/
|
||||
class Program {
|
||||
SCP_vector<std::unique_ptr<Action>> _actions;
|
||||
|
||||
public:
|
||||
Program();
|
||||
|
||||
Program(const Program& other);
|
||||
Program& operator=(const Program& other);
|
||||
|
||||
Program(Program&& other) noexcept;
|
||||
Program& operator=(Program&& other) noexcept;
|
||||
|
||||
const SCP_vector<std::unique_ptr<actions::Action>>& getActions() const;
|
||||
|
||||
/**
|
||||
* @brief Creates a new program instance from this program
|
||||
*
|
||||
* The program instance is initialized to its initial values.
|
||||
*
|
||||
* @return The new program instance
|
||||
*/
|
||||
ProgramInstance newInstance() const;
|
||||
|
||||
void parseActions(const flagset<ProgramContextFlags>& context_flags, const expression::ParseContext& context);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A set of parsed programs
|
||||
*
|
||||
* Programs are parsed in sets of zero or more programs to make it possible for the content creator to split up
|
||||
* programs.
|
||||
*/
|
||||
class ProgramSet {
|
||||
flagset<ProgramContextFlags> _contextFlags;
|
||||
|
||||
SCP_vector<Program> _programs;
|
||||
|
||||
public:
|
||||
ProgramSet();
|
||||
explicit ProgramSet(const flagset<ProgramContextFlags>& context_flags);
|
||||
|
||||
const SCP_vector<actions::Program>& getPrograms() const;
|
||||
|
||||
/**
|
||||
* @brief Starts the execution of all programs within this set with the specified parameters
|
||||
* @param objp The object on which this program is executed
|
||||
* @param local_pos The local position relative to the object or the subsystem if specified
|
||||
* @param local_orien The local orientation relative to the object or the subsystem if specified
|
||||
* @param submodel The submodel of the model to which this program is attached to.
|
||||
*/
|
||||
void start(object* objp, const vec3d* local_pos, const matrix* local_orient, int submodel = -1) const;
|
||||
|
||||
/**
|
||||
* @brief Parses a program set of zero or more programs
|
||||
*
|
||||
* The tag is entirely optional and if it doesn't appear in the current table then an empty set will be returned
|
||||
* which does nothing if started.
|
||||
*
|
||||
* @param tag The tag which is required before a program can be specified
|
||||
* @param context_flags A set of flags which specify in which environment the program will be used. This is used
|
||||
* mainly for validating the used actions.
|
||||
* @return The parsed program set.
|
||||
*/
|
||||
static ProgramSet parseProgramSet(const char* tag, const flagset<ProgramContextFlags>& context_flags = {});
|
||||
};
|
||||
|
||||
} // namespace actions
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
namespace actions {
|
||||
|
||||
expression::ParseContext getTableParseContext()
|
||||
{
|
||||
expression::ParseContext ctx;
|
||||
|
||||
auto& locals = ctx.variables.addScope("locals");
|
||||
|
||||
locals.addMember("position", expression::ValueType::Vector);
|
||||
locals.addMember("direction", expression::ValueType::Vector);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
expression::ProgramVariables getDefaultTableVariables()
|
||||
{
|
||||
expression::ProgramVariables vars;
|
||||
|
||||
vars.setValue({"locals", "position"}, expression::Value(vmd_zero_vector));
|
||||
vars.setValue({"locals", "direction"}, expression::Value(vmd_zero_vector));
|
||||
|
||||
return vars;
|
||||
}
|
||||
|
||||
} // namespace actions
|
||||
@@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/flagset.h"
|
||||
|
||||
#include "actions/expression/ParseContext.h"
|
||||
#include "actions/expression/ProgramVariables.h"
|
||||
#include "math/vecmat.h"
|
||||
#include "object/object.h"
|
||||
|
||||
class object;
|
||||
class ship_subsys;
|
||||
|
||||
namespace actions {
|
||||
|
||||
struct ProgramLocals {
|
||||
object_h host;
|
||||
vec3d localPosition = vmd_zero_vector;
|
||||
matrix localOrient = vmd_identity_matrix;
|
||||
int hostSubobject = -1;
|
||||
|
||||
int waitTimestamp = 0;
|
||||
|
||||
expression::ProgramVariables variables;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A list of flags for altering the program parsing behavior
|
||||
*
|
||||
* This should be used for differentiating between different program environments when validating the program (e.g. a
|
||||
* particle effect requires an object host).
|
||||
*/
|
||||
// clang-format off
|
||||
FLAG_LIST(ProgramContextFlags) {
|
||||
HasObject = 0, //!< The program will be started with an object host
|
||||
HasSubobject, //!< The program will be started with a subobject host
|
||||
|
||||
NUM_VALUES
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* @brief Constructs the "table" parse context
|
||||
*
|
||||
* This is the context that is available when parsing a program from an external table (e.g. ships or weapons).
|
||||
*
|
||||
* @return The parse context
|
||||
*/
|
||||
expression::ParseContext getTableParseContext();
|
||||
|
||||
/**
|
||||
* @brief Gets a program variables instance initialized with the default values
|
||||
*
|
||||
* The values are consistent with what is required for the default table parse context;
|
||||
*
|
||||
* @return The program variables
|
||||
*/
|
||||
expression::ProgramVariables getDefaultTableVariables();
|
||||
|
||||
} // namespace actions
|
||||
@@ -1,66 +0,0 @@
|
||||
|
||||
#include "ActionExpression.h"
|
||||
|
||||
#include "ExpressionParser.h"
|
||||
#include "TypeDefinition.h"
|
||||
|
||||
#include "actions/expression/nodes/AbstractExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace {
|
||||
|
||||
SCP_string getTypeName(ValueType type)
|
||||
{
|
||||
const auto& typeDef = TypeDefinition::forValueType(type);
|
||||
return typeDef.getName();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ActionExpression ActionExpression::parseFromTable(ValueType expectedReturnType, const ParseContext& context)
|
||||
{
|
||||
SCP_string expressionText;
|
||||
|
||||
stuff_string(expressionText, F_NAME);
|
||||
|
||||
ExpressionParser parser(expressionText);
|
||||
|
||||
auto expression = parser.parse(context);
|
||||
|
||||
if (!expression) {
|
||||
error_display(0, "Failed to parse action expression:\n%s", parser.getErrorText().c_str());
|
||||
return ActionExpression();
|
||||
}
|
||||
|
||||
auto type = expression->getExpressionType();
|
||||
|
||||
// Check if the type this evaluates to matches what we expect
|
||||
if (!checkTypeWithImplicitConversion(type, expectedReturnType)) {
|
||||
error_display(0,
|
||||
"Expression evaluates to type <%s> but <%s> was expected!",
|
||||
getTypeName(type).c_str(),
|
||||
getTypeName(expectedReturnType).c_str());
|
||||
return ActionExpression();
|
||||
}
|
||||
|
||||
// Everything is valid
|
||||
return ActionExpression(expression);
|
||||
}
|
||||
|
||||
Value ActionExpression::execute(const ProgramVariables& variables) const
|
||||
{
|
||||
// Do not crash on invalid expressions
|
||||
if (!isValid()) {
|
||||
return Value();
|
||||
}
|
||||
|
||||
return m_expression->execute(variables);
|
||||
}
|
||||
bool ActionExpression::isValid() const
|
||||
{
|
||||
return m_expression != nullptr;
|
||||
}
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,70 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ParseContext.h"
|
||||
#include "ProgramVariables.h"
|
||||
#include "TypeDefinition.h"
|
||||
|
||||
#include "parse/parselo.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
class AbstractExpression;
|
||||
}
|
||||
template <typename T>
|
||||
class TypedActionExpression;
|
||||
|
||||
class ActionExpression {
|
||||
public:
|
||||
ActionExpression() = default;
|
||||
|
||||
Value execute(const ProgramVariables& variables) const;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
template <typename T>
|
||||
TypedActionExpression<T> asTyped() const;
|
||||
|
||||
static ActionExpression parseFromTable(ValueType expectedReturnType, const ParseContext& context);
|
||||
|
||||
protected:
|
||||
explicit ActionExpression(std::shared_ptr<nodes::AbstractExpression> expression)
|
||||
: m_expression(std::move(expression))
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<nodes::AbstractExpression> m_expression;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class TypedActionExpression {
|
||||
public:
|
||||
explicit TypedActionExpression(ActionExpression expression) : m_expression(std::move(expression)) {}
|
||||
|
||||
T execute(const ProgramVariables& variables) const
|
||||
{
|
||||
// Do not crash on invalid expressions
|
||||
if (!m_expression.isValid()) {
|
||||
return T();
|
||||
}
|
||||
|
||||
return m_expression.execute(variables).template get<T>();
|
||||
}
|
||||
|
||||
static TypedActionExpression<T> parseFromTable(const ParseContext& context)
|
||||
{
|
||||
return ActionExpression::parseFromTable(ValueTypeTraits<T>::type, context).template asTyped<T>();
|
||||
}
|
||||
|
||||
private:
|
||||
ActionExpression m_expression;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
TypedActionExpression<T> ActionExpression::asTyped() const
|
||||
{
|
||||
return TypedActionExpression<T>(*this);
|
||||
}
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,194 +0,0 @@
|
||||
|
||||
#include "ExpressionParser.h"
|
||||
|
||||
#include "actions/expression/nodes/FunctionCallExpression.h"
|
||||
#include "actions/expression/nodes/LiteralExpression.h"
|
||||
#include "actions/expression/nodes/RandomRangeExpression.h"
|
||||
#include "actions/expression/nodes/VariableReferenceExpression.h"
|
||||
#include "actions/expression/nodes/VectorConstructorExpression.h"
|
||||
#include "libs/antlr/ErrorListener.h"
|
||||
|
||||
PUSH_SUPPRESS_WARNINGS
|
||||
#include "action_expression/generated/ActionExpressionLexer.h"
|
||||
#include "action_expression/generated/ActionExpressionParser.h"
|
||||
#include "action_expression/generated/ActionExpressionVisitor.h"
|
||||
POP_SUPPRESS_WARNINGS
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
namespace {
|
||||
|
||||
class ExpressionBuilderVisitor : public ActionExpressionVisitor {
|
||||
public:
|
||||
antlrcpp::Any visitExpression_main(ActionExpressionParser::Expression_mainContext* context) override
|
||||
{
|
||||
return visit(context->expression());
|
||||
}
|
||||
antlrcpp::Any visitExpression(ActionExpressionParser::ExpressionContext* context) override
|
||||
{
|
||||
if (context->value_expression() != nullptr) {
|
||||
return visit(context->value_expression());
|
||||
}
|
||||
if (context->parenthesis_expression()) {
|
||||
return visit(context->parenthesis_expression());
|
||||
}
|
||||
if (context->random_range_expression()) {
|
||||
return visit(context->random_range_expression());
|
||||
}
|
||||
if (context->variable_reference_expression()) {
|
||||
return visit(context->variable_reference_expression());
|
||||
}
|
||||
|
||||
// This is an operator so treat it as one.
|
||||
antlr4::Token* operatorTok;
|
||||
SCP_string operatorText;
|
||||
if (context->PLUS() != nullptr) {
|
||||
operatorTok = context->PLUS()->getSymbol();
|
||||
operatorText = context->PLUS()->getText();
|
||||
} else {
|
||||
operatorTok = context->MINUS()->getSymbol();
|
||||
operatorText = context->MINUS()->getText();
|
||||
}
|
||||
|
||||
const auto leftExpr = visit(context->expression(0)).as<std::shared_ptr<nodes::AbstractExpression>>();
|
||||
const auto rightExpr = visit(context->expression(1)).as<std::shared_ptr<nodes::AbstractExpression>>();
|
||||
|
||||
auto operatorExpression = std::make_shared<nodes::FunctionCallExpression>(operatorTok,
|
||||
true,
|
||||
std::move(operatorText),
|
||||
SCP_vector<std::shared_ptr<nodes::AbstractExpression>>{leftExpr, rightExpr});
|
||||
return std::static_pointer_cast<nodes::AbstractExpression>(operatorExpression);
|
||||
}
|
||||
antlrcpp::Any visitVariable_reference_expression(
|
||||
ActionExpressionParser::Variable_reference_expressionContext* context) override
|
||||
{
|
||||
const auto identifiers = context->IDENTIFIER();
|
||||
|
||||
SCP_vector<SCP_string> partNames;
|
||||
partNames.reserve(identifiers.size());
|
||||
|
||||
std::transform(identifiers.begin(),
|
||||
identifiers.end(),
|
||||
std::back_inserter(partNames),
|
||||
[](antlr4::tree::TerminalNode* node) { return node->getText(); });
|
||||
|
||||
SCP_vector<antlr4::Token*> partTokens;
|
||||
partTokens.reserve(identifiers.size());
|
||||
|
||||
std::transform(identifiers.begin(),
|
||||
identifiers.end(),
|
||||
std::back_inserter(partTokens),
|
||||
[](antlr4::tree::TerminalNode* node) { return node->getSymbol(); });
|
||||
|
||||
auto expression =
|
||||
std::make_shared<nodes::VariableReferenceExpression>(std::move(partNames), std::move(partTokens));
|
||||
return std::static_pointer_cast<nodes::AbstractExpression>(expression);
|
||||
}
|
||||
antlrcpp::Any visitRandom_range_expression(ActionExpressionParser::Random_range_expressionContext* context) override
|
||||
{
|
||||
auto expression = std::make_shared<nodes::RandomRangeExpression>(context->RAND_L_PAREN()->getSymbol(),
|
||||
visit(context->expression(0)).as<std::shared_ptr<nodes::AbstractExpression>>(),
|
||||
visit(context->expression(1)).as<std::shared_ptr<nodes::AbstractExpression>>());
|
||||
return std::static_pointer_cast<nodes::AbstractExpression>(expression);
|
||||
}
|
||||
antlrcpp::Any visitLiteral_expression(ActionExpressionParser::Literal_expressionContext* context) override
|
||||
{
|
||||
std::shared_ptr<nodes::LiteralExpression> expression;
|
||||
|
||||
if (context->FLOAT() != nullptr) {
|
||||
expression = std::make_shared<nodes::LiteralExpression>(context->FLOAT()->getSymbol(),
|
||||
Value(std::stof(context->FLOAT()->getText())));
|
||||
} else if (context->INT() != nullptr) {
|
||||
expression = std::make_shared<nodes::LiteralExpression>(context->INT()->getSymbol(),
|
||||
Value(std::stoi(context->INT()->getText())));
|
||||
} else {
|
||||
const auto idText = context->STRING()->getText();
|
||||
|
||||
// Cut off the first and last characters since those are the < > braces
|
||||
expression = std::make_shared<nodes::LiteralExpression>(context->STRING()->getSymbol(),
|
||||
Value(idText.substr(1, idText.size() - 2)));
|
||||
}
|
||||
|
||||
return std::static_pointer_cast<nodes::AbstractExpression>(expression);
|
||||
}
|
||||
antlrcpp::Any visitParenthesis_expression(ActionExpressionParser::Parenthesis_expressionContext* context) override
|
||||
{
|
||||
return visit(context->expression());
|
||||
}
|
||||
antlrcpp::Any visitValue_expression(ActionExpressionParser::Value_expressionContext* context) override
|
||||
{
|
||||
return visitChildren(context);
|
||||
}
|
||||
antlrcpp::Any visitVec3d_constructor(ActionExpressionParser::Vec3d_constructorContext* context) override
|
||||
{
|
||||
auto xExpression = visit(context->expression(0)).as<std::shared_ptr<nodes::AbstractExpression>>();
|
||||
auto yExpression = visit(context->expression(1)).as<std::shared_ptr<nodes::AbstractExpression>>();
|
||||
auto zExpression = visit(context->expression(2)).as<std::shared_ptr<nodes::AbstractExpression>>();
|
||||
|
||||
auto expression = std::make_shared<nodes::VectorConstructorExpression>(context->L_PAREN()->getSymbol(),
|
||||
xExpression,
|
||||
yExpression,
|
||||
zExpression);
|
||||
|
||||
return std::static_pointer_cast<nodes::AbstractExpression>(expression);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ExpressionParser::ExpressionParser(SCP_string expressionText) : m_expressionText(std::move(expressionText)) {}
|
||||
|
||||
std::shared_ptr<nodes::AbstractExpression> ExpressionParser::parse(const ParseContext& context)
|
||||
{
|
||||
antlr4::ANTLRInputStream input(m_expressionText);
|
||||
ActionExpressionLexer lexer(&input);
|
||||
antlr4::CommonTokenStream tokens(&lexer);
|
||||
|
||||
::ActionExpressionParser parser(&tokens);
|
||||
// By default we log to stderr which we do not want
|
||||
parser.removeErrorListeners();
|
||||
libs::antlr::ErrorListener errListener;
|
||||
parser.addErrorListener(&errListener);
|
||||
|
||||
antlr4::tree::ParseTree* tree = parser.expression_main();
|
||||
|
||||
std::shared_ptr<nodes::AbstractExpression> exprReturn;
|
||||
if (errListener.diagnostics.empty()) {
|
||||
// Only do semantic checking if syntax is valid
|
||||
ExpressionBuilderVisitor builder;
|
||||
exprReturn = tree->accept(&builder).as<std::shared_ptr<nodes::AbstractExpression>>();
|
||||
|
||||
if (!exprReturn->validate(&parser, context)) {
|
||||
exprReturn = nullptr;
|
||||
} else {
|
||||
exprReturn->validationDone();
|
||||
}
|
||||
}
|
||||
|
||||
if (!errListener.diagnostics.empty()) {
|
||||
SCP_stringstream outStream;
|
||||
for (const auto& diag : errListener.diagnostics) {
|
||||
SCP_string tokenUnderline;
|
||||
if (diag.tokenLength > 1) {
|
||||
tokenUnderline = SCP_string(diag.tokenLength - 1, '~');
|
||||
}
|
||||
outStream << m_expressionText << "\n"
|
||||
<< SCP_string(diag.columnInLine, ' ') << "^" << tokenUnderline << "\n"
|
||||
<< diag.errorMessage << "\n";
|
||||
}
|
||||
|
||||
m_errorText = outStream.str();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return exprReturn;
|
||||
}
|
||||
|
||||
const SCP_string& ExpressionParser::getErrorText() const
|
||||
{
|
||||
return m_errorText;
|
||||
}
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
#include "ParseContext.h"
|
||||
|
||||
#include "actions/expression/nodes/AbstractExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
class ExpressionParser {
|
||||
public:
|
||||
ExpressionParser(SCP_string expressionText);
|
||||
|
||||
std::shared_ptr<nodes::AbstractExpression> parse(const ParseContext& context);
|
||||
|
||||
const SCP_string& getErrorText() const;
|
||||
|
||||
private:
|
||||
SCP_string m_expressionText;
|
||||
|
||||
SCP_string m_errorText;
|
||||
};
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,93 +0,0 @@
|
||||
|
||||
#include "FunctionManager.h"
|
||||
|
||||
#include "TypeDefinition.h"
|
||||
|
||||
#include "math/vecmat.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace {
|
||||
template <typename T>
|
||||
Value value_plus(const SCP_vector<Value>& parameters)
|
||||
{
|
||||
return Value(parameters[0].get<T>() + parameters[1].get<T>());
|
||||
}
|
||||
template <typename T>
|
||||
Value value_minus(const SCP_vector<Value>& parameters)
|
||||
{
|
||||
return Value(parameters[0].get<T>() - parameters[1].get<T>());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const FunctionManager& FunctionManager::instance()
|
||||
{
|
||||
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,
|
||||
std::initializer_list<ValueType> parameterTypes,
|
||||
ValueType returnType,
|
||||
FunctionImplementation implementation)
|
||||
{
|
||||
m_operators[name].push_back({name, parameterTypes, returnType, std::move(implementation)});
|
||||
}
|
||||
const FunctionDefinition* FunctionManager::findOperator(const SCP_string& name,
|
||||
const SCP_vector<ValueType>& parameters) const
|
||||
{
|
||||
const auto iter = m_operators.find(name);
|
||||
|
||||
if (iter == m_operators.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Prefer exact matches
|
||||
for (const auto& funcDef : iter->second) {
|
||||
if (funcDef.parameterTypes == parameters) {
|
||||
return &funcDef;
|
||||
}
|
||||
}
|
||||
|
||||
// Now check if there are matches with implicit conversions
|
||||
for (const auto& funcDef : iter->second) {
|
||||
// Reject functions with different amount of elements
|
||||
if (funcDef.parameterTypes.size() != parameters.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto funcParamIter = funcDef.parameterTypes.cbegin();
|
||||
auto actualParamIter = parameters.cbegin();
|
||||
|
||||
bool match = true;
|
||||
for (; funcParamIter != funcDef.parameterTypes.cend(); ++funcParamIter, ++actualParamIter) {
|
||||
if (!checkTypeWithImplicitConversion(*actualParamIter, *funcParamIter)) {
|
||||
// Also doesn't match with implicit conversions
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
return &funcDef;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FunctionManager::FunctionManager() = default;
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
using FunctionImplementation = std::function<Value(const SCP_vector<Value>& parameters)>;
|
||||
|
||||
struct FunctionDefinition {
|
||||
SCP_string name;
|
||||
|
||||
SCP_vector<ValueType> parameterTypes;
|
||||
|
||||
ValueType returnType;
|
||||
|
||||
FunctionImplementation implementation;
|
||||
};
|
||||
|
||||
class FunctionManager {
|
||||
public:
|
||||
// This type is supposed to be global so it may not be copied
|
||||
FunctionManager(const FunctionManager&) = delete;
|
||||
FunctionManager& operator=(const FunctionManager&) = delete;
|
||||
|
||||
FunctionManager(FunctionManager&&) noexcept = default;
|
||||
FunctionManager& operator=(FunctionManager&&) noexcept = default;
|
||||
|
||||
void addOperator(const SCP_string& name,
|
||||
std::initializer_list<ValueType> parameterTypes,
|
||||
ValueType returnType,
|
||||
FunctionImplementation implementation);
|
||||
|
||||
const FunctionDefinition* findOperator(const SCP_string& name, const SCP_vector<ValueType>& parameterTypes) const;
|
||||
|
||||
static const FunctionManager& instance();
|
||||
|
||||
private:
|
||||
FunctionManager();
|
||||
|
||||
SCP_unordered_map<SCP_string, SCP_vector<FunctionDefinition>> m_operators;
|
||||
};
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ProgramVariables.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
struct ParseContext {
|
||||
ProgramVariablesDefinition variables;
|
||||
};
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,61 +0,0 @@
|
||||
|
||||
#include "ProgramVariables.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
ProgramVariablesDefinition& ProgramVariablesDefinition::addScope(const SCP_string& name)
|
||||
{
|
||||
auto scope = std::unique_ptr<ProgramVariablesDefinition>(new ProgramVariablesDefinition());
|
||||
|
||||
auto scopePtr = scope.get();
|
||||
|
||||
m_scopes.insert(std::make_pair(name, std::move(scope)));
|
||||
|
||||
return *scopePtr;
|
||||
}
|
||||
void ProgramVariablesDefinition::addMember(const SCP_string& name, ValueType type)
|
||||
{
|
||||
m_memberTypes[name] = type;
|
||||
}
|
||||
ProgramVariablesDefinition* ProgramVariablesDefinition::getScope(const SCP_string& name) const
|
||||
{
|
||||
const auto iter = m_scopes.find(name);
|
||||
|
||||
if (iter == m_scopes.cend()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return iter->second.get();
|
||||
}
|
||||
ValueType ProgramVariablesDefinition::getMemberType(const SCP_string& name) const
|
||||
{
|
||||
const auto iter = m_memberTypes.find(name);
|
||||
|
||||
if (iter == m_memberTypes.cend()) {
|
||||
return ValueType::Invalid;
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
Value ProgramVariables::getValue(std::initializer_list<const char*> memberPathParts) const
|
||||
{
|
||||
return getValue<decltype(memberPathParts)>(memberPathParts);
|
||||
}
|
||||
void ProgramVariables::setValue(std::initializer_list<const char*> memberPathParts, Value value)
|
||||
{
|
||||
setValue<decltype(memberPathParts)>(memberPathParts, std::move(value));
|
||||
}
|
||||
Value ProgramVariables::getValue(const SCP_string& fullMemberName) const
|
||||
{
|
||||
const auto iter = m_values.find(fullMemberName);
|
||||
|
||||
// This should have been validated earlier
|
||||
Assertion(iter != m_values.end(), "Could not find member %s in variables!", fullMemberName.c_str());
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,61 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
#include "utils/join_string.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
struct ProgramVariables {
|
||||
public:
|
||||
template <typename TContainer>
|
||||
static SCP_string getMemberName(const TContainer& memberPathParts)
|
||||
{
|
||||
return util::join_container(memberPathParts, ".");
|
||||
}
|
||||
|
||||
template <typename TContainer>
|
||||
Value getValue(const TContainer& memberPathParts) const
|
||||
{
|
||||
const auto name = getMemberName(memberPathParts);
|
||||
|
||||
return getValue(name);
|
||||
}
|
||||
|
||||
Value getValue(std::initializer_list<const char*> memberPathParts) const;
|
||||
|
||||
Value getValue(const SCP_string& fullMemberName) const;
|
||||
|
||||
template <typename TContainer>
|
||||
void setValue(const TContainer& memberPathParts, Value value)
|
||||
{
|
||||
const auto name = getMemberName(memberPathParts);
|
||||
|
||||
m_values[name] = std::move(value);
|
||||
}
|
||||
|
||||
void setValue(std::initializer_list<const char*> memberPathParts, Value value);
|
||||
|
||||
private:
|
||||
SCP_unordered_map<SCP_string, Value> m_values;
|
||||
};
|
||||
|
||||
class ProgramVariablesDefinition {
|
||||
public:
|
||||
ProgramVariablesDefinition& addScope(const SCP_string& name);
|
||||
|
||||
void addMember(const SCP_string& name, ValueType type);
|
||||
|
||||
ProgramVariablesDefinition* getScope(const SCP_string& name) const;
|
||||
|
||||
ValueType getMemberType(const SCP_string& name) const;
|
||||
|
||||
private:
|
||||
SCP_unordered_map<SCP_string, std::unique_ptr<ProgramVariablesDefinition>> m_scopes;
|
||||
|
||||
SCP_unordered_map<SCP_string, ValueType> m_memberTypes;
|
||||
};
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,60 +0,0 @@
|
||||
|
||||
#include "TypeDefinition.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
// Integers can be safely converted to floats (for the most part)
|
||||
TypeDefinition TypeDefinition::s_integer("Integer", {ValueType::Float});
|
||||
// No implicit conversions here
|
||||
TypeDefinition TypeDefinition::s_float("Float", {});
|
||||
TypeDefinition TypeDefinition::s_vector("Vector", {});
|
||||
// Identifiers are a technicality needed for making non-math values work but they have no further use other than their
|
||||
// string content
|
||||
TypeDefinition TypeDefinition::s_identifier("Identifier", {});
|
||||
|
||||
TypeDefinition::TypeDefinition(SCP_string name, SCP_vector<actions::expression::ValueType> allowedImplicitConversion)
|
||||
: m_name(std::move(name)), m_allowedImplicitConversions(std::move(allowedImplicitConversion))
|
||||
{
|
||||
}
|
||||
|
||||
const SCP_string& TypeDefinition::getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
const SCP_vector<actions::expression::ValueType>& TypeDefinition::getAllowedImplicitConversions() const
|
||||
{
|
||||
return m_allowedImplicitConversions;
|
||||
}
|
||||
const TypeDefinition& TypeDefinition::forValueType(ValueType type)
|
||||
{
|
||||
switch (type) {
|
||||
case ValueType::Integer:
|
||||
return s_integer;
|
||||
case ValueType::Float:
|
||||
return s_float;
|
||||
case ValueType::Vector:
|
||||
return s_vector;
|
||||
case ValueType::String:
|
||||
return s_identifier;
|
||||
default:
|
||||
UNREACHABLE("Invalid value type!");
|
||||
return s_integer; // Make compiler happy
|
||||
}
|
||||
}
|
||||
|
||||
bool checkTypeWithImplicitConversion(ValueType currentType, ValueType expectedType)
|
||||
{
|
||||
if (currentType == expectedType) {
|
||||
// No need to consider implicit conversions;
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& currentTypeDef = TypeDefinition::forValueType(currentType);
|
||||
const auto& allowedConversions = currentTypeDef.getAllowedImplicitConversions();
|
||||
|
||||
// For now, no transitiv conversions are allowed
|
||||
return std::find(allowedConversions.cbegin(), allowedConversions.cend(), expectedType) != allowedConversions.cend();
|
||||
}
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
class TypeDefinition {
|
||||
public:
|
||||
TypeDefinition(SCP_string name, SCP_vector<actions::expression::ValueType> allowedImplicitConversion);
|
||||
|
||||
const SCP_string& getName() const;
|
||||
|
||||
const SCP_vector<actions::expression::ValueType>& getAllowedImplicitConversions() const;
|
||||
|
||||
static const TypeDefinition& forValueType(ValueType type);
|
||||
|
||||
// This type is supposed to be global so it may not be copied
|
||||
TypeDefinition(const TypeDefinition&) = delete;
|
||||
TypeDefinition& operator=(const TypeDefinition&) = delete;
|
||||
|
||||
TypeDefinition(TypeDefinition&&) = delete;
|
||||
TypeDefinition& operator=(TypeDefinition&&) = delete;
|
||||
|
||||
private:
|
||||
SCP_string m_name;
|
||||
|
||||
// The types this type is allowed to implicitly convert to
|
||||
SCP_vector<ValueType> m_allowedImplicitConversions;
|
||||
|
||||
static TypeDefinition s_integer;
|
||||
static TypeDefinition s_float;
|
||||
static TypeDefinition s_vector;
|
||||
static TypeDefinition s_identifier;
|
||||
};
|
||||
|
||||
bool checkTypeWithImplicitConversion(ValueType currentType, ValueType expectedType);
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,80 +0,0 @@
|
||||
|
||||
#include "Value.h"
|
||||
#include "math/vecmat.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
Value::Value(int val) : m_value(val) {}
|
||||
Value::Value(float val) : m_value(val) {}
|
||||
Value::Value(const vec3d& val) : m_value(val) {}
|
||||
Value::Value(SCP_string val) : m_value(std::move(val)) {}
|
||||
|
||||
ValueType actions::expression::Value::getType() const
|
||||
{
|
||||
switch (m_value.index()) {
|
||||
case 1:
|
||||
return ValueType::Integer;
|
||||
case 2:
|
||||
return ValueType::Float;
|
||||
case 3:
|
||||
return ValueType::Vector;
|
||||
case 4:
|
||||
return ValueType::String;
|
||||
default:
|
||||
return ValueType::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
int Value::getInteger() const
|
||||
{
|
||||
return get<int>();
|
||||
}
|
||||
float Value::getFloat() const
|
||||
{
|
||||
return get<float>();
|
||||
}
|
||||
vec3d Value::getVector() const
|
||||
{
|
||||
return get<vec3d>();
|
||||
}
|
||||
SCP_string Value::getIdentifier() const
|
||||
{
|
||||
return get<SCP_string>();
|
||||
}
|
||||
bool operator==(const Value& lhs, const Value& rhs)
|
||||
{
|
||||
return lhs.m_value == rhs.m_value;
|
||||
}
|
||||
bool operator!=(const Value& lhs, const Value& rhs)
|
||||
{
|
||||
return !(rhs == lhs);
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const Value& value)
|
||||
{
|
||||
os << "Value<";
|
||||
|
||||
switch (value.getType()) {
|
||||
case ValueType::Invalid:
|
||||
os << "INVALID";
|
||||
break;
|
||||
case ValueType::Integer:
|
||||
os << value.get<int>();
|
||||
break;
|
||||
case ValueType::Float:
|
||||
os << value.get<float>();
|
||||
break;
|
||||
case ValueType::Vector:
|
||||
os << value.get<vec3d>();
|
||||
break;
|
||||
case ValueType::String:
|
||||
os << value.get<SCP_string>();
|
||||
break;
|
||||
}
|
||||
|
||||
os << ">";
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,104 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
#include <mpark/variant.hpp>
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
namespace detail {
|
||||
template <typename ToType>
|
||||
struct implicit_conversion {
|
||||
template <typename T>
|
||||
ToType operator()(const T&)
|
||||
{
|
||||
throw std::runtime_error("Invalid implicit conversion.");
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct implicit_conversion<float> {
|
||||
inline float operator()(int val) {
|
||||
return i2fl(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
float operator()(const T&)
|
||||
{
|
||||
throw std::runtime_error("Invalid implicit conversion.");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
enum class ValueType
|
||||
{
|
||||
Invalid,
|
||||
Integer,
|
||||
Float,
|
||||
Vector,
|
||||
String,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ValueTypeTraits;
|
||||
|
||||
template <>
|
||||
struct ValueTypeTraits<int> {
|
||||
static constexpr ValueType type = ValueType::Integer;
|
||||
};
|
||||
template <>
|
||||
struct ValueTypeTraits<float> {
|
||||
static constexpr ValueType type = ValueType::Float;
|
||||
};
|
||||
template <>
|
||||
struct ValueTypeTraits<vec3d> {
|
||||
static constexpr ValueType type = ValueType::Vector;
|
||||
};
|
||||
template <>
|
||||
struct ValueTypeTraits<SCP_string> {
|
||||
static constexpr ValueType type = ValueType::String;
|
||||
};
|
||||
|
||||
class Value {
|
||||
public:
|
||||
Value() = default;
|
||||
|
||||
explicit Value(int val);
|
||||
explicit Value(float val);
|
||||
explicit Value(const vec3d& val);
|
||||
explicit Value(SCP_string val);
|
||||
|
||||
ValueType getType() const;
|
||||
|
||||
int getInteger() const;
|
||||
float getFloat() const;
|
||||
vec3d getVector() const;
|
||||
SCP_string getIdentifier() const;
|
||||
|
||||
template <typename T>
|
||||
T get() const
|
||||
{
|
||||
const auto currentType = getType();
|
||||
const auto expectedType = ValueTypeTraits<T>::type;
|
||||
|
||||
if (currentType == expectedType) {
|
||||
// No conversion necessary
|
||||
return mpark::get<T>(m_value);
|
||||
}
|
||||
|
||||
return mpark::visit(detail::implicit_conversion<T>{}, m_value);
|
||||
}
|
||||
|
||||
friend bool operator==(const Value& lhs, const Value& rhs);
|
||||
friend bool operator!=(const Value& lhs, const Value& rhs);
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Value& value);
|
||||
|
||||
private:
|
||||
mpark::variant<mpark::monostate, int, float, vec3d, SCP_string> m_value;
|
||||
};
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,31 +0,0 @@
|
||||
|
||||
#include "AbstractExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
AbstractExpression::AbstractExpression(antlr4::Token* token) : m_token(token) {}
|
||||
AbstractExpression::~AbstractExpression() = default;
|
||||
|
||||
antlr4::Token* AbstractExpression::getToken() const
|
||||
{
|
||||
return m_token;
|
||||
}
|
||||
ValueType AbstractExpression::getExpressionType() const
|
||||
{
|
||||
// Since expressions are immutable, caching this will be more efficient
|
||||
if (m_cachedType == ValueType::Invalid) {
|
||||
m_cachedType = determineReturnType();
|
||||
}
|
||||
return m_cachedType;
|
||||
}
|
||||
void AbstractExpression::validationDone()
|
||||
{
|
||||
// This pointer will become invalid after this so reset it now to avoid issues
|
||||
m_token = nullptr;
|
||||
}
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "actions/expression/ParseContext.h"
|
||||
#include "actions/expression/Value.h"
|
||||
|
||||
#include <Parser.h>
|
||||
#include <Token.h>
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
class AbstractExpression {
|
||||
public:
|
||||
AbstractExpression(antlr4::Token* token);
|
||||
virtual ~AbstractExpression();
|
||||
|
||||
virtual Value execute(const ProgramVariables& variables) const = 0;
|
||||
|
||||
virtual bool validate(antlr4::Parser* parser, const ParseContext& context) = 0;
|
||||
|
||||
virtual void validationDone();
|
||||
|
||||
ValueType getExpressionType() const;
|
||||
|
||||
antlr4::Token* getToken() const;
|
||||
|
||||
protected:
|
||||
virtual ValueType determineReturnType() const = 0;
|
||||
|
||||
antlr4::Token* m_token = nullptr;
|
||||
|
||||
mutable ValueType m_cachedType = ValueType::Invalid;
|
||||
};
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,95 +0,0 @@
|
||||
|
||||
#include "FunctionCallExpression.h"
|
||||
|
||||
#include "actions/expression/TypeDefinition.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
FunctionCallExpression::FunctionCallExpression(antlr4::Token* token,
|
||||
bool isOperator,
|
||||
SCP_string functionName,
|
||||
SCP_vector<std::shared_ptr<AbstractExpression>> parameterExpressions)
|
||||
: AbstractExpression(token), m_isOperator(isOperator), m_functionName(std::move(functionName)),
|
||||
m_parameterExpressions(std::move(parameterExpressions))
|
||||
{
|
||||
}
|
||||
FunctionCallExpression::~FunctionCallExpression() = default;
|
||||
|
||||
Value FunctionCallExpression::execute(const ProgramVariables& variables) const
|
||||
{
|
||||
SCP_vector<Value> parameterValues;
|
||||
parameterValues.reserve(m_parameterExpressions.size());
|
||||
|
||||
std::transform(m_parameterExpressions.cbegin(),
|
||||
m_parameterExpressions.cend(),
|
||||
std::back_inserter(parameterValues),
|
||||
[&variables](const std::shared_ptr<AbstractExpression>& expression) { return expression->execute(variables); });
|
||||
|
||||
return m_functionDef->implementation(parameterValues);
|
||||
}
|
||||
bool FunctionCallExpression::validate(antlr4::Parser* parser, const ParseContext& context)
|
||||
{
|
||||
bool valid = true;
|
||||
|
||||
for (const auto& paramExpression : m_parameterExpressions) {
|
||||
valid &= paramExpression->validate(parser, context);
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
// Exit early if there were errors since the values below are meaningless otherwise
|
||||
return false;
|
||||
}
|
||||
|
||||
Assertion(m_isOperator, "Non-operator functions are not supported yet!");
|
||||
|
||||
SCP_vector<ValueType> parameterTypes;
|
||||
|
||||
std::transform(m_parameterExpressions.cbegin(),
|
||||
m_parameterExpressions.cend(),
|
||||
std::back_inserter(parameterTypes),
|
||||
[](const std::shared_ptr<AbstractExpression>& expression) { return expression->getExpressionType(); });
|
||||
|
||||
const auto functionDef = FunctionManager::instance().findOperator(m_functionName, parameterTypes);
|
||||
|
||||
if (functionDef == nullptr) {
|
||||
SCP_stringstream paramListStream;
|
||||
if (!parameterTypes.empty()) {
|
||||
paramListStream << "<" << TypeDefinition::forValueType(parameterTypes.front()).getName() << ">";
|
||||
|
||||
for (auto iter = parameterTypes.cbegin() + 1; iter != parameterTypes.cend(); ++iter) {
|
||||
paramListStream << ", <" << TypeDefinition::forValueType(*iter).getName() << ">";
|
||||
}
|
||||
}
|
||||
|
||||
parser->notifyErrorListeners(m_token,
|
||||
SCP_string("Could not find a definition for the ") + (m_isOperator ? "operator" : "function") + " '" +
|
||||
m_functionName + "' with parameters (" + paramListStream.str() + ")",
|
||||
nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_functionDef = functionDef;
|
||||
return true;
|
||||
}
|
||||
ValueType FunctionCallExpression::determineReturnType() const
|
||||
{
|
||||
return m_functionDef->returnType;
|
||||
}
|
||||
void FunctionCallExpression::validationDone()
|
||||
{
|
||||
AbstractExpression::validationDone();
|
||||
|
||||
// We no longer need this so we can save some memory
|
||||
m_functionName.clear();
|
||||
m_functionName.shrink_to_fit();
|
||||
|
||||
for (const auto& paramExpression : m_parameterExpressions) {
|
||||
paramExpression->validationDone();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbstractExpression.h"
|
||||
|
||||
#include "actions/expression/FunctionManager.h"
|
||||
#include "actions/expression/Value.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
class FunctionCallExpression : public AbstractExpression {
|
||||
public:
|
||||
FunctionCallExpression(antlr4::Token* token,
|
||||
bool isOperator,
|
||||
SCP_string functionName,
|
||||
SCP_vector<std::shared_ptr<AbstractExpression>> parameterExpressions);
|
||||
~FunctionCallExpression() override;
|
||||
|
||||
Value execute(const ProgramVariables& variables) const override;
|
||||
bool validate(antlr4::Parser* parser, const ParseContext& context) override;
|
||||
void validationDone() override;
|
||||
|
||||
protected:
|
||||
ValueType determineReturnType() const override;
|
||||
|
||||
private:
|
||||
bool m_isOperator;
|
||||
SCP_string m_functionName;
|
||||
|
||||
const FunctionDefinition* m_functionDef = nullptr;
|
||||
|
||||
SCP_vector<std::shared_ptr<AbstractExpression>> m_parameterExpressions;
|
||||
};
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,30 +0,0 @@
|
||||
|
||||
#include "LiteralExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
LiteralExpression::LiteralExpression(antlr4::Token* token, Value literalValue)
|
||||
: AbstractExpression(token), m_literalValue(std::move(literalValue))
|
||||
{
|
||||
}
|
||||
LiteralExpression::~LiteralExpression() = default;
|
||||
|
||||
Value LiteralExpression::execute(const ProgramVariables& /*variables*/) const
|
||||
{
|
||||
return m_literalValue;
|
||||
}
|
||||
bool LiteralExpression::validate(antlr4::Parser* /*parser*/, const ParseContext& /*context*/)
|
||||
{
|
||||
// Literals are always valid
|
||||
return true;
|
||||
}
|
||||
ValueType LiteralExpression::determineReturnType() const
|
||||
{
|
||||
return m_literalValue.getType();
|
||||
}
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbstractExpression.h"
|
||||
|
||||
#include "actions/expression/Value.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
class LiteralExpression : public AbstractExpression {
|
||||
public:
|
||||
LiteralExpression(antlr4::Token* token, Value literalValue);
|
||||
~LiteralExpression() override;
|
||||
|
||||
Value execute(const ProgramVariables& variables) const override;
|
||||
bool validate(antlr4::Parser* parser, const ParseContext& context) override;
|
||||
|
||||
protected:
|
||||
ValueType determineReturnType() const override;
|
||||
|
||||
private:
|
||||
Value m_literalValue;
|
||||
};
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,108 +0,0 @@
|
||||
|
||||
#include "RandomRangeExpression.h"
|
||||
|
||||
#include "actions/expression/TypeDefinition.h"
|
||||
#include "utils/RandomRange.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
Value executeHelper(const Value& leftVal, const Value& rightVal)
|
||||
{
|
||||
auto leftContentVal = leftVal.get<T>();
|
||||
auto rightContentVal = rightVal.get<T>();
|
||||
|
||||
if (leftContentVal == rightContentVal) {
|
||||
// no need to do random stuff here
|
||||
return Value(rightContentVal);
|
||||
}
|
||||
|
||||
if (rightContentVal < leftContentVal) {
|
||||
// We can fix this issue
|
||||
std::swap(leftContentVal, rightContentVal);
|
||||
}
|
||||
|
||||
// Might be a bit inefficient not to cache this somehow but the bounds can be dynamic
|
||||
return Value(util::UniformRange<T>(leftContentVal, rightContentVal).next());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
RandomRangeExpression::RandomRangeExpression(antlr4::Token* token,
|
||||
std::shared_ptr<AbstractExpression> leftExpression,
|
||||
std::shared_ptr<AbstractExpression> rightExpression)
|
||||
: AbstractExpression(token), m_leftExpression(std::move(leftExpression)),
|
||||
m_rightExpression(std::move(rightExpression))
|
||||
{
|
||||
}
|
||||
RandomRangeExpression::~RandomRangeExpression() = default;
|
||||
|
||||
Value RandomRangeExpression::execute(const ProgramVariables& variables) const
|
||||
{
|
||||
const auto leftVal = m_leftExpression->execute(variables);
|
||||
const auto rightVal = m_rightExpression->execute(variables);
|
||||
|
||||
if (leftVal.getType() == ValueType::Integer) {
|
||||
return executeHelper<int>(leftVal, rightVal);
|
||||
} else {
|
||||
return executeHelper<float>(leftVal, rightVal);
|
||||
}
|
||||
}
|
||||
bool RandomRangeExpression::validate(antlr4::Parser* parser, const ParseContext& context)
|
||||
{
|
||||
bool valid = true;
|
||||
|
||||
valid &= m_leftExpression->validate(parser, context);
|
||||
valid &= m_rightExpression->validate(parser, context);
|
||||
|
||||
if (!valid) {
|
||||
// Exit early if there were errors since the values below are meaningless otherwise
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto leftType = m_leftExpression->getExpressionType();
|
||||
const auto rightType = m_rightExpression->getExpressionType();
|
||||
|
||||
const auto& leftTypedef = TypeDefinition::forValueType(leftType);
|
||||
const auto& rightTypedef = TypeDefinition::forValueType(rightType);
|
||||
|
||||
// Both values must be the same
|
||||
if (leftType != rightType) {
|
||||
parser->notifyErrorListeners(m_rightExpression->getToken(),
|
||||
"Inconsistent types for random range. Must be the same but got <" + leftTypedef.getName() + "> and <" +
|
||||
rightTypedef.getName() + ">.",
|
||||
nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// And we only support floats or integers
|
||||
if (leftType != ValueType::Float && leftType != ValueType::Integer) {
|
||||
parser->notifyErrorListeners(m_leftExpression->getToken(),
|
||||
"Invalid parameter type for random range. Must be <Float> or <Integer> but got <" + leftTypedef.getName() +
|
||||
">.",
|
||||
nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Everything is fine.
|
||||
return true;
|
||||
}
|
||||
ValueType RandomRangeExpression::determineReturnType() const
|
||||
{
|
||||
// Return type is the same as the contained return types
|
||||
return m_leftExpression->getExpressionType();
|
||||
}
|
||||
void RandomRangeExpression::validationDone()
|
||||
{
|
||||
AbstractExpression::validationDone();
|
||||
|
||||
m_leftExpression->validationDone();
|
||||
m_rightExpression->validationDone();
|
||||
}
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbstractExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
class RandomRangeExpression : public AbstractExpression {
|
||||
public:
|
||||
RandomRangeExpression(antlr4::Token* token,
|
||||
std::shared_ptr<AbstractExpression> leftExpression,
|
||||
std::shared_ptr<AbstractExpression> rightExpression);
|
||||
~RandomRangeExpression() override;
|
||||
|
||||
Value execute(const ProgramVariables& variables) const override;
|
||||
bool validate(antlr4::Parser* parser, const ParseContext& context) override;
|
||||
void validationDone() override;
|
||||
|
||||
protected:
|
||||
ValueType determineReturnType() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<AbstractExpression> m_leftExpression;
|
||||
std::shared_ptr<AbstractExpression> m_rightExpression;
|
||||
};
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,69 +0,0 @@
|
||||
|
||||
#include "VariableReferenceExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
VariableReferenceExpression::VariableReferenceExpression(SCP_vector<SCP_string> references,
|
||||
SCP_vector<antlr4::Token*> referenceTokens)
|
||||
: AbstractExpression(referenceTokens.back()), m_references(std::move(references)),
|
||||
m_referenceTokens(std::move(referenceTokens))
|
||||
{
|
||||
}
|
||||
VariableReferenceExpression::~VariableReferenceExpression() = default;
|
||||
|
||||
Value VariableReferenceExpression::execute(const ProgramVariables& variables) const
|
||||
{
|
||||
return variables.getValue(m_fullVariablePath);
|
||||
}
|
||||
bool VariableReferenceExpression::validate(antlr4::Parser* parser, const ParseContext& context)
|
||||
{
|
||||
// Start out in the global scope
|
||||
auto currentScope = &context.variables;
|
||||
SCP_string currentScopeName("<global>");
|
||||
|
||||
for (size_t i = 0; i < m_references.size() - 1; ++i) {
|
||||
const auto nextScope = currentScope->getScope(m_references[i]);
|
||||
|
||||
if (nextScope == nullptr) {
|
||||
parser->notifyErrorListeners(m_referenceTokens[i],
|
||||
"Could not find scope " + m_references[i] + " in scope " + currentScopeName + ".",
|
||||
nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
currentScope = nextScope;
|
||||
currentScopeName = m_references[i];
|
||||
}
|
||||
|
||||
auto variableType = currentScope->getMemberType(m_references.back());
|
||||
|
||||
if (variableType == ValueType::Invalid) {
|
||||
parser->notifyErrorListeners(m_referenceTokens.back(),
|
||||
"Could not find variable " + m_references.back() + " in scope " + currentScopeName + ".",
|
||||
nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cache this so we don't duplicate all this code in determineReturnType()
|
||||
m_variableType = variableType;
|
||||
m_fullVariablePath = ProgramVariables::getMemberName(m_references);
|
||||
return true;
|
||||
}
|
||||
ValueType VariableReferenceExpression::determineReturnType() const
|
||||
{
|
||||
return m_variableType;
|
||||
}
|
||||
void VariableReferenceExpression::validationDone()
|
||||
{
|
||||
AbstractExpression::validationDone();
|
||||
|
||||
// Clear these since they are not necessary anymore
|
||||
m_references.clear();
|
||||
m_referenceTokens.clear();
|
||||
}
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbstractExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
class VariableReferenceExpression : public AbstractExpression {
|
||||
public:
|
||||
VariableReferenceExpression(const SCP_vector<SCP_string> references,
|
||||
SCP_vector<antlr4::Token*> referenceTokens);
|
||||
~VariableReferenceExpression() override;
|
||||
|
||||
Value execute(const ProgramVariables& variables) const override;
|
||||
bool validate(antlr4::Parser* parser, const ParseContext& context) override;
|
||||
void validationDone() override;
|
||||
|
||||
protected:
|
||||
ValueType determineReturnType() const override;
|
||||
|
||||
private:
|
||||
ValueType m_variableType = ValueType::Invalid;
|
||||
SCP_string m_fullVariablePath;
|
||||
|
||||
SCP_vector<SCP_string> m_references;
|
||||
SCP_vector<antlr4::Token*> m_referenceTokens;
|
||||
};
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,87 +0,0 @@
|
||||
|
||||
#include "VectorConstructorExpression.h"
|
||||
|
||||
#include "actions/expression/TypeDefinition.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
VectorConstructorExpression::VectorConstructorExpression(antlr4::Token* token,
|
||||
std::shared_ptr<AbstractExpression> xExpression,
|
||||
std::shared_ptr<AbstractExpression> yExpression,
|
||||
std::shared_ptr<AbstractExpression> zExpression)
|
||||
: AbstractExpression(token), m_xExpression(std::move(xExpression)), m_yExpression(std::move(yExpression)),
|
||||
m_zExpression(std::move(zExpression))
|
||||
{
|
||||
}
|
||||
VectorConstructorExpression::~VectorConstructorExpression() = default;
|
||||
|
||||
Value VectorConstructorExpression::execute(const ProgramVariables& variables) const
|
||||
{
|
||||
auto xValue = m_xExpression->execute(variables);
|
||||
auto yValue = m_yExpression->execute(variables);
|
||||
auto zValue = m_zExpression->execute(variables);
|
||||
|
||||
vec3d vecVal;
|
||||
|
||||
vecVal.xyz.x = xValue.getFloat();
|
||||
vecVal.xyz.y = yValue.getFloat();
|
||||
vecVal.xyz.z = zValue.getFloat();
|
||||
|
||||
return Value(vecVal);
|
||||
}
|
||||
bool VectorConstructorExpression::validate(antlr4::Parser* parser, const ParseContext& context)
|
||||
{
|
||||
bool valid = true;
|
||||
|
||||
valid &= m_xExpression->validate(parser, context);
|
||||
valid &= m_yExpression->validate(parser, context);
|
||||
valid &= m_zExpression->validate(parser, context);
|
||||
|
||||
if (!valid) {
|
||||
// Exit early if there were errors since the values below are meaningless otherwise
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkTypeWithImplicitConversion(m_xExpression->getExpressionType(), ValueType::Float)) {
|
||||
const auto& typeDef = TypeDefinition::forValueType(m_xExpression->getExpressionType());
|
||||
parser->notifyErrorListeners(m_xExpression->getToken(),
|
||||
"Invalid value type for vector constructor. Expected <Float>, got <" + typeDef.getName() + ">",
|
||||
nullptr);
|
||||
valid = false;
|
||||
}
|
||||
if (!checkTypeWithImplicitConversion(m_yExpression->getExpressionType(), ValueType::Float)) {
|
||||
const auto& typeDef = TypeDefinition::forValueType(m_yExpression->getExpressionType());
|
||||
parser->notifyErrorListeners(m_yExpression->getToken(),
|
||||
"Invalid value type for vector constructor. Expected <Float>, got <" + typeDef.getName() + ">",
|
||||
nullptr);
|
||||
valid = false;
|
||||
}
|
||||
if (!checkTypeWithImplicitConversion(m_zExpression->getExpressionType(), ValueType::Float)) {
|
||||
const auto& typeDef = TypeDefinition::forValueType(m_zExpression->getExpressionType());
|
||||
parser->notifyErrorListeners(m_zExpression->getToken(),
|
||||
"Invalid value type for vector constructor. Expected <Float>, got <" + typeDef.getName() + ">",
|
||||
nullptr);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
ValueType VectorConstructorExpression::determineReturnType() const
|
||||
{
|
||||
// No need to look at the expressions here since we will always be a vector
|
||||
return ValueType::Vector;
|
||||
}
|
||||
void VectorConstructorExpression::validationDone()
|
||||
{
|
||||
AbstractExpression::validationDone();
|
||||
|
||||
m_xExpression->validationDone();
|
||||
m_yExpression->validationDone();
|
||||
m_zExpression->validationDone();
|
||||
}
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbstractExpression.h"
|
||||
|
||||
#include "actions/expression/Value.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
class VectorConstructorExpression : public AbstractExpression {
|
||||
public:
|
||||
VectorConstructorExpression(antlr4::Token* token,
|
||||
std::shared_ptr<AbstractExpression> xExpression,
|
||||
std::shared_ptr<AbstractExpression> yExpression,
|
||||
std::shared_ptr<AbstractExpression> zExpression);
|
||||
~VectorConstructorExpression() override;
|
||||
|
||||
Value execute(const ProgramVariables& variables) const override;
|
||||
bool validate(antlr4::Parser* parser, const ParseContext& context) override;
|
||||
void validationDone() override;
|
||||
|
||||
protected:
|
||||
ValueType determineReturnType() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<AbstractExpression> m_xExpression;
|
||||
std::shared_ptr<AbstractExpression> m_yExpression;
|
||||
std::shared_ptr<AbstractExpression> m_zExpression;
|
||||
};
|
||||
|
||||
} // namespace nodes
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
@@ -1,79 +0,0 @@
|
||||
|
||||
#include "MoveToSubmodel.h"
|
||||
|
||||
#include "math/vecmat.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "ship/ship.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
flagset<ProgramContextFlags> MoveToSubmodel::getRequiredExecutionContextFlags()
|
||||
{
|
||||
return flagset<ProgramContextFlags>{ProgramContextFlags::HasObject, ProgramContextFlags::HasSubobject};
|
||||
}
|
||||
|
||||
MoveToSubmodel::MoveToSubmodel(expression::TypedActionExpression<ValueType> subObjectExpression)
|
||||
: m_subObjectExpression(std::move(subObjectExpression))
|
||||
{
|
||||
}
|
||||
MoveToSubmodel::~MoveToSubmodel() = default;
|
||||
|
||||
ActionResult MoveToSubmodel::execute(ProgramLocals& locals) const
|
||||
{
|
||||
// The calling code should ensure that this never happens
|
||||
Assertion(locals.hostSubobject >= 0, "Did not have a valid host subobject.");
|
||||
|
||||
auto instance = object_get_model_instance(locals.host.objp);
|
||||
Assertion(instance != -1, "Model instances are required if a host subobject is specified.");
|
||||
|
||||
auto pmi = model_get_instance(instance);
|
||||
auto pm = model_get(pmi->model_num);
|
||||
|
||||
const auto destinationSubObject = m_subObjectExpression.execute(locals.variables);
|
||||
bool goToParent = false;
|
||||
if (destinationSubObject == "<parent>") {
|
||||
goToParent = true;
|
||||
}
|
||||
|
||||
if (goToParent) {
|
||||
// Special case when we simply want to go to our parent without explicitly specifying its name
|
||||
auto parentSubobject = pm->submodel[locals.hostSubobject].parent;
|
||||
|
||||
if (parentSubobject < 0) {
|
||||
Warning(LOCATION,
|
||||
"Current program subobject %s on model %s has no parent!",
|
||||
pm->submodel[locals.hostSubobject].name,
|
||||
pm->filename);
|
||||
return ActionResult::Errored;
|
||||
}
|
||||
|
||||
// We have a valid parent so this move will work properly
|
||||
locals.hostSubobject = parentSubobject;
|
||||
return ActionResult::Finished;
|
||||
}
|
||||
|
||||
// We need to do a linear search for the right subobject
|
||||
for (int i = 0; i < pm->n_models; ++i) {
|
||||
const auto submodel = pm->submodel[i];
|
||||
|
||||
if (subsystem_stricmp(destinationSubObject.c_str(), submodel.name) == 0) {
|
||||
// Found something!
|
||||
locals.hostSubobject = i;
|
||||
return ActionResult::Finished;
|
||||
}
|
||||
}
|
||||
|
||||
Warning(LOCATION, "Could not find subobject %s in model %s!", destinationSubObject.c_str(), pm->filename);
|
||||
return ActionResult::Errored;
|
||||
}
|
||||
|
||||
std::unique_ptr<Action> MoveToSubmodel::clone() const
|
||||
{
|
||||
return std::unique_ptr<Action>(new MoveToSubmodel(*this));
|
||||
}
|
||||
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/flagset.h"
|
||||
|
||||
#include "actions/Action.h"
|
||||
#include "actions/expression/ActionExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
class MoveToSubmodel : public Action {
|
||||
public:
|
||||
using ValueType = SCP_string;
|
||||
|
||||
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
|
||||
|
||||
explicit MoveToSubmodel(expression::TypedActionExpression<ValueType> subObjectExpression);
|
||||
~MoveToSubmodel() override;
|
||||
|
||||
ActionResult execute(ProgramLocals& locals) const override;
|
||||
|
||||
std::unique_ptr<Action> clone() const override;
|
||||
|
||||
private:
|
||||
expression::TypedActionExpression<ValueType> m_subObjectExpression;
|
||||
};
|
||||
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
@@ -1,76 +0,0 @@
|
||||
|
||||
#include "ParticleEffectAction.h"
|
||||
|
||||
#include "math/vecmat.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "ship/ship.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
flagset<ProgramContextFlags> ParticleEffectAction::getRequiredExecutionContextFlags()
|
||||
{
|
||||
return flagset<ProgramContextFlags>{ProgramContextFlags::HasObject};
|
||||
}
|
||||
|
||||
ParticleEffectAction::ParticleEffectAction(expression::TypedActionExpression<ValueType> effectExpression)
|
||||
: m_effectExpression(std::move(effectExpression))
|
||||
{
|
||||
}
|
||||
ParticleEffectAction::~ParticleEffectAction() = default;
|
||||
|
||||
ActionResult ParticleEffectAction::execute(ProgramLocals& locals) const
|
||||
{
|
||||
auto effectIdx = particle::ParticleManager::get()->getEffectByName(m_effectExpression.execute(locals.variables));
|
||||
|
||||
if (!effectIdx.isValid()) {
|
||||
// In case the parsing code failed
|
||||
return ActionResult::Errored;
|
||||
}
|
||||
|
||||
using namespace particle;
|
||||
|
||||
auto source = ParticleManager::get()->createSource(effectIdx);
|
||||
|
||||
vec3d local_pos;
|
||||
matrix local_orient;
|
||||
if (locals.hostSubobject != -1) {
|
||||
auto instance = object_get_model_instance(locals.host.objp);
|
||||
Assertion(instance != -1, "Model instances are required if a host subobject is specified.");
|
||||
|
||||
auto pmi = model_get_instance(instance);
|
||||
auto pm = model_get(pmi->model_num);
|
||||
|
||||
find_submodel_instance_point_orient(&local_pos,
|
||||
&local_orient,
|
||||
pm,
|
||||
pmi,
|
||||
locals.hostSubobject,
|
||||
&locals.localPosition,
|
||||
&locals.localOrient);
|
||||
} else {
|
||||
local_pos = locals.localPosition;
|
||||
local_orient = locals.localOrient;
|
||||
}
|
||||
|
||||
local_pos += locals.variables.getValue({"locals", "position"}).getVector();
|
||||
|
||||
auto direction = locals.variables.getValue({"locals", "direction"}).getVector();
|
||||
|
||||
source.moveToObject(locals.host.objp, &local_pos);
|
||||
source.setOrientationFromNormalizedVec(&direction, true);
|
||||
|
||||
source.finish();
|
||||
|
||||
return ActionResult::Finished;
|
||||
}
|
||||
|
||||
std::unique_ptr<Action> ParticleEffectAction::clone() const
|
||||
{
|
||||
return std::unique_ptr<Action>(new ParticleEffectAction(*this));
|
||||
}
|
||||
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "actions/Action.h"
|
||||
#include "actions/expression/ActionExpression.h"
|
||||
#include "particle/ParticleManager.h"
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
class ParticleEffectAction : public Action {
|
||||
|
||||
public:
|
||||
using ValueType = SCP_string;
|
||||
|
||||
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
|
||||
|
||||
explicit ParticleEffectAction(expression::TypedActionExpression<ValueType> effectExpression);
|
||||
~ParticleEffectAction() override;
|
||||
|
||||
ActionResult execute(ProgramLocals& locals) const override;
|
||||
|
||||
std::unique_ptr<Action> clone() const override;
|
||||
|
||||
private:
|
||||
expression::TypedActionExpression<ValueType> m_effectExpression;
|
||||
};
|
||||
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
@@ -1,65 +0,0 @@
|
||||
//
|
||||
//
|
||||
|
||||
#include "PlaySoundAction.h"
|
||||
|
||||
#include "ship/ship.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
flagset<ProgramContextFlags> PlaySoundAction::getRequiredExecutionContextFlags()
|
||||
{
|
||||
return flagset<ProgramContextFlags>{ProgramContextFlags::HasObject};
|
||||
}
|
||||
|
||||
PlaySoundAction::PlaySoundAction(expression::TypedActionExpression<ValueType> soundIdExpression)
|
||||
: m_soundIdExpression(std::move(soundIdExpression))
|
||||
{
|
||||
}
|
||||
PlaySoundAction::~PlaySoundAction() = default;
|
||||
|
||||
ActionResult PlaySoundAction::execute(ProgramLocals& locals) const
|
||||
{
|
||||
vec3d local_pos;
|
||||
matrix local_orient;
|
||||
if (locals.hostSubobject != -1) {
|
||||
auto instance = object_get_model_instance(locals.host.objp);
|
||||
Assertion(instance != -1, "Model instances are required if a host subobject is specified.");
|
||||
|
||||
auto pmi = model_get_instance(instance);
|
||||
auto pm = model_get(pmi->model_num);
|
||||
|
||||
find_submodel_instance_point_orient(&local_pos,
|
||||
&local_orient,
|
||||
pm,
|
||||
pmi,
|
||||
locals.hostSubobject,
|
||||
&locals.localPosition,
|
||||
&locals.localOrient);
|
||||
} else {
|
||||
local_pos = locals.localPosition;
|
||||
local_orient = locals.localOrient;
|
||||
}
|
||||
|
||||
local_pos += locals.variables.getValue({"locals", "position"}).getVector();
|
||||
|
||||
vec3d global_pos;
|
||||
vm_vec_unrotate(&global_pos, &local_pos, &locals.host.objp->orient);
|
||||
global_pos += locals.host.objp->pos;
|
||||
|
||||
const auto soundId = gamesnd_get_by_name(m_soundIdExpression.execute(locals.variables).c_str());
|
||||
|
||||
// Sound is not attached to the host but the object sound system currently only supports persistent sounds
|
||||
snd_play_3d(gamesnd_get_game_sound(soundId), &global_pos, &Eye_position);
|
||||
|
||||
return ActionResult::Finished;
|
||||
}
|
||||
std::unique_ptr<Action> PlaySoundAction::clone() const
|
||||
{
|
||||
return std::unique_ptr<Action>(new PlaySoundAction(*this));
|
||||
}
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "actions/Action.h"
|
||||
#include "actions/expression/ActionExpression.h"
|
||||
#include "gamesnd/gamesnd.h"
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
class PlaySoundAction : public Action {
|
||||
public:
|
||||
using ValueType = SCP_string;
|
||||
|
||||
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
|
||||
|
||||
explicit PlaySoundAction(expression::TypedActionExpression<ValueType> soundIdExpression);
|
||||
~PlaySoundAction() override;
|
||||
|
||||
ActionResult execute(ProgramLocals& locals) const override;
|
||||
|
||||
std::unique_ptr<Action> clone() const override;
|
||||
|
||||
private:
|
||||
expression::TypedActionExpression<ValueType> m_soundIdExpression;
|
||||
};
|
||||
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
#include "SetDirectionAction.h"
|
||||
|
||||
#include "math/vecmat.h"
|
||||
#include "parse/parselo.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
flagset<ProgramContextFlags> SetDirectionAction::getRequiredExecutionContextFlags()
|
||||
{
|
||||
return flagset<ProgramContextFlags>{};
|
||||
}
|
||||
|
||||
SetDirectionAction::SetDirectionAction(expression::TypedActionExpression<vec3d> newDirExpression)
|
||||
: m_newDirExpression(std::move(newDirExpression))
|
||||
{
|
||||
}
|
||||
SetDirectionAction::~SetDirectionAction() = default;
|
||||
|
||||
ActionResult SetDirectionAction::execute(ProgramLocals& locals) const
|
||||
{
|
||||
auto dir = m_newDirExpression.execute(locals.variables);
|
||||
|
||||
vm_vec_normalize_safe(&dir);
|
||||
|
||||
locals.variables.setValue({"locals", "direction"}, expression::Value(dir));
|
||||
return ActionResult::Finished;
|
||||
}
|
||||
|
||||
std::unique_ptr<Action> SetDirectionAction::clone() const
|
||||
{
|
||||
return std::unique_ptr<Action>(new SetDirectionAction(*this));
|
||||
}
|
||||
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "actions/Action.h"
|
||||
#include "actions/expression/ActionExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
class SetDirectionAction : public Action {
|
||||
public:
|
||||
using ValueType = vec3d;
|
||||
|
||||
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
|
||||
|
||||
SetDirectionAction(expression::TypedActionExpression<ValueType> newDirExpression);
|
||||
~SetDirectionAction() override;
|
||||
|
||||
ActionResult execute(ProgramLocals& locals) const override;
|
||||
|
||||
std::unique_ptr<Action> clone() const override;
|
||||
|
||||
private:
|
||||
expression::TypedActionExpression<ValueType> m_newDirExpression;
|
||||
};
|
||||
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
#include "SetPositionAction.h"
|
||||
|
||||
#include "parse/parselo.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
flagset<ProgramContextFlags> SetPositionAction::getRequiredExecutionContextFlags()
|
||||
{
|
||||
return flagset<ProgramContextFlags>{};
|
||||
}
|
||||
|
||||
SetPositionAction::SetPositionAction(expression::TypedActionExpression<ValueType> newPositionExpression)
|
||||
: m_newPosExpression(std::move(newPositionExpression))
|
||||
{
|
||||
}
|
||||
SetPositionAction::~SetPositionAction() = default;
|
||||
|
||||
ActionResult SetPositionAction::execute(ProgramLocals& locals) const
|
||||
{
|
||||
locals.variables.setValue({"locals", "position"}, expression::Value(m_newPosExpression.execute(locals.variables)));
|
||||
return ActionResult::Finished;
|
||||
}
|
||||
|
||||
std::unique_ptr<Action> SetPositionAction::clone() const
|
||||
{
|
||||
return std::unique_ptr<Action>(new SetPositionAction(*this));
|
||||
}
|
||||
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "actions/Action.h"
|
||||
#include "actions/expression/ActionExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
class SetPositionAction : public Action {
|
||||
|
||||
public:
|
||||
using ValueType = vec3d;
|
||||
|
||||
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
|
||||
|
||||
SetPositionAction(expression::TypedActionExpression<ValueType> newPositionExpression);
|
||||
~SetPositionAction() override;
|
||||
|
||||
ActionResult execute(ProgramLocals& locals) const override;
|
||||
|
||||
std::unique_ptr<Action> clone() const override;
|
||||
|
||||
private:
|
||||
expression::TypedActionExpression<vec3d> m_newPosExpression;
|
||||
};
|
||||
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
@@ -1,48 +0,0 @@
|
||||
|
||||
#include "WaitAction.h"
|
||||
|
||||
#include "io/timer.h"
|
||||
#include "parse/parselo.h"
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
flagset<ProgramContextFlags> WaitAction::getRequiredExecutionContextFlags()
|
||||
{
|
||||
return flagset<ProgramContextFlags>{};
|
||||
}
|
||||
|
||||
WaitAction::WaitAction(expression::TypedActionExpression<float> waitTimeExpression)
|
||||
: _waitTimeExpression(std::move(waitTimeExpression))
|
||||
{
|
||||
}
|
||||
WaitAction::~WaitAction() = default;
|
||||
|
||||
ActionResult WaitAction::execute(actions::ProgramLocals& locals) const
|
||||
{
|
||||
if (!timestamp_valid(locals.waitTimestamp)) {
|
||||
auto waitTime = _waitTimeExpression.execute(locals.variables);
|
||||
|
||||
// Catch errors in the expression
|
||||
if (waitTime < 0.001f) {
|
||||
waitTime = 0.001f;
|
||||
}
|
||||
|
||||
locals.waitTimestamp = timestamp(fl2i(waitTime * TIMESTAMP_FREQUENCY));
|
||||
}
|
||||
if (!timestamp_elapsed(locals.waitTimestamp)) {
|
||||
// Wait until the timestamp is elapsed
|
||||
return ActionResult::Wait;
|
||||
}
|
||||
// Timestamp is elapsed. The timestamp is elapsed so that timestamp_valid above returns false for the next wait
|
||||
// action
|
||||
locals.waitTimestamp = 0;
|
||||
return ActionResult::Finished;
|
||||
}
|
||||
std::unique_ptr<Action> WaitAction::clone() const
|
||||
{
|
||||
return std::unique_ptr<Action>(new WaitAction(*this));
|
||||
}
|
||||
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "actions/Action.h"
|
||||
#include "actions/common.h"
|
||||
#include "actions/expression/ActionExpression.h"
|
||||
#include "utils/RandomRange.h"
|
||||
|
||||
namespace actions {
|
||||
namespace types {
|
||||
|
||||
class WaitAction : public Action {
|
||||
public:
|
||||
using ValueType = float;
|
||||
|
||||
static flagset<ProgramContextFlags> getRequiredExecutionContextFlags();
|
||||
|
||||
explicit WaitAction(expression::TypedActionExpression<ValueType> waitTimeExpression);
|
||||
~WaitAction() override;
|
||||
|
||||
ActionResult execute(ProgramLocals& locals) const override;
|
||||
|
||||
std::unique_ptr<Action> clone() const override;
|
||||
|
||||
private:
|
||||
expression::TypedActionExpression<ValueType> _waitTimeExpression;
|
||||
};
|
||||
|
||||
} // namespace types
|
||||
} // namespace actions
|
||||
+10
-70
@@ -3321,9 +3321,7 @@ void ai_start_fly_to_ship(object *objp, int shipnum)
|
||||
aip->mode = AIM_FLY_TO_SHIP;
|
||||
aip->submode_start_time = Missiontime;
|
||||
|
||||
aip->target_objnum = Ships[shipnum].objnum;
|
||||
// clear targeted subsystem
|
||||
set_targeted_subsys(aip, nullptr, -1);
|
||||
aip->target_objnum = Ships[shipnum].objnum;
|
||||
|
||||
Assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
|
||||
}
|
||||
@@ -5777,15 +5775,6 @@ int ai_fire_primary_weapon(object *objp)
|
||||
aip->primary_select_timestamp = timestamp(5 * 1000); // Maybe change primary weapon five seconds from now.
|
||||
}
|
||||
|
||||
//We can only check LoS if we have a target defined
|
||||
weapon_info* wip = &Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]];
|
||||
if (aip->target_objnum != -1 && (The_mission.ai_profile->flags[AI::Profile_Flags::Require_exact_los] || wip->wi_flags[Weapon::Info_Flags::Require_exact_los])) {
|
||||
//Check if th AI has Line of Sight and is allowed to fire
|
||||
if (!check_los(OBJ_INDEX(objp), aip->target_objnum, 10.0f, swp->current_primary_bank, -1, nullptr)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
|
||||
float dot;
|
||||
vec3d v2t;
|
||||
@@ -5936,7 +5925,7 @@ void ai_select_secondary_weapon(object *objp, ship_weapon *swp, flagset<Weapon::
|
||||
int num_weapon_types;
|
||||
int weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
|
||||
int i;
|
||||
flagset<Weapon::Info_Flags> ignore_mask, ignore_mask_without_huge, prio1, prio2;
|
||||
flagset<Weapon::Info_Flags> ignore_mask, prio1, prio2;
|
||||
int initial_bank;
|
||||
ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
|
||||
|
||||
@@ -5981,14 +5970,11 @@ void ai_select_secondary_weapon(object *objp, ship_weapon *swp, flagset<Weapon::
|
||||
ignore_mask.set(Weapon::Info_Flags::Homing_aspect).set(Weapon::Info_Flags::Homing_heat).set(Weapon::Info_Flags::Homing_javelin);
|
||||
}
|
||||
|
||||
ignore_mask_without_huge = ignore_mask;
|
||||
ignore_mask_without_huge.remove(Weapon::Info_Flags::Huge).remove(Weapon::Info_Flags::Capital_plus);
|
||||
|
||||
int priority2_index = -1;
|
||||
|
||||
for (i=0; i<num_weapon_types; i++) {
|
||||
auto wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
|
||||
auto ignore_mask_to_use = ((aip->ai_profile_flags[AI::Profile_Flags::Smart_secondary_weapon_selection]) && (wi_flags[Weapon::Info_Flags::Bomber_plus])) ? ignore_mask_without_huge : ignore_mask;
|
||||
auto ignore_mask_to_use = ((aip->ai_profile_flags[AI::Profile_Flags::Smart_secondary_weapon_selection]) && (wi_flags[Weapon::Info_Flags::Bomber_plus])) ? (ignore_mask - Weapon::Info_Flags::Huge) : ignore_mask;
|
||||
|
||||
if (!(wi_flags & ignore_mask_to_use).any_set()) { // Maybe bombs are illegal.
|
||||
if ((wi_flags & prio1).any_set()) {
|
||||
@@ -6121,7 +6107,7 @@ float compute_incoming_payload(object *target_objp)
|
||||
// OR: secondary los flag is set but no line of sight is availabe
|
||||
// Note: If player is attacking a ship, that ship is allowed to fire at player. Otherwise, we get in a situation in which
|
||||
// player is attacking a large ship, but that large ship is not defending itself with missiles.
|
||||
int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int secondary_bank, ship_subsys* turret)
|
||||
int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
|
||||
{
|
||||
int num_homers = 0;
|
||||
|
||||
@@ -6137,7 +6123,7 @@ int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int second
|
||||
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Require_exact_los] || wip->wi_flags[Weapon::Info_Flags::Require_exact_los]) {
|
||||
//Check if th AI has Line of Sight and is allowed to fire
|
||||
if (!check_los(objnum, target_objnum, 10.0f, -1, secondary_bank, turret)) {
|
||||
if (!check_los(objnum, target_objnum, 10.0f)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -6184,55 +6170,9 @@ int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int second
|
||||
// --------------------------------------------------------------------------
|
||||
// Returns true if *aip has a line of sight to its current target.
|
||||
// threshold defines the minimum radius for an object to be considered relevant for LoS
|
||||
bool check_los(int objnum, int target_objnum, float threshold, int primary_bank, int secondary_bank, ship_subsys* turret) {
|
||||
|
||||
//Do both checks over an XOR-Check for more detailed messages
|
||||
int sources = (primary_bank != -1 ? 1 : 0) + (secondary_bank != -1 ? 1 : 0) + (turret != nullptr ? 1 : 0);
|
||||
Assertion(sources >= 1, "Cannot check line of sight from a model with neither a primar bank, nor secondary bank nor a turret set as a source.");
|
||||
Assertion(sources <= 1, "Cannot check line of sight from a model with more than one source set.");
|
||||
|
||||
vec3d start;
|
||||
|
||||
object* firing_ship = &Objects[objnum];
|
||||
|
||||
if (turret != nullptr) {
|
||||
vec3d turret_fvec;
|
||||
//It doesn't like not having an fvec output vector, so give it a dummy vector
|
||||
ship_get_global_turret_gun_info(firing_ship, turret, &start, &turret_fvec, 1, nullptr);
|
||||
} else {
|
||||
bool is_primary = secondary_bank == -1;
|
||||
ship *shipp = &Ships[firing_ship->instance];
|
||||
ship_weapon *swp = &shipp->weapons;
|
||||
weapon_info *wip = &Weapon_info[is_primary ? swp->primary_bank_weapons[primary_bank] : swp->secondary_bank_weapons[secondary_bank]];
|
||||
polymodel* pm = model_get(Ship_info[shipp->ship_info_index].model_num);
|
||||
|
||||
vec3d pnt = is_primary ? pm->gun_banks[primary_bank].pnt[swp->primary_next_slot[primary_bank]] : pm->missile_banks[secondary_bank].pnt[swp->secondary_next_slot[secondary_bank]];
|
||||
vec3d firing_point;
|
||||
|
||||
//Following Section taken from ship.cpp ship_fire_secondary()
|
||||
polymodel* weapon_model = nullptr;
|
||||
if (wip->external_model_num >= 0) {
|
||||
weapon_model = model_get(wip->external_model_num);
|
||||
}
|
||||
|
||||
if (weapon_model && weapon_model->n_guns) {
|
||||
int external_bank = is_primary ? primary_bank : secondary_bank + MAX_SHIP_PRIMARY_BANKS;
|
||||
if (wip->wi_flags[Weapon::Info_Flags::External_weapon_fp]) {
|
||||
if ((weapon_model->n_guns <= swp->external_model_fp_counter[external_bank]) || (swp->external_model_fp_counter[external_bank] < 0))
|
||||
swp->external_model_fp_counter[external_bank] = 0;
|
||||
vm_vec_add2(&pnt, &weapon_model->gun_banks[0].pnt[swp->external_model_fp_counter[external_bank]]);
|
||||
swp->external_model_fp_counter[external_bank]++;
|
||||
}
|
||||
else {
|
||||
// make it use the 0 index slot
|
||||
vm_vec_add2(&pnt, &weapon_model->gun_banks[0].pnt[0]);
|
||||
}
|
||||
}
|
||||
vm_vec_unrotate(&firing_point, &pnt, &firing_ship->orient);
|
||||
vm_vec_add(&start, &firing_point, &firing_ship->pos);
|
||||
}
|
||||
|
||||
bool check_los(int objnum, int target_objnum, float threshold) {
|
||||
vec3d& end = Objects[target_objnum].pos;
|
||||
vec3d& start = Objects[objnum].pos;
|
||||
|
||||
object* objp;
|
||||
|
||||
@@ -6370,7 +6310,7 @@ int ai_fire_secondary_weapon(object *objp)
|
||||
|
||||
// Note, maybe don't fire if firing at player and any homers yet fired.
|
||||
// Decreasing chance to fire the more homers are incoming on player.
|
||||
if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip, current_bank, nullptr)) {
|
||||
if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
|
||||
if (ship_fire_secondary(objp)) {
|
||||
rval = 1;
|
||||
swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
|
||||
@@ -7988,7 +7928,7 @@ void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
|
||||
|
||||
if (is_big_ship)
|
||||
{
|
||||
wif_priority1.set(Weapon::Info_Flags::Huge).set(Weapon::Info_Flags::Capital_plus);
|
||||
wif_priority1.set(Weapon::Info_Flags::Huge);
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Smart_secondary_weapon_selection]) {
|
||||
wif_priority2.set(Weapon::Info_Flags::Bomber_plus);
|
||||
}
|
||||
@@ -11485,7 +11425,7 @@ void process_subobjects(int objnum)
|
||||
}
|
||||
//Only move turrets if enemies are present
|
||||
if(enemies_present == 1 || pss->turret_enemy_objnum >= 0)
|
||||
ai_fire_from_turret(shipp, pss);
|
||||
ai_fire_from_turret(shipp, pss, objnum);
|
||||
} else {
|
||||
Warning( LOCATION, "Turret %s on ship %s has no firing points assigned to it.\nThis needs to be fixed in the model.\n", psub->name, shipp->ship_name );
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@ int object_is_targetable(object *target, ship *viewer);
|
||||
int num_nearby_fighters(int enemy_team_mask, vec3d *pos, float threshold);
|
||||
|
||||
//Returns true if OK for *aip to fire its current weapon at its current target.
|
||||
int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int secondary_bank, ship_subsys* turret);
|
||||
int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip);
|
||||
|
||||
//Returns true if *aip has a line of sight to its current target.
|
||||
bool check_los(int objnum, int target_objnum, float threshold, int primary_bank, int secondary_bank, ship_subsys* turret);
|
||||
bool check_los(int objnum, int target_objnum, float threshold);
|
||||
|
||||
//Does all the stuff needed to aim and fire a turret.
|
||||
void ai_fire_from_turret(ship *shipp, ship_subsys *ss);
|
||||
void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum);
|
||||
|
||||
#endif
|
||||
+24
-18
@@ -1361,11 +1361,10 @@ void turret_ai_update_aim(ai_info *aip, object *En_Objp, ship_subsys *ss)
|
||||
*
|
||||
* @return TRUE if caller should use angles in subsequent rotations.
|
||||
*/
|
||||
int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep, vec3d *predicted_enemy_pos, vec3d *gvec)
|
||||
int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vec3d *predicted_enemy_pos, vec3d *gvec)
|
||||
{
|
||||
int ret_val __UNUSED = 0; // to be used in future, see comment @ end of function
|
||||
auto pmi = model_get_instance(shipp->model_instance_num);
|
||||
auto pm = model_get(pmi->model_num);
|
||||
polymodel *pm = model_get(Ship_info[shipp->ship_info_index].model_num);
|
||||
|
||||
if (ss->turret_enemy_objnum != -1) {
|
||||
model_subsystem *tp = ss->system_info;
|
||||
@@ -1386,7 +1385,7 @@ int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep,
|
||||
// weapon_system_strength scales time enemy in range in 0..1. So, the lower this is, the worse the aiming will be.
|
||||
weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
|
||||
|
||||
ship_get_global_turret_info(objp, tp, &gun_pos, &gun_vec);
|
||||
ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
|
||||
|
||||
//Update "known" position and velocity of target. Only matters if max_aim_update_delay is set.
|
||||
turret_ai_update_aim(&Ai_info[shipp->ai_index], &Objects[ss->turret_enemy_objnum], ss);
|
||||
@@ -1439,13 +1438,20 @@ int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep,
|
||||
in_fov = is_object_radius_in_turret_fov(&Objects[ss->turret_enemy_objnum], ss, gvec, &gun_pos, &v2e, predicted_enemy_pos, 0.0f);
|
||||
}
|
||||
|
||||
auto base_angles = &ss->submodel_instance_1->angs;
|
||||
auto gun_angles = &ss->submodel_instance_2->angs;
|
||||
if (in_fov) {
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, tp, predicted_enemy_pos);
|
||||
ret_val = model_rotate_gun(pm,
|
||||
tp, &Objects[parent_objnum].orient,
|
||||
base_angles, gun_angles,
|
||||
&Objects[parent_objnum].pos, predicted_enemy_pos, shipp->objnum);
|
||||
} else if ((tp->flags[Model::Subsystem_Flags::Turret_reset_idle]) &&(timestamp_elapsed(ss->rotation_timestamp))) {
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, tp, predicted_enemy_pos, true);
|
||||
ret_val = model_rotate_gun(pm, tp, &Objects[parent_objnum].orient, base_angles, gun_angles, &Objects[parent_objnum].pos, predicted_enemy_pos, shipp->objnum, true);
|
||||
}
|
||||
} else if ((ss->system_info->flags[Model::Subsystem_Flags::Turret_reset_idle]) && (timestamp_elapsed(ss->rotation_timestamp))) {
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, ss->system_info, predicted_enemy_pos, true);
|
||||
auto base_angles = &ss->submodel_instance_1->angs;
|
||||
auto gun_angles = &ss->submodel_instance_2->angs;
|
||||
ret_val = model_rotate_gun(pm, ss->system_info, &Objects[parent_objnum].orient, base_angles, gun_angles, &Objects[parent_objnum].pos, predicted_enemy_pos, shipp->objnum, true);
|
||||
}
|
||||
|
||||
// by default "ret_val" should be set to 1 for multi-part turrets, and 0 for single-part turrets
|
||||
@@ -1833,7 +1839,7 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, wip, -1, turret)) {
|
||||
if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, wip)) {
|
||||
vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
|
||||
turret->turret_last_fire_direction = *turret_fvec;
|
||||
|
||||
@@ -2160,7 +2166,7 @@ int Num_turrets_fired = 0;
|
||||
* Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
|
||||
*/
|
||||
extern int Nebula_sec_range;
|
||||
void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
|
||||
{
|
||||
float weapon_firing_range;
|
||||
float WeaponMinRange; // *Weapon minimum firing range -Et1
|
||||
@@ -2237,10 +2243,10 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
lep = nullptr;
|
||||
}
|
||||
|
||||
Assert((shipp->objnum >= 0) && (shipp->objnum < MAX_OBJECTS));
|
||||
int parent_objnum = shipp->objnum;
|
||||
objp = &Objects[shipp->objnum];
|
||||
Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
|
||||
objp = &Objects[parent_objnum];
|
||||
Assert(objp->type == OBJ_SHIP);
|
||||
Assert(shipp->objnum == parent_objnum);
|
||||
|
||||
// Wanderer - make sure turrets already have all the data
|
||||
if ( !(tp->flags[Model::Subsystem_Flags::Turret_matrix]) )
|
||||
@@ -2255,14 +2261,14 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
|
||||
// Use the turret info for all guns, not one gun in particular.
|
||||
vec3d gvec, gpos;
|
||||
ship_get_global_turret_info(objp, tp, &gpos, &gvec);
|
||||
ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
|
||||
|
||||
if (tp->flags[Model::Subsystem_Flags::Turret_alt_math]) {
|
||||
vm_matrix_x_matrix( &ss->world_to_turret_matrix, &objp->orient, &tp->turret_matrix );
|
||||
vm_matrix_x_matrix( &ss->world_to_turret_matrix, &Objects[parent_objnum].orient, &tp->turret_matrix );
|
||||
}
|
||||
|
||||
// Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
|
||||
int use_angles = aifft_rotate_turret(objp, shipp, ss, lep, &predicted_enemy_pos, &gvec);
|
||||
int use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
|
||||
|
||||
if ((tp->flags[Model::Subsystem_Flags::Fire_on_target]) && (ss->points_to_target >= 0.0f))
|
||||
{
|
||||
@@ -2286,7 +2292,7 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
}
|
||||
|
||||
// count the number of enemies, in case we have a spawning weapon
|
||||
int num_ships_nearby = num_nearby_fighters(iff_get_attackee_mask(obj_team(objp)), &gpos, 1500.0f);
|
||||
int num_ships_nearby = num_nearby_fighters(iff_get_attackee_mask(obj_team(&Objects[parent_objnum])), &gpos, 1500.0f);
|
||||
|
||||
// some flags considering there may be different weapon types on this turret
|
||||
bool we_did_non_spawning_logic = false;
|
||||
@@ -2578,7 +2584,7 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
|
||||
// We're ready to fire... now get down to specifics, like where is the
|
||||
// actual gun point and normal, not just the one for whole turret.
|
||||
ship_get_global_turret_gun_info(objp, ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
|
||||
ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
|
||||
|
||||
// Fire in the direction the turret is facing, not right at the target regardless of turret dir.
|
||||
// [Yet this retail comment precedes the calculation of vector-to-enemy...]
|
||||
@@ -2722,7 +2728,7 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
|
||||
// make sure salvo fire mode does not turn into autofire
|
||||
if ((tp->flags[Model::Subsystem_Flags::Turret_salvo]) && ((i + 1) == number_of_firings)) {
|
||||
ai_info *parent_aip = &Ai_info[shipp->ai_index];
|
||||
ai_info *parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
|
||||
turret_set_next_fire_timestamp(valid_weapons[valid_index], wip, ss, parent_aip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1085,7 +1085,7 @@ int asteroid_check_collision(object *pasteroid, object *other_obj, vec3d *hitpos
|
||||
|
||||
}
|
||||
|
||||
// Now complete base model collision checks that do not take into account rotating submodels.
|
||||
// Recover and do usual ship_ship collision, but without rotating submodels
|
||||
mc.flags = copy_flags;
|
||||
*mc.p0 = copy_p0;
|
||||
*mc.p1 = copy_p1;
|
||||
|
||||
@@ -940,7 +940,7 @@ int debris_check_collision(object *pdebris, object *other_obj, vec3d *hitpos, co
|
||||
}
|
||||
}
|
||||
|
||||
// Now complete base model collision checks that do not take into account rotating submodels.
|
||||
// Recover and do usual ship_ship collision, but without rotating submodels
|
||||
mc.flags = copy_flags;
|
||||
*mc.p0 = copy_p0;
|
||||
*mc.p1 = copy_p1;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <sstream>
|
||||
#include <climits>
|
||||
|
||||
#include "gamesnd.h"
|
||||
#include "gamesnd/gamesnd.h"
|
||||
#include "localization/localize.h"
|
||||
#include "parse/parselo.h"
|
||||
@@ -422,30 +421,19 @@ bool parse_game_sound(const char* tag, gamesnd_id* idx_dest)
|
||||
{
|
||||
if (optional_string(tag))
|
||||
{
|
||||
*idx_dest = parse_game_sound_inline();
|
||||
return true;
|
||||
}
|
||||
SCP_string buf;
|
||||
stuff_string(buf, F_NAME);
|
||||
|
||||
return false;
|
||||
}
|
||||
*idx_dest = gamesnd_get_by_name(buf.c_str());
|
||||
|
||||
/**
|
||||
* @brief Parses a game sound that should appear at the current parsing location
|
||||
* @return The game sound id or invalid id when parsing fails
|
||||
*/
|
||||
gamesnd_id parse_game_sound_inline()
|
||||
{
|
||||
SCP_string buf;
|
||||
stuff_string(buf, F_NAME);
|
||||
|
||||
auto id = gamesnd_get_by_name(buf.c_str());
|
||||
|
||||
// The special case "-1" is needed to silence warnings where sounds are intentionally removed
|
||||
if (!id.isValid() && buf != "-1") {
|
||||
// The special case "-1" is needed to silence warnings where sounds are intentionally removed
|
||||
if (idx_dest->isValid() || buf == "-1")
|
||||
return true;
|
||||
|
||||
error_display(0, "Could not find game sound with name '%s'!", buf.c_str());
|
||||
}
|
||||
|
||||
return id;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -372,8 +372,6 @@ interface_snd_id gamesnd_get_by_iface_tbl_index(int index);
|
||||
// Does all parsing for a sound. Returns true if a sound was successfully parsed.
|
||||
bool parse_game_sound(const char* tag, gamesnd_id* idx_dest);
|
||||
|
||||
gamesnd_id parse_game_sound_inline();
|
||||
|
||||
void parse_iface_sound(const char* tag, interface_snd_id* idx_dest);
|
||||
void parse_iface_sound_list(const char* tag, SCP_vector<interface_snd_id>& destination, const char* object_name, bool scp_list = false);
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
|
||||
#include "ErrorListener.h"
|
||||
|
||||
namespace libs {
|
||||
namespace antlr {
|
||||
|
||||
void ErrorListener::syntaxError(antlr4::Recognizer*,
|
||||
antlr4::Token* offendingSymbol,
|
||||
size_t line,
|
||||
size_t charPositionInLine,
|
||||
const std::string& msg,
|
||||
std::exception_ptr)
|
||||
{
|
||||
// Apparently stop < start is a thing?
|
||||
const auto left = std::min(offendingSymbol->getStartIndex(), offendingSymbol->getStopIndex());
|
||||
const auto right = std::max(offendingSymbol->getStartIndex(), offendingSymbol->getStopIndex());
|
||||
const auto length = right - left + 1;
|
||||
|
||||
diagnostics.push_back(Diagnostic{line, charPositionInLine, length, msg});
|
||||
}
|
||||
|
||||
} // namespace antlr
|
||||
} // namespace libs
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
#include <BaseErrorListener.h>
|
||||
#include <Token.h>
|
||||
|
||||
namespace libs {
|
||||
namespace antlr {
|
||||
|
||||
struct Diagnostic {
|
||||
size_t line;
|
||||
size_t columnInLine;
|
||||
size_t tokenLength;
|
||||
SCP_string errorMessage;
|
||||
};
|
||||
|
||||
struct ErrorListener : public antlr4::BaseErrorListener {
|
||||
SCP_vector<Diagnostic> diagnostics;
|
||||
|
||||
void syntaxError(antlr4::Recognizer* /*recognizer*/,
|
||||
antlr4::Token* offendingSymbol,
|
||||
size_t line,
|
||||
size_t charPositionInLine,
|
||||
const std::string& msg,
|
||||
std::exception_ptr /*e*/) override;
|
||||
};
|
||||
|
||||
} // namespace antlr
|
||||
} // namespace libs
|
||||
+9
-50
@@ -157,17 +157,6 @@ void vm_set_identity(matrix *m)
|
||||
m->vec.fvec.xyz.x = 0.0f; m->vec.fvec.xyz.y = 0.0f; m->vec.fvec.xyz.z = 1.0f;
|
||||
}
|
||||
|
||||
vec3d vm_vec_new(float x, float y, float z)
|
||||
{
|
||||
vec3d vec;
|
||||
|
||||
vec.xyz.x = x;
|
||||
vec.xyz.y = y;
|
||||
vec.xyz.z = z;
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
//adds two vectors, fills in dest, returns ptr to dest
|
||||
//ok for dest to equal either source, but should use vm_vec_add2() if so
|
||||
//dest = src0 + src1
|
||||
@@ -551,12 +540,10 @@ vec3d *vm_vec_perp(vec3d *dest, const vec3d *p0, const vec3d *p1,const vec3d *p2
|
||||
|
||||
//computes the delta angle between two vectors.
|
||||
//vectors need not be normalized. if they are, call vm_vec_delta_ang_norm()
|
||||
//the up vector (third parameter) can be NULL, in which case the absolute
|
||||
//value of the angle in returned.
|
||||
//Otherwise, the delta ang will be positive if the v0 -> v1 direction from the
|
||||
//point of view of uvec is clockwise, negative if counterclockwise.
|
||||
//This vector should be orthogonal to v0 and v1
|
||||
float vm_vec_delta_ang(const vec3d *v0, const vec3d *v1, const vec3d *uvec)
|
||||
//the forward vector (third parameter) can be NULL, in which case the absolute
|
||||
//value of the angle in returned. Otherwise the angle around that vector is
|
||||
//returned.
|
||||
float vm_vec_delta_ang(const vec3d *v0, const vec3d *v1, const vec3d *fvec)
|
||||
{
|
||||
float t;
|
||||
vec3d t0,t1,t2;
|
||||
@@ -564,10 +551,10 @@ float vm_vec_delta_ang(const vec3d *v0, const vec3d *v1, const vec3d *uvec)
|
||||
vm_vec_copy_normalize(&t0,v0);
|
||||
vm_vec_copy_normalize(&t1,v1);
|
||||
|
||||
if (uvec == nullptr) {
|
||||
if (NULL == fvec) {
|
||||
t = vm_vec_delta_ang_norm(&t0, &t1, NULL);
|
||||
} else {
|
||||
vm_vec_copy_normalize(&t2,uvec);
|
||||
vm_vec_copy_normalize(&t2,fvec);
|
||||
t = vm_vec_delta_ang_norm(&t0,&t1,&t2);
|
||||
}
|
||||
|
||||
@@ -575,38 +562,16 @@ float vm_vec_delta_ang(const vec3d *v0, const vec3d *v1, const vec3d *uvec)
|
||||
}
|
||||
|
||||
//computes the delta angle between two normalized vectors.
|
||||
float vm_vec_delta_ang_norm(const vec3d *v0, const vec3d *v1, const vec3d *uvec)
|
||||
float vm_vec_delta_ang_norm(const vec3d *v0, const vec3d *v1, const vec3d *fvec)
|
||||
{
|
||||
float a;
|
||||
vec3d t;
|
||||
|
||||
a = acosf(vm_vec_dot(v0,v1));
|
||||
|
||||
if (uvec) {
|
||||
if (fvec) {
|
||||
vm_vec_cross(&t,v0,v1);
|
||||
if ( vm_vec_dot(&t,uvec) < 0.0 ) {
|
||||
a = -a;
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
float vm_vec_delta_ang_norm_safe(const vec3d *v0, const vec3d *v1, const vec3d *uvec)
|
||||
{
|
||||
float a, dot;
|
||||
vec3d t;
|
||||
|
||||
// to avoid round-off errors...
|
||||
// testing produced a dot of 1.00000012 which evaluates to an angle of -nan(ind)
|
||||
dot = vm_vec_dot(v0, v1);
|
||||
CLAMP(dot, -1.0f, 1.0f);
|
||||
|
||||
a = acosf(dot);
|
||||
|
||||
if (uvec) {
|
||||
vm_vec_cross(&t,v0,v1);
|
||||
if ( vm_vec_dot(&t,uvec) < 0.0 ) {
|
||||
if ( vm_vec_dot(&t,fvec) < 0.0 ) {
|
||||
a = -a;
|
||||
}
|
||||
}
|
||||
@@ -2842,9 +2807,3 @@ void vm_interpolate_angles_quick(angles *dest0, angles *src0, angles *src1, floa
|
||||
dest0->h = src0->h + (arc_measures.h * interp_perc);
|
||||
dest0->b = src0->b + (arc_measures.b * interp_perc);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const vec3d& vec)
|
||||
{
|
||||
os << "vec3d<" << vec.xyz.x << ", " << vec.xyz.y << ", " << vec.xyz.z << ")";
|
||||
return os;
|
||||
}
|
||||
+5
-12
@@ -52,8 +52,6 @@ extern void vm_set_identity(matrix *m);
|
||||
|
||||
#define vm_vec_make(v,_x,_y,_z) ((v)->xyz.x=(_x), (v)->xyz.y=(_y), (v)->xyz.z=(_z))
|
||||
|
||||
extern vec3d vm_vec_new(float x, float y, float z);
|
||||
|
||||
//Global constants
|
||||
|
||||
extern vec3d vmd_zero_vector;
|
||||
@@ -226,16 +224,13 @@ vec3d *vm_vec_perp(vec3d *dest, const vec3d *p0, const vec3d *p1, const vec3d *p
|
||||
|
||||
//computes the delta angle between two vectors.
|
||||
//vectors need not be normalized. if they are, call vm_vec_delta_ang_norm()
|
||||
//the up vector (third parameter) can be NULL, in which case the absolute
|
||||
//value of the angle in returned.
|
||||
//Otherwise, the delta ang will be positive if the v0 -> v1 direction from the
|
||||
//point of view of uvec is clockwise, negative if counterclockwise.
|
||||
//This vector should be orthogonal to v0 and v1
|
||||
float vm_vec_delta_ang(const vec3d *v0, const vec3d *v1, const vec3d *uvec);
|
||||
//the forward vector (third parameter) can be NULL, in which case the absolute
|
||||
//value of the angle in returned. Otherwise the angle around that vector is
|
||||
//returned.
|
||||
float vm_vec_delta_ang(const vec3d *v0, const vec3d *v1, const vec3d *fvec);
|
||||
|
||||
//computes the delta angle between two normalized vectors.
|
||||
float vm_vec_delta_ang_norm(const vec3d *v0, const vec3d *v1,const vec3d *uvec);
|
||||
float vm_vec_delta_ang_norm_safe(const vec3d *v0, const vec3d *v1, const vec3d *uvec);
|
||||
float vm_vec_delta_ang_norm(const vec3d *v0, const vec3d *v1,const vec3d *fvec);
|
||||
|
||||
//computes a matrix from a set of three angles. returns ptr to matrix
|
||||
matrix *vm_angles_2_matrix(matrix *m, const angles *a);
|
||||
@@ -656,8 +651,6 @@ inline matrix operator*(const matrix& left, const matrix& right) {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const vec3d& vec);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -3366,8 +3366,6 @@ int parse_object(mission *pm, int /*flag*/, p_object *p_objp)
|
||||
texture_replace tr;
|
||||
char *p;
|
||||
|
||||
tr.from_table = false;
|
||||
|
||||
while (optional_string("+old:"))
|
||||
{
|
||||
strcpy_s(tr.ship_name, p_objp->name);
|
||||
|
||||
@@ -310,7 +310,6 @@ typedef struct texture_replace {
|
||||
char old_texture[MAX_FILENAME_LEN];
|
||||
char new_texture[MAX_FILENAME_LEN];
|
||||
int new_texture_id;
|
||||
bool from_table;
|
||||
} texture_replace;
|
||||
|
||||
extern SCP_vector<texture_replace> Fred_texture_replacements;
|
||||
|
||||
@@ -3426,7 +3426,6 @@ void wl_bash_ship_weapons(ship_weapon *swp, wss_unit *slot)
|
||||
if ( (slot->wep_count[sidx] > 0) && (slot->wep[sidx] >= 0) ) {
|
||||
swp->secondary_bank_weapons[j] = slot->wep[sidx];
|
||||
swp->secondary_bank_ammo[j] = slot->wep_count[sidx];
|
||||
swp->secondary_bank_start_ammo[j] = (int)std::lround(Ship_info[slot->ship_class].secondary_bank_ammo_capacity[i] / Weapon_info[swp->secondary_bank_weapons[j]].cargo_size);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-10
@@ -12,16 +12,14 @@
|
||||
#ifndef _MODEL_H
|
||||
#define _MODEL_H
|
||||
|
||||
#include "globalincs/globals.h" // for NAME_LENGTH
|
||||
#include "globalincs/globals.h" // for NAME_LENGTH
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
#include "actions/Program.h"
|
||||
#include "gamesnd/gamesnd.h"
|
||||
#include "graphics/2d.h"
|
||||
#include "gamesnd/gamesnd.h"
|
||||
#include "io/timer.h"
|
||||
#include "model/model_flags.h"
|
||||
#include "object/object.h"
|
||||
#include "ship/ship_flags.h"
|
||||
#include "model/model_flags.h"
|
||||
|
||||
class object;
|
||||
class model_render_params;
|
||||
@@ -206,13 +204,13 @@ public:
|
||||
|
||||
//Per-turret ownage settings - SUSHI
|
||||
int turret_max_bomb_ownage;
|
||||
int turret_max_target_ownage;
|
||||
|
||||
actions::ProgramSet beam_warmdown_program;
|
||||
int turret_max_target_ownage;
|
||||
|
||||
void reset();
|
||||
|
||||
model_subsystem();
|
||||
model_subsystem() {
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
typedef struct model_special {
|
||||
@@ -910,7 +908,8 @@ extern int modelstats_num_sortnorms;
|
||||
#endif
|
||||
|
||||
// Tries to move joints so that the turret points to the point dst.
|
||||
extern int model_rotate_gun(object *objp, polymodel *pm, polymodel_instance *pmi, model_subsystem *turret, vec3d *dst, bool reset = false);
|
||||
// turret1 is the angles of the turret, turret2 is the angles of the gun from turret
|
||||
extern int model_rotate_gun(polymodel *pm, model_subsystem *turret, matrix *orient, angles *base_angles, angles *gun_angles, vec3d *pos, vec3d *dst, int obj_idx, bool reset = false);
|
||||
|
||||
// Gets and sets turret rotation matrix
|
||||
extern void model_make_turret_matrix(polymodel *pm, polymodel_instance *pmi, model_subsystem *turret);
|
||||
|
||||
@@ -1233,8 +1233,21 @@ void submodel_get_two_random_points(int model_num, int submodel_num, vec3d *v1,
|
||||
return;
|
||||
}
|
||||
|
||||
int vn1 = rand32() % nv;
|
||||
int vn2 = rand32() % nv;
|
||||
#ifndef NDEBUG
|
||||
if (RAND_MAX < nv)
|
||||
{
|
||||
static int submodel_get_two_random_points_warned = false;
|
||||
if (!submodel_get_two_random_points_warned)
|
||||
{
|
||||
polymodel *pm = model_get(model_num);
|
||||
Warning(LOCATION, "RAND_MAX is only %d, but submodel %d for model %s has %d vertices! Explosions will not propagate through the entire model!\n", RAND_MAX, submodel_num, pm->filename, nv);
|
||||
submodel_get_two_random_points_warned = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int vn1 = myrand() % nv;
|
||||
int vn2 = myrand() % nv;
|
||||
|
||||
*v1 = *Interp_verts[vn1];
|
||||
*v2 = *Interp_verts[vn2];
|
||||
@@ -1279,8 +1292,20 @@ void submodel_get_two_random_points_better(int model_num, int submodel_num, vec3
|
||||
return;
|
||||
}
|
||||
|
||||
int vn1 = rand32() % nv;
|
||||
int vn2 = rand32() % nv;
|
||||
#ifndef NDEBUG
|
||||
if (RAND_MAX < nv)
|
||||
{
|
||||
static int submodel_get_two_random_points_warned = false;
|
||||
if (!submodel_get_two_random_points_warned)
|
||||
{
|
||||
Warning(LOCATION, "RAND_MAX is only %d, but submodel %d for model %s has %d vertices! Explosions will not propagate through the entire model!\n", RAND_MAX, submodel_num, pm->filename, nv);
|
||||
submodel_get_two_random_points_warned = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int vn1 = myrand() % nv;
|
||||
int vn2 = myrand() % nv;
|
||||
|
||||
*v1 = tree->point_list[vn1];
|
||||
*v2 = tree->point_list[vn2];
|
||||
|
||||
+137
-143
@@ -462,61 +462,6 @@ void get_user_prop_value(char *buf, char *value)
|
||||
*p1 = c;
|
||||
}
|
||||
|
||||
// routine to parse out a vec3d from a user property field of an object
|
||||
bool get_user_vec3d_value(char *buf, vec3d *value, bool require_brackets)
|
||||
{
|
||||
float f1, f2, f3;
|
||||
char closing_bracket = '\0';
|
||||
bool success = false;
|
||||
|
||||
pause_parse();
|
||||
Mp = buf;
|
||||
|
||||
// Note that we can't simply return from within this block
|
||||
// because we always need to call unpause_parse before we
|
||||
// leave the function. A one-iteration loop with break
|
||||
// statements allows the code to jump to the end. Alternatively,
|
||||
// goto could have been used.
|
||||
do {
|
||||
// skip white space and equal sign or colon
|
||||
while (isspace(*Mp) || (*Mp == '=') || (*Mp == ':'))
|
||||
Mp++;
|
||||
|
||||
if (require_brackets)
|
||||
{
|
||||
// look for vector bracket
|
||||
if (*Mp == '{')
|
||||
closing_bracket = '}';
|
||||
else if (*Mp == '[')
|
||||
closing_bracket = ']';
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// get comma-separated floats
|
||||
if (stuff_float_optional(&f1) != 2)
|
||||
break;
|
||||
if (stuff_float_optional(&f2) != 2)
|
||||
break;
|
||||
if (stuff_float_optional(&f3) != 2)
|
||||
break;
|
||||
|
||||
if (require_brackets)
|
||||
{
|
||||
ignore_white_space();
|
||||
|
||||
if (*Mp != closing_bracket)
|
||||
break;
|
||||
}
|
||||
|
||||
value->xyz = { f1, f2, f3 };
|
||||
success = true;
|
||||
} while (false);
|
||||
|
||||
unpause_parse();
|
||||
return success;
|
||||
}
|
||||
|
||||
// routine to look for one of the specified user properties
|
||||
// if p is not null, sets p to the next character AFTER the string and a space/equals/colon (not the beginning of the string, as strstr would)
|
||||
// returns the index of the property found, or -1 if not found
|
||||
@@ -1392,7 +1337,9 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
|
||||
if ( strstr(pm->submodel[n].name, "turret") || strstr(pm->submodel[n].name, "gun") || strstr(pm->submodel[n].name, "cannon")) {
|
||||
pm->submodel[n].movement_type = MOVEMENT_TYPE_ROT_SPECIAL;
|
||||
pm->submodel[n].can_move = true;
|
||||
} else if (pm->submodel[n].movement_type == MOVEMENT_TYPE_ROT) {
|
||||
} else
|
||||
|
||||
if (pm->submodel[n].movement_type == MOVEMENT_TYPE_ROT) {
|
||||
if (strstr(pm->submodel[n].name, "thruster")) {
|
||||
pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE;
|
||||
pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE;
|
||||
@@ -1599,52 +1546,131 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
|
||||
}
|
||||
}
|
||||
|
||||
// KeldorKatarn, with modifications
|
||||
if ( (p = strstr(props, "$uvec")) != nullptr ) {
|
||||
matrix submodel_orient;
|
||||
// Added for new handling of turret orientation - KeldorKatarn
|
||||
matrix *orient = &pm->submodel[n].orientation;
|
||||
|
||||
if (get_user_vec3d_value(p + 5, &submodel_orient.vec.uvec, false)) {
|
||||
if ( (p = strstr(props, "$uvec:")) != NULL ) {
|
||||
p += 6;
|
||||
|
||||
if ((p = strstr(props, "$fvec")) != nullptr) {
|
||||
char *parsed_string = p;
|
||||
|
||||
if (get_user_vec3d_value(p + 5, &submodel_orient.vec.fvec, false)) {
|
||||
while (*parsed_string == ' ') {
|
||||
parsed_string++; // Skip spaces
|
||||
}
|
||||
|
||||
vm_vec_normalize(&submodel_orient.vec.uvec);
|
||||
vm_vec_normalize(&submodel_orient.vec.fvec);
|
||||
orient->vec.uvec.xyz.x = (float)(strtod(parsed_string, (char **)NULL));
|
||||
|
||||
vm_vec_cross(&submodel_orient.vec.rvec, &submodel_orient.vec.uvec, &submodel_orient.vec.fvec);
|
||||
vm_vec_cross(&submodel_orient.vec.fvec, &submodel_orient.vec.rvec, &submodel_orient.vec.uvec);
|
||||
// Find end of number
|
||||
parsed_string = strchr(parsed_string, ',');
|
||||
if (parsed_string == NULL) {
|
||||
Error( LOCATION,
|
||||
"Submodel '%s' of model '%s' has an improperly formatted $uvec: declaration in its properties."
|
||||
"\n\n$uvec: should be followed by 3 numbers separated with commas."
|
||||
"\n\nCouldn't find first comma (,)!",
|
||||
pm->submodel[n].name, filename);
|
||||
}
|
||||
parsed_string++;
|
||||
|
||||
vm_vec_normalize(&submodel_orient.vec.fvec);
|
||||
vm_vec_normalize(&submodel_orient.vec.rvec);
|
||||
while (*parsed_string == ' ') {
|
||||
parsed_string++; // Skip spaces
|
||||
}
|
||||
|
||||
vm_orthogonalize_matrix(&submodel_orient);
|
||||
orient->vec.uvec.xyz.y = (float)(strtod(parsed_string, (char **)NULL));
|
||||
|
||||
pm->submodel[n].orientation = submodel_orient;
|
||||
pm->submodel[n].force_turret_normal = true;
|
||||
// Find end of number
|
||||
parsed_string = strchr(parsed_string, ',');
|
||||
if (parsed_string == NULL) {
|
||||
Error( LOCATION,
|
||||
"Submodel '%s' of model '%s' has an improperly formatted $uvec: declaration in its properties."
|
||||
"\n\n$uvec: should be followed by 3 numbers separated with commas."
|
||||
"\n\nCouldn't find second comma (,)!",
|
||||
pm->submodel[n].name, filename);
|
||||
}
|
||||
parsed_string++;
|
||||
|
||||
} else {
|
||||
Warning(LOCATION,
|
||||
"Submodel '%s' of model '%s' has an improperly formatted $fvec declaration in its properties."
|
||||
"\n\n$fvec should be followed by 3 numbers separated with commas.",
|
||||
pm->submodel[n].name, filename);
|
||||
}
|
||||
} else {
|
||||
Warning(LOCATION, "Improper custom orientation matrix for subsystem %s; you must define both an up vector and a forward vector", pm->submodel[n].name);
|
||||
while (*parsed_string == ' ') {
|
||||
parsed_string++; // Skip spaces
|
||||
}
|
||||
|
||||
orient->vec.uvec.xyz.z = (float)(strtod(parsed_string, (char **)NULL));
|
||||
|
||||
if ( (p = strstr(props, "$fvec:")) != NULL ) {
|
||||
parsed_string = p + 6;
|
||||
|
||||
while (*parsed_string == ' ') {
|
||||
parsed_string++; // Skip spaces
|
||||
}
|
||||
|
||||
orient->vec.fvec.xyz.x = (float)(strtod(parsed_string, (char **)NULL));
|
||||
|
||||
// Find end of number
|
||||
parsed_string = strchr(parsed_string, ',');
|
||||
if (parsed_string == NULL) {
|
||||
Error( LOCATION,
|
||||
"Submodel '%s' of model '%s' has an improperly formatted $fvec: declaration in its properties."
|
||||
"\n\n$fvec: should be followed by 3 numbers separated with commas."
|
||||
"\n\nCouldn't find first comma (,)!",
|
||||
pm->submodel[n].name, filename);
|
||||
}
|
||||
parsed_string++;
|
||||
|
||||
while (*parsed_string == ' ') {
|
||||
parsed_string++; // Skip spaces
|
||||
}
|
||||
|
||||
orient->vec.fvec.xyz.y = (float)(strtod(parsed_string, (char **)NULL));
|
||||
|
||||
// Find end of number
|
||||
parsed_string = strchr(parsed_string, ',');
|
||||
if (parsed_string == NULL) {
|
||||
Error( LOCATION,
|
||||
"Submodel '%s' of model '%s' has an improperly formatted $fvec: declaration in its properties."
|
||||
"\n\n$fvec: should be followed by 3 numbers separated with commas."
|
||||
"\n\nCouldn't find second comma (,)!",
|
||||
pm->submodel[n].name, filename);
|
||||
}
|
||||
parsed_string++;
|
||||
|
||||
while (*parsed_string == ' ') {
|
||||
parsed_string++; // Skip spaces
|
||||
}
|
||||
|
||||
orient->vec.fvec.xyz.z = (float)(strtod(parsed_string, (char **)NULL));
|
||||
|
||||
pm->submodel[n].force_turret_normal = true;
|
||||
|
||||
vm_vec_normalize(&orient->vec.uvec);
|
||||
vm_vec_normalize(&orient->vec.fvec);
|
||||
|
||||
vm_vec_cross(&orient->vec.rvec, &orient->vec.uvec, &orient->vec.fvec);
|
||||
vm_vec_cross(&orient->vec.fvec, &orient->vec.rvec, &orient->vec.uvec);
|
||||
|
||||
vm_vec_normalize(&orient->vec.fvec);
|
||||
vm_vec_normalize(&orient->vec.rvec);
|
||||
|
||||
vm_orthogonalize_matrix(orient);
|
||||
} else {
|
||||
Warning(LOCATION,
|
||||
"Submodel '%s' of model '%s' has an improperly formatted $uvec declaration in its properties."
|
||||
"\n\n$uvec should be followed by 3 numbers separated with commas.",
|
||||
pm->submodel[n].name, filename);
|
||||
int parent_num = pm->submodel[n].parent;
|
||||
|
||||
if (parent_num > -1) {
|
||||
*orient = pm->submodel[parent_num].orientation;
|
||||
} else {
|
||||
*orient = vmd_identity_matrix;
|
||||
}
|
||||
|
||||
Warning( LOCATION, "Improper custom orientation matrix for subsystem %s, you must define a up vector, then a forward vector", pm->submodel[n].name);
|
||||
}
|
||||
} else {
|
||||
int parent_num = pm->submodel[n].parent;
|
||||
|
||||
if (parent_num >= 0) {
|
||||
pm->submodel[n].orientation = pm->submodel[parent_num].orientation;
|
||||
|
||||
if (parent_num > -1) {
|
||||
*orient = pm->submodel[parent_num].orientation;
|
||||
} else {
|
||||
pm->submodel[n].orientation = vmd_identity_matrix;
|
||||
*orient = vmd_identity_matrix;
|
||||
}
|
||||
|
||||
if (strstr(props, "$fvec:") != NULL) {
|
||||
Warning( LOCATION, "Improper custom orientation matrix for subsystem %s, you must define a up vector, then a forward vector", pm->submodel[n].name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3778,25 +3804,14 @@ void model_make_turret_matrix(polymodel *pm, polymodel_instance *pmi, model_subs
|
||||
// Tries to move joints so that the turret points to the point dst.
|
||||
// turret1 is the angles of the turret, turret2 is the angles of the gun from turret
|
||||
// Returns 1 if rotated gun, 0 if no gun to rotate (rotation handled by AI)
|
||||
int model_rotate_gun(object *objp, polymodel *pm, polymodel_instance *pmi, model_subsystem *turret, vec3d *dst, bool reset)
|
||||
int model_rotate_gun(polymodel *pm, model_subsystem *turret, matrix *orient, angles *base_angles, angles *gun_angles, vec3d *pos, vec3d *dst, int obj_idx, bool reset)
|
||||
{
|
||||
object *objp = &Objects[obj_idx];
|
||||
ship *shipp = &Ships[objp->instance];
|
||||
ship_subsys *ss = ship_get_subsys(shipp, turret->subobj_name);
|
||||
|
||||
// This should not happen
|
||||
if ( turret->subobj_num == turret->turret_gun_sobj ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto base_sm = &pm->submodel[turret->subobj_num];
|
||||
// auto gun_sm = &pm->submodel[turret->turret_gun_sobj];
|
||||
|
||||
auto base_smi = &pmi->submodel[turret->subobj_num];
|
||||
auto gun_smi = &pmi->submodel[turret->turret_gun_sobj];
|
||||
|
||||
auto base_angles = &base_smi->angs;
|
||||
auto gun_angles = &gun_smi->angs;
|
||||
|
||||
bsp_info * gun = &pm->submodel[turret->turret_gun_sobj];
|
||||
bsp_info * base = &pm->submodel[turret->subobj_num];
|
||||
bool limited_base_rotation = false;
|
||||
|
||||
// Check for a valid turret
|
||||
@@ -3804,8 +3819,14 @@ int model_rotate_gun(object *objp, polymodel *pm, polymodel_instance *pmi, model
|
||||
// Check for a valid subsystem
|
||||
Assert( ss != NULL );
|
||||
|
||||
//This should not happen
|
||||
if ( base == gun ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Build the correct turret matrix if there isn't already one
|
||||
if ( !(turret->flags[Model::Subsystem_Flags::Turret_matrix]) ) {
|
||||
auto pmi = model_get_instance(shipp->model_instance_num);
|
||||
model_make_turret_matrix(pm, pmi, turret);
|
||||
}
|
||||
|
||||
@@ -3822,13 +3843,13 @@ int model_rotate_gun(object *objp, polymodel *pm, polymodel_instance *pmi, model
|
||||
vec3d world_to_turret_translate; // converts world coordinates to turret's FOR
|
||||
vec3d tempv;
|
||||
|
||||
vm_vec_unrotate( &tempv, &base_sm->offset, &objp->orient );
|
||||
vm_vec_add( &world_to_turret_translate, &objp->pos, &tempv );
|
||||
vm_vec_unrotate( &tempv, &base->offset, orient);
|
||||
vm_vec_add( &world_to_turret_translate, pos, &tempv );
|
||||
|
||||
if (turret->flags[Model::Subsystem_Flags::Turret_alt_math])
|
||||
world_to_turret_matrix = ss->world_to_turret_matrix;
|
||||
else
|
||||
vm_matrix_x_matrix( &world_to_turret_matrix, &objp->orient, &turret->turret_matrix );
|
||||
vm_matrix_x_matrix( &world_to_turret_matrix, orient, &turret->turret_matrix );
|
||||
|
||||
vm_vec_sub( &tempv, dst, &world_to_turret_translate );
|
||||
vm_vec_rotate( &of_dst, &tempv, &world_to_turret_matrix );
|
||||
@@ -4479,16 +4500,13 @@ void model_update_instance(int model_instance_num, int submodel_num, flagset<Shi
|
||||
model_update_instance(pm, pmi, submodel_num, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function handles copying submodel instance information to other submodel instances as appropriate. The copy_from parameter is used for
|
||||
* copying data to other LODs, and is only specified from within model_update_instance itself. The "public" function header omits this parameter.
|
||||
*/
|
||||
void model_update_instance(polymodel *pm, polymodel_instance *pmi, const submodel_instance *copy_from, int submodel_num, flagset<Ship::Subsystem_Flags>& flags)
|
||||
void model_update_instance(polymodel *pm, polymodel_instance *pmi, int submodel_num, flagset<Ship::Subsystem_Flags>& flags)
|
||||
{
|
||||
int i;
|
||||
Assert(pm->id == pmi->model_num);
|
||||
|
||||
Assertion(submodel_num >= 0 && submodel_num < pm->n_models,
|
||||
"Submodel number (%d) which should be updated is out of range! Must be between 0 and %d. This happened on model %s.",
|
||||
"Sub model number (%d) which should be updated is out of range! Must be between 0 and %d. This happend on model %s.",
|
||||
submodel_num, pm->n_models - 1, pm->filename);
|
||||
|
||||
if ( submodel_num < 0 ) return;
|
||||
@@ -4497,51 +4515,33 @@ void model_update_instance(polymodel *pm, polymodel_instance *pmi, const submode
|
||||
submodel_instance *smi = &pmi->submodel[submodel_num];
|
||||
bsp_info *sm = &pm->submodel[submodel_num];
|
||||
|
||||
// Set the "blown out" flags.
|
||||
// Set the "blown out" flags
|
||||
if ( flags[Ship::Subsystem_Flags::No_disappear] ) {
|
||||
smi->blown_off = false;
|
||||
} else if ( copy_from ) {
|
||||
smi->blown_off = copy_from->blown_off;
|
||||
}
|
||||
|
||||
if ( smi->blown_off ) {
|
||||
if ( sm->my_replacement >= 0 && !(flags[Ship::Subsystem_Flags::No_replace]) ) {
|
||||
if ( smi->blown_off && !(flags[Ship::Subsystem_Flags::No_replace]) ) {
|
||||
if ( sm->my_replacement > -1 ) {
|
||||
auto r_smi = &pmi->submodel[sm->my_replacement];
|
||||
r_smi->blown_off = false;
|
||||
if ( copy_from ) {
|
||||
r_smi->angs = copy_from->angs;
|
||||
r_smi->prev_angs = copy_from->prev_angs;
|
||||
} else {
|
||||
r_smi->angs = smi->angs;
|
||||
r_smi->prev_angs = smi->prev_angs;
|
||||
}
|
||||
r_smi->angs = smi->angs;
|
||||
r_smi->prev_angs = smi->prev_angs;
|
||||
}
|
||||
} else {
|
||||
// If submodel isn't yet blown off and has a -destroyed replacement model, we prevent
|
||||
// the replacement model from being drawn by marking it as having been blown off
|
||||
if ( sm->my_replacement >= 0 && sm->my_replacement != submodel_num) {
|
||||
if ( sm->my_replacement > -1 && sm->my_replacement != submodel_num) {
|
||||
auto r_smi = &pmi->submodel[sm->my_replacement];
|
||||
r_smi->blown_off = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the angles.
|
||||
if ( copy_from ) {
|
||||
smi->angs = copy_from->angs;
|
||||
smi->prev_angs = copy_from->prev_angs;
|
||||
}
|
||||
|
||||
// For all the detail levels of this submodel, set them also.
|
||||
for ( int i=0; i<sm->num_details; i++ ) {
|
||||
model_update_instance( pm, pmi, smi, sm->details[i], flags );
|
||||
for (i=0; i<sm->num_details; i++ ) {
|
||||
model_update_instance(pm, pmi, sm->details[i], flags );
|
||||
}
|
||||
}
|
||||
|
||||
void model_update_instance(polymodel *pm, polymodel_instance *pmi, int submodel_num, flagset<Ship::Subsystem_Flags>& flags)
|
||||
{
|
||||
model_update_instance(pm, pmi, nullptr, submodel_num, flags);
|
||||
}
|
||||
|
||||
void model_do_intrinsic_rotations_sub(intrinsic_rotation *ir)
|
||||
{
|
||||
polymodel_instance *pmi = model_get_instance(ir->model_instance_num);
|
||||
@@ -5565,10 +5565,4 @@ void model_subsystem::reset()
|
||||
|
||||
turret_max_bomb_ownage = 0;
|
||||
turret_max_target_ownage = 0;
|
||||
|
||||
beam_warmdown_program = actions::ProgramSet();
|
||||
}
|
||||
|
||||
model_subsystem::model_subsystem() {
|
||||
reset();
|
||||
}
|
||||
@@ -2605,14 +2605,6 @@ void model_render_queue(model_render_params* interp, model_draw_list* scene, int
|
||||
|
||||
if (model_instance_num >= 0) {
|
||||
pmi = model_get_instance(model_instance_num);
|
||||
|
||||
// This can happen if we supply a HUD target model for a real ship.
|
||||
// The passed parameter was -1 but it was assigned an instance from
|
||||
// the actual object. Set it back to -1 if there is a mismatch.
|
||||
if (pmi->model_num != pm->id) {
|
||||
model_instance_num = -1;
|
||||
pmi = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the flags we will pass to the tmapper
|
||||
|
||||
@@ -2059,13 +2059,6 @@ int multi_oo_unpack_data(net_player* pl, ubyte* data, int seq_num)
|
||||
object *objp = multi_get_network_object( dock_sig );
|
||||
if(objp != nullptr){
|
||||
Ai_info[shipp->ai_index].support_ship_objnum = OBJ_INDEX(objp);
|
||||
if ((objp->instance > -1) && (objp->type == OBJ_SHIP)) {
|
||||
Ai_info[shipp->ai_index].goals[0].target_name = Ships[objp->instance].ship_name;
|
||||
Ai_info[shipp->ai_index].goals[0].target_signature = objp->signature;
|
||||
} else {
|
||||
Ai_info[shipp->ai_index].goals[0].target_name = nullptr;
|
||||
Ai_info[shipp->ai_index].goals[0].target_signature = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,10 +52,6 @@ struct weapon;
|
||||
#define OO_SUBSYS_ROTATION_2p (1<<6) // Did this subsystem's barrel rotation angles change
|
||||
#define OO_SUBSYS_TRANSLATION (1<<7) // Only for backwards compatibility of future builds.
|
||||
|
||||
// combo values
|
||||
#define OO_SUBSYS_ROTATION_1 (OO_SUBSYS_ROTATION_1b | OO_SUBSYS_ROTATION_1h | OO_SUBSYS_ROTATION_1p)
|
||||
#define OO_SUBSYS_ROTATION_2 (OO_SUBSYS_ROTATION_2b | OO_SUBSYS_ROTATION_2h | OO_SUBSYS_ROTATION_2p)
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
// POSITION AND ORIENTATION RECORDING
|
||||
|
||||
@@ -3301,23 +3301,8 @@ void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_obj
|
||||
}
|
||||
ADD_SHORT( static_cast<short>(Weapons[objp->instance].weapon_info_index) );
|
||||
ADD_SHORT( static_cast<short>(subsys_index) );
|
||||
|
||||
// where we might accidentally dereference nullptrs, send 0 instead
|
||||
constexpr float ZERO_VALUE = 0.0f;
|
||||
|
||||
if (ssp->submodel_instance_1 != nullptr) {
|
||||
ADD_FLOAT( ssp->submodel_instance_1->angs.h );
|
||||
}
|
||||
else {
|
||||
ADD_FLOAT( ZERO_VALUE );
|
||||
}
|
||||
|
||||
if (ssp->submodel_instance_2 != nullptr) {
|
||||
ADD_FLOAT( ssp->submodel_instance_2->angs.p );
|
||||
}
|
||||
else {
|
||||
ADD_FLOAT(ZERO_VALUE);
|
||||
}
|
||||
ADD_FLOAT( ssp->submodel_instance_1->angs.h );
|
||||
ADD_FLOAT( ssp->submodel_instance_2->angs.p );
|
||||
|
||||
multi_io_send_to_all(data, packet_size);
|
||||
|
||||
@@ -3384,14 +3369,8 @@ void process_turret_fired_packet( ubyte *data, header *hinfo )
|
||||
}
|
||||
|
||||
// bash the position and orientation of the turret
|
||||
// but only if the submodels are not null
|
||||
if (ssp->submodel_instance_1 != nullptr) {
|
||||
ssp->submodel_instance_1->angs.h = heading;
|
||||
}
|
||||
|
||||
if (ssp->submodel_instance_2 != nullptr) {
|
||||
ssp->submodel_instance_2->angs.p = pitch;
|
||||
}
|
||||
ssp->submodel_instance_1->angs.h = heading;
|
||||
ssp->submodel_instance_2->angs.p = pitch;
|
||||
|
||||
// get the world position of the weapon
|
||||
ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
|
||||
@@ -6159,7 +6138,6 @@ void process_post_sync_data_packet(ubyte *data, header *hinfo)
|
||||
for (j = 0; j < shipp->weapons.num_primary_banks; j++) {
|
||||
GET_SHORT(val_short);
|
||||
shipp->weapons.primary_bank_weapons[j] = static_cast<int>(val_short);
|
||||
shipp->weapons.primary_bank_start_ammo[j] = (int)std::lround(Ship_info[shipp->ship_info_index].primary_bank_ammo_capacity[j] / Weapon_info[shipp->weapons.primary_bank_weapons[j]].cargo_size);
|
||||
}
|
||||
|
||||
// secondary weapon info
|
||||
@@ -6168,7 +6146,6 @@ void process_post_sync_data_packet(ubyte *data, header *hinfo)
|
||||
shipp->weapons.secondary_bank_weapons[j] = static_cast<int>(val_short);
|
||||
GET_SHORT(val_short);
|
||||
shipp->weapons.secondary_bank_ammo[j] = static_cast<int>(val_short);
|
||||
shipp->weapons.secondary_bank_start_ammo[j] = (int)std::lround(Ship_info[shipp->ship_info_index].secondary_bank_ammo_capacity[j] / Weapon_info[shipp->weapons.secondary_bank_weapons[j]].cargo_size);
|
||||
}
|
||||
|
||||
// other flags
|
||||
@@ -8451,24 +8428,8 @@ void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum
|
||||
ADD_USHORT( pnet_signature );
|
||||
ADD_SHORT( static_cast<short>(Weapons[Objects[weapon_objnum].instance].weapon_info_index) );
|
||||
ADD_SHORT( static_cast<short>(subsys_index) );
|
||||
|
||||
// where we might accidentally dereference nullptrs, send 0 instead
|
||||
constexpr float ZERO_VALUE = 0.0f;
|
||||
|
||||
// ensure a nullptr is not dereferenced for the next two values.
|
||||
if (ssp->submodel_instance_1 != nullptr) {
|
||||
ADD_FLOAT( ssp->submodel_instance_1->angs.h );
|
||||
}
|
||||
else {
|
||||
ADD_FLOAT( ZERO_VALUE );
|
||||
}
|
||||
|
||||
if (ssp->submodel_instance_2 != nullptr) {
|
||||
ADD_FLOAT( ssp->submodel_instance_2->angs.p );
|
||||
}
|
||||
else {
|
||||
ADD_FLOAT(ZERO_VALUE);
|
||||
}
|
||||
ADD_FLOAT( ssp->submodel_instance_1->angs.h );
|
||||
ADD_FLOAT( ssp->submodel_instance_2->angs.p );
|
||||
ADD_FLOAT( flak_range );
|
||||
|
||||
multi_io_send_to_all(data, packet_size);
|
||||
@@ -8529,14 +8490,9 @@ void process_flak_fired_packet(ubyte *data, header *hinfo)
|
||||
return;
|
||||
}
|
||||
|
||||
// bash the position and orientation of the turret, if it's not a nulltpr
|
||||
if (ssp->submodel_instance_1 != nullptr) {
|
||||
ssp->submodel_instance_1->angs.h = heading;
|
||||
}
|
||||
|
||||
if (ssp->submodel_instance_2 != nullptr) {
|
||||
ssp->submodel_instance_2->angs.p = pitch;
|
||||
}
|
||||
// bash the position and orientation of the turret
|
||||
ssp->submodel_instance_1->angs.h = heading;
|
||||
ssp->submodel_instance_2->angs.p = pitch;
|
||||
|
||||
// get the world position of the weapon
|
||||
ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
|
||||
|
||||
@@ -326,7 +326,7 @@ int ship_ship_check_collision(collision_info_struct *ship_ship_hit_info)
|
||||
|
||||
}
|
||||
|
||||
// Now complete base model collision checks that do not take into account rotating submodels.
|
||||
// Recover and do usual ship_ship collision, but without rotating submodels
|
||||
mc.flags = copy_flags;
|
||||
*mc.p0 = copy_p0;
|
||||
*mc.p1 = copy_p1;
|
||||
|
||||
@@ -105,16 +105,8 @@ bool dock_check_find_direct_docked_object(object *objp, object *other_objp)
|
||||
|
||||
bool dock_check_find_docked_object(object *objp, object *other_objp)
|
||||
{
|
||||
Assert(objp != nullptr);
|
||||
Assert(objp->signature > 0);
|
||||
Assert(other_objp != nullptr);
|
||||
Assert(other_objp->signature > 0);
|
||||
|
||||
|
||||
if (!(objp != nullptr && objp->signature > 0))
|
||||
return false;
|
||||
if (!(other_objp != nullptr && other_objp->signature > 0))
|
||||
return false;
|
||||
Assert(objp != NULL);
|
||||
Assert(other_objp != NULL);
|
||||
|
||||
dock_function_info dfi;
|
||||
dfi.parameter_variables.objp_value = other_objp;
|
||||
|
||||
+2
-14
@@ -42,20 +42,8 @@ namespace
|
||||
// Lazily initialize the preferences path
|
||||
if (!preferencesPath) {
|
||||
preferencesPath = SDL_GetPrefPath(ORGANIZATION_NAME, APPLICATION_NAME);
|
||||
|
||||
// this section will at least tell the user if something is seriously wrong instead of just crashing without a message or debug log.
|
||||
// It may crash later, especially when trying to load sound. But let's let it *try* to run in the current directory at least.
|
||||
if (preferencesPath == nullptr) {
|
||||
static bool sdl_is_borked_warning = false;
|
||||
if (!sdl_is_borked_warning) {
|
||||
ReleaseWarning(LOCATION, "%s\n\nSDL and Windows are unable to get the preferred path for the reason above. "
|
||||
"Installing FSO, its executables and DLLs in another non-protected folder may fix the issue.\n\n"
|
||||
"You may experience issues if you continue playing, and FSO may crash. Please report this error if it persists.\n\n"
|
||||
"Report at www.hard-light.net or the hard-light discord.", SDL_GetError());
|
||||
sdl_is_borked_warning = true;
|
||||
}
|
||||
// No preferences path, try current directory.
|
||||
return "." DIR_SEPARATOR_STR;
|
||||
if (!preferencesPath) {
|
||||
mprintf(("Failed to get preferences path from SDL: %s\n", SDL_GetError()));
|
||||
}
|
||||
#ifdef WIN32
|
||||
try {
|
||||
|
||||
@@ -250,12 +250,7 @@ ParticleSourceWrapper ParticleManager::createSource(ParticleEffectHandle index)
|
||||
|
||||
// UGH, HACK! To implement the source wrapper we need constant pointers to all sources.
|
||||
// To ensure this we reserve the number of sources we will need (current sources + sources being created)
|
||||
if (m_processingSources) {
|
||||
// If we are already in our onFrame, we need to apply the hack to the right vector though
|
||||
m_deferredSourceAdding.reserve(m_sources.size() + childEffects.size());
|
||||
} else {
|
||||
m_sources.reserve(m_sources.size() + childEffects.size());
|
||||
}
|
||||
m_sources.reserve(m_sources.size() + childEffects.size());
|
||||
|
||||
for (auto& effect : childEffects) {
|
||||
ParticleSource* source = createSource();
|
||||
|
||||
@@ -165,22 +165,22 @@ bool SourceOrigin::isValid() const {
|
||||
|
||||
SourceOrientation::SourceOrientation() : m_orientation(vmd_identity_matrix) {}
|
||||
|
||||
void SourceOrientation::setFromVector(const vec3d& vec, bool relative) {
|
||||
void SourceOrientation::setFromVector(const vec3d& vec) {
|
||||
vec3d workVec = vec;
|
||||
|
||||
vm_vec_normalize(&workVec);
|
||||
|
||||
this->setFromNormalizedVector(workVec, relative);
|
||||
this->setFromNormalizedVector(workVec);
|
||||
}
|
||||
|
||||
void SourceOrientation::setFromNormalizedVector(const vec3d& vec, bool relative) {
|
||||
void SourceOrientation::setFromNormalizedVector(const vec3d& vec) {
|
||||
vm_vector_2_matrix_norm(&m_orientation, &vec);
|
||||
m_isRelative = relative;
|
||||
m_isRelative = false;
|
||||
}
|
||||
|
||||
void SourceOrientation::setFromMatrix(const matrix& mat, bool relative) {
|
||||
void SourceOrientation::setFromMatrix(const matrix& mat) {
|
||||
m_orientation = mat;
|
||||
m_isRelative = relative;
|
||||
m_isRelative = false;
|
||||
}
|
||||
|
||||
void SourceOrientation::setNormal(const vec3d& normal) {
|
||||
|
||||
@@ -155,18 +155,18 @@ class SourceOrientation {
|
||||
*
|
||||
* @param vec The vector to create the matrix from
|
||||
*/
|
||||
void setFromVector(const vec3d& vec, bool relative = false);
|
||||
void setFromVector(const vec3d& vec);
|
||||
|
||||
/**
|
||||
* @brief Sets the direction from an already normalized vector
|
||||
*
|
||||
* @param vec The normalized vector
|
||||
*/
|
||||
void setFromNormalizedVector(const vec3d& vec, bool relative = false);
|
||||
void setFromNormalizedVector(const vec3d& vec);
|
||||
|
||||
void setNormal(const vec3d& normal);
|
||||
|
||||
void setFromMatrix(const matrix& mat, bool relative = false);
|
||||
void setFromMatrix(const matrix& mat);
|
||||
|
||||
vec3d getDirectionVector(const SourceOrigin* origin) const;
|
||||
|
||||
|
||||
@@ -84,28 +84,28 @@ namespace particle
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleSourceWrapper::setOrientationFromNormalizedVec(vec3d* normalizedDir, bool relative)
|
||||
void ParticleSourceWrapper::setOrientationFromNormalizedVec(vec3d* normalizedDir)
|
||||
{
|
||||
for (auto& source : m_sources)
|
||||
{
|
||||
source->getOrientation()->setFromNormalizedVector(*normalizedDir, relative);
|
||||
source->getOrientation()->setFromNormalizedVector(*normalizedDir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParticleSourceWrapper::setOrientationFromVec(vec3d* dir, bool relative)
|
||||
void ParticleSourceWrapper::setOrientationFromVec(vec3d* dir)
|
||||
{
|
||||
for (auto& source : m_sources)
|
||||
{
|
||||
source->getOrientation()->setFromVector(*dir, relative);
|
||||
source->getOrientation()->setFromVector(*dir);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleSourceWrapper::setOrientationMatrix(matrix* mtx, bool relative)
|
||||
void ParticleSourceWrapper::setOrientationMatrix(matrix* mtx)
|
||||
{
|
||||
for (auto& source : m_sources)
|
||||
{
|
||||
source->getOrientation()->setFromMatrix(*mtx, relative);
|
||||
source->getOrientation()->setFromMatrix(*mtx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,11 +54,11 @@ namespace particle
|
||||
|
||||
void moveTo(vec3d* pos);
|
||||
|
||||
void setOrientationFromNormalizedVec(vec3d* normalizedDir, bool relative = false);
|
||||
void setOrientationFromNormalizedVec(vec3d* normalizedDir);
|
||||
|
||||
void setOrientationFromVec(vec3d* dir, bool relative = false);
|
||||
void setOrientationFromVec(vec3d* dir);
|
||||
|
||||
void setOrientationMatrix(matrix* mtx, bool relative = false);
|
||||
void setOrientationMatrix(matrix* mtx);
|
||||
|
||||
void setOrientationNormal(vec3d* normal);
|
||||
|
||||
|
||||
@@ -1394,7 +1394,7 @@ ADE_FUNC_DEPRECATED(drawMonochromeImage,
|
||||
"Draws a monochrome image from a texture or file using the current color",
|
||||
"boolean",
|
||||
"Whether image was drawn",
|
||||
gameversion::version(21, 0),
|
||||
gameversion::version(20, 1),
|
||||
"gr.drawImage now has support for drawing monochrome images with full UV scaling support")
|
||||
{
|
||||
if(!Gr_inited)
|
||||
|
||||
@@ -200,7 +200,7 @@ bool ship_bank_h::IsValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(WeaponClass, l_WeaponBank, "weaponclass", "Class of weapon mounted in the bank. As of FSO 21.0, also changes the maximum ammo to its proper value, which is what the support ship will rearm the ship to.", "weaponclass", "Weapon class, or an invalid weaponclass handle if bank handle is invalid")
|
||||
ADE_VIRTVAR(WeaponClass, l_WeaponBank, "weaponclass", "Class of weapon mounted in the bank", "weaponclass", "Weapon class, or an invalid weaponclass handle if bank handle is invalid")
|
||||
{
|
||||
ship_bank_h *bh = NULL;
|
||||
int weaponclass=-1;
|
||||
@@ -215,16 +215,12 @@ ADE_VIRTVAR(WeaponClass, l_WeaponBank, "weaponclass", "Class of weapon mounted i
|
||||
case SWH_PRIMARY:
|
||||
if(ADE_SETTING_VAR && weaponclass > -1) {
|
||||
bh->sw->primary_bank_weapons[bh->bank] = weaponclass;
|
||||
if (Weapon_info[weaponclass].wi_flags[Weapon::Info_Flags::Ballistic]) {
|
||||
bh->sw->primary_bank_ammo[bh->bank] = (int)std::lround(bh->sw->primary_bank_capacity[bh->bank] / Weapon_info[weaponclass].cargo_size);
|
||||
}
|
||||
}
|
||||
|
||||
return ade_set_args(L, "o", l_Weaponclass.Set(bh->sw->primary_bank_weapons[bh->bank]));
|
||||
case SWH_SECONDARY:
|
||||
if(ADE_SETTING_VAR && weaponclass > -1) {
|
||||
bh->sw->secondary_bank_weapons[bh->bank] = weaponclass;
|
||||
bh->sw->secondary_bank_ammo[bh->bank] = (int)std::lround(bh->sw->secondary_bank_capacity[bh->bank] / Weapon_info[weaponclass].cargo_size);
|
||||
}
|
||||
|
||||
return ade_set_args(L, "o", l_Weaponclass.Set(bh->sw->secondary_bank_weapons[bh->bank]));
|
||||
|
||||
@@ -642,6 +642,7 @@ ADE_FUNC(rotateTurret, l_Subsystem, "vector Pos, boolean reset=false", "Rotates
|
||||
vec3d gpos, gvec;
|
||||
model_subsystem *tp = sso->ss->system_info;
|
||||
object *objp = sso->objp;
|
||||
ship *shipp = &Ships[objp->instance];
|
||||
|
||||
//Rotate turret position with ship
|
||||
vm_vec_unrotate(&gpos, &tp->pnt, &objp->orient);
|
||||
@@ -655,7 +656,7 @@ ADE_FUNC(rotateTurret, l_Subsystem, "vector Pos, boolean reset=false", "Rotates
|
||||
// Find direction of turret
|
||||
model_instance_find_world_dir(&gvec, &tp->turret_norm, pm, pmi, tp->turret_gun_sobj, &objp->orient);
|
||||
|
||||
int ret_val = model_rotate_gun(objp, pm, pmi, tp, &pos, reset);
|
||||
int ret_val = model_rotate_gun(pm, tp, &objp->orient, &sso->ss->submodel_instance_1->angs, &sso->ss->submodel_instance_2->angs, &objp->pos, &pos, shipp->objnum, reset);
|
||||
|
||||
if (ret_val)
|
||||
return ADE_RETURN_TRUE;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "doc_parser.h"
|
||||
|
||||
#include "parse/parselo.h"
|
||||
#include "libs/antlr/ErrorListener.h"
|
||||
|
||||
#undef TRUE
|
||||
#undef FALSE
|
||||
@@ -16,6 +15,32 @@ namespace scripting {
|
||||
|
||||
namespace {
|
||||
|
||||
struct Diagnostic {
|
||||
size_t line;
|
||||
size_t columnInLine;
|
||||
size_t tokenLength;
|
||||
SCP_string errorMessage;
|
||||
};
|
||||
|
||||
struct ErrorListener : public antlr4::BaseErrorListener {
|
||||
SCP_vector<Diagnostic> diagnostics;
|
||||
|
||||
void syntaxError(antlr4::Recognizer* /*recognizer*/,
|
||||
antlr4::Token* offendingSymbol,
|
||||
size_t line,
|
||||
size_t charPositionInLine,
|
||||
const std::string& msg,
|
||||
std::exception_ptr /*e*/) override
|
||||
{
|
||||
// Apparently stop < start is a thing?
|
||||
const auto left = std::min(offendingSymbol->getStartIndex(), offendingSymbol->getStopIndex());
|
||||
const auto right = std::max(offendingSymbol->getStartIndex(), offendingSymbol->getStopIndex());
|
||||
const auto length = right - left + 1;
|
||||
|
||||
diagnostics.push_back(Diagnostic{line, charPositionInLine, length, msg});
|
||||
}
|
||||
};
|
||||
|
||||
ade_type_info merge_alternatives(const ade_type_info& left, const ade_type_info& right)
|
||||
{
|
||||
SCP_vector<ade_type_info> elements;
|
||||
@@ -344,7 +369,7 @@ bool argument_list_parser::parse(const SCP_string& argumentList)
|
||||
ArgumentListParser parser(&tokens);
|
||||
// By default we log to stderr which we do not want
|
||||
parser.removeErrorListeners();
|
||||
libs::antlr::ErrorListener errListener;
|
||||
ErrorListener errListener;
|
||||
parser.addErrorListener(&errListener);
|
||||
|
||||
antlr4::tree::ParseTree* tree = parser.arg_list();
|
||||
|
||||
+14
-26
@@ -943,7 +943,7 @@ void ship_info::clone(const ship_info& other)
|
||||
|
||||
if ( other.n_subsystems > 0 ) {
|
||||
if( n_subsystems < 1 ) {
|
||||
subsystems = new model_subsystem[other.n_subsystems];
|
||||
subsystems = (model_subsystem *)vm_malloc(sizeof(model_subsystem) * other.n_subsystems);
|
||||
} else {
|
||||
for ( int i = 0; i < n_subsystems; i++ ) {
|
||||
for ( int j = 0; j < subsystems[i].n_triggers; j++ ) {
|
||||
@@ -954,11 +954,10 @@ void ship_info::clone(const ship_info& other)
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] subsystems;
|
||||
subsystems = new model_subsystem[other.n_subsystems];
|
||||
subsystems = (model_subsystem *)vm_realloc(subsystems, sizeof(model_subsystem) * other.n_subsystems);
|
||||
}
|
||||
|
||||
Assert(subsystems != nullptr);
|
||||
Assert(subsystems != NULL);
|
||||
for ( int i = n_subsystems; i < other.n_subsystems; i++ ) {
|
||||
subsystems[i].triggers = (queued_animation*) vm_malloc(sizeof(queued_animation) * (other.subsystems[i].n_triggers));
|
||||
}
|
||||
@@ -1897,8 +1896,8 @@ ship_info::~ship_info()
|
||||
}
|
||||
}
|
||||
|
||||
delete[] subsystems;
|
||||
subsystems = nullptr;
|
||||
vm_free(subsystems);
|
||||
subsystems = NULL;
|
||||
}
|
||||
|
||||
free_strings();
|
||||
@@ -2839,8 +2838,6 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
|
||||
texture_replace tr;
|
||||
char *p;
|
||||
|
||||
tr.from_table = true;
|
||||
|
||||
while (optional_string("+old:"))
|
||||
{
|
||||
strcpy_s(tr.ship_name, sip->name);
|
||||
@@ -4532,9 +4529,9 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
|
||||
|
||||
int n_subsystems = 0;
|
||||
int cont_flag = 1;
|
||||
model_subsystem subsystems[MAX_MODEL_SUBSYSTEMS] = {}; // see model.h for max_model_subsystems
|
||||
model_subsystem subsystems[MAX_MODEL_SUBSYSTEMS]; // see model.h for max_model_subsystems
|
||||
for (auto i=0; i<MAX_MODEL_SUBSYSTEMS; i++) {
|
||||
subsystems[i].reset();
|
||||
subsystems[i].stepped_rotation = NULL;
|
||||
}
|
||||
|
||||
float hull_percentage_of_hits = 100.0f;
|
||||
@@ -5034,8 +5031,6 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
|
||||
}
|
||||
}
|
||||
|
||||
sp->beam_warmdown_program = actions::ProgramSet::parseProgramSet("$On Beam Warmdown:",
|
||||
{actions::ProgramContextFlags::HasObject, actions::ProgramContextFlags::HasSubobject});
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
@@ -5058,21 +5053,14 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
|
||||
// when done reading subsystems, malloc and copy the subsystem data to the ship info structure
|
||||
int orig_n_subsystems = sip->n_subsystems;
|
||||
if ( n_subsystems > 0 ) {
|
||||
// Let's make sure that n_subsystems is not negative
|
||||
Assertion(sip->n_subsystems >= 0, "Invalid n_subsystems detected!");
|
||||
auto new_n = sip->n_subsystems + n_subsystems;
|
||||
|
||||
std::unique_ptr<model_subsystem[]> subsys_storage(new model_subsystem[new_n]);
|
||||
|
||||
if(sip->n_subsystems <= 0) {
|
||||
if(sip->n_subsystems < 1) {
|
||||
sip->n_subsystems = n_subsystems;
|
||||
} else {
|
||||
// This used realloc originally so we need to copy the existing subsystems to the new storage
|
||||
std::copy(sip->subsystems, sip->subsystems + sip->n_subsystems, subsys_storage.get());
|
||||
sip->subsystems = (model_subsystem*)vm_malloc(sizeof(model_subsystem) * sip->n_subsystems);
|
||||
|
||||
} else {
|
||||
sip->n_subsystems += n_subsystems;
|
||||
}
|
||||
sip->subsystems = subsys_storage.release();
|
||||
sip->subsystems = (model_subsystem *)vm_realloc(sip->subsystems, sizeof(model_subsystem) * sip->n_subsystems);
|
||||
}
|
||||
|
||||
Assert(sip->subsystems != NULL);
|
||||
}
|
||||
@@ -13338,7 +13326,7 @@ int ship_type_name_lookup(const char *name)
|
||||
// Return true/false for subsystem found/not found.
|
||||
// Stuff vector *pos with absolute position.
|
||||
// subsysp is a pointer to the subsystem.
|
||||
int get_subsystem_pos(vec3d* pos, object* objp, ship_subsys* subsysp)
|
||||
int get_subsystem_pos(vec3d *pos, object *objp, ship_subsys *subsysp)
|
||||
{
|
||||
if (subsysp == NULL) {
|
||||
*pos = objp->pos;
|
||||
@@ -13346,7 +13334,7 @@ int get_subsystem_pos(vec3d* pos, object* objp, ship_subsys* subsysp)
|
||||
}
|
||||
Assertion(objp->type == OBJ_SHIP, "Only ships can have subsystems!");
|
||||
|
||||
model_subsystem* mss = subsysp->system_info;
|
||||
model_subsystem *mss = subsysp->system_info;
|
||||
|
||||
if (mss->subobj_num == -1) {
|
||||
// If it's a special point subsys, we can use its offset directly
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@ public:
|
||||
// end dynamic weapon linking
|
||||
|
||||
int secondary_bank_ammo[MAX_SHIP_SECONDARY_BANKS]; // Number of missiles left in secondary bank
|
||||
int secondary_bank_start_ammo[MAX_SHIP_SECONDARY_BANKS]; // Number of missiles starting in secondary bank -- Every time the secondary bank changes, this must change too!
|
||||
int secondary_bank_start_ammo[MAX_SHIP_SECONDARY_BANKS]; // Number of missiles starting in secondary bank
|
||||
int secondary_bank_capacity[MAX_SHIP_SECONDARY_BANKS]; // Max number of missiles in bank
|
||||
int secondary_next_slot[MAX_SHIP_SECONDARY_BANKS]; // Next slot to fire in the bank
|
||||
int secondary_bank_rearm_time[MAX_SHIP_SECONDARY_BANKS]; // timestamp which indicates when bank can get new missile
|
||||
|
||||
@@ -6,67 +6,6 @@ add_file_folder(""
|
||||
prefix_header.h
|
||||
)
|
||||
|
||||
add_file_folder("Actions"
|
||||
actions/Action.cpp
|
||||
actions/Action.h
|
||||
actions/ActionDefinition.cpp
|
||||
actions/ActionDefinition.h
|
||||
actions/ActionDefinitionManager.cpp
|
||||
actions/ActionDefinitionManager.h
|
||||
actions/BuiltinActionDefinition.cpp
|
||||
actions/BuiltinActionDefinition.h
|
||||
actions/common.cpp
|
||||
actions/common.h
|
||||
actions/Program.cpp
|
||||
actions/Program.h
|
||||
)
|
||||
|
||||
add_file_folder("Actions\\\\Expression"
|
||||
actions/expression/ActionExpression.cpp
|
||||
actions/expression/ActionExpression.h
|
||||
actions/expression/ExpressionParser.cpp
|
||||
actions/expression/ExpressionParser.h
|
||||
actions/expression/FunctionManager.cpp
|
||||
actions/expression/FunctionManager.h
|
||||
actions/expression/ParseContext.h
|
||||
actions/expression/ProgramVariables.cpp
|
||||
actions/expression/ProgramVariables.h
|
||||
actions/expression/TypeDefinition.cpp
|
||||
actions/expression/TypeDefinition.h
|
||||
actions/expression/Value.cpp
|
||||
actions/expression/Value.h
|
||||
)
|
||||
|
||||
add_file_folder("Actions\\\\Expression\\\\Nodes"
|
||||
actions/expression/nodes/AbstractExpression.cpp
|
||||
actions/expression/nodes/AbstractExpression.h
|
||||
actions/expression/nodes/FunctionCallExpression.cpp
|
||||
actions/expression/nodes/FunctionCallExpression.h
|
||||
actions/expression/nodes/LiteralExpression.cpp
|
||||
actions/expression/nodes/LiteralExpression.h
|
||||
actions/expression/nodes/RandomRangeExpression.cpp
|
||||
actions/expression/nodes/RandomRangeExpression.h
|
||||
actions/expression/nodes/VariableReferenceExpression.cpp
|
||||
actions/expression/nodes/VariableReferenceExpression.h
|
||||
actions/expression/nodes/VectorConstructorExpression.cpp
|
||||
actions/expression/nodes/VectorConstructorExpression.h
|
||||
)
|
||||
|
||||
add_file_folder("Actions\\\\Types"
|
||||
actions/types/MoveToSubmodel.cpp
|
||||
actions/types/MoveToSubmodel.h
|
||||
actions/types/ParticleEffectAction.cpp
|
||||
actions/types/ParticleEffectAction.h
|
||||
actions/types/PlaySoundAction.cpp
|
||||
actions/types/PlaySoundAction.h
|
||||
actions/types/SetDirectionAction.cpp
|
||||
actions/types/SetDirectionAction.h
|
||||
actions/types/SetPositionAction.cpp
|
||||
actions/types/SetPositionAction.h
|
||||
actions/types/WaitAction.cpp
|
||||
actions/types/WaitAction.h
|
||||
)
|
||||
|
||||
# AI files
|
||||
add_file_folder("AI"
|
||||
ai/ai.cpp
|
||||
@@ -698,11 +637,6 @@ add_file_folder("Libs"
|
||||
libs/jansson.h
|
||||
)
|
||||
|
||||
add_file_folder("Libs\\\\AntLR"
|
||||
libs/antlr/ErrorListener.cpp
|
||||
libs/antlr/ErrorListener.h
|
||||
)
|
||||
|
||||
add_file_folder("Libs\\\\Discord"
|
||||
libs/discord/discord.cpp
|
||||
libs/discord/discord.h
|
||||
@@ -1524,7 +1458,6 @@ add_file_folder("Utils"
|
||||
utils/HeapAllocator.cpp
|
||||
utils/HeapAllocator.h
|
||||
utils/id.h
|
||||
utils/join_string.h
|
||||
utils/RandomRange.h
|
||||
utils/string_utils.cpp
|
||||
utils/string_utils.h
|
||||
|
||||
@@ -5,35 +5,10 @@ using namespace tracing;
|
||||
|
||||
namespace tracing {
|
||||
|
||||
MonitorBase::MonitorBase(const char* name) : _name(name), _tracing_cat(name, false) {}
|
||||
void MonitorBase::valueChanged(float newVal)
|
||||
{
|
||||
MonitorBase::MonitorBase(const char* name) : _name(name), _tracing_cat(name, false) {
|
||||
}
|
||||
void MonitorBase::valueChanged(float newVal) {
|
||||
tracing::counter::value(_tracing_cat, newVal);
|
||||
}
|
||||
|
||||
RunningCounter::RunningCounter(Monitor<int>& monitor) : _monitor(monitor)
|
||||
{
|
||||
_monitor += 1;
|
||||
}
|
||||
RunningCounter::~RunningCounter()
|
||||
{
|
||||
_monitor -= 1;
|
||||
}
|
||||
RunningCounter::RunningCounter(const RunningCounter& other) : _monitor(other._monitor)
|
||||
{
|
||||
_monitor += 1;
|
||||
}
|
||||
RunningCounter& RunningCounter::operator=(const RunningCounter&)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
RunningCounter::RunningCounter(RunningCounter&& other) noexcept : _monitor(other._monitor)
|
||||
{
|
||||
_monitor += 1;
|
||||
}
|
||||
RunningCounter& RunningCounter::operator=(RunningCounter&&) noexcept
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
+1
-18
@@ -58,24 +58,7 @@ class Monitor: public MonitorBase {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class that keeps track of how many operations are currently running
|
||||
*/
|
||||
class RunningCounter {
|
||||
Monitor<int>& _monitor;
|
||||
|
||||
public:
|
||||
explicit RunningCounter(Monitor<int>& monitor);
|
||||
~RunningCounter();
|
||||
|
||||
RunningCounter(const RunningCounter&);
|
||||
RunningCounter& operator=(const RunningCounter&);
|
||||
|
||||
RunningCounter(RunningCounter&&) noexcept;
|
||||
RunningCounter& operator=(RunningCounter&&) noexcept;
|
||||
};
|
||||
|
||||
} // namespace tracing
|
||||
}
|
||||
|
||||
// Creates a monitor variable
|
||||
#define MONITOR(function_name) static ::tracing::Monitor<int> mon_##function_name(#function_name, 0);
|
||||
|
||||
@@ -143,6 +143,4 @@ Category RenderDecals("Render all decals", true);
|
||||
Category RenderSingleDecal("Render single decal", true);
|
||||
Category GpuHeapAllocate("GPU heap allocate", false);
|
||||
Category GpuHeapDeallocate("GPU heap deallocate", false);
|
||||
|
||||
Category ProgramStepOne("Step one program", false);
|
||||
}
|
||||
@@ -159,8 +159,6 @@ extern Category RenderSingleDecal;
|
||||
extern Category GpuHeapAllocate;
|
||||
extern Category GpuHeapDeallocate;
|
||||
|
||||
extern Category ProgramStepOne;
|
||||
|
||||
}
|
||||
|
||||
#endif // _TRACING_CATEGORIES_H
|
||||
+1
-1
@@ -46,7 +46,7 @@ public:
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline bool isValid() const { return m_val != default_value; }
|
||||
inline bool isValid() { return m_val != default_value; }
|
||||
|
||||
private:
|
||||
Impl m_val;
|
||||
|
||||
Loaded 100 of 171 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user