mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 07:28:22 -10:00
Compare commits
62
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f25d95041d | ||
|
|
60047116fd | ||
|
|
4aa33c7b3e | ||
|
|
373dbeb63f | ||
|
|
361cc76c30 | ||
|
|
3f27183d6d | ||
|
|
3d976f53b4 | ||
|
|
e5af389803 | ||
|
|
ba0f2e7f83 | ||
|
|
f0987a4ad1 | ||
|
|
61dd4356f0 | ||
|
|
679f7f20d7 | ||
|
|
a939a73c14 | ||
|
|
3f5ad1239d | ||
|
|
4e715352c9 | ||
|
|
5f538fa514 | ||
|
|
506ba911d0 | ||
|
|
f44cfa06c8 | ||
|
|
eeb6f5b091 | ||
|
|
18cb07dc72 | ||
|
|
3a4df0d89a | ||
|
|
d33f9db1f0 | ||
|
|
75b26ef94b | ||
|
|
398f4d9053 | ||
|
|
069947e397 | ||
|
|
580d67eca7 | ||
|
|
4bf93e08df | ||
|
|
6c475130d1 | ||
|
|
6f0dec4169 | ||
|
|
3b99402a0a | ||
|
|
6c6ade116f | ||
|
|
4f6c834587 | ||
|
|
c486843e5f | ||
|
|
ec93ac4c0e | ||
|
|
2307d05086 | ||
|
|
d3d48ee6fa | ||
|
|
ac7bdbf269 | ||
|
|
ddf00588d6 | ||
|
|
85a2a8f11e | ||
|
|
e76fdea8b9 | ||
|
|
726ce953c7 | ||
|
|
a545a4ec27 | ||
|
|
fc0662a9c4 | ||
|
|
e5685ded86 | ||
|
|
226cbf1204 | ||
|
|
043466309a | ||
|
|
766624600d | ||
|
|
dab9a41f45 | ||
|
|
51d8ab13d8 | ||
|
|
b93d30e12b | ||
|
|
945db25578 | ||
|
|
67de007ecb | ||
|
|
e9a9b3b978 | ||
|
|
e10522a2c7 | ||
|
|
9f33b432fc | ||
|
|
7a0578a797 | ||
|
|
b81825326c | ||
|
|
b8c2c975c0 | ||
|
|
dc0272e664 | ||
|
|
3e329d64dd | ||
|
|
882ac2b1a1 | ||
|
|
b3153df6be |
No files matched your search
@@ -17,18 +17,18 @@ jobs:
|
||||
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 }}
|
||||
# - 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
|
||||
@@ -107,30 +107,30 @@ jobs:
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache Qt
|
||||
id: cache-qt-win
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt (32 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.arch == 'Win32' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win32_msvc2017
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
- name: Install Qt (64 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.arch == 'x64' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win64_msvc2017_64
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-win
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt (32 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.arch == 'Win32' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win32_msvc2017
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
# - name: Install Qt (64 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.arch == 'x64' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win64_msvc2017_64
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
@@ -144,7 +144,7 @@ jobs:
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_USE_SPEECH="ON" \
|
||||
-DFSO_USE_VOICEREC="ON" -DMSVC_SIMD_INSTRUCTIONS="$SIMD" \
|
||||
-DFSO_BUILD_QTFRED=ON -DFSO_BUILD_TESTS=ON \
|
||||
-DFSO_BUILD_QTFRED=OFF -DFSO_BUILD_TESTS=ON \
|
||||
-DFSO_INSTALL_DEBUG_FILES="ON" -A "$ARCHITECTURE" \
|
||||
-G "Visual Studio 16 2019" -T "v142" ..
|
||||
- name: Compile
|
||||
|
||||
@@ -41,18 +41,18 @@ jobs:
|
||||
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 }}
|
||||
# - 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
|
||||
@@ -142,30 +142,30 @@ jobs:
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache Qt
|
||||
id: cache-qt-win
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt (32 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.arch == 'Win32' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win32_msvc2017
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
- name: Install Qt (64 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.arch == 'x64' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win64_msvc2017_64
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-win
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt (32 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.arch == 'Win32' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win32_msvc2017
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
# - name: Install Qt (64 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.arch == 'x64' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win64_msvc2017_64
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
@@ -179,7 +179,7 @@ jobs:
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_USE_SPEECH="ON" \
|
||||
-DFSO_USE_VOICEREC="ON" -DMSVC_SIMD_INSTRUCTIONS="$SIMD" \
|
||||
-DFSO_BUILD_QTFRED=ON -DFSO_BUILD_TESTS=ON \
|
||||
-DFSO_BUILD_QTFRED=OFF -DFSO_BUILD_TESTS=ON \
|
||||
-DFSO_INSTALL_DEBUG_FILES="ON" -A "$ARCHITECTURE" \
|
||||
-G "Visual Studio 16 2019" -T "v142" ..
|
||||
- name: Compile
|
||||
|
||||
@@ -8,7 +8,6 @@ env:
|
||||
jobs:
|
||||
build_linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
compiler: [gcc-5, gcc-10, clang-9]
|
||||
@@ -25,19 +24,19 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-e977d36
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
setup-python: 'false'
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-lin
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
# setup-python: 'false'
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
@@ -84,30 +83,30 @@ jobs:
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache Qt
|
||||
id: cache-qt-win
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt (32 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.compiler == 'MSVC' && matrix.arch == 'Win32' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win32_msvc2017
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
- name: Install Qt (64 bit)
|
||||
uses: jurplel/install-qt-action@v2
|
||||
if: ${{ matrix.compiler == 'MSVC' && matrix.arch == 'x64' }}
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
arch: win64_msvc2017_64
|
||||
cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
aqtversion: ==0.8
|
||||
# - name: Cache Qt
|
||||
# id: cache-qt-win
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ${{ github.workspace }}/../Qt
|
||||
# key: ${{ runner.os }}-${{ matrix.arch }}-QtCache-${{ env.QT_VERSION }}
|
||||
# - name: Install Qt (32 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.compiler == 'MSVC' && matrix.arch == 'Win32' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win32_msvc2017
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
# - name: Install Qt (64 bit)
|
||||
# uses: jurplel/install-qt-action@v2
|
||||
# if: ${{ matrix.compiler == 'MSVC' && matrix.arch == 'x64' }}
|
||||
# with:
|
||||
# version: ${{ env.QT_VERSION }}
|
||||
# dir: ${{ github.workspace }}/..
|
||||
# arch: win64_msvc2017_64
|
||||
# cached: ${{ steps.cache-qt-win.outputs.cache-hit }}
|
||||
# aqtversion: ==0.8
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
@@ -125,7 +124,7 @@ jobs:
|
||||
else
|
||||
cmake -DFSO_USE_SPEECH="ON" -DFSO_FATAL_WARNINGS="ON" -DFSO_USE_VOICEREC="OFF" -DFSO_BUILD_TESTS="ON" \
|
||||
-DMSVC_SIMD_INSTRUCTIONS=SSE2 -DFSO_BUILD_FRED2="ON" -G "Visual Studio 16 2019" \
|
||||
-DFSO_BUILD_QTFRED=ON -T "v142" -A "$ARCHITECTURE" ..
|
||||
-DFSO_BUILD_QTFRED=OFF -T "v142" -A "$ARCHITECTURE" ..
|
||||
fi
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
|
||||
@@ -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=OFF ..
|
||||
@@ -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
|
||||
+2
-2
@@ -7840,7 +7840,7 @@ void update_aspect_lock_information(ai_info *aip, vec3d *vec_to_enemy, float dis
|
||||
object *aiobjp = &Objects[shipp->objnum];
|
||||
|
||||
// AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
|
||||
if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
|
||||
if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank >= swp->num_secondary_banks) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -9065,7 +9065,7 @@ void ai_chase()
|
||||
}
|
||||
}
|
||||
|
||||
if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank]) && weapon_can_lock_on_ship(swip, En_objp->instance)) {
|
||||
if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank]) && weapon_target_satisfies_lock_restrictions(swip, En_objp)) {
|
||||
if (current_bank >= 0) {
|
||||
float firing_range;
|
||||
|
||||
|
||||
@@ -274,7 +274,6 @@ cmdline_parm gameclosed_arg("-closed", NULL, AT_NONE); // Cmdline_closed_game
|
||||
cmdline_parm gamerestricted_arg("-restricted", NULL, AT_NONE); // Cmdline_restricted_game
|
||||
cmdline_parm port_arg("-port", "Multiplayer network port", AT_INT);
|
||||
cmdline_parm multilog_arg("-multilog", NULL, AT_NONE); // Cmdline_multi_log
|
||||
cmdline_parm client_dodamage("-clientdamage", NULL, AT_NONE); // Cmdline_client_dodamage
|
||||
cmdline_parm pof_spew("-pofspew", NULL, AT_NONE); // Cmdline_spew_pof_info
|
||||
cmdline_parm weapon_spew("-weaponspew", nullptr, AT_STRING); // Cmdline_spew_weapon_stats
|
||||
cmdline_parm mouse_coords("-coords", NULL, AT_NONE); // Cmdline_mouse_coords
|
||||
@@ -287,7 +286,6 @@ char *Cmdline_game_password = NULL;
|
||||
char *Cmdline_rank_above = NULL;
|
||||
char *Cmdline_rank_below = NULL;
|
||||
int Cmdline_cd_check = 1;
|
||||
int Cmdline_client_dodamage = 0;
|
||||
int Cmdline_closed_game = 0;
|
||||
int Cmdline_freespace_no_music = 0;
|
||||
int Cmdline_freespace_no_sound = 0;
|
||||
@@ -812,7 +810,7 @@ void os_validate_parms(int argc, char *argv[])
|
||||
if (!stricmp(token, "-help") || !stricmp(token, "--help") || !stricmp(token, "-h") || !stricmp(token, "-?")) {
|
||||
printf("FreeSpace 2 Open, version %s\n", FS_VERSION_FULL);
|
||||
printf("Website: http://scp.indiegames.us\n");
|
||||
printf("Mantis (bug reporting): http://scp.indiegames.us/mantis/\n\n");
|
||||
printf("Github (bug reporting): https://github.com/scp-fs2open/fs2open.github.com/issues\n\n");
|
||||
printf("Usage: fs2_open [options]\n");
|
||||
|
||||
// not the prettiest thing but the job gets done
|
||||
@@ -1663,12 +1661,6 @@ bool SetCmdlineParams()
|
||||
Cmdline_multi_log = 1;
|
||||
}
|
||||
|
||||
|
||||
// maybe use old-school client damage
|
||||
if(client_dodamage.found()){
|
||||
Cmdline_client_dodamage = 1;
|
||||
}
|
||||
|
||||
// spew pof info
|
||||
if(pof_spew.found()){
|
||||
Cmdline_spew_pof_info = 1;
|
||||
|
||||
@@ -30,7 +30,6 @@ extern char *Cmdline_game_password;
|
||||
extern char *Cmdline_rank_above;
|
||||
extern char *Cmdline_rank_below;
|
||||
extern int Cmdline_cd_check;
|
||||
extern int Cmdline_client_dodamage;
|
||||
extern int Cmdline_closed_game;
|
||||
extern int Cmdline_freespace_no_music;
|
||||
extern int Cmdline_freespace_no_sound;
|
||||
|
||||
@@ -74,6 +74,7 @@ void GetLightInfo(vec3 position, out vec3 lightDir, out float attenuation)
|
||||
if(dist > lightRadius) {
|
||||
discard;
|
||||
}
|
||||
attenuation = 1.0 - clamp(dist / lightRadius, 0.0, 1.0);
|
||||
} else if (lightType == LT_CONE) {
|
||||
float coneDot = dot(normalize(-lightDir), coneDir);
|
||||
if(dualCone) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -566,7 +566,7 @@ typedef struct hc_col {
|
||||
ubyte r, g, b;
|
||||
} hc_col;
|
||||
|
||||
hc_col HC_colors[3] =
|
||||
hc_col HC_colors[HUD_COLOR_SIZE] =
|
||||
{
|
||||
{0, 255, 0}, // Green - get RGB from Adam so it matches palette?-??.pcx
|
||||
{67, 123, 203}, // Blue - get RGB from Adam so it matches palette?-??.pcx
|
||||
|
||||
+15
-11
@@ -21,6 +21,7 @@ struct ai_info;
|
||||
#define HUD_COLOR_GREEN 0
|
||||
#define HUD_COLOR_BLUE 1
|
||||
#define HUD_COLOR_AMBER 2
|
||||
#define HUD_COLOR_SIZE 3 // Number of default colors. Keep this up to date.
|
||||
|
||||
// specify the max distance that the radar should detect objects
|
||||
// Index in Radar_ranges[] array to get values
|
||||
@@ -45,19 +46,22 @@ extern int HUD_default_popup_mask2;
|
||||
extern int HUD_config_default_flags;
|
||||
extern int HUD_config_default_flags2;
|
||||
|
||||
/**
|
||||
* @brief Contains core HUD configuration data
|
||||
* @note Is not default init'd. Assumes new player, PLR, or CSG reads will correctly set data.
|
||||
*/
|
||||
typedef struct HUD_CONFIG_TYPE {
|
||||
int show_flags; // whether to show gauge
|
||||
int show_flags2; // whether to show gauge
|
||||
int popup_flags; // whether gauge is popup
|
||||
int popup_flags2; // whether gauge is popup
|
||||
int rp_flags; // see RP_ flags above
|
||||
int rp_dist; // one of RR_ #defines above
|
||||
int is_observer; // 1 or 0, observer mode or not, respectively
|
||||
int main_color; // the main color
|
||||
ubyte num_msg_window_lines;
|
||||
int show_flags; //!< bitfield, whether to show gauge (0 ~ 31)
|
||||
int show_flags2; //!< bitfield, whether to show gauge (32 ~ 63)
|
||||
int popup_flags; //!< bitfield, whether gauge is popup (0 ~ 31)
|
||||
int popup_flags2; //!< bitfield, whether gauge is popup (32 ~ 63)
|
||||
int rp_flags; //!< one of RP_ #defines in hudconfig.h; Chiefly shows/hides non-ship objects
|
||||
int rp_dist; //!< one of RR_ #defines above; Is the maxium radar view distance setting
|
||||
int is_observer; //!< 1 or 0, observer mode or not, respectively
|
||||
int main_color; //!< the default HUD_COLOR selection for all gauges; each gauge may override this with a custom RGB
|
||||
ubyte num_msg_window_lines; //!< Number of message lines. (Deprecated by HudGaugeMessages::Max_lines)
|
||||
|
||||
// colors for all the gauges
|
||||
color clr[NUM_HUD_GAUGES];
|
||||
color clr[NUM_HUD_GAUGES]; //!< colors for all the gauges
|
||||
} HUD_CONFIG_TYPE;
|
||||
|
||||
extern HUD_CONFIG_TYPE HUD_config;
|
||||
|
||||
@@ -924,7 +924,7 @@ void hud_lock_acquire_uncaged_target(lock_info *current_lock, weapon_info *wip)
|
||||
continue;
|
||||
}*/
|
||||
|
||||
if (!weapon_can_lock_on_ship(wip, A->instance)) {
|
||||
if (!weapon_target_satisfies_lock_restrictions(wip, A)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1042,7 +1042,7 @@ void hud_lock_determine_lock_target(lock_info *lock_slot, weapon_info *wip)
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !weapon_can_lock_on_ship(wip, lock_slot->obj->instance) ) {
|
||||
if ( !weapon_target_satisfies_lock_restrictions(wip, lock_slot->obj) ) {
|
||||
ship_clear_lock(lock_slot);
|
||||
return;
|
||||
}
|
||||
@@ -1110,7 +1110,7 @@ void hud_lock_determine_lock_target(lock_info *lock_slot, weapon_info *wip)
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !weapon_can_lock_on_ship(wip, lock_slot->obj->instance) ) {
|
||||
if ( !weapon_target_satisfies_lock_restrictions(wip, lock_slot->obj) ) {
|
||||
ship_clear_lock(lock_slot);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5323,17 +5323,18 @@ SCP_string hud_get_ship_class(const ship *shipp)
|
||||
// try to get alt name
|
||||
if (Fred_running) {
|
||||
ship_class_text = Fred_alt_names[shipp-Ships];
|
||||
|
||||
if (ship_class_text.empty()) {
|
||||
ship_class_text = Ship_info[shipp->ship_info_index].get_display_name();
|
||||
}
|
||||
} else {
|
||||
if (shipp->alt_type_index >= 0) {
|
||||
ship_class_text = mission_parse_lookup_alt_index(shipp->alt_type_index);
|
||||
} else {
|
||||
ship_class_text = Ship_info[shipp->ship_info_index].get_display_name();
|
||||
}
|
||||
}
|
||||
|
||||
// maybe get ship class
|
||||
if (ship_class_text.empty()) {
|
||||
ship_class_text = Ship_info[shipp->ship_info_index].get_display_name();
|
||||
}
|
||||
|
||||
if (!Disable_built_in_translations) {
|
||||
// handle translation
|
||||
if (Lcl_gr) {
|
||||
|
||||
@@ -133,6 +133,8 @@ public:
|
||||
int setAmbientFactor(int factor) {
|
||||
ambientFactor = factor;
|
||||
Cmdline_ambient_factor = factor;
|
||||
gr_calculate_ambient_factor();
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -2842,9 +2831,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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -656,8 +654,6 @@ inline matrix operator*(const matrix& left, const matrix& right) {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const vec3d& vec);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+122
-226
@@ -5,7 +5,8 @@
|
||||
* or otherwise commercially exploit the source or things you created based on the
|
||||
* source.
|
||||
*
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +43,7 @@
|
||||
|
||||
#define NUM_BUTTONS 16
|
||||
#define NUM_TABS 3
|
||||
#define LIST_BUTTONS_MAX 42
|
||||
#define LIST_BUTTONS_MAX 50
|
||||
|
||||
#define SHIPS_DATA_MODE (1<<0)
|
||||
#define WEAPONS_DATA_MODE (1<<1)
|
||||
@@ -235,14 +236,10 @@ typedef struct {
|
||||
int textures_loaded; // if the model has textures loaded for it or not (hacky mem management)
|
||||
} tech_list_entry;
|
||||
|
||||
static tech_list_entry *Ship_list = nullptr;
|
||||
static int Ship_list_size = 0;
|
||||
static tech_list_entry *Weapon_list = nullptr;
|
||||
static int Weapon_list_size = 0;
|
||||
static tech_list_entry *Intel_list = nullptr;
|
||||
static int Intel_list_size = 0;
|
||||
static tech_list_entry *Current_list; // points to currently valid display list
|
||||
static int Current_list_size = 0;
|
||||
static SCP_vector<tech_list_entry> Ship_list;
|
||||
static SCP_vector<tech_list_entry> Weapon_list;
|
||||
static SCP_vector<tech_list_entry> Intel_list;
|
||||
static SCP_vector<tech_list_entry>* Current_list = &Ship_list; // A pointer to the current display list
|
||||
|
||||
// slider stuff
|
||||
static UI_SLIDER2 Tech_slider;
|
||||
@@ -274,64 +271,63 @@ void techroom_init_desc(const char *src, int w)
|
||||
|
||||
void techroom_unload_animation()
|
||||
{
|
||||
int i;
|
||||
|
||||
//clear everything, just in case, it will get loaded when needed later
|
||||
if (Weapon_list != NULL) {
|
||||
for (i = 0; i < Weapon_list_size; i++) {
|
||||
if (Weapon_list[i].animation.num_frames != 0) {
|
||||
generic_anim_unload(&Weapon_list[i].animation);
|
||||
}
|
||||
for (auto& list_entry : Weapon_list) {
|
||||
if (list_entry.animation.type != BM_TYPE_NONE && list_entry.has_anim != 0) {
|
||||
generic_anim_unload(&list_entry.animation);
|
||||
}
|
||||
|
||||
if (Weapon_list[i].bitmap >= 0) {
|
||||
bm_release(Weapon_list[i].bitmap);
|
||||
Weapon_list[i].bitmap = -1;
|
||||
}
|
||||
if (list_entry.bitmap >= 0) {
|
||||
bm_release(list_entry.bitmap);
|
||||
list_entry.bitmap = -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < Intel_list_size; i++) {
|
||||
if (Intel_list[i].animation.num_frames != 0) {
|
||||
generic_anim_unload(&Intel_list[i].animation);
|
||||
for (auto & intel_entry : Intel_list) {
|
||||
if (intel_entry.animation.type != BM_TYPE_NONE && intel_entry.has_anim != 0) {
|
||||
generic_anim_unload(&intel_entry.animation);
|
||||
}
|
||||
|
||||
if (Intel_list[i].bitmap >= 0) {
|
||||
bm_release(Intel_list[i].bitmap);
|
||||
Intel_list[i].bitmap = -1;
|
||||
if (intel_entry.bitmap >= 0) {
|
||||
bm_release(intel_entry.bitmap);
|
||||
intel_entry.bitmap = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void techroom_select_new_entry()
|
||||
{
|
||||
Assert(Current_list != NULL);
|
||||
if (Current_list == NULL || Current_list_size <= 0) {
|
||||
if (Current_list->empty()) {
|
||||
Cur_entry_index = Cur_entry = -1;
|
||||
techroom_init_desc(NULL,0);
|
||||
techroom_init_desc(nullptr,0);
|
||||
return;
|
||||
}
|
||||
|
||||
Cur_entry_index = Current_list[Cur_entry].index;
|
||||
Assert(Cur_entry < static_cast<int>(Current_list->size()));
|
||||
|
||||
Cur_entry_index = Current_list->at(Cur_entry).index;
|
||||
Assert( Cur_entry_index >= 0 );
|
||||
|
||||
// if we are in the ships tab, load the ship model
|
||||
if (Tab == SHIPS_DATA_TAB) {
|
||||
ship_info *sip = &Ship_info[Cur_entry_index];
|
||||
|
||||
int i = 0;
|
||||
// little memory management, kinda hacky but it should keep the techroom at around
|
||||
// 100meg rather than the 700+ it can get to with all ships loaded - taylor
|
||||
for (int i=0; i<Current_list_size; i++) {
|
||||
if ((Current_list[i].model_num > -1) && (Current_list[i].textures_loaded)) {
|
||||
for (auto & list_entry : *Current_list) {
|
||||
if ((list_entry.model_num > -1) && (list_entry.textures_loaded)) {
|
||||
// don't unload any spot within 5 of current
|
||||
if ( (i < Cur_entry + 5) && (i > Cur_entry - 5) )
|
||||
if ((i < Cur_entry + 5) && (i > Cur_entry - 5) )
|
||||
continue;
|
||||
|
||||
mprintf(("TECH ROOM: Dumping excess ship textures...\n"));
|
||||
|
||||
model_page_out_textures(Current_list[i].model_num);
|
||||
model_page_out_textures(list_entry.model_num);
|
||||
|
||||
Current_list[i].textures_loaded = 0;
|
||||
list_entry.textures_loaded = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
Techroom_ship_modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
|
||||
@@ -341,12 +337,14 @@ void techroom_select_new_entry()
|
||||
}
|
||||
Techroom_ship_model_instance = model_create_instance(true, Techroom_ship_modelnum);
|
||||
|
||||
Current_list[Cur_entry].model_num = Techroom_ship_modelnum;
|
||||
model_set_up_techroom_instance(sip, Techroom_ship_model_instance);
|
||||
|
||||
Current_list->at(Cur_entry).model_num = Techroom_ship_modelnum;
|
||||
|
||||
// page in ship textures properly (takes care of nondimming pixels)
|
||||
model_page_in_textures(Techroom_ship_modelnum, Cur_entry_index);
|
||||
|
||||
Current_list[Cur_entry].textures_loaded = 1;
|
||||
Current_list->at(Cur_entry).textures_loaded = 1;
|
||||
} else {
|
||||
Techroom_ship_modelnum = -1;
|
||||
|
||||
@@ -358,19 +356,19 @@ void techroom_select_new_entry()
|
||||
Trackball_mode = 0;
|
||||
|
||||
// load animation here, we now only have one loaded
|
||||
int stream_result = generic_anim_init_and_stream(&Current_list[Cur_entry].animation, Current_list[Cur_entry].tech_anim_filename, bm_get_type(Tech_background_bitmap), true);
|
||||
int stream_result = generic_anim_init_and_stream(&Current_list->at(Cur_entry).animation, Current_list->at(Cur_entry).tech_anim_filename, bm_get_type(Tech_background_bitmap), true);
|
||||
|
||||
if (stream_result >= 0) {
|
||||
Current_list[Cur_entry].has_anim = 1;
|
||||
Current_list->at(Cur_entry).has_anim = 1;
|
||||
} else {
|
||||
// we've failed to load any animation
|
||||
// load an image and treat it like a 1 frame animation
|
||||
Current_list[Cur_entry].bitmap = bm_load(Current_list[Cur_entry].tech_anim_filename);
|
||||
Current_list->at(Cur_entry).bitmap = bm_load(Current_list->at(Cur_entry).tech_anim_filename);
|
||||
}
|
||||
}
|
||||
|
||||
techroom_init_desc(Current_list[Cur_entry].desc, Tech_desc_coords[gr_screen.res][SHIP_W_COORD]);
|
||||
fsspeech_play(FSSPEECH_FROM_TECHROOM, Current_list[Cur_entry].desc);
|
||||
techroom_init_desc(Current_list->at(Cur_entry).desc, Tech_desc_coords[gr_screen.res][SHIP_W_COORD]);
|
||||
fsspeech_play(FSSPEECH_FROM_TECHROOM, Current_list->at(Cur_entry).desc);
|
||||
}
|
||||
|
||||
// write out the current description in the bottom window
|
||||
@@ -432,7 +430,7 @@ void tech_common_render()
|
||||
y = 0;
|
||||
z = List_offset;
|
||||
while (y + font_height <= Tech_list_coords[gr_screen.res][SHIP_H_COORD]) {
|
||||
if (z >= Current_list_size) {
|
||||
if ((z - List_offset) >= LIST_BUTTONS_MAX || z >= static_cast<int>(Current_list->size())) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -445,7 +443,7 @@ void tech_common_render()
|
||||
}
|
||||
|
||||
memset( buf, 0, sizeof(buf) );
|
||||
strncpy(buf, Current_list[z].name, sizeof(buf) - 1);
|
||||
strncpy(buf, Current_list->at(z).name, sizeof(buf) - 1);
|
||||
|
||||
if (Lcl_gr && !Disable_built_in_translations)
|
||||
lcl_translate_ship_name_gr(buf);
|
||||
@@ -467,8 +465,6 @@ void tech_common_render()
|
||||
}
|
||||
}
|
||||
|
||||
void light_set_all_relevent();
|
||||
|
||||
void techroom_ships_render(float frametime)
|
||||
{
|
||||
// render all the common stuff
|
||||
@@ -480,7 +476,6 @@ void techroom_ships_render(float frametime)
|
||||
// now render the trackball ship, which is unique to the ships tab
|
||||
float rev_rate = REVOLUTION_RATE;
|
||||
angles rot_angles, view_angles;
|
||||
int i, j;
|
||||
ship_info *sip = &Ship_info[Cur_entry_index];
|
||||
model_render_params render_info;
|
||||
|
||||
@@ -551,31 +546,6 @@ void techroom_ships_render(float frametime)
|
||||
model_clear_instance(Techroom_ship_modelnum);
|
||||
render_info.set_detail_level_lock(0);
|
||||
|
||||
polymodel *pm = model_get(Techroom_ship_modelnum);
|
||||
polymodel_instance *pmi = model_get_instance(Techroom_ship_model_instance);
|
||||
|
||||
for (i = 0; i < sip->n_subsystems; i++) {
|
||||
model_subsystem *msp = &sip->subsystems[i];
|
||||
if (msp->type == SUBSYSTEM_TURRET) {
|
||||
|
||||
float p = 0.0f;
|
||||
float h = 0.0f;
|
||||
|
||||
for (j = 0; j < msp->n_triggers; j++) {
|
||||
|
||||
// special case for turrets
|
||||
p = msp->triggers[j].angle.xyz.x;
|
||||
h = msp->triggers[j].angle.xyz.y;
|
||||
}
|
||||
if ( msp->subobj_num >= 0 ) {
|
||||
model_set_instance_techroom(pm, pmi, msp->subobj_num, 0.0f, h );
|
||||
}
|
||||
if ( (msp->subobj_num != msp->turret_gun_sobj) && (msp->turret_gun_sobj >= 0) ) {
|
||||
model_set_instance_techroom(pm, pmi, msp->turret_gun_sobj, p, 0.0f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sip->replacement_textures.size() > 0)
|
||||
{
|
||||
render_info.set_replacement_textures(Techroom_ship_modelnum, sip->replacement_textures);
|
||||
@@ -585,10 +555,12 @@ void techroom_ships_render(float frametime)
|
||||
{
|
||||
gr_reset_clip();
|
||||
|
||||
auto pm = model_get(Techroom_ship_modelnum);
|
||||
|
||||
shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -sip->closeup_pos.xyz.z + pm->rad, -sip->closeup_pos.xyz.z + pm->rad + 200.0f, -sip->closeup_pos.xyz.z + pm->rad + 2000.0f, -sip->closeup_pos.xyz.z + pm->rad + 10000.0f);
|
||||
render_info.set_flags(MR_NO_TEXTURING | MR_NO_LIGHTING | MR_AUTOCENTER);
|
||||
|
||||
model_render_immediate(&render_info, Techroom_ship_modelnum, &Techroom_ship_orient, &vmd_zero_vector);
|
||||
model_render_immediate(&render_info, Techroom_ship_modelnum, Techroom_ship_model_instance, &Techroom_ship_orient, &vmd_zero_vector);
|
||||
shadows_end_render();
|
||||
|
||||
gr_set_clip(Tech_ship_display_coords[gr_screen.res][SHIP_X_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_Y_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_W_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_H_COORD], GR_RESIZE_MENU);
|
||||
@@ -604,7 +576,7 @@ void techroom_ships_render(float frametime)
|
||||
|
||||
render_info.set_flags(render_flags);
|
||||
|
||||
model_render_immediate(&render_info, Techroom_ship_modelnum, &Techroom_ship_orient, &vmd_zero_vector);
|
||||
model_render_immediate(&render_info, Techroom_ship_modelnum, Techroom_ship_model_instance, &Techroom_ship_orient, &vmd_zero_vector);
|
||||
|
||||
Glowpoint_use_depth_buffer = true;
|
||||
|
||||
@@ -626,7 +598,7 @@ void tech_prev_entry()
|
||||
|
||||
Cur_entry--;
|
||||
if (Cur_entry < 0) {
|
||||
Cur_entry = Current_list_size - 1;
|
||||
Cur_entry = static_cast<int>(Current_list->size() - 1);
|
||||
|
||||
// scroll to end of list
|
||||
List_offset = Cur_entry - Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() + 1;
|
||||
@@ -654,7 +626,7 @@ void tech_next_entry()
|
||||
techroom_unload_animation();
|
||||
|
||||
Cur_entry++;
|
||||
if (Cur_entry >= Current_list_size) {
|
||||
if (Cur_entry >= static_cast<int>(Current_list->size())) {
|
||||
Cur_entry = 0;
|
||||
|
||||
// scroll to beginning of list
|
||||
@@ -710,7 +682,7 @@ void tech_scroll_list_up()
|
||||
|
||||
void tech_scroll_list_down()
|
||||
{
|
||||
if (List_offset + Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() < Current_list_size) {
|
||||
if (List_offset + Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() < static_cast<int>(Current_list->size())) {
|
||||
List_offset++;
|
||||
gamesnd_play_iface(InterfaceSounds::SCROLL);
|
||||
} else {
|
||||
@@ -735,27 +707,27 @@ void techroom_anim_render(float frametime)
|
||||
tech_common_render();
|
||||
|
||||
// exit now if there are no entries to show
|
||||
if (Current_list_size == 0 || Cur_entry < 0)
|
||||
if (Current_list->empty() || Cur_entry < 0 || Cur_entry >= static_cast<int>(Current_list->size()))
|
||||
return;
|
||||
|
||||
// render the animation
|
||||
if(Current_list[Cur_entry].animation.num_frames > 0)
|
||||
if(Current_list->at(Cur_entry).animation.num_frames > 0)
|
||||
{
|
||||
//grab dimensions
|
||||
bm_get_info((Current_list[Cur_entry].animation.streaming) ? Current_list[Cur_entry].animation.bitmap_id : Current_list[Cur_entry].animation.first_frame, &x, &y, NULL, NULL, NULL);
|
||||
bm_get_info((Current_list->at(Cur_entry).animation.streaming) ? Current_list->at(Cur_entry).animation.bitmap_id : Current_list->at(Cur_entry).animation.first_frame, &x, &y, nullptr, nullptr, nullptr);
|
||||
//get the centre point - adjust
|
||||
x = Tech_ani_centre_coords[gr_screen.res][0] - x / 2;
|
||||
y = Tech_ani_centre_coords[gr_screen.res][1] - y / 2;
|
||||
generic_anim_render(&Current_list[Cur_entry].animation, frametime, x, y, true);
|
||||
generic_anim_render(&Current_list->at(Cur_entry).animation, frametime, x, y, true);
|
||||
}
|
||||
// if our active item has a bitmap instead of an animation, draw it
|
||||
else if((Cur_entry >= 0) && (Current_list[Cur_entry].bitmap >= 0)){
|
||||
else if((Cur_entry >= 0) && (Current_list->at(Cur_entry).bitmap >= 0)){
|
||||
//grab dimensions
|
||||
bm_get_info(Current_list[Cur_entry].bitmap, &x, &y, NULL, NULL, NULL);
|
||||
bm_get_info(Current_list->at(Cur_entry).bitmap, &x, &y, nullptr, nullptr, nullptr);
|
||||
//get the centre point - adjust
|
||||
x = Tech_ani_centre_coords[gr_screen.res][0] - x / 2;
|
||||
y = Tech_ani_centre_coords[gr_screen.res][1] - y / 2;
|
||||
gr_set_bitmap(Current_list[Cur_entry].bitmap);
|
||||
gr_set_bitmap(Current_list->at(Cur_entry).bitmap);
|
||||
gr_bitmap(x, y, GR_RESIZE_MENU);
|
||||
}
|
||||
}
|
||||
@@ -792,50 +764,40 @@ void techroom_change_tab(int num)
|
||||
|
||||
// load ship info if necessary
|
||||
if ( !Ships_loaded ) {
|
||||
if (Ship_list == NULL) {
|
||||
Ship_list = new tech_list_entry[Ship_info.size()];
|
||||
if (Ship_list.empty()) {
|
||||
Ship_list.reserve(Ship_info.size());
|
||||
}
|
||||
Ship_list_size = 0;
|
||||
|
||||
tech_list_entry temp_entry;
|
||||
|
||||
// we always initially set these values, so keep them outside the loop.
|
||||
temp_entry.bitmap = -1;
|
||||
temp_entry.animation.num_frames = 0; // no anim for ships
|
||||
temp_entry.has_anim = 0; // no anim for ships
|
||||
temp_entry.model_num = -1;
|
||||
temp_entry.textures_loaded = 0;
|
||||
|
||||
for (auto it = Ship_info.begin(); it != Ship_info.end(); ++it)
|
||||
{
|
||||
if (Techroom_show_all || (it->flags & si_mask).any_set())
|
||||
{
|
||||
// this ship should be displayed, fill out the entry struct
|
||||
Ship_list[Ship_list_size].bitmap = -1;
|
||||
Ship_list[Ship_list_size].index = (int)std::distance(Ship_info.begin(), it);
|
||||
Ship_list[Ship_list_size].animation.num_frames = 0; // no anim for ships
|
||||
Ship_list[Ship_list_size].has_anim = 0; // no anim for ships
|
||||
Ship_list[Ship_list_size].name = *it->tech_title ? it->tech_title : it->get_display_name();
|
||||
Ship_list[Ship_list_size].desc = it->tech_desc;
|
||||
Ship_list[Ship_list_size].model_num = -1;
|
||||
Ship_list[Ship_list_size].textures_loaded = 0;
|
||||
temp_entry.index = (int)std::distance(Ship_info.begin(), it);
|
||||
temp_entry.name = *it->tech_title ? it->tech_title : it->get_display_name();
|
||||
temp_entry.desc = it->tech_desc;
|
||||
|
||||
Ship_list_size++;
|
||||
Ship_list.push_back(temp_entry);
|
||||
}
|
||||
}
|
||||
|
||||
// make sure that at least the default entry is cleared out if we didn't grab anything
|
||||
if (!Ship_info.empty() && !Ship_list_size) {
|
||||
Ship_list[0].index = -1;
|
||||
Ship_list[0].desc = NULL;
|
||||
Ship_list[0].name = NULL;
|
||||
Ship_list[0].bitmap = -1;
|
||||
Ship_list[0].has_anim = 0;
|
||||
Ship_list[0].animation.num_frames = 0;
|
||||
Ship_list[0].model_num = -1;
|
||||
Ship_list[0].textures_loaded = 0;
|
||||
}
|
||||
|
||||
Ships_loaded = true;
|
||||
}
|
||||
|
||||
Current_list = Ship_list;
|
||||
Current_list_size = Ship_list_size;
|
||||
Current_list = &Ship_list;
|
||||
|
||||
font_height = gr_get_font_height();
|
||||
max_num_entries_viewable = Tech_list_coords[gr_screen.res][SHIP_H_COORD] / font_height;
|
||||
Tech_slider.set_numberItems(Current_list_size > max_num_entries_viewable ? Current_list_size-max_num_entries_viewable : 0);
|
||||
Tech_slider.set_numberItems((int)Current_list->size() > max_num_entries_viewable ? (int)Current_list->size()-max_num_entries_viewable : 0);
|
||||
|
||||
// no anim to start here
|
||||
break;
|
||||
@@ -844,57 +806,45 @@ void techroom_change_tab(int num)
|
||||
|
||||
// load weapon info & anims if necessary
|
||||
if ( !Weapons_loaded ) {
|
||||
if (Weapon_list == NULL) {
|
||||
Weapon_list = new tech_list_entry[Weapon_info.size()];
|
||||
}
|
||||
Weapon_list_size = 0;
|
||||
Weapon_list.reserve(Weapon_info.size());
|
||||
|
||||
wi_mask.set(multi ? Weapon::Info_Flags::Player_allowed : Weapon::Info_Flags::In_tech_database);
|
||||
wi_mask.set(Weapon::Info_Flags::Default_in_tech_database);
|
||||
|
||||
int i = 0;
|
||||
tech_list_entry temp_entry;
|
||||
|
||||
// we always initially set these values, so keep them outside the loop.
|
||||
temp_entry.has_anim = 1;
|
||||
temp_entry.bitmap = -1;
|
||||
temp_entry.animation.num_frames = 0;
|
||||
temp_entry.model_num = -1;
|
||||
temp_entry.textures_loaded = 0;
|
||||
|
||||
for (auto &wi : Weapon_info)
|
||||
{
|
||||
if (Techroom_show_all || (wi.wi_flags & wi_mask).any_set())
|
||||
{
|
||||
// we have a weapon that should be in the tech db, so fill out the entry struct
|
||||
Weapon_list[Weapon_list_size].index = i;
|
||||
Weapon_list[Weapon_list_size].desc = wi.tech_desc;
|
||||
Weapon_list[Weapon_list_size].has_anim = 1;
|
||||
Weapon_list[Weapon_list_size].name = wi.tech_title[0] ? wi.tech_title : wi.get_display_name();
|
||||
Weapon_list[Weapon_list_size].bitmap = -1;
|
||||
Weapon_list[Weapon_list_size].animation.num_frames = 0;
|
||||
Weapon_list[Weapon_list_size].model_num = -1;
|
||||
Weapon_list[Weapon_list_size].textures_loaded = 0;
|
||||
// we have a weapon that should be in the tech db, so fill out specific info
|
||||
temp_entry.index = i;
|
||||
temp_entry.desc = wi.tech_desc;
|
||||
temp_entry.name = wi.tech_title[0] ? wi.tech_title : wi.get_display_name();
|
||||
// copy the weapon animation filename
|
||||
strncpy(Weapon_list[Weapon_list_size].tech_anim_filename, wi.tech_anim_filename, MAX_FILENAME_LEN - 1);
|
||||
|
||||
++Weapon_list_size;
|
||||
strncpy(temp_entry.tech_anim_filename, wi.tech_anim_filename, MAX_FILENAME_LEN - 1);
|
||||
|
||||
Weapon_list.push_back(temp_entry);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
// make sure that at least the default entry is cleared out if we didn't grab anything
|
||||
if (!Weapon_info.empty() && !Weapon_list_size) {
|
||||
Weapon_list[0].index = -1;
|
||||
Weapon_list[0].desc = NULL;
|
||||
Weapon_list[0].name = NULL;
|
||||
Weapon_list[0].bitmap = -1;
|
||||
Weapon_list[0].has_anim = 0;
|
||||
Weapon_list[0].animation.num_frames = 0;
|
||||
Weapon_list[0].model_num = -1;
|
||||
Weapon_list[0].textures_loaded = 0;
|
||||
}
|
||||
|
||||
Weapons_loaded = true;
|
||||
}
|
||||
|
||||
Current_list = Weapon_list;
|
||||
Current_list_size = Weapon_list_size;
|
||||
Current_list = &Weapon_list;
|
||||
|
||||
font_height = gr_get_font_height();
|
||||
max_num_entries_viewable = Tech_list_coords[gr_screen.res][SHIP_H_COORD] / font_height;
|
||||
Tech_slider.set_numberItems(Current_list_size > max_num_entries_viewable ? Current_list_size-max_num_entries_viewable : 0);
|
||||
Tech_slider.set_numberItems(static_cast<int>(Current_list->size()) > max_num_entries_viewable ? static_cast<int>(Current_list->size())-max_num_entries_viewable : 0);
|
||||
|
||||
break;
|
||||
|
||||
@@ -902,60 +852,46 @@ void techroom_change_tab(int num)
|
||||
|
||||
// load intel if necessary
|
||||
if ( !Intel_loaded ) {
|
||||
if (Intel_list == nullptr) {
|
||||
Intel_list = new tech_list_entry[Intel_info.size()];
|
||||
if (Intel_list.empty()) {
|
||||
Intel_list.reserve(Intel_info.size());
|
||||
}
|
||||
|
||||
Intel_list_size = 0;
|
||||
|
||||
int i = 0;
|
||||
tech_list_entry temp_entry;
|
||||
|
||||
// we always initially set these values, so keep them outside the loop.
|
||||
temp_entry.has_anim = 0;
|
||||
temp_entry.model_num = -1;
|
||||
temp_entry.textures_loaded = 0;
|
||||
|
||||
for (auto &ii : Intel_info) {
|
||||
|
||||
if (Techroom_show_all || (ii.flags & IIF_IN_TECH_DATABASE)) {
|
||||
// leave option for no animation if string == "none"
|
||||
if (!strcmp(ii.anim_filename, "none")) {
|
||||
Intel_list[Intel_list_size].has_anim = 0;
|
||||
Intel_list[Intel_list_size].animation.num_frames = 0;
|
||||
temp_entry.animation.num_frames = 0;
|
||||
} else {
|
||||
// try and load as an animation
|
||||
Intel_list[Intel_list_size].has_anim = 0;
|
||||
Intel_list[Intel_list_size].bitmap = -1;
|
||||
strncpy(Intel_list[Intel_list_size].tech_anim_filename, ii.anim_filename, NAME_LENGTH - 1);
|
||||
temp_entry.bitmap = -1;
|
||||
strncpy(temp_entry.tech_anim_filename, ii.anim_filename, NAME_LENGTH - 1);
|
||||
}
|
||||
|
||||
Intel_list[Intel_list_size].desc = ii.desc.c_str();
|
||||
Intel_list[Intel_list_size].index = i;
|
||||
Intel_list[Intel_list_size].name = ii.name;
|
||||
Intel_list[Intel_list_size].model_num = -1;
|
||||
Intel_list[Intel_list_size].textures_loaded = 0;
|
||||
temp_entry.desc = ii.desc.c_str();
|
||||
temp_entry.index = i;
|
||||
temp_entry.name = ii.name;
|
||||
|
||||
Intel_list_size++;
|
||||
Intel_list.push_back(temp_entry);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
// make sure that at least the default entry is cleared out if we didn't grab anything
|
||||
if (!Intel_info.empty() && !Intel_list_size) {
|
||||
Intel_list[0].index = -1;
|
||||
Intel_list[0].desc = NULL;
|
||||
Intel_list[0].name = NULL;
|
||||
Intel_list[0].bitmap = -1;
|
||||
Intel_list[0].has_anim = 0;
|
||||
Intel_list[0].animation.num_frames = 0;
|
||||
Intel_list[0].model_num = -1;
|
||||
Intel_list[0].textures_loaded = 0;
|
||||
}
|
||||
|
||||
Intel_loaded = true;
|
||||
}
|
||||
|
||||
// index lookup on intel is a pretty pointless, but it keeps everything
|
||||
// consistent and doesn't really hurt anything
|
||||
Current_list = Intel_list;
|
||||
Current_list_size = Intel_list_size;
|
||||
Current_list = &Intel_list;
|
||||
|
||||
font_height = gr_get_font_height();
|
||||
max_num_entries_viewable = Tech_list_coords[gr_screen.res][SHIP_H_COORD] / font_height;
|
||||
Tech_slider.set_numberItems(Current_list_size > max_num_entries_viewable ? Current_list_size-max_num_entries_viewable : 0);
|
||||
Tech_slider.set_numberItems(static_cast<int>(Current_list->size()) > max_num_entries_viewable ? static_cast<int>(Current_list->size())-max_num_entries_viewable : 0);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1123,7 +1059,7 @@ void techroom_intel_reset()
|
||||
|
||||
void techroom_init()
|
||||
{
|
||||
int i, idx;
|
||||
int i;
|
||||
techroom_buttons *b;
|
||||
|
||||
Ships_loaded = false;
|
||||
@@ -1212,9 +1148,9 @@ void techroom_init()
|
||||
Tech_slider.create(&Ui_window, Tech_slider_coords[gr_screen.res][SHIP_X_COORD], Tech_slider_coords[gr_screen.res][SHIP_Y_COORD], Tech_slider_coords[gr_screen.res][SHIP_W_COORD], Tech_slider_coords[gr_screen.res][SHIP_H_COORD], (int)Ship_info.size(), Tech_slider_filename[gr_screen.res], &tech_scroll_list_up, &tech_scroll_list_down, &tech_ship_scroll_capture);
|
||||
|
||||
// zero intel anim/bitmap stuff
|
||||
for(idx=0; idx<Intel_list_size; idx++){
|
||||
Intel_list[idx].animation.num_frames = 0;
|
||||
Intel_list[idx].bitmap = -1;
|
||||
for(auto & intel_item : Intel_list){
|
||||
intel_item.animation.num_frames = 0;
|
||||
intel_item.bitmap = -1;
|
||||
}
|
||||
|
||||
mprintf(("Techroom successfully initialized, now changing tab...\n"));
|
||||
@@ -1223,63 +1159,23 @@ void techroom_init()
|
||||
|
||||
void techroom_lists_reset()
|
||||
{
|
||||
int i;
|
||||
|
||||
//unload the current animation, we load another one for the new current entry
|
||||
if(Tab != SHIPS_DATA_TAB)
|
||||
if (Tab != SHIPS_DATA_TAB)
|
||||
techroom_unload_animation();
|
||||
|
||||
Current_list = NULL;
|
||||
Current_list_size = 0;
|
||||
|
||||
model_free_all();
|
||||
Techroom_ship_modelnum = -1;
|
||||
Techroom_ship_model_instance = -1;
|
||||
|
||||
if (Ship_list != NULL) {
|
||||
delete[] Ship_list;
|
||||
Ship_list = NULL;
|
||||
}
|
||||
|
||||
Ship_list_size = 0;
|
||||
// This can be cleared immediately because there are no anims or bitmaps associated.
|
||||
Ship_list.clear();
|
||||
Ships_loaded = false;
|
||||
|
||||
if (Weapon_list != NULL) {
|
||||
for (i = 0; i < Weapon_list_size; i++) {
|
||||
if (Weapon_list[i].animation.num_frames != 0) {
|
||||
generic_anim_unload(&Weapon_list[i].animation);
|
||||
}
|
||||
|
||||
if (Weapon_list[i].bitmap >= 0) {
|
||||
bm_release(Weapon_list[i].bitmap);
|
||||
Weapon_list[i].bitmap = -1;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] Weapon_list;
|
||||
Weapon_list = NULL;
|
||||
}
|
||||
|
||||
Weapon_list_size = 0;
|
||||
// now that we're sure all the bitmaps are released, clear the vectors.
|
||||
Weapon_list.clear();
|
||||
Weapons_loaded = false;
|
||||
|
||||
if (Intel_list != nullptr) {
|
||||
for (i = 0; i < Intel_list_size; i++) {
|
||||
if (Intel_list[i].animation.num_frames != 0) {
|
||||
generic_anim_unload(&Intel_list[i].animation);
|
||||
}
|
||||
|
||||
if (Intel_list[i].bitmap >= 0) {
|
||||
bm_release(Intel_list[i].bitmap);
|
||||
Intel_list[i].bitmap = -1;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] Intel_list;
|
||||
Intel_list = nullptr;
|
||||
}
|
||||
|
||||
Intel_list_size = 0;
|
||||
Intel_list.clear();
|
||||
Intel_loaded = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -463,6 +463,7 @@ void mission_brief_common_reset()
|
||||
memset( Briefings[i].stages[j].icons, 0, sizeof(brief_icon) * MAX_STAGE_ICONS );
|
||||
Briefings[i].stages[j].icons->ship_class = -1;
|
||||
Briefings[i].stages[j].icons->modelnum = -1;
|
||||
Briefings[i].stages[j].icons->model_instance_num = -1;
|
||||
Briefings[i].stages[j].icons->bitmap_id = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ typedef struct brief_icon {
|
||||
int hold_x, hold_y; // 2D screen position of icon, used to place animations
|
||||
int ship_class;
|
||||
int modelnum;
|
||||
int model_instance_num;
|
||||
float radius;
|
||||
int type; // ICON_* defines from MissionBriefCommon.h
|
||||
int bitmap_id;
|
||||
|
||||
@@ -1509,6 +1509,8 @@ void parse_briefing(mission * /*pm*/, int flags)
|
||||
find_and_stuff("$team:", &bi->team, F_NAME, temp_team_names, Num_iffs, "team name");
|
||||
|
||||
find_and_stuff("$class:", &bi->ship_class, F_NAME, Ship_class_names, Ship_info.size(), "ship class");
|
||||
bi->modelnum = -1;
|
||||
bi->model_instance_num = -1;
|
||||
|
||||
// Goober5000 - import
|
||||
if (flags & MPF_IMPORT_FSM)
|
||||
@@ -8085,6 +8087,33 @@ void mission_parse_reset_alt()
|
||||
Mission_alt_type_count = 0;
|
||||
}
|
||||
|
||||
// For compatibility purposes some mods want alt names to truncate at the hash symbol. But we can't actually do that at mission load,
|
||||
// since we have to save them again in FRED. So this function processes the names just before the mission starts.
|
||||
// To further complicate things, some mods actually want the hash to be displayed. So this function uses the double-hash as an escape sequence for a single hash.
|
||||
void mission_process_alt_types()
|
||||
{
|
||||
for (int i = 0; i < Mission_alt_type_count; ++i)
|
||||
{
|
||||
// truncate at a single hash
|
||||
end_string_at_first_hash_symbol(Mission_alt_types[i], true);
|
||||
|
||||
// consolidate double hashes
|
||||
auto src = Mission_alt_types[i];
|
||||
auto dest = src;
|
||||
while (*src)
|
||||
{
|
||||
if (*src == '#' && *(src + 1) == '#')
|
||||
dest--;
|
||||
|
||||
++src;
|
||||
++dest;
|
||||
|
||||
if (src != dest)
|
||||
*dest = *src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callsign stuff
|
||||
*/
|
||||
|
||||
@@ -518,9 +518,6 @@ int get_parse_name_index(const char *name);
|
||||
// called from freespace game level loop
|
||||
void mission_parse_eval_stuff();
|
||||
|
||||
// function to set the ramaing time left in the mission
|
||||
void mission_parse_set_end_time( int seconds );
|
||||
|
||||
// code to bring in a repair ship.
|
||||
void mission_bring_in_support_ship( object *requester_objp );
|
||||
int mission_is_support_ship_arriving( void );
|
||||
@@ -535,6 +532,7 @@ const char *mission_parse_lookup_alt_index(int index);
|
||||
int mission_parse_add_alt(const char *name);
|
||||
void mission_parse_remove_alt(const char *name);
|
||||
void mission_parse_reset_alt();
|
||||
void mission_process_alt_types();
|
||||
|
||||
// callsign stuff
|
||||
int mission_parse_lookup_callsign(const char *name);
|
||||
|
||||
@@ -704,8 +704,13 @@ brief_icon *brief_get_closeup_icon()
|
||||
// stop showing the closeup view of an icon
|
||||
void brief_turn_off_closeup_icon()
|
||||
{
|
||||
// turn off closup
|
||||
// turn off closeup
|
||||
if ( Closeup_icon != NULL ) {
|
||||
if (Closeup_icon->model_instance_num >= 0) {
|
||||
model_delete_instance(Closeup_icon->model_instance_num);
|
||||
Closeup_icon->model_instance_num = -1;
|
||||
}
|
||||
|
||||
gamesnd_play_iface(InterfaceSounds::BRIEF_ICON_SELECT);
|
||||
Closeup_icon = NULL;
|
||||
Closeup_close_button.disable();
|
||||
@@ -1075,7 +1080,7 @@ void brief_render_closeup(int ship_class, float frametime)
|
||||
render_info.set_flags(MR_AUTOCENTER);
|
||||
}
|
||||
|
||||
model_render_immediate( &render_info, Closeup_icon->modelnum, &Closeup_orient, &Closeup_pos );
|
||||
model_render_immediate( &render_info, Closeup_icon->modelnum, Closeup_icon->model_instance_num, &Closeup_orient, &Closeup_pos );
|
||||
|
||||
The_mission.flags.set(Mission::Mission_Flags::Fullneb, neb_restore);
|
||||
|
||||
@@ -1198,28 +1203,6 @@ void brief_set_closeup_pos(brief_icon * /*bi*/)
|
||||
Closeup_x1 = (int)std::lround(320 - Closeup_coords[gr_screen.res][BRIEF_W_COORD]/2.0f);
|
||||
}
|
||||
|
||||
void brief_get_closeup_ship_modelnum(brief_icon *ci)
|
||||
{
|
||||
object *objp;
|
||||
ship *sp;
|
||||
|
||||
// find the model number for the ship to display
|
||||
for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
|
||||
|
||||
if ( objp == &obj_used_list || objp->type != OBJ_SHIP ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sp = &Ships[objp->instance];
|
||||
if ( sp->ship_info_index == ci->ship_class ) {
|
||||
ci->ship_class = sp->ship_info_index;
|
||||
ci->modelnum = Ship_info[sp->ship_info_index].model_num;
|
||||
ci->radius = objp->radius;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// brief_setup_closeup()
|
||||
//
|
||||
@@ -1234,6 +1217,7 @@ int brief_setup_closeup(brief_icon *bi)
|
||||
Closeup_icon = bi;
|
||||
Closeup_icon->ship_class = bi->ship_class;
|
||||
Closeup_icon->modelnum = -1;
|
||||
Closeup_icon->model_instance_num = -1;
|
||||
|
||||
Closeup_one_revolution_time = ONE_REV_TIME;
|
||||
|
||||
@@ -1270,7 +1254,6 @@ int brief_setup_closeup(brief_icon *bi)
|
||||
Closeup_zoom = 0.5f;
|
||||
break;
|
||||
default:
|
||||
brief_get_closeup_ship_modelnum(Closeup_icon);
|
||||
Assert( Closeup_icon->ship_class != -1 );
|
||||
sip = &Ship_info[Closeup_icon->ship_class];
|
||||
|
||||
@@ -1284,12 +1267,17 @@ int brief_setup_closeup(brief_icon *bi)
|
||||
break;
|
||||
}
|
||||
|
||||
if ( Closeup_icon->modelnum == -1 ) {
|
||||
if ( Closeup_icon->modelnum < 0 ) {
|
||||
if ( sip == NULL ) {
|
||||
Closeup_icon->modelnum = model_load(pof_filename, 0, NULL);
|
||||
} else {
|
||||
Closeup_icon->modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
|
||||
Closeup_icon->model_instance_num = model_create_instance(true, Closeup_icon->modelnum);
|
||||
model_set_up_techroom_instance(sip, Closeup_icon->model_instance_num);
|
||||
}
|
||||
}
|
||||
|
||||
if ( Closeup_icon->modelnum >= 0 ) {
|
||||
Closeup_icon->radius = model_get_radius(Closeup_icon->modelnum);
|
||||
}
|
||||
|
||||
|
||||
@@ -2414,12 +2414,10 @@ int create_wings()
|
||||
ss_wing_info *wb;
|
||||
ss_slot_info *ws;
|
||||
wing *wp;
|
||||
p_object *p_objp;
|
||||
|
||||
int shipnum, objnum, slot_index;
|
||||
int cleanup_ship_index[MAX_WING_SLOTS];
|
||||
int i,j,k;
|
||||
int found_pobj;
|
||||
|
||||
Assert( (Ss_wings != NULL) && (Wss_slots != NULL) );
|
||||
|
||||
@@ -2451,21 +2449,22 @@ int create_wings()
|
||||
objnum = OBJ_INDEX(Player_obj);
|
||||
shipnum = Objects[objnum].instance;
|
||||
} else {
|
||||
if ( wb->is_late) {
|
||||
found_pobj = 0;
|
||||
for ( p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp) ) {
|
||||
if ( p_objp->wingnum == WING_INDEX(wp) ) {
|
||||
if ( ws->sa_index == POBJ_INDEX(p_objp) ) {
|
||||
p_objp->ship_class = Wss_slots[slot_index].ship_class;
|
||||
wl_update_parse_object_weapons(p_objp, &Wss_slots[i*MAX_WING_SLOTS+j]);
|
||||
found_pobj = 1;
|
||||
break;
|
||||
}
|
||||
// We should always update the parse object information, even if the ship is present at start,
|
||||
// because the wing might have more than one wave or scripting functions might need accurate data
|
||||
bool found_pobj = false;
|
||||
for ( auto &p_obj: Parse_objects ) {
|
||||
if ( p_obj.wingnum == WING_INDEX(wp) ) {
|
||||
if ( p_obj.pos_in_wing == j ) {
|
||||
p_obj.ship_class = Wss_slots[slot_index].ship_class;
|
||||
wl_update_parse_object_weapons(&p_obj, &Wss_slots[i*MAX_WING_SLOTS+j]);
|
||||
found_pobj = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert(found_pobj);
|
||||
}
|
||||
else {
|
||||
Assert(found_pobj);
|
||||
|
||||
if (!wb->is_late) {
|
||||
// AL 10/04/97
|
||||
// Change the ship type of the ship if different than current.
|
||||
// NOTE: This will reset the weapons for this ship. I think this is
|
||||
|
||||
+10
-11
@@ -12,18 +12,17 @@
|
||||
#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 ship_info;
|
||||
class model_render_params;
|
||||
|
||||
extern flag_def_list model_render_flags[];
|
||||
@@ -206,13 +205,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 {
|
||||
@@ -805,7 +804,7 @@ polymodel *model_get(int model_num);
|
||||
|
||||
polymodel_instance* model_get_instance(int model_instance_num);
|
||||
|
||||
// routine to copy susbsystems. Must be called when subsystems sets are the same -- see ship.cpp
|
||||
// routine to copy subsystems. Must be called when subsystems sets are the same -- see ship.cpp
|
||||
void model_copy_subsystems(int n_subsystems, model_subsystem *d_sp, model_subsystem *s_sp);
|
||||
|
||||
// If MR_FLAG_OUTLINE bit set this color will be used for outlines.
|
||||
@@ -970,7 +969,7 @@ extern void model_clear_instance(int model_num);
|
||||
extern void model_set_submodel_turn_info(submodel_instance *smi, float turn_rate, float turn_accel);
|
||||
|
||||
// Sets the submodel instance data in a submodel
|
||||
extern void model_set_instance_techroom(polymodel *pm, polymodel_instance *pmi, int submodel_num, float angle_1, float angle_2);
|
||||
extern void model_set_up_techroom_instance(ship_info *sip, int model_instance_num);
|
||||
|
||||
void model_update_instance(int model_instance_num, 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);
|
||||
|
||||
+86
-27
@@ -462,6 +462,55 @@ void get_user_prop_value(char *buf, char *value)
|
||||
*p1 = c;
|
||||
}
|
||||
|
||||
// Stuff a floating point value pointed at by Mp.
|
||||
// Advances past float characters.
|
||||
int stuff_float_temp(float *f, bool optional)
|
||||
{
|
||||
char *str_start = Mp;
|
||||
char *str_end;
|
||||
|
||||
// since strtof ignores white space anyway, might as well make it explicit
|
||||
ignore_white_space();
|
||||
|
||||
auto result = strtof(Mp, &str_end);
|
||||
bool success = false, comma = false;
|
||||
int retval = 0;
|
||||
|
||||
// no float found?
|
||||
if (result == 0.0f && str_end == Mp)
|
||||
{
|
||||
if (!optional)
|
||||
error_display(1, "Expecting float, found [%.32s].\n", next_tokens());
|
||||
}
|
||||
else
|
||||
{
|
||||
*f = result;
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (success)
|
||||
Mp = str_end;
|
||||
|
||||
if (*Mp == ',')
|
||||
{
|
||||
comma = true;
|
||||
Mp++;
|
||||
}
|
||||
|
||||
if (optional && !success)
|
||||
Mp = str_start;
|
||||
|
||||
if (success)
|
||||
retval = 2;
|
||||
else if (optional)
|
||||
retval = comma ? 1 : 0;
|
||||
else
|
||||
skip_token();
|
||||
|
||||
diag_printf("Stuffed float: %f\n", *f);
|
||||
return retval;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -494,11 +543,11 @@ bool get_user_vec3d_value(char *buf, vec3d *value, bool require_brackets)
|
||||
}
|
||||
|
||||
// get comma-separated floats
|
||||
if (stuff_float_optional(&f1) != 2)
|
||||
if (stuff_float_temp(&f1, true) != 2)
|
||||
break;
|
||||
if (stuff_float_optional(&f2) != 2)
|
||||
if (stuff_float_temp(&f2, true) != 2)
|
||||
break;
|
||||
if (stuff_float_optional(&f3) != 2)
|
||||
if (stuff_float_temp(&f3, true) != 2)
|
||||
break;
|
||||
|
||||
if (require_brackets)
|
||||
@@ -2722,6 +2771,9 @@ int model_load(const char *filename, int n_subsystems, model_subsystem *subsyst
|
||||
Assert(pm->submodel[i].num_live_debris < MAX_LIVE_DEBRIS);
|
||||
pm->submodel[i].live_debris[pm->submodel[i].num_live_debris++] = j;
|
||||
pm->submodel[j].is_live_debris = 1;
|
||||
|
||||
// make sure live debris doesn't have a parent
|
||||
pm->submodel[j].parent = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4448,28 +4500,41 @@ void model_set_submodel_turn_info(submodel_instance *smi, float turn_rate, float
|
||||
smi->turn_accel = turn_accel;
|
||||
}
|
||||
|
||||
// Sets the submodel instance data in a submodel (for all detail levels)
|
||||
// Techroom version uses two floats of setting rotation angles (for turrets)
|
||||
// instead of using larger but largely unused structures for storing the same data
|
||||
void model_set_instance_techroom(polymodel *pm, polymodel_instance *pmi, int sub_model_num, float angle_1, float angle_2)
|
||||
// Sets the submodel instance data when a tech room model instance is created.
|
||||
// This only needs to be done at creation, not every frame.
|
||||
void model_set_up_techroom_instance(ship_info *sip, int model_instance_num)
|
||||
{
|
||||
Assert( sub_model_num >= 0 );
|
||||
Assert( sub_model_num < pm->n_models );
|
||||
auto pmi = model_get_instance(model_instance_num);
|
||||
auto pm = model_get(pmi->model_num);
|
||||
flagset<Ship::Subsystem_Flags> empty;
|
||||
|
||||
if ( sub_model_num < 0 ) return;
|
||||
if ( sub_model_num >= pm->n_models ) return;
|
||||
bsp_info *sm = &pm->submodel[sub_model_num];
|
||||
auto smi = &pmi->submodel[sub_model_num];
|
||||
for (int i = 0; i < sip->n_subsystems; ++i)
|
||||
{
|
||||
model_subsystem *msp = &sip->subsystems[i];
|
||||
|
||||
// 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 > -1 && sm->my_replacement != sub_model_num) {
|
||||
pmi->submodel[sm->my_replacement].blown_off = true;
|
||||
for (int j = 0; j < msp->n_triggers; ++j)
|
||||
{
|
||||
if (msp->triggers[j].type == AnimationTriggerType::Initial)
|
||||
{
|
||||
// special case for turrets
|
||||
if (msp->type == SUBSYSTEM_TURRET)
|
||||
{
|
||||
if (msp->subobj_num >= 0)
|
||||
pmi->submodel[msp->subobj_num].angs.h = msp->triggers[j].angle.xyz.y;
|
||||
|
||||
if ((msp->subobj_num != msp->turret_gun_sobj) && (msp->turret_gun_sobj >= 0))
|
||||
pmi->submodel[msp->turret_gun_sobj].angs.p = msp->triggers[j].angle.xyz.x;
|
||||
}
|
||||
// we can't support non-turrets, as in modelanim, because we need a ship subsystem but we don't actually have a ship
|
||||
}
|
||||
}
|
||||
|
||||
if (msp->subobj_num >= 0)
|
||||
model_update_instance(pm, pmi, msp->subobj_num, empty);
|
||||
|
||||
if (msp->turret_gun_sobj >= 0)
|
||||
model_update_instance(pm, pmi, msp->turret_gun_sobj, empty);
|
||||
}
|
||||
|
||||
// Set the angles
|
||||
smi->angs.p = angle_1;
|
||||
smi->angs.h = angle_2;
|
||||
}
|
||||
|
||||
void model_update_instance(int model_instance_num, int submodel_num, flagset<Ship::Subsystem_Flags>& flags)
|
||||
@@ -5565,10 +5630,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();
|
||||
}
|
||||
+11
-11
@@ -1896,18 +1896,18 @@ void model_render_set_glow_points(polymodel *pm, int objnum)
|
||||
bank->glow_timestamp=time;
|
||||
}
|
||||
|
||||
if ( ( gpo && gpo->off_time_override ) ? gpo->off_time : bank->off_time ) {
|
||||
if ( bank->is_on ) {
|
||||
if( ((gpo && gpo->on_time_override) ? gpo->on_time : bank->on_time) > ((time - ((gpo && gpo->disp_time_override) ? gpo->disp_time : bank->disp_time)) % (((gpo && gpo->on_time_override) ? gpo->on_time : bank->on_time) + ((gpo && gpo->off_time_override) ? gpo->off_time : bank->off_time))) ){
|
||||
int on_time = (gpo && gpo->on_time_override) ? gpo->on_time : bank->on_time;
|
||||
int off_time = (gpo && gpo->off_time_override) ? gpo->off_time : bank->off_time;
|
||||
int disp_time = (gpo && gpo->disp_time_override) ? gpo->disp_time : bank->disp_time;
|
||||
|
||||
|
||||
if (off_time) {
|
||||
bool glow_state = ((time - disp_time) % (on_time + off_time)) < on_time;
|
||||
|
||||
if ( glow_state != bank->is_on )
|
||||
bank->glow_timestamp = time;
|
||||
bank->is_on = 0;
|
||||
}
|
||||
} else {
|
||||
if( ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time) < ((time - ((gpo && gpo->disp_time_override)?gpo->disp_time:bank->disp_time)) % (((gpo && gpo->on_time_override)?gpo->on_time:bank->on_time) + ((gpo && gpo->off_time_override)?gpo->off_time:bank->off_time))) ){
|
||||
bank->glow_timestamp = time;
|
||||
bank->is_on = 1;
|
||||
}
|
||||
}
|
||||
|
||||
bank->is_on = glow_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,16 +69,14 @@ static bool check_for_docking_collision(object *objp1, object *objp2)
|
||||
}
|
||||
|
||||
|
||||
if (aip1->mode == AIM_DOCK) {
|
||||
if (dock_check_find_docked_object(&Objects[aip1->goal_objnum], objp2)){
|
||||
if (aip1->mode == AIM_DOCK && aip1->goal_objnum >= 0 && aip1->goal_objnum < MAX_OBJECTS) {
|
||||
if (dock_check_find_docked_object(&Objects[aip1->goal_objnum], objp2))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (aip2->mode == AIM_DOCK) {
|
||||
if (dock_check_find_docked_object(&Objects[aip2->goal_objnum], objp1)){
|
||||
if (aip2->mode == AIM_DOCK && aip2->goal_objnum >= 0 && aip2->goal_objnum < MAX_OBJECTS) {
|
||||
if (dock_check_find_docked_object(&Objects[aip2->goal_objnum], objp1))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -268,30 +268,39 @@ int weapon_will_never_hit( object *obj_weapon, object *other, obj_pair * current
|
||||
// look for two time solutions to Xw = Xs, where Xw = Xw0 + Vwt*t Xs = Xs + Vs*(t+dt), where Vs*dt = radius of ship
|
||||
// Since direction of Vs is unknown, solve for (Vs*t) and find norm of both sides
|
||||
if ( !(wip->wi_flags[Weapon::Info_Flags::Turns]) ) {
|
||||
vec3d delta_x, laser_vel;
|
||||
vec3d delta_x, weapon_vel;
|
||||
float a,b,c, delta_x_dot_vl, delta_t;
|
||||
float root1, root2, root, earliest_time;
|
||||
|
||||
if (max_vel_weapon == max_vel_other) {
|
||||
vm_vec_sub( &delta_x, &obj_weapon->pos, &other->pos );
|
||||
weapon_vel = obj_weapon->phys_info.vel;
|
||||
float weapon_speed = vm_vec_mag(&weapon_vel);
|
||||
|
||||
if (weapon_speed == max_vel_other) {
|
||||
// this will give us NAN using the below formula, so check every frame
|
||||
current_pair->next_check_time = timestamp(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
vm_vec_sub( &delta_x, &obj_weapon->pos, &other->pos );
|
||||
laser_vel = obj_weapon->phys_info.vel;
|
||||
// vm_vec_copy_scale( &laser_vel, &weapon->orient.vec.fvec, max_vel_weapon );
|
||||
delta_t = (other->radius + 10.0f) / max_vel_other; // time to get from center to radius of other obj
|
||||
delta_x_dot_vl = vm_vec_dot( &delta_x, &laser_vel );
|
||||
delta_x_dot_vl = vm_vec_dot( &delta_x, &weapon_vel);
|
||||
|
||||
a = max_vel_weapon*max_vel_weapon - max_vel_other*max_vel_other;
|
||||
a = weapon_speed * weapon_speed - max_vel_other*max_vel_other;
|
||||
b = 2.0f * (delta_x_dot_vl - max_vel_other*max_vel_other*delta_t);
|
||||
c = vm_vec_mag_squared( &delta_x ) - max_vel_other*max_vel_other*delta_t*delta_t;
|
||||
|
||||
float discriminant = b*b - 4.0f*a*c;
|
||||
if ( discriminant < 0) {
|
||||
// never hit
|
||||
return 1;
|
||||
// neither entity passes the other
|
||||
if (c < 0) {
|
||||
// ship outpaces weapon
|
||||
current_pair->next_check_time = timestamp(0); // check next time
|
||||
return 0;
|
||||
} else {
|
||||
// weapon outpaces ship; will never hit
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
root = fl_sqrt( discriminant );
|
||||
root1 = (-b + root) / (2.0f * a) * 1000.0f; // get time in ms
|
||||
@@ -299,7 +308,7 @@ int weapon_will_never_hit( object *obj_weapon, object *other, obj_pair * current
|
||||
}
|
||||
|
||||
// standard algorithm
|
||||
if (max_vel_weapon > max_vel_other) {
|
||||
if (weapon_speed > max_vel_other) {
|
||||
// find earliest positive time
|
||||
if ( root1 > root2 ) {
|
||||
float temp = root1;
|
||||
|
||||
+60
-11
@@ -4004,12 +4004,12 @@ void sprintf(SCP_string &dest, const char *format, ...)
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
bool end_string_at_first_hash_symbol(char *src)
|
||||
bool end_string_at_first_hash_symbol(char *src, bool ignore_doubled_hash)
|
||||
{
|
||||
char *p;
|
||||
Assert(src);
|
||||
|
||||
p = get_pointer_to_first_hash_symbol(src);
|
||||
p = get_pointer_to_first_hash_symbol(src, ignore_doubled_hash);
|
||||
if (p)
|
||||
{
|
||||
while ((p != src) && (*(p-1) == ' '))
|
||||
@@ -4023,9 +4023,9 @@ bool end_string_at_first_hash_symbol(char *src)
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
bool end_string_at_first_hash_symbol(SCP_string &src)
|
||||
bool end_string_at_first_hash_symbol(SCP_string &src, bool ignore_doubled_hash)
|
||||
{
|
||||
int index = get_index_of_first_hash_symbol(src);
|
||||
int index = get_index_of_first_hash_symbol(src, ignore_doubled_hash);
|
||||
if (index >= 0)
|
||||
{
|
||||
while (index > 0 && src[index-1] == ' ')
|
||||
@@ -4039,24 +4039,73 @@ bool end_string_at_first_hash_symbol(SCP_string &src)
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
char *get_pointer_to_first_hash_symbol(char *src)
|
||||
char *get_pointer_to_first_hash_symbol(char *src, bool ignore_doubled_hash)
|
||||
{
|
||||
Assert(src);
|
||||
return strchr(src, '#');
|
||||
|
||||
if (ignore_doubled_hash)
|
||||
{
|
||||
for (auto ch = src; *ch; ++ch)
|
||||
{
|
||||
if (*ch == '#')
|
||||
{
|
||||
if (*(ch + 1) == '#')
|
||||
++ch;
|
||||
else
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
return strchr(src, '#');
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
const char *get_pointer_to_first_hash_symbol(const char *src)
|
||||
const char *get_pointer_to_first_hash_symbol(const char *src, bool ignore_doubled_hash)
|
||||
{
|
||||
Assert(src);
|
||||
return strchr(src, '#');
|
||||
|
||||
if (ignore_doubled_hash)
|
||||
{
|
||||
for (auto ch = src; *ch; ++ch)
|
||||
{
|
||||
if (*ch == '#')
|
||||
{
|
||||
if (*(ch + 1) == '#')
|
||||
++ch;
|
||||
else
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
return strchr(src, '#');
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
int get_index_of_first_hash_symbol(SCP_string &src)
|
||||
int get_index_of_first_hash_symbol(SCP_string &src, bool ignore_doubled_hash)
|
||||
{
|
||||
size_t pos = src.find('#');
|
||||
return (pos == SCP_string::npos) ? -1 : (int)pos;
|
||||
if (ignore_doubled_hash)
|
||||
{
|
||||
for (auto ch = src.begin(); ch != src.end(); ++ch)
|
||||
{
|
||||
if (*ch == '#')
|
||||
{
|
||||
if ((ch + 1) != src.end() && *(ch + 1) == '#')
|
||||
++ch;
|
||||
else
|
||||
return (int)std::distance(src.begin(), ch);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t pos = src.find('#');
|
||||
return (pos == SCP_string::npos) ? -1 : (int)pos;
|
||||
}
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
|
||||
@@ -67,11 +67,11 @@ extern int Token_found_flag;
|
||||
#define SEXP_ERROR_CHECK_MODE 2
|
||||
|
||||
// Goober5000 - this seems to be a pretty universal function
|
||||
extern bool end_string_at_first_hash_symbol(char *src);
|
||||
extern bool end_string_at_first_hash_symbol(SCP_string &src);
|
||||
extern char *get_pointer_to_first_hash_symbol(char *src);
|
||||
extern const char *get_pointer_to_first_hash_symbol(const char *src);
|
||||
extern int get_index_of_first_hash_symbol(SCP_string &src);
|
||||
extern bool end_string_at_first_hash_symbol(char *src, bool ignore_doubled_hash = false);
|
||||
extern bool end_string_at_first_hash_symbol(SCP_string &src, bool ignore_doubled_hash = false);
|
||||
extern char *get_pointer_to_first_hash_symbol(char *src, bool ignore_doubled_hash = false);
|
||||
extern const char *get_pointer_to_first_hash_symbol(const char *src, bool ignore_doubled_hash = false);
|
||||
extern int get_index_of_first_hash_symbol(SCP_string &src, bool ignore_doubled_hash = false);
|
||||
|
||||
// white space
|
||||
extern int is_white_space(char ch);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+16
-3
@@ -966,6 +966,7 @@ void pilotfile::csg_write_redalert()
|
||||
void pilotfile::csg_read_hud()
|
||||
{
|
||||
int idx;
|
||||
int strikes = 0;
|
||||
|
||||
// flags
|
||||
HUD_config.show_flags = cfread_int(cfp);
|
||||
@@ -980,16 +981,28 @@ void pilotfile::csg_read_hud()
|
||||
HUD_config.rp_flags = cfread_int(cfp);
|
||||
HUD_config.rp_dist = cfread_int(cfp);
|
||||
if (HUD_config.rp_dist < 0 || HUD_config.rp_dist >= RR_MAX_RANGES) {
|
||||
Warning(LOCATION, "Campaign file has invalid radar range %d, setting to default.\n", HUD_config.rp_dist);
|
||||
ReleaseWarning(LOCATION, "Campaign file has invalid radar range %d, setting to default.\n", HUD_config.rp_dist);
|
||||
HUD_config.rp_dist = RR_INFINITY;
|
||||
strikes++;
|
||||
}
|
||||
|
||||
// basic colors
|
||||
HUD_config.main_color = cfread_int(cfp);
|
||||
HUD_color_alpha = cfread_int(cfp);
|
||||
if (HUD_config.main_color < 0 || HUD_config.main_color >= HUD_COLOR_SIZE) {
|
||||
ReleaseWarning(LOCATION, "Campaign file has invalid main color selection %i, setting to default.\n", HUD_config.main_color);
|
||||
HUD_config.main_color = HUD_COLOR_GREEN;
|
||||
strikes++;
|
||||
}
|
||||
|
||||
if (HUD_color_alpha < HUD_COLOR_ALPHA_USER_MIN) {
|
||||
HUD_color_alpha = cfread_int(cfp);
|
||||
if (HUD_color_alpha < HUD_COLOR_ALPHA_USER_MIN || HUD_color_alpha > HUD_COLOR_ALPHA_USER_MAX) {
|
||||
ReleaseWarning(LOCATION, "Campaign file has invalid alpha color %i, setting to default.\n", HUD_color_alpha);
|
||||
HUD_color_alpha = HUD_COLOR_ALPHA_DEFAULT;
|
||||
strikes++;
|
||||
}
|
||||
|
||||
if (strikes == 3) {
|
||||
ReleaseWarning(LOCATION, "Campaign file has too many hud config errors, and is likely corrupted. Please verify and save your settings in the hud config menu.");
|
||||
}
|
||||
|
||||
hud_config_record_color(HUD_config.main_color);
|
||||
|
||||
+17
-3
@@ -172,6 +172,7 @@ void pilotfile::plr_write_info()
|
||||
|
||||
void pilotfile::plr_read_hud()
|
||||
{
|
||||
int strikes = 0;
|
||||
// flags
|
||||
HUD_config.show_flags = handler->readInt("show_flags");
|
||||
HUD_config.show_flags2 = handler->readInt("show_flags2");
|
||||
@@ -185,15 +186,28 @@ void pilotfile::plr_read_hud()
|
||||
HUD_config.rp_flags = handler->readInt("rp_flags");
|
||||
HUD_config.rp_dist = handler->readInt("rp_dist");
|
||||
if (HUD_config.rp_dist < 0 || HUD_config.rp_dist >= RR_MAX_RANGES) {
|
||||
Warning(LOCATION, "Player file has invalid radar range %d, setting to default.\n", HUD_config.rp_dist);
|
||||
ReleaseWarning(LOCATION, "Player file has invalid radar range %d, setting to default.\n", HUD_config.rp_dist);
|
||||
HUD_config.rp_dist = RR_INFINITY;
|
||||
strikes++;
|
||||
}
|
||||
|
||||
// basic colors
|
||||
HUD_config.main_color = handler->readInt("main_color");
|
||||
HUD_color_alpha = handler->readInt("color_alpha");
|
||||
if (HUD_config.main_color < 0 || HUD_config.main_color >= HUD_COLOR_SIZE) {
|
||||
ReleaseWarning(LOCATION, "Player file has invalid main color selection %i, setting to default.\n", HUD_config.main_color);
|
||||
HUD_config.main_color = HUD_COLOR_GREEN;
|
||||
strikes++;
|
||||
}
|
||||
|
||||
if (HUD_color_alpha < HUD_COLOR_ALPHA_USER_MIN) {
|
||||
HUD_color_alpha = handler->readInt("color_alpha");
|
||||
if (HUD_color_alpha < HUD_COLOR_ALPHA_USER_MIN || HUD_color_alpha > HUD_COLOR_ALPHA_USER_MAX) {
|
||||
ReleaseWarning(LOCATION, "Player file has invalid alpha color %i, setting to default.\n", HUD_color_alpha);
|
||||
HUD_color_alpha = HUD_COLOR_ALPHA_DEFAULT;
|
||||
strikes++;
|
||||
}
|
||||
|
||||
if (strikes == 3) {
|
||||
ReleaseWarning(LOCATION, "Player file has too many hud config errors, and is likely corrupted. Please verify and save your settings in the hud config menu.");
|
||||
}
|
||||
|
||||
hud_config_set_color(HUD_config.main_color);
|
||||
|
||||
@@ -899,9 +899,16 @@ ADE_FUNC(drawTargetingBrackets, l_Graphics, "object Object, [boolean draw=true,
|
||||
}
|
||||
break;
|
||||
case OBJ_WEAPON:
|
||||
Assert(Weapon_info[Weapons[targetp->instance].weapon_info_index].subtype == WP_MISSILE);
|
||||
modelnum = Weapon_info[Weapons[targetp->instance].weapon_info_index].model_num;
|
||||
bound_rc = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 );
|
||||
if (modelnum != -1)
|
||||
bound_rc = model_find_2d_bound_min(modelnum, &targetp->orient, &targetp->pos, &x1, &y1, &x2, &y2);
|
||||
else {
|
||||
vertex vtx;
|
||||
g3_rotate_vertex(&vtx, &targetp->pos);
|
||||
g3_project_vertex(&vtx);
|
||||
x1 = x2 = (int)vtx.screen.xyw.x;
|
||||
y1 = y2 = (int)vtx.screen.xyw.y;
|
||||
}
|
||||
break;
|
||||
case OBJ_ASTEROID:
|
||||
pof = Asteroids[targetp->instance].asteroid_subtype;
|
||||
|
||||
@@ -216,7 +216,7 @@ ADE_VIRTVAR(WeaponClass, l_WeaponBank, "weaponclass", "Class of weapon mounted i
|
||||
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);
|
||||
bh->sw->primary_bank_start_ammo[bh->bank] = (int)std::lround(bh->sw->primary_bank_capacity[bh->bank] / Weapon_info[weaponclass].cargo_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ ADE_VIRTVAR(WeaponClass, l_WeaponBank, "weaponclass", "Class of weapon mounted i
|
||||
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);
|
||||
bh->sw->secondary_bank_start_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]));
|
||||
|
||||
@@ -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();
|
||||
|
||||
+16
-24
@@ -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();
|
||||
@@ -4532,9 +4531,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 +5033,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 +5055,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);
|
||||
}
|
||||
@@ -10667,10 +10657,12 @@ void change_ship_type(int n, int ship_type, int by_sexp)
|
||||
if ( by_sexp ) {
|
||||
if (sip_orig->num_primary_banks > sip->num_primary_banks) {
|
||||
sp->weapons.current_primary_bank = 0;
|
||||
sp->weapons.previous_primary_bank = 0;
|
||||
}
|
||||
|
||||
if (sip_orig->num_secondary_banks > sip->num_secondary_banks) {
|
||||
sp->weapons.current_secondary_bank = 0;
|
||||
sp->weapons.previous_secondary_bank = 0;
|
||||
}
|
||||
|
||||
// While we're at it, let's copy over the ETS settings too
|
||||
@@ -13338,7 +13330,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 +13338,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
|
||||
|
||||
@@ -285,7 +285,7 @@ void shipfx_blow_off_subsystem(object *ship_objp, ship *ship_p, ship_subsys *sub
|
||||
shipfx_blow_up_model(ship_objp, psub->subobj_num, 50, &subobj_pos );
|
||||
|
||||
// create live debris objects, if any
|
||||
// TODO: some MULITPLAYER implcations here!!
|
||||
// TODO: some MULTIPLAYER implcations here!!
|
||||
shipfx_subsystem_maybe_create_live_debris(ship_objp, ship_p, subsys, exp_center, 1.0f);
|
||||
|
||||
int fireball_type = fireball_ship_explosion_type(&Ship_info[ship_p->ship_info_index]);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -918,6 +918,32 @@ void stars_post_level_init()
|
||||
// FRED doesn't do normal page_in stuff so we need to load up the bitmaps here instead
|
||||
if (Fred_running) {
|
||||
stars_load_all_bitmaps();
|
||||
|
||||
// see whether we are missing any suns or bitmaps
|
||||
for (i = 0; i < (int)Backgrounds.size(); ++i) {
|
||||
SCP_string failed_suns;
|
||||
for (auto &sun : Backgrounds[i].suns) {
|
||||
if (stars_find_sun(sun.filename) < 0) {
|
||||
failed_suns += sun.filename;
|
||||
failed_suns += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
SCP_string failed_bitmaps;
|
||||
for (auto &bitmap : Backgrounds[i].bitmaps) {
|
||||
if (stars_find_bitmap(bitmap.filename) < 0) {
|
||||
failed_bitmaps += bitmap.filename;
|
||||
failed_bitmaps += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!failed_suns.empty()) {
|
||||
Warning(LOCATION, "In Background %d, failed to load the following suns:\n%s", (i + 1), failed_suns.c_str());
|
||||
}
|
||||
if (!failed_bitmaps.empty()) {
|
||||
Warning(LOCATION, "In Background %d, failed to load the following bitmaps:\n%s", (i + 1), failed_bitmaps.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
starfield_generate_bitmap_buffers();
|
||||
@@ -2279,7 +2305,9 @@ int stars_add_sun_entry(starfield_list_entry *sun_ptr)
|
||||
idx = stars_find_sun(sun_ptr->filename);
|
||||
|
||||
if (idx == -1) {
|
||||
Warning(LOCATION, "Trying to add a sun '%s' that does not exist in stars.tbl!", sun_ptr->filename);
|
||||
if (!Fred_running) {
|
||||
Warning(LOCATION, "Trying to add a sun '%s' that does not exist in stars.tbl!", sun_ptr->filename);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -2373,7 +2401,9 @@ int stars_add_bitmap_entry(starfield_list_entry *sle)
|
||||
idx = stars_find_bitmap(sle->filename);
|
||||
|
||||
if (idx == -1) {
|
||||
Warning(LOCATION, "Trying to add a bitmap '%s' that does not exist in stars.tbl!", sle->filename);
|
||||
if (!Fred_running) {
|
||||
Warning(LOCATION, "Trying to add a bitmap '%s' that does not exist in stars.tbl!", sle->filename);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -2676,7 +2706,7 @@ int stars_get_first_valid_background()
|
||||
if (stars_find_bitmap(background->bitmaps[j].filename) < 0)
|
||||
{
|
||||
mprintf(("Failed to load bitmap %s for background %d, falling back to background %d\n",
|
||||
background->suns[j].filename, i + 1, i + 2));
|
||||
background->bitmaps[j].filename, i + 1, i + 2));
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Loaded 100 of 169 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user