mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
Compare commits
156
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1111dcf0a8 | ||
|
|
a94550ee1a | ||
|
|
3dbe27e772 | ||
|
|
8f11d2e11c | ||
|
|
e4036b9e5a | ||
|
|
c86e94a4e7 | ||
|
|
a6475f59f0 | ||
|
|
69ebdd0417 | ||
|
|
9444b9ff9f | ||
|
|
8d3eeec238 | ||
|
|
da0ff33fcb | ||
|
|
764b4b8422 | ||
|
|
a5d22589ec | ||
|
|
6a6940e0f0 | ||
|
|
2dfd9936d8 | ||
|
|
fb0847f4b5 | ||
|
|
7b959b09d2 | ||
|
|
f4b713c56c | ||
|
|
d32d3f55d1 | ||
|
|
1ebb72ee88 | ||
|
|
a988d0e972 | ||
|
|
8aa2f3b099 | ||
|
|
8365f0abb7 | ||
|
|
4afe739cbb | ||
|
|
2c469e0c88 | ||
|
|
822e61665d | ||
|
|
5f5616c0fa | ||
|
|
b38f6aa014 | ||
|
|
d88e6f5a88 | ||
|
|
760b601a95 | ||
|
|
0bf116b0cc | ||
|
|
2180296f9c | ||
|
|
b84fe72778 | ||
|
|
c5e460a90d | ||
|
|
271d3f97bd | ||
|
|
e70d6a89ee | ||
|
|
d1a8e66fce | ||
|
|
964f8f5ef1 | ||
|
|
8ebe012e12 | ||
|
|
92f142452a | ||
|
|
cfb80cfd16 | ||
|
|
39313ed8d7 | ||
|
|
1da2c03cef | ||
|
|
f9dbe4941b | ||
|
|
46319f1b4e | ||
|
|
8653d824cd | ||
|
|
3e7e7cde00 | ||
|
|
f63667b29e | ||
|
|
b42fc2122a | ||
|
|
3ca4ba16fe | ||
|
|
36c14e21e8 | ||
|
|
ce1ea75714 | ||
|
|
30373e9138 | ||
|
|
9e460332a6 | ||
|
|
473e2588bf | ||
|
|
6350110b21 | ||
|
|
0c92134de5 | ||
|
|
681dd9b871 | ||
|
|
213422076e | ||
|
|
41aff9d094 | ||
|
|
337ca94b53 | ||
|
|
13f5037b0c | ||
|
|
221d96f5d6 | ||
|
|
bd6441004b | ||
|
|
14b6e86e5d | ||
|
|
4824428971 | ||
|
|
6af8d4ccbd | ||
|
|
d2d730138f | ||
|
|
7d67d16094 | ||
|
|
3f4eb05f3e | ||
|
|
49f8660f7a | ||
|
|
2833ae150f | ||
|
|
8470548376 | ||
|
|
ea6bd1d9a4 | ||
|
|
18c0314652 | ||
|
|
98165dbce5 | ||
|
|
60bf6dec2c | ||
|
|
252ab4e282 | ||
|
|
bd93c8e661 | ||
|
|
0e4056f795 | ||
|
|
8f877d3826 | ||
|
|
6025ab8318 | ||
|
|
679cbe2526 | ||
|
|
fa0140fea7 | ||
|
|
5343503385 | ||
|
|
edd2cb89c7 | ||
|
|
6a2327088f | ||
|
|
f798c5b916 | ||
|
|
76115122a0 | ||
|
|
b4cb602e31 | ||
|
|
d26b5310e7 | ||
|
|
cdb65c154c | ||
|
|
fd0b612c64 | ||
|
|
07d83d473c | ||
|
|
1662a3c317 | ||
|
|
c70e4a0e7e | ||
|
|
d82479fb14 | ||
|
|
be90059b65 | ||
|
|
04abbf55ed | ||
|
|
1bc925ee9b | ||
|
|
d558c2d632 | ||
|
|
1c799653ce | ||
|
|
35af3a8b73 | ||
|
|
8586a319b0 | ||
|
|
15748adfda | ||
|
|
6b0f73e427 | ||
|
|
1d744d347c | ||
|
|
9332311b57 | ||
|
|
47e9c01ce6 | ||
|
|
5f176173bd | ||
|
|
8d6538d7a9 | ||
|
|
cc9e0a1096 | ||
|
|
223e78926f | ||
|
|
6679b623e4 | ||
|
|
8aa2d0e212 | ||
|
|
1dc449ffa6 | ||
|
|
6369cdec7c | ||
|
|
31fe15c4f4 | ||
|
|
dd384e7f1d | ||
|
|
c8fa5803b9 | ||
|
|
b57a608177 | ||
|
|
f9f7a23b4d | ||
|
|
7e88feff16 | ||
|
|
c6bf5e927b | ||
|
|
ad1ede7b74 | ||
|
|
3c812915e4 | ||
|
|
62963d23ec | ||
|
|
dc71bb47d7 | ||
|
|
b79a3385a1 | ||
|
|
dfac2e98e9 | ||
|
|
37f2c8249d | ||
|
|
5fa5a199bf | ||
|
|
c00a2897f4 | ||
|
|
ad4b1ca965 | ||
|
|
f1f784f237 | ||
|
|
2c307ef5d3 | ||
|
|
6f24d8d079 | ||
|
|
9fc94ab873 | ||
|
|
728ee2921f | ||
|
|
caea0f56ed | ||
|
|
94c9e1b494 | ||
|
|
7dd1788aee | ||
|
|
77934c7215 | ||
|
|
4a7c69082e | ||
|
|
74c4075aa6 | ||
|
|
008d66c706 | ||
|
|
1c385901cb | ||
|
|
5b9b06ab84 | ||
|
|
f8da2cdc83 | ||
|
|
b790d6a388 | ||
|
|
a7ba3fa373 | ||
|
|
6d7c786a38 | ||
|
|
49d5356043 | ||
|
|
572565c7bc | ||
|
|
c090ee5405 | ||
|
|
5597b56139 |
No files matched your search
@@ -0,0 +1,182 @@
|
||||
version: 2
|
||||
|
||||
references:
|
||||
container_config: &container_config
|
||||
docker:
|
||||
- image: "debian:stretch"
|
||||
working_directory: /root/build
|
||||
|
||||
restore_repo: &restore_repo
|
||||
restore_cache:
|
||||
keys:
|
||||
- source-repo-{{ .Branch }}-{{ .Revision }}
|
||||
- source-repo-{{ .Branch }}
|
||||
- source-repo
|
||||
|
||||
cmake_configure: &cmake_configure
|
||||
run:
|
||||
name: Creating Build Files
|
||||
command: |
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
CXXFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
CFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
|
||||
if [[ "$CC" =~ ^clang.*$ ]]; then
|
||||
CXXFLAGS="$CXXFLAGS -stdlib=libc++"
|
||||
fi
|
||||
|
||||
eval $CC --version
|
||||
|
||||
export CXXFLAGS
|
||||
export CFLAGS
|
||||
CMAKE="cmake -G Ninja -DFSO_FATAL_WARNINGS=ON $CMAKE_OPTIONS"
|
||||
eval $CMAKE -DCMAKE_BUILD_TYPE=$CONFIGURATION -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON ..
|
||||
|
||||
compile: &compile
|
||||
run:
|
||||
name: Compiling Source code
|
||||
command: |
|
||||
cd build
|
||||
ninja -k 20 all
|
||||
|
||||
run_tests: &run_tests
|
||||
run:
|
||||
name: Run unit tests
|
||||
command: |
|
||||
./build/bin/unittests --gtest_shuffle
|
||||
|
||||
compile_steps: &compile_steps
|
||||
steps:
|
||||
- *restore_repo
|
||||
- *cmake_configure
|
||||
- *compile
|
||||
- *run_tests
|
||||
jobs:
|
||||
checkout_code:
|
||||
<<: *container_config
|
||||
steps:
|
||||
- run:
|
||||
name: Installing Git
|
||||
command: 'apt-get update && apt-get -yq install git'
|
||||
- *restore_repo
|
||||
- checkout
|
||||
- run:
|
||||
name: Updating submodules
|
||||
command: 'git submodule update --init --recursive'
|
||||
- save_cache:
|
||||
key: source-repo-{{ .Branch }}-{{ .Revision }}
|
||||
paths:
|
||||
- .
|
||||
|
||||
build_gcc_lowest_debug:
|
||||
docker:
|
||||
- image: asarium/fso_compile:gcc-lowest
|
||||
working_directory: /root/build
|
||||
environment:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURATION: Debug
|
||||
<<: *compile_steps
|
||||
|
||||
build_gcc_lowest_release:
|
||||
docker:
|
||||
- image: asarium/fso_compile:gcc-lowest
|
||||
working_directory: /root/build
|
||||
environment:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURATION: Release
|
||||
<<: *compile_steps
|
||||
|
||||
build_gcc_latest_debug:
|
||||
docker:
|
||||
- image: asarium/fso_compile:gcc-latest
|
||||
working_directory: /root/build
|
||||
environment:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURATION: Debug
|
||||
<<: *compile_steps
|
||||
|
||||
build_gcc_latest_release:
|
||||
docker:
|
||||
- image: asarium/fso_compile:gcc-latest
|
||||
working_directory: /root/build
|
||||
environment:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CONFIGURATION: Release
|
||||
<<: *compile_steps
|
||||
|
||||
build_clang_latest_debug:
|
||||
docker:
|
||||
- image: asarium/fso_compile:clang-latest
|
||||
working_directory: /root/build
|
||||
environment:
|
||||
CC: clang-8
|
||||
CXX: clang++-8
|
||||
CONFIGURATION: Debug
|
||||
<<: *compile_steps
|
||||
|
||||
build_clang_latest_release:
|
||||
docker:
|
||||
- image: asarium/fso_compile:clang-latest
|
||||
working_directory: /root/build
|
||||
environment:
|
||||
CC: clang-8
|
||||
CXX: clang++-8
|
||||
CONFIGURATION: Release
|
||||
<<: *compile_steps
|
||||
|
||||
build_clang_lowest_debug:
|
||||
docker:
|
||||
- image: asarium/fso_compile:clang-lowest
|
||||
working_directory: /root/build
|
||||
environment:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURATION: Debug
|
||||
<<: *compile_steps
|
||||
|
||||
build_clang_lowest_release:
|
||||
docker:
|
||||
- image: asarium/fso_compile:clang-lowest
|
||||
working_directory: /root/build
|
||||
environment:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CONFIGURATION: Release
|
||||
<<: *compile_steps
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
build_test:
|
||||
jobs:
|
||||
- checkout_code
|
||||
- build_gcc_lowest_debug:
|
||||
requires:
|
||||
- checkout_code
|
||||
- build_gcc_lowest_release:
|
||||
requires:
|
||||
- checkout_code
|
||||
- build_gcc_latest_debug:
|
||||
requires:
|
||||
- checkout_code
|
||||
- build_gcc_latest_release:
|
||||
requires:
|
||||
- checkout_code
|
||||
- build_clang_latest_debug:
|
||||
requires:
|
||||
- checkout_code
|
||||
- build_clang_latest_release:
|
||||
requires:
|
||||
- checkout_code
|
||||
- build_clang_lowest_debug:
|
||||
requires:
|
||||
- checkout_code
|
||||
- build_clang_lowest_release:
|
||||
requires:
|
||||
- checkout_code
|
||||
@@ -5,6 +5,7 @@ cache:
|
||||
directories:
|
||||
- $HOME/cmake-3.8
|
||||
- $HOME/.ccache
|
||||
- $HOME/curl_cache
|
||||
addons:
|
||||
apt:
|
||||
packages: &global_deps
|
||||
|
||||
+53
-18
@@ -10,27 +10,62 @@ if [[ ! "$(curl -V)" == *"sftp"* ]]; then
|
||||
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
|
||||
# Since the Travis CI people are not capable of enabling SSH/SCP support for their libcurl we need to build that manually...
|
||||
# These commands are based on the tutorial from here: http://zeroset.mnim.org/2013/03/14/sftp-support-for-curl-in-ubuntu-12-10-quantal-quetzal-and-later/
|
||||
mkdir /tmp/curl
|
||||
cd /tmp/curl
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential debhelper libssh2-1-dev libgnutls-dev libidn11-dev libkrb5-dev libldap2-dev libnss3-dev librtmp-dev libtool openssh-server quilt
|
||||
apt-get source curl
|
||||
sudo apt-get build-dep curl
|
||||
if [ -f ~/curl_cache/curl*.deb ]; then
|
||||
cd ~/curl_cache
|
||||
sudo dpkg -i libcurl3*.deb
|
||||
sudo dpkg -i curl*.deb
|
||||
else
|
||||
# Since the Travis CI people are not capable of enabling SSH/SCP support for their libcurl we need to build that manually...
|
||||
# These commands are based on the tutorial from here: http://zeroset.mnim.org/2013/03/14/sftp-support-for-curl-in-ubuntu-12-10-quantal-quetzal-and-later/
|
||||
mkdir /tmp/curl
|
||||
cd /tmp/curl
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential debhelper libssh2-1-dev libgnutls-dev libidn11-dev libkrb5-dev libldap2-dev libnss3-dev librtmp-dev libtool openssh-server quilt
|
||||
apt-get source curl
|
||||
sudo apt-get build-dep curl
|
||||
|
||||
cd curl-*
|
||||
export DEB_BUILD_OPTIONS="nocheck"
|
||||
sudo dpkg-buildpackage -uc -us -j"$(grep -c ^processor /proc/cpuinfo)"
|
||||
cd curl-*
|
||||
export DEB_BUILD_OPTIONS="nocheck"
|
||||
sudo dpkg-buildpackage -uc -us -j"$(grep -c ^processor /proc/cpuinfo)"
|
||||
|
||||
cd ..
|
||||
dpkg -l | grep curl
|
||||
ls -al
|
||||
sudo dpkg -i libcurl3*.deb
|
||||
sudo dpkg -i curl*.deb
|
||||
cd ..
|
||||
dpkg -l | grep curl
|
||||
ls -al
|
||||
|
||||
if [ ! -d ~/curl_cache ]; then
|
||||
mkdir ~/curl_cache
|
||||
fi
|
||||
|
||||
cp libcurl3*.deb curl*.deb ~/curl_cache
|
||||
|
||||
sudo dpkg -i libcurl3*.deb
|
||||
sudo dpkg -i curl*.deb
|
||||
fi
|
||||
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
brew install curl --with-libssh2
|
||||
export PATH="/usr/local/opt/curl/bin:$PATH"
|
||||
brew install libssh2
|
||||
|
||||
if [ -d ~/curl_cache/curl ]; then
|
||||
cd ~/curl_cache/curl
|
||||
sudo make install
|
||||
else
|
||||
mkdir /tmp/curl
|
||||
cd /tmp/curl
|
||||
|
||||
curl -LO "https://curl.haxx.se/download/curl-7.64.0.tar.bz2"
|
||||
tar -xjf curl-7.64.0.tar.bz2
|
||||
|
||||
cd curl-7.64.0
|
||||
./configure --disable-debug --disable-dependency-tracking --disable-silent-rules --with-libssh2 --with-darwinssl --without-ca-bundle --without-ca-path
|
||||
make -j"$(sysctl hw.logicalcpu | cut -d " " -f 2)"
|
||||
|
||||
if [ ! -d ~/curl_cache ]; then
|
||||
mkdir ~/curl_cache
|
||||
fi
|
||||
|
||||
cp -a . ~/curl_cache/curl
|
||||
sudo make install
|
||||
fi
|
||||
export PATH=/usr/local/bin:$PATH
|
||||
fi
|
||||
|
||||
# After
|
||||
|
||||
+17
-2
@@ -58,6 +58,8 @@
|
||||
#include "physics/physics.h"
|
||||
#include "playerman/player.h"
|
||||
#include "render/3d.h"
|
||||
#include "scripting/api/objs/wing.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "ship/afterburner.h"
|
||||
#include "ship/awacs.h"
|
||||
#include "ship/ship.h"
|
||||
@@ -1345,6 +1347,7 @@ void ai_object_init(object * obj, int ai_index)
|
||||
aip->wing = -1; // Member of what wing? -1 means none.
|
||||
aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
|
||||
aip->behavior = AIM_NONE;
|
||||
aip->mode = aip->behavior;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4586,6 +4589,11 @@ void ai_waypoints()
|
||||
ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
|
||||
mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, aip->wp_list->get_name(), -1 );
|
||||
}
|
||||
// adds scripting hook for 'On Waypoints Done' --wookieejedi
|
||||
Script_system.SetHookObject("Ship", &Objects[Ships[Pl_objp->instance].objnum]);
|
||||
Script_system.SetHookVar("Wing", 'o', scripting::api::l_Wing.Set(Ships[Pl_objp->instance].wingnum));
|
||||
Script_system.RunCondition(CHA_ONWAYPOINTSDONE);
|
||||
Script_system.RemHookVars(2, "Ship", "Wing");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5346,7 +5354,7 @@ int ai_select_primary_weapon(object *objp, object *other_objp, Weapon::Info_Flag
|
||||
if (Weapon_info[swp->primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Capital_plus])
|
||||
{
|
||||
swp->current_primary_bank = i;
|
||||
nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
|
||||
nprintf(("AI", "%i: Ship %s selecting weapon %s (capital+) vs target %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name, Ships[other_objp->instance].ship_name));
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -5384,6 +5392,7 @@ int ai_select_primary_weapon(object *objp, object *other_objp, Weapon::Info_Flag
|
||||
i_hullfactor_prev_bank = 0; // Just switch to the first one
|
||||
}
|
||||
swp->current_primary_bank = i_hullfactor_prev_bank; // Select the best weapon
|
||||
nprintf(("AI", "%i: Ship %s selecting weapon %s (no shields) vs target %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i_hullfactor_prev_bank]].name, (other_objp->type == OBJ_SHIP ? Ships[other_objp->instance].ship_name : "non-ship") ));
|
||||
return i_hullfactor_prev_bank; // Return
|
||||
}
|
||||
|
||||
@@ -5398,6 +5407,7 @@ int ai_select_primary_weapon(object *objp, object *other_objp, Weapon::Info_Flag
|
||||
|
||||
if ((Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags[Weapon::Info_Flags::Pierce_shields]) && !(Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags[Weapon::Info_Flags::Capital_plus]))
|
||||
{
|
||||
nprintf(("AI", "%i: Ship %s selecting weapon %s (>10%% shields)\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
|
||||
return swp->current_primary_bank;
|
||||
}
|
||||
}
|
||||
@@ -5412,6 +5422,7 @@ int ai_select_primary_weapon(object *objp, object *other_objp, Weapon::Info_Flag
|
||||
if ((Weapon_info[weapon_info_index].wi_flags[Weapon::Info_Flags::Pierce_shields]) && !(Weapon_info[swp->primary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::Capital_plus]))
|
||||
{
|
||||
swp->current_primary_bank = i;
|
||||
nprintf(("AI", "%i: Ship %s selecting weapon %s (>10%% shields)\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -5446,6 +5457,7 @@ int ai_select_primary_weapon(object *objp, object *other_objp, Weapon::Info_Flag
|
||||
i_hullfactor_prev_bank = 0; // Just switch to the first one
|
||||
}
|
||||
swp->current_primary_bank = i_hullfactor_prev_bank; // Select the best weapon
|
||||
nprintf(("AI", "%i: Ship %s selecting weapon %s (<50%% shields)\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i_hullfactor_prev_bank]].name));
|
||||
return i_hullfactor_prev_bank; // Return
|
||||
}
|
||||
else
|
||||
@@ -5475,6 +5487,7 @@ int ai_select_primary_weapon(object *objp, object *other_objp, Weapon::Info_Flag
|
||||
i_hullfactor_prev_bank = 0; // Just switch to the first one
|
||||
}
|
||||
swp->current_primary_bank = i_hullfactor_prev_bank; // Select the best weapon
|
||||
nprintf(("AI", "%i: Ship %s selecting weapon %s (>50%% shields)\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i_hullfactor_prev_bank]].name));
|
||||
return i_hullfactor_prev_bank; // Return
|
||||
}
|
||||
}
|
||||
@@ -5502,7 +5515,9 @@ void set_primary_weapon_linkage(object *objp)
|
||||
// If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
|
||||
if ( Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].is_big_ship() ) {
|
||||
if ( aip->targeted_subsys == NULL ) {
|
||||
shipp->flags.set(Ship::Ship_Flags::Primary_linked);
|
||||
if (!sip->flags[Ship::Info_Flags::No_primary_linking] ) {
|
||||
shipp->flags.set(Ship::Ship_Flags::Primary_linked);
|
||||
}
|
||||
shipp->flags.set(Ship::Ship_Flags::Secondary_dual_fire);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -733,6 +733,12 @@ void ai_add_goal_sub_scripting(int type, int mode, int submode, int priority, ch
|
||||
aigp->target_signature = Objects[Weapons[submode].objnum].signature;
|
||||
}
|
||||
|
||||
if ( mode == AI_GOAL_WAYPOINTS_ONCE || mode == AI_GOAL_WAYPOINTS ) {
|
||||
// should be the same as setting via sexp, see ai_add_goal_sub_sexp
|
||||
// set to null so the AI code can setup the new waypoint list, see ai_mission_goal_achievable
|
||||
aigp->wp_list = nullptr;
|
||||
}
|
||||
|
||||
if ( target_name != NULL )
|
||||
aigp->target_name = ai_get_goal_target_name( target_name, &aigp->target_name_index );
|
||||
else
|
||||
|
||||
@@ -1143,8 +1143,6 @@ int asteroid_check_collision(object *pasteroid, object *other_obj, vec3d *hitpos
|
||||
|
||||
// SET PHYSICS PARAMETERS
|
||||
// already have (hitpos - heavy) and light_cm_pos
|
||||
// get heavy cm pos - already have light_cm_pos
|
||||
asteroid_hit_info->heavy_collision_cm_pos = zero;
|
||||
|
||||
// get r_heavy and r_light
|
||||
asteroid_hit_info->r_heavy = asteroid_hit_info->hit_pos;
|
||||
|
||||
+19
-1
@@ -7,9 +7,10 @@
|
||||
#include "math/vecmat.h"
|
||||
#include "mod_table/mod_table.h"
|
||||
#include "model/model.h" //polymodel, model_get
|
||||
#include "options/Option.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "playerman/player.h" //player_get_padlock_orient
|
||||
#include "ship/ship.h" //compute_slew_matrix
|
||||
#include "ship/ship.h" //compute_slew_matrix
|
||||
|
||||
//*************************IMPORTANT GLOBALS*************************
|
||||
float VIEWER_ZOOM_DEFAULT = 0.75f; // Default viewer zoom, 0.625 as per multi-lateral agreement on 3/24/97
|
||||
@@ -24,6 +25,23 @@ SCP_vector<camera*> Cameras;
|
||||
camid Current_camera;
|
||||
camid Main_camera;
|
||||
|
||||
static SCP_string fov_display(float val)
|
||||
{
|
||||
auto degrees = fl_degrees(val);
|
||||
SCP_string out;
|
||||
sprintf(out, u8"%.1f\u00B0", degrees);
|
||||
return out;
|
||||
}
|
||||
auto FovOption = options::OptionBuilder<float>("Graphics.FOV", "Field Of View", "The vertical field of view.")
|
||||
.category("Graphics")
|
||||
.range(0.436332f, 1.5708f)
|
||||
.bind_to(&VIEWER_ZOOM_DEFAULT)
|
||||
.display(fov_display)
|
||||
.default_val(0.75f)
|
||||
.level(options::ExpertLevel::Advanced)
|
||||
.importance(60)
|
||||
.finish();
|
||||
|
||||
//*************************CLASS: camera*************************
|
||||
//This is where the camera class beings! :D
|
||||
camera::camera(const char *in_name, int in_signature)
|
||||
|
||||
+116
-263
@@ -38,9 +38,6 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
char Cfile_root_dir[CFILE_ROOT_DIRECTORY_LEN] = "";
|
||||
char Cfile_user_dir[CFILE_ROOT_DIRECTORY_LEN] = "";
|
||||
|
||||
// During cfile_init, verify that Pathtypes[n].index == n for each item
|
||||
// Each path must have a valid parent that can be tracable all the way back to the root
|
||||
// so that we can create directories when we need to.
|
||||
@@ -101,10 +98,7 @@ static int Cfile_stack_pos = 0;
|
||||
|
||||
static char Cfile_stack[CFILE_STACK_MAX][CFILE_ROOT_DIRECTORY_LEN];
|
||||
|
||||
Cfile_block Cfile_block_list[MAX_CFILE_BLOCKS];
|
||||
static CFILE Cfile_list[MAX_CFILE_BLOCKS];
|
||||
|
||||
static const char *Cfile_cdrom_dir = NULL;
|
||||
std::array<CFILE, MAX_CFILE_BLOCKS> Cfile_block_list;
|
||||
|
||||
//
|
||||
// Function prototypes for internally-called functions
|
||||
@@ -148,47 +142,15 @@ void cfile_close()
|
||||
#define MIN_NUM_PATH_COMPONENTS 3 /* Drive + directory + file */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Determine if the given path is in the root directory
|
||||
*
|
||||
* @param exe_path Path to executable
|
||||
*
|
||||
* @return true if root directory, false if not
|
||||
*/
|
||||
static bool cfile_in_root_dir(const char *exe_path)
|
||||
{
|
||||
int new_token;
|
||||
int token_count = 0;
|
||||
const char *p = exe_path;
|
||||
|
||||
Assert(exe_path != NULL);
|
||||
|
||||
do {
|
||||
new_token = 0;
|
||||
while (*p == DIR_SEPARATOR_CHAR) {
|
||||
p++;
|
||||
}
|
||||
|
||||
while ((*p != '\0') && (*p != DIR_SEPARATOR_CHAR)) {
|
||||
new_token = 1;
|
||||
p++;
|
||||
}
|
||||
token_count += new_token;
|
||||
} while (*p != '\0');
|
||||
|
||||
return (token_count < MIN_NUM_PATH_COMPONENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the cfile system. Called once at application start.
|
||||
*
|
||||
* @param exe_dir Path to a file (not a directory)
|
||||
* @param cdrom_dir Path to a CD drive mount point (may be NULL)
|
||||
*
|
||||
* @return 0 On success
|
||||
* @return 1 On error
|
||||
*/
|
||||
int cfile_init(const char *exe_dir, const char *cdrom_dir)
|
||||
int cfile_init(const SCP_vector<cfile::ModRoot>& modRoots)
|
||||
{
|
||||
// initialize encryption
|
||||
encrypt_init();
|
||||
@@ -197,47 +159,16 @@ int cfile_init(const char *exe_dir, const char *cdrom_dir)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char buf[CFILE_ROOT_DIRECTORY_LEN];
|
||||
|
||||
strncpy(buf, exe_dir, CFILE_ROOT_DIRECTORY_LEN - 1);
|
||||
buf[CFILE_ROOT_DIRECTORY_LEN - 1] = '\0';
|
||||
size_t i = strlen(buf);
|
||||
|
||||
// are we in a root directory?
|
||||
if(cfile_in_root_dir(buf)){
|
||||
os::dialogs::Message(os::dialogs::MESSAGEBOX_ERROR, "FreeSpace2/Fred2 cannot be run from a drive root directory!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// This needs to be set here because cf_build_secondary_filelist assumes it to be true
|
||||
cfile_inited = 1;
|
||||
|
||||
/*
|
||||
* Determine the executable's directory. Note that DIR_SEPARATOR_CHAR
|
||||
* is guaranteed to be found in the string else cfile_in_root_dir()
|
||||
* would have failed.
|
||||
*/
|
||||
|
||||
char *p;
|
||||
|
||||
p = strrchr(buf, DIR_SEPARATOR_CHAR);
|
||||
*p = '\0';
|
||||
|
||||
cfile_chdir(buf);
|
||||
|
||||
// set root directory
|
||||
strcpy_s(Cfile_root_dir, buf);
|
||||
strcpy_s(Cfile_user_dir, os_get_config_path().c_str());
|
||||
|
||||
for (i = 0; i < MAX_CFILE_BLOCKS; i++) {
|
||||
Cfile_block_list[i].type = CFILE_BLOCK_UNUSED;
|
||||
}
|
||||
// Initialize the block list with proper data
|
||||
Cfile_block_list.fill({});
|
||||
|
||||
// 32 bit CRC table init
|
||||
cf_chksum_long_init();
|
||||
|
||||
Cfile_cdrom_dir = cdrom_dir;
|
||||
cf_build_secondary_filelist(Cfile_cdrom_dir);
|
||||
cf_build_secondary_filelist(modRoots);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -865,14 +796,14 @@ CFILE *ctmpfile()
|
||||
static int cfget_cfile_block()
|
||||
{
|
||||
int i;
|
||||
Cfile_block *cb;
|
||||
CFILE* cfile;
|
||||
|
||||
for ( i = 0; i < MAX_CFILE_BLOCKS; i++ ) {
|
||||
cb = &Cfile_block_list[i];
|
||||
if ( cb->type == CFILE_BLOCK_UNUSED ) {
|
||||
cb->data = NULL;
|
||||
cb->fp = NULL;
|
||||
cb->type = CFILE_BLOCK_USED;
|
||||
cfile = &Cfile_block_list[i];
|
||||
if (cfile->type == CFILE_BLOCK_UNUSED) {
|
||||
cfile->data = nullptr;
|
||||
cfile->fp = nullptr;
|
||||
cfile->type = CFILE_BLOCK_USED;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -900,39 +831,36 @@ int cfclose( CFILE * cfile )
|
||||
int result;
|
||||
|
||||
Assert(cfile != NULL);
|
||||
Cfile_block *cb;
|
||||
Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
result = 0;
|
||||
if ( cb->data && cb->mem_mapped ) {
|
||||
if ( cfile->data && cfile->mem_mapped ) {
|
||||
// close memory mapped file
|
||||
#if defined _WIN32
|
||||
result = UnmapViewOfFile((void*)cb->data);
|
||||
result = UnmapViewOfFile((void*)cfile->data);
|
||||
Assert(result);
|
||||
result = CloseHandle(cb->hInFile);
|
||||
result = CloseHandle(cfile->hInFile);
|
||||
Assert(result); // Ensure file handle is closed properly
|
||||
result = CloseHandle(cb->hMapFile);
|
||||
result = CloseHandle(cfile->hMapFile);
|
||||
Assert(result); // Ensure file handle is closed properly
|
||||
result = 0;
|
||||
#elif defined SCP_UNIX
|
||||
// FIXME: result is wrong after munmap() but it is successful
|
||||
//result = munmap(cb->data, cb->data_length);
|
||||
//result = munmap(cfile->data, cfile->data_length);
|
||||
//Assert(result);
|
||||
// This const_cast is safe since the pointer returned by mmap was also non-const
|
||||
munmap(const_cast<void*>(cb->data), cb->data_length);
|
||||
if ( cb->fp != NULL)
|
||||
result = fclose(cb->fp);
|
||||
munmap(const_cast<void*>(cfile->data), cfile->data_length);
|
||||
if ( cfile->fp != nullptr)
|
||||
result = fclose(cfile->fp);
|
||||
#endif
|
||||
|
||||
} else if ( cb->fp != NULL ) {
|
||||
Assert(cb->fp != NULL);
|
||||
result = fclose(cb->fp);
|
||||
} else if ( cfile->fp != nullptr ) {
|
||||
Assert(cfile->fp != nullptr);
|
||||
result = fclose(cfile->fp);
|
||||
} else {
|
||||
// VP do nothing
|
||||
}
|
||||
|
||||
cb->type = CFILE_BLOCK_UNUSED;
|
||||
cfile->type = CFILE_BLOCK_UNUSED;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -942,13 +870,8 @@ int cf_is_valid(CFILE *cfile)
|
||||
if(cfile == NULL)
|
||||
return 0;
|
||||
|
||||
//Does it have a valid ID?
|
||||
if(cfile->id < 0 || cfile->id >= MAX_CFILE_BLOCKS)
|
||||
return 0;
|
||||
|
||||
//Is it used?
|
||||
Cfile_block *cb = &Cfile_block_list[cfile->id];
|
||||
if(cb->type != CFILE_BLOCK_USED && (cb->fp != NULL || cb->data != NULL))
|
||||
if (cfile->type != CFILE_BLOCK_USED && (cfile->fp != nullptr || cfile->data != nullptr))
|
||||
return 0;
|
||||
|
||||
//It's good, as near as we can tell.
|
||||
@@ -973,20 +896,15 @@ static CFILE *cf_open_fill_cfblock(const char* source, int line, FILE *fp, int t
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
} else {
|
||||
CFILE *cfp;
|
||||
Cfile_block *cfbp;
|
||||
cfbp = &Cfile_block_list[cfile_block_index];
|
||||
cfp = &Cfile_list[cfile_block_index];
|
||||
cfp->id = cfile_block_index;
|
||||
cfp->version = 0;
|
||||
cfbp->data = NULL;
|
||||
cfbp->mem_mapped = false;
|
||||
cfbp->fp = fp;
|
||||
cfbp->dir_type = type;
|
||||
cfbp->max_read_len = 0;
|
||||
CFILE *cfp = &Cfile_block_list[cfile_block_index];
|
||||
cfp->data = nullptr;
|
||||
cfp->mem_mapped = false;
|
||||
cfp->fp = fp;
|
||||
cfp->dir_type = type;
|
||||
cfp->max_read_len = 0;
|
||||
|
||||
cfbp->source_file = source;
|
||||
cfbp->line_num = line;
|
||||
cfp->source_file = source;
|
||||
cfp->line_num = line;
|
||||
|
||||
int pos = ftell(fp);
|
||||
if(pos == -1L)
|
||||
@@ -1014,21 +932,16 @@ static CFILE *cf_open_packed_cfblock(const char* source, int line, FILE *fp, int
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
} else {
|
||||
CFILE *cfp;
|
||||
Cfile_block *cfbp;
|
||||
cfbp = &Cfile_block_list[cfile_block_index];
|
||||
|
||||
cfp = &Cfile_list[cfile_block_index];
|
||||
cfp->id = cfile_block_index;
|
||||
cfp->version = 0;
|
||||
cfbp->data = NULL;
|
||||
cfbp->fp = fp;
|
||||
cfbp->mem_mapped = false;
|
||||
cfbp->dir_type = type;
|
||||
cfbp->max_read_len = 0;
|
||||
CFILE *cfp = &Cfile_block_list[cfile_block_index];
|
||||
|
||||
cfbp->source_file = source;
|
||||
cfbp->line_num = line;
|
||||
cfp->data = nullptr;
|
||||
cfp->fp = fp;
|
||||
cfp->mem_mapped = false;
|
||||
cfp->dir_type = type;
|
||||
cfp->max_read_len = 0;
|
||||
|
||||
cfp->source_file = source;
|
||||
cfp->line_num = line;
|
||||
|
||||
cf_init_lowlevel_read_code(cfp,offset, size, 0 );
|
||||
|
||||
@@ -1060,44 +973,39 @@ static CFILE *cf_open_mapped_fill_cfblock(const char* source, int line, FILE *fp
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
CFILE *cfp;
|
||||
Cfile_block *cfbp;
|
||||
cfbp = &Cfile_block_list[cfile_block_index];
|
||||
CFILE *cfp = &Cfile_block_list[cfile_block_index];
|
||||
|
||||
cfp = &Cfile_list[cfile_block_index];
|
||||
cfp->id = cfile_block_index;
|
||||
cfp->version = 0;
|
||||
cfbp->max_read_len = 0;
|
||||
cfbp->fp = NULL;
|
||||
cfbp->mem_mapped = true;
|
||||
cfp->max_read_len = 0;
|
||||
cfp->fp = nullptr;
|
||||
cfp->mem_mapped = true;
|
||||
#if defined _WIN32
|
||||
cfbp->hInFile = hFile;
|
||||
cfp->hInFile = hFile;
|
||||
#endif
|
||||
cfbp->dir_type = type;
|
||||
cfp->dir_type = type;
|
||||
|
||||
cfbp->source_file = source;
|
||||
cfbp->line_num = line;
|
||||
cfp->source_file = source;
|
||||
cfp->line_num = line;
|
||||
|
||||
cf_init_lowlevel_read_code(cfp, 0, 0, 0 );
|
||||
#if defined _WIN32
|
||||
cfbp->hMapFile = CreateFileMapping(cfbp->hInFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (cfbp->hMapFile == NULL) {
|
||||
cfp->hMapFile = CreateFileMapping(cfp->hInFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (cfp->hMapFile == NULL) {
|
||||
nprintf(("Error", "Could not create file-mapping object.\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cfbp->data = (ubyte*)MapViewOfFile(cfbp->hMapFile, FILE_MAP_READ, 0, 0, 0);
|
||||
Assert( cfbp->data != NULL );
|
||||
cfp->data = (ubyte*)MapViewOfFile(cfp->hMapFile, FILE_MAP_READ, 0, 0, 0);
|
||||
Assert( cfp->data != NULL );
|
||||
#elif defined SCP_UNIX
|
||||
cfbp->fp = fp;
|
||||
cfbp->data_length = filelength( fileno(fp) );
|
||||
cfbp->data = mmap(NULL, // start
|
||||
cfbp->data_length, // length
|
||||
PROT_READ, // prot
|
||||
MAP_SHARED, // flags
|
||||
fileno(fp), // fd
|
||||
0); // offset
|
||||
Assert( cfbp->data != NULL );
|
||||
cfp->fp = fp;
|
||||
cfp->data_length = filelength(fileno(fp));
|
||||
cfp->data = mmap(nullptr, // start
|
||||
cfp->data_length, // length
|
||||
PROT_READ, // prot
|
||||
MAP_SHARED, // flags
|
||||
fileno(fp), // fd
|
||||
0); // offset
|
||||
Assert(cfp->data != nullptr);
|
||||
#endif
|
||||
|
||||
return cfp;
|
||||
@@ -1113,23 +1021,18 @@ static CFILE *cf_open_memory_fill_cfblock(const char* source, int line, const vo
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
CFILE *cfp;
|
||||
Cfile_block *cfbp;
|
||||
cfbp = &Cfile_block_list[cfile_block_index];
|
||||
CFILE *cfp = &Cfile_block_list[cfile_block_index];
|
||||
|
||||
cfp = &Cfile_list[cfile_block_index];
|
||||
cfp->id = cfile_block_index;
|
||||
cfp->version = 0;
|
||||
cfbp->max_read_len = 0;
|
||||
cfbp->fp = NULL;
|
||||
cfbp->mem_mapped = false;
|
||||
cfbp->dir_type = dir_type;
|
||||
cfp->max_read_len = 0;
|
||||
cfp->fp = nullptr;
|
||||
cfp->mem_mapped = false;
|
||||
cfp->dir_type = dir_type;
|
||||
|
||||
cfbp->source_file = source;
|
||||
cfbp->line_num = line;
|
||||
cfp->source_file = source;
|
||||
cfp->line_num = line;
|
||||
|
||||
cf_init_lowlevel_read_code(cfp, 0, size, 0 );
|
||||
cfbp->data = data;
|
||||
cfp->data = data;
|
||||
|
||||
return cfp;
|
||||
}
|
||||
@@ -1137,7 +1040,7 @@ static CFILE *cf_open_memory_fill_cfblock(const char* source, int line, const vo
|
||||
|
||||
int cf_get_dir_type(CFILE *cfile)
|
||||
{
|
||||
return Cfile_block_list[cfile->id].dir_type;
|
||||
return cfile->dir_type;
|
||||
}
|
||||
|
||||
// cf_returndata() returns the data pointer for a memory-mapped file that is associated
|
||||
@@ -1148,11 +1051,8 @@ int cf_get_dir_type(CFILE *cfile)
|
||||
const void *cf_returndata(CFILE *cfile)
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
Cfile_block *cb;
|
||||
Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
Assert(cb->data != NULL);
|
||||
return cb->data;
|
||||
Assert(cfile->data != nullptr);
|
||||
return cfile->data;
|
||||
}
|
||||
|
||||
// cutoff point where cfread() will throw an error when it hits this limit
|
||||
@@ -1160,27 +1060,21 @@ const void *cf_returndata(CFILE *cfile)
|
||||
void cf_set_max_read_len( CFILE * cfile, size_t len )
|
||||
{
|
||||
Assert( cfile != NULL );
|
||||
Assert( (cfile->id >= 0) && (cfile->id < MAX_CFILE_BLOCKS) );
|
||||
|
||||
Cfile_block *cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
if (len) {
|
||||
cb->max_read_len = cb->raw_position + len;
|
||||
cfile->max_read_len = cfile->raw_position + len;
|
||||
} else {
|
||||
cb->max_read_len = 0;
|
||||
cfile->max_read_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// routines to read basic data types from CFILE's. Put here to
|
||||
// simplify mac/pc reading from cfiles.
|
||||
|
||||
float cfread_float(CFILE *file, int ver, float deflt)
|
||||
float cfread_float(CFILE* file, float deflt)
|
||||
{
|
||||
float f;
|
||||
|
||||
if (file->version < ver)
|
||||
return deflt;
|
||||
|
||||
if (cfread( &f, sizeof(f), 1, file) != 1)
|
||||
return deflt;
|
||||
|
||||
@@ -1188,13 +1082,10 @@ float cfread_float(CFILE *file, int ver, float deflt)
|
||||
return f;
|
||||
}
|
||||
|
||||
int cfread_int(CFILE *file, int ver, int deflt)
|
||||
int cfread_int(CFILE* file, int deflt)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (file->version < ver)
|
||||
return deflt;
|
||||
|
||||
if (cfread( &i, sizeof(i), 1, file) != 1)
|
||||
return deflt;
|
||||
|
||||
@@ -1202,13 +1093,10 @@ int cfread_int(CFILE *file, int ver, int deflt)
|
||||
return i;
|
||||
}
|
||||
|
||||
uint cfread_uint(CFILE *file, int ver, uint deflt)
|
||||
uint cfread_uint(CFILE* file, uint deflt)
|
||||
{
|
||||
uint i;
|
||||
|
||||
if (file->version < ver)
|
||||
return deflt;
|
||||
|
||||
if (cfread( &i, sizeof(i), 1, file) != 1)
|
||||
return deflt;
|
||||
|
||||
@@ -1216,13 +1104,10 @@ uint cfread_uint(CFILE *file, int ver, uint deflt)
|
||||
return i;
|
||||
}
|
||||
|
||||
short cfread_short(CFILE *file, int ver, short deflt)
|
||||
short cfread_short(CFILE* file, short deflt)
|
||||
{
|
||||
short s;
|
||||
|
||||
if (file->version < ver)
|
||||
return deflt;
|
||||
|
||||
if (cfread( &s, sizeof(s), 1, file) != 1)
|
||||
return deflt;
|
||||
|
||||
@@ -1230,13 +1115,10 @@ short cfread_short(CFILE *file, int ver, short deflt)
|
||||
return s;
|
||||
}
|
||||
|
||||
ushort cfread_ushort(CFILE *file, int ver, ushort deflt)
|
||||
ushort cfread_ushort(CFILE* file, ushort deflt)
|
||||
{
|
||||
ushort s;
|
||||
|
||||
if (file->version < ver)
|
||||
return deflt;
|
||||
|
||||
if (cfread( &s, sizeof(s), 1, file) != 1)
|
||||
return deflt;
|
||||
|
||||
@@ -1244,42 +1126,26 @@ ushort cfread_ushort(CFILE *file, int ver, ushort deflt)
|
||||
return s;
|
||||
}
|
||||
|
||||
ubyte cfread_ubyte(CFILE *file, int ver, ubyte deflt)
|
||||
ubyte cfread_ubyte(CFILE* file, ubyte deflt)
|
||||
{
|
||||
ubyte b;
|
||||
|
||||
if (file->version < ver)
|
||||
return deflt;
|
||||
|
||||
if (cfread( &b, sizeof(b), 1, file) != 1)
|
||||
return deflt;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void cfread_vector(vec3d *vec, CFILE *file, int ver, vec3d *deflt)
|
||||
{
|
||||
if (file->version < ver) {
|
||||
if (deflt)
|
||||
*vec = *deflt;
|
||||
else
|
||||
vec->xyz.x = vec->xyz.y = vec->xyz.z = 0.0f;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
vec->xyz.x = cfread_float(file, ver, deflt ? deflt->xyz.x : 0.0f);
|
||||
vec->xyz.y = cfread_float(file, ver, deflt ? deflt->xyz.y : 0.0f);
|
||||
vec->xyz.z = cfread_float(file, ver, deflt ? deflt->xyz.z : 0.0f);
|
||||
void cfread_vector(vec3d* vec, CFILE* file, vec3d* deflt) {
|
||||
vec->xyz.x = cfread_float(file, deflt ? deflt->xyz.x : 0.0f);
|
||||
vec->xyz.y = cfread_float(file, deflt ? deflt->xyz.y : 0.0f);
|
||||
vec->xyz.z = cfread_float(file, deflt ? deflt->xyz.z : 0.0f);
|
||||
}
|
||||
|
||||
char cfread_char(CFILE *file, int ver, char deflt)
|
||||
char cfread_char(CFILE* file, char deflt)
|
||||
{
|
||||
char b;
|
||||
|
||||
if (file->version < ver)
|
||||
return deflt;
|
||||
|
||||
if (cfread( &b, sizeof(b), 1, file) != 1)
|
||||
return deflt;
|
||||
|
||||
@@ -1392,22 +1258,18 @@ int cfwrite_string_len(const char *buf, CFILE *file)
|
||||
}
|
||||
|
||||
// Get the filelength
|
||||
int cfilelength( CFILE * cfile )
|
||||
{
|
||||
int cfilelength(CFILE* cfile) {
|
||||
Assert(cfile != NULL);
|
||||
Cfile_block *cb;
|
||||
Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
// TODO: return length of memory mapped file
|
||||
Assert( !cb->mem_mapped );
|
||||
Assert(!cfile->mem_mapped);
|
||||
|
||||
// cfile->size gets set at cfopen
|
||||
|
||||
// cb->size gets set at cfopen
|
||||
|
||||
// The rest of the code still uses ints, do an overflow check to detect cases where this fails
|
||||
Assertion(cb->size <= static_cast<size_t>(std::numeric_limits<int>::max()),
|
||||
"Integer overflow in cfilelength! A file is too large (but I don't know which...).");
|
||||
return (int) cb->size;
|
||||
Assertion(cfile->size <= static_cast<size_t>(std::numeric_limits<int>::max()),
|
||||
"Integer overflow in cfilelength! A file is too large (but I don't know which...).");
|
||||
return (int) cfile->size;
|
||||
}
|
||||
|
||||
// cfwrite() writes to the file
|
||||
@@ -1423,15 +1285,13 @@ int cfwrite(const void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
if(buf == NULL || elsize == 0 || nelem == 0)
|
||||
return 0;
|
||||
|
||||
Cfile_block *cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
if(cb->lib_offset != 0)
|
||||
if(cfile->lib_offset != 0)
|
||||
{
|
||||
Error(LOCATION, "Attempt to write to a VP file (unsupported)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cb->data != NULL)
|
||||
if(cfile->data != nullptr)
|
||||
{
|
||||
Warning(LOCATION, "Writing is not supported for mem-mapped files");
|
||||
return EOF;
|
||||
@@ -1440,16 +1300,16 @@ int cfwrite(const void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
size_t bytes_written = 0;
|
||||
size_t size = elsize * nelem;
|
||||
|
||||
bytes_written = fwrite(buf, 1, size, cb->fp);
|
||||
bytes_written = fwrite(buf, 1, size, cfile->fp);
|
||||
|
||||
//WMC - update filesize and position
|
||||
if (bytes_written > 0) {
|
||||
cb->size += bytes_written;
|
||||
cb->raw_position += bytes_written;
|
||||
cfile->size += bytes_written;
|
||||
cfile->raw_position += bytes_written;
|
||||
}
|
||||
|
||||
#if defined(CHECK_SIZE) && !defined(NDEBUG)
|
||||
Assert( cb->size == filelength(fileno(cb->fp)) );
|
||||
Assert( cfile->size == filelength(fileno(cfile->fp)) );
|
||||
#endif
|
||||
|
||||
return (int)(bytes_written / elsize);
|
||||
@@ -1466,34 +1326,32 @@ int cfputc(int c, CFILE *cfile)
|
||||
if(!cf_is_valid(cfile))
|
||||
return EOF;
|
||||
|
||||
Cfile_block *cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
if(cb->lib_offset != 0)
|
||||
if(cfile->lib_offset != 0)
|
||||
{
|
||||
Error(LOCATION, "Attempt to write character to a VP file (unsupported)");
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if(cb->data != NULL)
|
||||
if(cfile->data != nullptr)
|
||||
{
|
||||
Warning(LOCATION, "Writing is not supported for mem-mapped files");
|
||||
return EOF;
|
||||
}
|
||||
|
||||
// writing not supported for memory-mapped files
|
||||
Assert( !cb->data );
|
||||
Assert( !cfile->data );
|
||||
|
||||
int result = fputc(c, cb->fp);
|
||||
int result = fputc(c, cfile->fp);
|
||||
|
||||
//WMC - update filesize and position
|
||||
if(result != EOF)
|
||||
{
|
||||
cb->size += 1;
|
||||
cb->raw_position += 1;
|
||||
cfile->size += 1;
|
||||
cfile->raw_position += 1;
|
||||
}
|
||||
|
||||
#if defined(CHECK_SIZE) && !defined(NDEBUG)
|
||||
Assert( cb->size == filelength(fileno(cb->fp)) );
|
||||
Assert( cfile->size == filelength(fileno(cfile->fp)) );
|
||||
#endif
|
||||
|
||||
return result;
|
||||
@@ -1512,31 +1370,29 @@ int cfputs(const char *str, CFILE *cfile)
|
||||
if(str == NULL)
|
||||
return EOF;
|
||||
|
||||
Cfile_block *cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
if(cb->lib_offset != 0)
|
||||
if(cfile->lib_offset != 0)
|
||||
{
|
||||
Error(LOCATION, "Attempt to write character to a VP file (unsupported)");
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if(cb->data != NULL)
|
||||
if(cfile->data != nullptr)
|
||||
{
|
||||
Warning(LOCATION, "Writing is not supported for mem-mapped files");
|
||||
return EOF;
|
||||
}
|
||||
|
||||
int result = fputs(str, cb->fp);
|
||||
int result = fputs(str, cfile->fp);
|
||||
|
||||
//WMC - update filesize and position
|
||||
if(result != EOF)
|
||||
{
|
||||
cb->size += strlen(str);
|
||||
cb->raw_position += strlen(str);
|
||||
cfile->size += strlen(str);
|
||||
cfile->raw_position += strlen(str);
|
||||
}
|
||||
|
||||
#if defined(CHECK_SIZE) && !defined(NDEBUG)
|
||||
Assert( cb->size == filelength(fileno(cb->fp)) );
|
||||
Assert( cfile->size == filelength(fileno(cfile->fp)) );
|
||||
#endif
|
||||
|
||||
return result;
|
||||
@@ -1898,19 +1754,16 @@ int cf_chksum_long(CFILE *file, uint *chksum, int max_size)
|
||||
int cflush(CFILE *cfile)
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
Cfile_block *cb;
|
||||
Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
// not supported for memory mapped files
|
||||
Assert( !cb->data );
|
||||
Assert( !cfile->data );
|
||||
|
||||
Assert(cb->fp != NULL);
|
||||
Assert(cfile->fp != nullptr);
|
||||
|
||||
int result = fflush(cb->fp);
|
||||
int result = fflush(cfile->fp);
|
||||
|
||||
//WMC - update filesize
|
||||
cb->size = filelength(fileno(cb->fp));
|
||||
cfile->size = filelength(fileno(cfile->fp));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+39
-16
@@ -13,10 +13,11 @@
|
||||
#define __CFILE_H__
|
||||
|
||||
|
||||
#include <ctime>
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
|
||||
#define CF_EOF (-1)
|
||||
@@ -25,10 +26,8 @@
|
||||
#define CF_SEEK_CUR (1)
|
||||
#define CF_SEEK_END (2)
|
||||
|
||||
typedef struct CFILE {
|
||||
int id; // Index into cfile.cpp specific structure
|
||||
int version; // version of this file
|
||||
} CFILE;
|
||||
// Opaque file handle
|
||||
struct CFILE;
|
||||
|
||||
// extra info that can be returned when getting a file listing
|
||||
typedef struct {
|
||||
@@ -166,11 +165,22 @@ extern const char *Get_file_list_child;
|
||||
|
||||
// cfile directory. valid after cfile_init() returns successfully
|
||||
#define CFILE_ROOT_DIRECTORY_LEN 256
|
||||
extern char Cfile_root_dir[CFILE_ROOT_DIRECTORY_LEN];
|
||||
extern char Cfile_user_dir[CFILE_ROOT_DIRECTORY_LEN];
|
||||
|
||||
namespace cfile {
|
||||
enum class ModRootType { Unknown, Folder, Package };
|
||||
|
||||
enum class LocationType { Unknown, SecondaryMod, PrimaryMod, GameRoot };
|
||||
|
||||
struct ModRoot {
|
||||
ModRootType type = ModRootType::Unknown;
|
||||
SCP_string path;
|
||||
LocationType location_type = LocationType::Unknown;
|
||||
bool user_location = false;
|
||||
};
|
||||
} // namespace cfile
|
||||
|
||||
//================= LOW-LEVEL FUNCTIONS ==================
|
||||
int cfile_init(const char *exe_dir, const char *cdrom_dir=NULL);
|
||||
int cfile_init(const SCP_vector<cfile::ModRoot>& modRoots);
|
||||
void cfile_close();
|
||||
|
||||
// add an extension to a filename if it doesn't already have it
|
||||
@@ -303,14 +313,14 @@ int cfile_flush_dir(int type);
|
||||
// These are all high level, built up from
|
||||
// cfread.
|
||||
char *cfgets(char *buf, size_t n, CFILE *fp);
|
||||
char cfread_char(CFILE *file, int ver = 0, char deflt = 0);
|
||||
ubyte cfread_ubyte(CFILE *file, int ver = 0, ubyte deflt = 0);
|
||||
short cfread_short(CFILE *file, int ver = 0, short deflt = 0);
|
||||
ushort cfread_ushort(CFILE *file, int ver = 0, ushort deflt = 0);
|
||||
int cfread_int(CFILE *file, int ver = 0, int deflt = 0);
|
||||
uint cfread_uint(CFILE *file, int ver = 0, uint deflt = 0);
|
||||
float cfread_float(CFILE *file, int ver = 0, float deflt = 0.0f);
|
||||
void cfread_vector(vec3d *vec, CFILE *file, int ver = 0, vec3d *deflt = NULL);
|
||||
char cfread_char(CFILE* file, char deflt = 0);
|
||||
ubyte cfread_ubyte(CFILE* file, ubyte deflt = 0);
|
||||
short cfread_short(CFILE* file, short deflt = 0);
|
||||
ushort cfread_ushort(CFILE* file, ushort deflt = 0);
|
||||
int cfread_int(CFILE* file, int deflt = 0);
|
||||
uint cfread_uint(CFILE* file, uint deflt = 0);
|
||||
float cfread_float(CFILE* file, float deflt = 0.0f);
|
||||
void cfread_vector(vec3d* vec, CFILE* file, vec3d* deflt = nullptr);
|
||||
|
||||
// Reads variable length, null-termined string. Will only read up
|
||||
// to n characters.
|
||||
@@ -462,4 +472,17 @@ namespace cfile
|
||||
|
||||
}
|
||||
|
||||
// This allows to use std::unique_ptr with CFILE
|
||||
namespace std {
|
||||
template <>
|
||||
struct default_delete<CFILE> {
|
||||
void operator()(CFILE* ptr)
|
||||
{
|
||||
if (cf_is_valid(ptr)) {
|
||||
cfclose(ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
#endif /* __CFILE_H__ */
|
||||
+55
-74
@@ -36,24 +36,20 @@
|
||||
|
||||
void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size, size_t pos )
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
Assert(cfile != nullptr);
|
||||
|
||||
Cfile_block *cb;
|
||||
Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
cfile->lib_offset = lib_offset;
|
||||
cfile->raw_position = pos;
|
||||
cfile->size = size;
|
||||
|
||||
cb->lib_offset = lib_offset;
|
||||
cb->raw_position = pos;
|
||||
cb->size = size;
|
||||
|
||||
if ( cb->fp ) {
|
||||
if ( cb->lib_offset ) {
|
||||
fseek( cb->fp, (long)cb->lib_offset, SEEK_SET );
|
||||
if ( cfile->fp ) {
|
||||
if ( cfile->lib_offset ) {
|
||||
fseek( cfile->fp, (long)cfile->lib_offset, SEEK_SET );
|
||||
}
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
auto raw_position = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(raw_position == cb->raw_position);
|
||||
auto raw_position = ftell(cfile->fp) - cfile->lib_offset;
|
||||
Assert(raw_position == cfile->raw_position);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -70,20 +66,16 @@ int cfeof(CFILE *cfile)
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
|
||||
Cfile_block *cb;
|
||||
Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
int result = 0;
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
if (cb->fp) {
|
||||
auto raw_position = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(raw_position == cb->raw_position);
|
||||
if (cfile->fp) {
|
||||
auto raw_position = ftell(cfile->fp) - cfile->lib_offset;
|
||||
Assert(raw_position == cfile->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cb->raw_position >= cb->size ) {
|
||||
if (cfile->raw_position >= cfile->size ) {
|
||||
result = 1;
|
||||
} else {
|
||||
result = 0;
|
||||
@@ -100,21 +92,18 @@ int cfeof(CFILE *cfile)
|
||||
int cftell( CFILE * cfile )
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
Cfile_block *cb;
|
||||
Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
if (cb->fp) {
|
||||
auto raw_position = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(raw_position == cb->raw_position);
|
||||
if (cfile->fp) {
|
||||
auto raw_position = ftell(cfile->fp) - cfile->lib_offset;
|
||||
Assert(raw_position == cfile->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
// The rest of the code still uses ints, do an overflow check to detect cases where this fails
|
||||
Assertion(cb->raw_position <= static_cast<size_t>(std::numeric_limits<int>::max()),
|
||||
Assertion(cfile->raw_position <= static_cast<size_t>(std::numeric_limits<int>::max()),
|
||||
"Integer overflow in cftell, a file is probably too large (but I don't know which one).");
|
||||
return (int) cb->raw_position;
|
||||
return (int) cfile->raw_position;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,27 +116,23 @@ int cfseek( CFILE *cfile, int offset, int where )
|
||||
{
|
||||
|
||||
Assert(cfile != NULL);
|
||||
Cfile_block *cb;
|
||||
Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
|
||||
cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
|
||||
// TODO: seek to offset in memory mapped file
|
||||
Assert( !cb->mem_mapped );
|
||||
Assert( !cfile->mem_mapped );
|
||||
|
||||
size_t goal_position;
|
||||
|
||||
switch( where ) {
|
||||
case CF_SEEK_SET:
|
||||
goal_position = offset+cb->lib_offset;
|
||||
goal_position = offset+cfile->lib_offset;
|
||||
break;
|
||||
case CF_SEEK_CUR:
|
||||
{
|
||||
goal_position = cb->raw_position+offset+cb->lib_offset;
|
||||
goal_position = cfile->raw_position+offset+cfile->lib_offset;
|
||||
}
|
||||
break;
|
||||
case CF_SEEK_END:
|
||||
goal_position = cb->size+offset+cb->lib_offset;
|
||||
goal_position = cfile->size+offset+cfile->lib_offset;
|
||||
break;
|
||||
default:
|
||||
Int3();
|
||||
@@ -155,23 +140,23 @@ int cfseek( CFILE *cfile, int offset, int where )
|
||||
}
|
||||
|
||||
// Make sure we don't seek beyond the end of the file
|
||||
CAP(goal_position, cb->lib_offset, cb->lib_offset + cb->size);
|
||||
CAP(goal_position, cfile->lib_offset, cfile->lib_offset + cfile->size);
|
||||
|
||||
int result = 0;
|
||||
|
||||
if (cb->fp) {
|
||||
if (cfile->fp) {
|
||||
// If we have a file pointer we can also seek in that file
|
||||
result = fseek(cb->fp, (long)goal_position, SEEK_SET );
|
||||
Assertion(goal_position >= cb->lib_offset, "Invalid offset values detected while seeking! Goal was " SIZE_T_ARG ", lib_offset is " SIZE_T_ARG ".", goal_position, cb->lib_offset);
|
||||
result = fseek(cfile->fp, (long)goal_position, SEEK_SET );
|
||||
Assertion(goal_position >= cfile->lib_offset, "Invalid offset values detected while seeking! Goal was " SIZE_T_ARG ", lib_offset is " SIZE_T_ARG ".", goal_position, cfile->lib_offset);
|
||||
}
|
||||
// If we only have a data pointer this will do all the work
|
||||
cb->raw_position = goal_position - cb->lib_offset;
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
cfile->raw_position = goal_position - cfile->lib_offset;
|
||||
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
if (cb->fp) {
|
||||
auto tmp_offset = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(tmp_offset == cb->raw_position);
|
||||
if (cfile->fp) {
|
||||
auto tmp_offset = ftell(cfile->fp) - cfile->lib_offset;
|
||||
Assert(tmp_offset == cfile->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -194,19 +179,17 @@ int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
if(buf == NULL || size <= 0)
|
||||
return 0;
|
||||
|
||||
Cfile_block *cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
if ( (cb->raw_position+size) > cb->size ) {
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
size = cb->size - cb->raw_position;
|
||||
if ( (cfile->raw_position+size) > cfile->size ) {
|
||||
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
|
||||
size = cfile->size - cfile->raw_position;
|
||||
if ( size < 1 ) {
|
||||
return 0;
|
||||
}
|
||||
//mprintf(( "CFILE: EOF encountered in file\n" ));
|
||||
}
|
||||
|
||||
if (cb->max_read_len) {
|
||||
if ( cb->raw_position+size > cb->max_read_len ) {
|
||||
if (cfile->max_read_len) {
|
||||
if ( cfile->raw_position+size > cfile->max_read_len ) {
|
||||
std::ostringstream s_buf;
|
||||
s_buf << "Attempted to read " << size << "-byte(s) beyond length limit";
|
||||
|
||||
@@ -215,22 +198,22 @@ int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
}
|
||||
|
||||
size_t bytes_read;
|
||||
if (cb->data != nullptr) {
|
||||
if (cfile->data != nullptr) {
|
||||
// This is a file from memory
|
||||
bytes_read = size;
|
||||
memcpy(buf, reinterpret_cast<const char*>(cb->data) + cb->raw_position, size);
|
||||
memcpy(buf, reinterpret_cast<const char*>(cfile->data) + cfile->raw_position, size);
|
||||
} else {
|
||||
bytes_read = fread( buf, 1, size, cb->fp );
|
||||
bytes_read = fread( buf, 1, size, cfile->fp );
|
||||
}
|
||||
if ( bytes_read > 0 ) {
|
||||
cb->raw_position += bytes_read;
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
cfile->raw_position += bytes_read;
|
||||
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
|
||||
}
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
if (cb->fp) {
|
||||
auto tmp_offset = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(tmp_offset==cb->raw_position);
|
||||
if (cfile->fp) {
|
||||
auto tmp_offset = ftell(cfile->fp) - cfile->lib_offset;
|
||||
Assert(tmp_offset==cfile->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -246,10 +229,8 @@ int cfread_lua_number(double *buf, CFILE *cfile)
|
||||
if(buf == NULL)
|
||||
return 0;
|
||||
|
||||
Cfile_block *cb = &Cfile_block_list[cfile->id];
|
||||
|
||||
// cfread() not supported for memory-mapped files
|
||||
if(cb->data != NULL)
|
||||
if(cfile->data != nullptr)
|
||||
{
|
||||
Warning(LOCATION, "Writing is not supported for mem-mapped files");
|
||||
return 0;
|
||||
@@ -257,28 +238,28 @@ int cfread_lua_number(double *buf, CFILE *cfile)
|
||||
|
||||
size_t advance = 0;
|
||||
int items_read;
|
||||
if (cb->fp) {
|
||||
long orig_pos = ftell(cb->fp);
|
||||
items_read = fscanf(cb->fp, LUA_NUMBER_SCAN, buf);
|
||||
advance = (size_t) (ftell(cb->fp)-orig_pos);
|
||||
if (cfile->fp) {
|
||||
long orig_pos = ftell(cfile->fp);
|
||||
items_read = fscanf(cfile->fp, LUA_NUMBER_SCAN, buf);
|
||||
advance = (size_t) (ftell(cfile->fp)-orig_pos);
|
||||
} else {
|
||||
int read;
|
||||
// %n returns the number of bytes currently read so we append that to the scan format at the end so it will return
|
||||
// how many bytes we have consumed
|
||||
items_read = sscanf(reinterpret_cast<const char*>(cb->data), LUA_NUMBER_SCAN "%n", buf, &read);
|
||||
items_read = sscanf(reinterpret_cast<const char*>(cfile->data), LUA_NUMBER_SCAN "%n", buf, &read);
|
||||
if (items_read == 2) {
|
||||
// We need to correct the items read counter since we read one additional item
|
||||
items_read = 1;
|
||||
}
|
||||
advance = (size_t) read;
|
||||
}
|
||||
cb->raw_position += advance;
|
||||
Assertion(cb->raw_position <= cb->size, "Invalid raw_position value detected!");
|
||||
cfile->raw_position += advance;
|
||||
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
if (cb->fp) {
|
||||
auto tmp_offset = ftell(cb->fp) - cb->lib_offset;
|
||||
Assert(tmp_offset==cb->raw_position);
|
||||
if (cfile->fp) {
|
||||
auto tmp_offset = ftell(cfile->fp) - cfile->lib_offset;
|
||||
Assert(tmp_offset==cfile->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+14
-14
@@ -22,30 +22,30 @@
|
||||
#define CFILE_BLOCK_UNUSED 0
|
||||
#define CFILE_BLOCK_USED 1
|
||||
|
||||
typedef struct Cfile_block {
|
||||
int type; // CFILE_BLOCK_UNUSED, CFILE_BLOCK_USED
|
||||
int dir_type; // directory location
|
||||
FILE *fp; // File pointer if opening an individual file
|
||||
const void *data; // Pointer for memory-mapped file access. NULL if not mem-mapped.
|
||||
bool mem_mapped; // Flag for memory mapped files (if data is not null and this is false it means that it's an embedded file)
|
||||
struct CFILE {
|
||||
int type = CFILE_BLOCK_UNUSED; // CFILE_BLOCK_UNUSED, CFILE_BLOCK_USED
|
||||
int dir_type; // directory location
|
||||
FILE* fp; // File pointer if opening an individual file
|
||||
const void* data; // Pointer for memory-mapped file access. NULL if not mem-mapped.
|
||||
bool mem_mapped; // Flag for memory mapped files (if data is not null and this is false it means that it's an embedded file)
|
||||
#ifdef _WIN32
|
||||
HANDLE hInFile; // Handle from CreateFile()
|
||||
HANDLE hMapFile; // Handle from CreateFileMapping()
|
||||
#else
|
||||
size_t data_length; // length of data for mmap
|
||||
size_t data_length; // length of data for mmap
|
||||
#endif
|
||||
size_t lib_offset;
|
||||
size_t raw_position;
|
||||
size_t size; // for packed files
|
||||
size_t lib_offset;
|
||||
size_t raw_position;
|
||||
size_t size; // for packed files
|
||||
|
||||
size_t max_read_len; // max read offset, for special error handling
|
||||
|
||||
size_t max_read_len; // max read offset, for special error handling
|
||||
|
||||
const char* source_file;
|
||||
int line_num;
|
||||
} Cfile_block;
|
||||
};
|
||||
|
||||
#define MAX_CFILE_BLOCKS 64
|
||||
extern Cfile_block Cfile_block_list[MAX_CFILE_BLOCKS];
|
||||
extern std::array<CFILE, MAX_CFILE_BLOCKS> Cfile_block_list;
|
||||
|
||||
// Called once to setup the low-level reading code.
|
||||
void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size, size_t pos );
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
#define _CFILE_INTERNAL
|
||||
|
||||
#include "cfilemod.h"
|
||||
#include "cfile.h"
|
||||
#include "cmdline/cmdline.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "cfilesystem.h"
|
||||
|
||||
#ifdef SCP_UNIX
|
||||
#include <glob.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <fnmatch.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
namespace cfile {
|
||||
|
||||
namespace {
|
||||
|
||||
typedef struct cf_root_sort {
|
||||
SCP_string path;
|
||||
int cf_type;
|
||||
} cf_root_sort;
|
||||
|
||||
// packfile sort function
|
||||
bool cf_packfile_sort_func(const cf_root_sort& r1, const cf_root_sort& r2) {
|
||||
// if the 2 directory types are the same, do a string compare
|
||||
if (r1.cf_type == r2.cf_type) {
|
||||
return (stricmp(r1.path.c_str(), r2.path.c_str()) < 0);
|
||||
}
|
||||
|
||||
// otherwise return them in order of CF_TYPE_* precedence
|
||||
return (r1.cf_type < r2.cf_type);
|
||||
}
|
||||
|
||||
// Go through a root and look for pack files
|
||||
void build_pack_list(SCP_vector<ModRoot>& mod_roots,
|
||||
const SCP_string& search_path,
|
||||
LocationType location_type,
|
||||
bool user_location) {
|
||||
char filespec[MAX_PATH_LEN];
|
||||
|
||||
// allocate a temporary array of temporary roots so we can easily sort them
|
||||
SCP_vector<cf_root_sort> temp_roots_sort;
|
||||
|
||||
// now just setup all the root info
|
||||
for (int i = CF_TYPE_ROOT; i < CF_MAX_PATH_TYPES; i++) {
|
||||
strcpy_s(filespec, search_path.c_str());
|
||||
|
||||
if (strlen(Pathtypes[i].path)) {
|
||||
strcat_s(filespec, Pathtypes[i].path);
|
||||
|
||||
if (filespec[strlen(filespec) - 1] != DIR_SEPARATOR_CHAR)
|
||||
strcat_s(filespec, DIR_SEPARATOR_STR);
|
||||
}
|
||||
|
||||
#if defined _WIN32
|
||||
strcat_s( filespec, "*.vp" );
|
||||
|
||||
intptr_t find_handle;
|
||||
_finddata_t find;
|
||||
|
||||
find_handle = _findfirst( filespec, &find );
|
||||
|
||||
if (find_handle != -1) {
|
||||
do {
|
||||
// add the new item
|
||||
if (!(find.attrib & _A_SUBDIR)) {
|
||||
|
||||
// get a temp pointer
|
||||
rptr_sort = &temp_roots_sort[root_index++];
|
||||
|
||||
// fill in all the proper info
|
||||
strcpy_s(rptr_sort->path, root->path);
|
||||
|
||||
if(strlen(Pathtypes[i].path)) {
|
||||
|
||||
strcat_s(rptr_sort->path, Pathtypes[i].path );
|
||||
strcat_s(rptr_sort->path, DIR_SEPARATOR_STR);
|
||||
}
|
||||
|
||||
strcat_s(rptr_sort->path, find.name );
|
||||
rptr_sort->roottype = CF_ROOTTYPE_PACK;
|
||||
rptr_sort->cf_type = i;
|
||||
}
|
||||
|
||||
} while (!_findnext(find_handle, &find));
|
||||
|
||||
_findclose( find_handle );
|
||||
}
|
||||
#elif defined SCP_UNIX
|
||||
strcat_s(filespec, "*.[vV][pP]");
|
||||
glob_t globinfo;
|
||||
|
||||
memset(&globinfo, 0, sizeof(globinfo));
|
||||
|
||||
int status = glob(filespec, 0, NULL, &globinfo);
|
||||
|
||||
if (status == 0) {
|
||||
for (uint j = 0; j < globinfo.gl_pathc; j++) {
|
||||
// Determine if this is a regular file
|
||||
struct stat statbuf;
|
||||
memset(&statbuf, 0, sizeof(statbuf));
|
||||
stat(globinfo.gl_pathv[j], &statbuf);
|
||||
|
||||
if (S_ISREG(statbuf.st_mode)) {
|
||||
|
||||
cf_root_sort sort;
|
||||
|
||||
// fill in all the proper info
|
||||
sort.path = globinfo.gl_pathv[j];
|
||||
sort.cf_type = i;
|
||||
temp_roots_sort.push_back(sort);
|
||||
}
|
||||
}
|
||||
|
||||
globfree(&globinfo);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// sort the roots
|
||||
std::sort(temp_roots_sort.begin(), temp_roots_sort.end(), cf_packfile_sort_func);
|
||||
|
||||
// now insert them all into the real root list properly
|
||||
for (const auto& pack_root : temp_roots_sort) {
|
||||
ModRoot root;
|
||||
root.type = ModRootType::Package;
|
||||
root.path = pack_root.path;
|
||||
root.user_location = user_location;
|
||||
root.location_type = location_type;
|
||||
|
||||
mod_roots.push_back(root);
|
||||
}
|
||||
}
|
||||
|
||||
char normalize_directory_separator(char in) {
|
||||
if (in == '/') {
|
||||
return DIR_SEPARATOR_CHAR;
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
void
|
||||
add_mod_roots(SCP_vector<ModRoot>& mod_roots, const char* mod_cmdline, const SCP_string& rootDirectory, bool user_location) {
|
||||
if (mod_cmdline) {
|
||||
bool primary = true;
|
||||
for (const char* cur_pos = mod_cmdline; strlen(cur_pos) != 0; cur_pos += (strlen(cur_pos) + 1)) {
|
||||
SCP_stringstream ss;
|
||||
ss << rootDirectory;
|
||||
|
||||
if (rootDirectory.back() != DIR_SEPARATOR_CHAR) {
|
||||
ss << DIR_SEPARATOR_CHAR;
|
||||
}
|
||||
|
||||
ss << cur_pos << DIR_SEPARATOR_STR;
|
||||
|
||||
SCP_string rootPath = ss.str();
|
||||
|
||||
// normalize the path to the native path format
|
||||
std::transform(rootPath.begin(), rootPath.end(), rootPath.begin(), normalize_directory_separator);
|
||||
|
||||
ModRoot root;
|
||||
root.type = ModRootType::Folder;
|
||||
root.path = rootPath;
|
||||
root.user_location = user_location;
|
||||
|
||||
if (primary) {
|
||||
root.location_type = LocationType::PrimaryMod;
|
||||
} else {
|
||||
root.location_type = LocationType::SecondaryMod;
|
||||
}
|
||||
|
||||
mod_roots.push_back(root);
|
||||
|
||||
build_pack_list(mod_roots, rootPath, root.location_type, user_location);
|
||||
|
||||
primary = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SCP_vector<ModRoot> getModRootsFromModCmdline(const SCP_string& content_root, const char* mod_cmdline) {
|
||||
SCP_vector<ModRoot> mod_roots;
|
||||
|
||||
if (os_is_legacy_mode()) {
|
||||
// =========================================================================
|
||||
#ifdef WIN32
|
||||
// Nothing to do here, Windows uses the current directory as the base
|
||||
#else
|
||||
add_mod_roots(mod_roots, mod_cmdline, os_get_legacy_user_dir(), true);
|
||||
|
||||
ModRoot root;
|
||||
root.type = ModRootType::Folder;
|
||||
root.path = os_get_legacy_user_dir();
|
||||
root.user_location = true;
|
||||
root.location_type = LocationType::GameRoot;
|
||||
|
||||
// do we already have a slash? as in the case of a root directory install
|
||||
if (root.path.back() != DIR_SEPARATOR_CHAR) {
|
||||
root.path += DIR_SEPARATOR_STR; // put trailing backslash on for easier path construction
|
||||
}
|
||||
|
||||
mod_roots.push_back(root);
|
||||
|
||||
// Next, check any VP files under the current directory.
|
||||
build_pack_list(mod_roots, root.path, root.location_type, root.user_location);
|
||||
#endif
|
||||
// =========================================================================
|
||||
} else if (!Cmdline_portable_mode) {
|
||||
// =========================================================================
|
||||
// now look for mods under the users HOME directory to use before system ones
|
||||
add_mod_roots(mod_roots, mod_cmdline, os_get_config_path(), true);
|
||||
// =========================================================================
|
||||
|
||||
// =========================================================================
|
||||
// set users HOME directory as default for loading and saving files
|
||||
ModRoot root;
|
||||
root.type = ModRootType::Folder;
|
||||
root.path = os_get_config_path();
|
||||
root.user_location = true;
|
||||
root.location_type = LocationType::GameRoot;
|
||||
|
||||
// do we already have a slash? as in the case of a root directory install
|
||||
if (root.path.back() != DIR_SEPARATOR_CHAR) {
|
||||
root.path += DIR_SEPARATOR_STR; // put trailing backslash on for easier path construction
|
||||
}
|
||||
|
||||
mod_roots.push_back(root);
|
||||
|
||||
// Next, check any VP files under the current directory.
|
||||
build_pack_list(mod_roots, root.path, root.location_type, root.user_location);
|
||||
// =========================================================================
|
||||
}
|
||||
|
||||
add_mod_roots(mod_roots, mod_cmdline, content_root, false);
|
||||
|
||||
ModRoot root;
|
||||
root.type = ModRootType::Folder;
|
||||
root.path = content_root;
|
||||
root.user_location = false;
|
||||
root.location_type = LocationType::GameRoot;
|
||||
|
||||
// do we already have a slash? as in the case of a root directory install
|
||||
if (root.path.back() != DIR_SEPARATOR_CHAR) {
|
||||
root.path += DIR_SEPARATOR_STR; // put trailing backslash on for easier path construction
|
||||
}
|
||||
|
||||
mod_roots.push_back(root);
|
||||
|
||||
//======================================================
|
||||
// Next, check any VP files under the current directory.
|
||||
build_pack_list(mod_roots, root.path, root.location_type, root.user_location);
|
||||
|
||||
return mod_roots;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "cfile/cfile.h"
|
||||
|
||||
namespace cfile {
|
||||
|
||||
SCP_vector<ModRoot> getModRootsFromModCmdline(const SCP_string& content_root, const char* mod_cmdline);
|
||||
|
||||
}
|
||||
+86
-352
@@ -160,379 +160,113 @@ cf_root *cf_create_root()
|
||||
return &Root_blocks[block]->roots[offset];
|
||||
}
|
||||
|
||||
// return the # of packfiles which exist
|
||||
int cf_get_packfile_count(cf_root *root)
|
||||
static bool has_mod_location(const SCP_vector<cfile::ModRoot>& modRoots)
|
||||
{
|
||||
SCP_string filespec;
|
||||
int i;
|
||||
int packfile_count;
|
||||
|
||||
// count up how many packfiles we're gonna have
|
||||
packfile_count = 0;
|
||||
for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ )
|
||||
for (const auto& root : modRoots)
|
||||
{
|
||||
filespec = root->path;
|
||||
|
||||
if(strlen(Pathtypes[i].path))
|
||||
if (root.location_type == cfile::LocationType::PrimaryMod)
|
||||
{
|
||||
filespec += Pathtypes[ i ].path;
|
||||
if ( filespec[ filespec.length( ) - 1 ] != DIR_SEPARATOR_CHAR )
|
||||
{
|
||||
filespec += DIR_SEPARATOR_STR;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined _WIN32
|
||||
filespec += "*.vp";
|
||||
|
||||
intptr_t find_handle;
|
||||
_finddata_t find;
|
||||
|
||||
find_handle = _findfirst( filespec.c_str( ), &find );
|
||||
|
||||
if (find_handle != -1) {
|
||||
do {
|
||||
if (!(find.attrib & _A_SUBDIR)) {
|
||||
packfile_count++;
|
||||
}
|
||||
|
||||
} while (!_findnext(find_handle, &find));
|
||||
|
||||
_findclose( find_handle );
|
||||
}
|
||||
#elif defined SCP_UNIX
|
||||
filespec += "*.[vV][pP]";
|
||||
|
||||
glob_t globinfo;
|
||||
memset(&globinfo, 0, sizeof(globinfo));
|
||||
int status = glob(filespec.c_str( ), 0, NULL, &globinfo);
|
||||
if (status == 0) {
|
||||
for (unsigned int j = 0; j < globinfo.gl_pathc; j++) {
|
||||
// Determine if this is a regular file
|
||||
struct stat statbuf;
|
||||
memset(&statbuf, 0, sizeof(statbuf));
|
||||
stat(globinfo.gl_pathv[j], &statbuf);
|
||||
if (S_ISREG(statbuf.st_mode)) {
|
||||
packfile_count++;
|
||||
}
|
||||
}
|
||||
globfree(&globinfo);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return packfile_count;
|
||||
}
|
||||
|
||||
// packfile sort function
|
||||
bool cf_packfile_sort_func(const cf_root_sort &r1, const cf_root_sort &r2)
|
||||
{
|
||||
// if the 2 directory types are the same, do a string compare
|
||||
if (r1.cf_type == r2.cf_type) {
|
||||
return (stricmp(r1.path, r2.path) < 0);
|
||||
}
|
||||
|
||||
// otherwise return them in order of CF_TYPE_* precedence
|
||||
return (r1.cf_type < r2.cf_type);
|
||||
}
|
||||
|
||||
// Go through a root and look for pack files
|
||||
void cf_build_pack_list( cf_root *root )
|
||||
{
|
||||
char filespec[MAX_PATH_LEN];
|
||||
int i;
|
||||
cf_root_sort *temp_roots_sort, *rptr_sort;
|
||||
int temp_root_count, root_index;
|
||||
|
||||
// determine how many packfiles there are
|
||||
temp_root_count = cf_get_packfile_count(root);
|
||||
|
||||
if (temp_root_count <= 0)
|
||||
return;
|
||||
|
||||
// allocate a temporary array of temporary roots so we can easily sort them
|
||||
temp_roots_sort = (cf_root_sort*)vm_malloc(sizeof(cf_root_sort) * temp_root_count);
|
||||
|
||||
if (temp_roots_sort == NULL) {
|
||||
Int3();
|
||||
return;
|
||||
}
|
||||
|
||||
// now just setup all the root info
|
||||
root_index = 0;
|
||||
for (i = CF_TYPE_ROOT; i < CF_MAX_PATH_TYPES; i++) {
|
||||
strcpy_s( filespec, root->path );
|
||||
|
||||
if ( strlen(Pathtypes[i].path) ) {
|
||||
strcat_s( filespec, Pathtypes[i].path );
|
||||
|
||||
if ( filespec[strlen(filespec)-1] != DIR_SEPARATOR_CHAR )
|
||||
strcat_s( filespec, DIR_SEPARATOR_STR );
|
||||
}
|
||||
|
||||
#if defined _WIN32
|
||||
strcat_s( filespec, "*.vp" );
|
||||
|
||||
intptr_t find_handle;
|
||||
_finddata_t find;
|
||||
|
||||
find_handle = _findfirst( filespec, &find );
|
||||
|
||||
if (find_handle != -1) {
|
||||
do {
|
||||
// add the new item
|
||||
if (!(find.attrib & _A_SUBDIR)) {
|
||||
Assert(root_index < temp_root_count);
|
||||
|
||||
// get a temp pointer
|
||||
rptr_sort = &temp_roots_sort[root_index++];
|
||||
|
||||
// fill in all the proper info
|
||||
strcpy_s(rptr_sort->path, root->path);
|
||||
|
||||
if(strlen(Pathtypes[i].path)) {
|
||||
|
||||
strcat_s(rptr_sort->path, Pathtypes[i].path );
|
||||
strcat_s(rptr_sort->path, DIR_SEPARATOR_STR);
|
||||
}
|
||||
|
||||
strcat_s(rptr_sort->path, find.name );
|
||||
rptr_sort->roottype = CF_ROOTTYPE_PACK;
|
||||
rptr_sort->cf_type = i;
|
||||
}
|
||||
|
||||
} while (!_findnext(find_handle, &find));
|
||||
|
||||
_findclose( find_handle );
|
||||
}
|
||||
#elif defined SCP_UNIX
|
||||
strcat_s( filespec, "*.[vV][pP]" );
|
||||
glob_t globinfo;
|
||||
|
||||
memset(&globinfo, 0, sizeof(globinfo));
|
||||
|
||||
int status = glob(filespec, 0, NULL, &globinfo);
|
||||
|
||||
if (status == 0) {
|
||||
for (uint j = 0; j < globinfo.gl_pathc; j++) {
|
||||
// Determine if this is a regular file
|
||||
struct stat statbuf;
|
||||
memset(&statbuf, 0, sizeof(statbuf));
|
||||
stat(globinfo.gl_pathv[j], &statbuf);
|
||||
|
||||
if ( S_ISREG(statbuf.st_mode) ) {
|
||||
Assert(root_index < temp_root_count);
|
||||
|
||||
// get a temp pointer
|
||||
rptr_sort = &temp_roots_sort[root_index++];
|
||||
|
||||
// fill in all the proper info
|
||||
strcpy_s(rptr_sort->path, globinfo.gl_pathv[j] );
|
||||
rptr_sort->roottype = CF_ROOTTYPE_PACK;
|
||||
rptr_sort->cf_type = i;
|
||||
}
|
||||
}
|
||||
|
||||
globfree(&globinfo);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// these should always be the same
|
||||
Assert(root_index == temp_root_count);
|
||||
|
||||
// sort the roots
|
||||
std::sort(temp_roots_sort, temp_roots_sort + temp_root_count, cf_packfile_sort_func);
|
||||
|
||||
// now insert them all into the real root list properly
|
||||
for (i = 0; i < temp_root_count; i++) {
|
||||
auto new_root = cf_create_root();
|
||||
new_root->location_flags = root->location_flags;
|
||||
strcpy_s( new_root->path, root->path );
|
||||
|
||||
#ifndef NDEBUG
|
||||
uint chksum = 0;
|
||||
cf_chksum_pack(temp_roots_sort[i].path, &chksum);
|
||||
mprintf(("Found root pack '%s' with a checksum of 0x%08x\n", temp_roots_sort[i].path, chksum));
|
||||
#endif
|
||||
|
||||
// mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there
|
||||
// to find the files.
|
||||
strcpy_s(new_root->path, temp_roots_sort[i].path);
|
||||
new_root->roottype = CF_ROOTTYPE_PACK;
|
||||
}
|
||||
|
||||
// free up the temp list
|
||||
vm_free(temp_roots_sort);
|
||||
}
|
||||
|
||||
static char normalize_directory_separator(char in)
|
||||
{
|
||||
if (in == '/')
|
||||
{
|
||||
return DIR_SEPARATOR_CHAR;
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
static void cf_add_mod_roots(const char* rootDirectory, uint32_t basic_location)
|
||||
{
|
||||
if (Cmdline_mod)
|
||||
{
|
||||
bool primary = true;
|
||||
for (const char* cur_pos=Cmdline_mod; strlen(cur_pos) != 0; cur_pos+= (strlen(cur_pos)+1))
|
||||
{
|
||||
SCP_stringstream ss;
|
||||
ss << rootDirectory;
|
||||
|
||||
if (rootDirectory[strlen(rootDirectory) - 1] != DIR_SEPARATOR_CHAR)
|
||||
{
|
||||
ss << DIR_SEPARATOR_CHAR;
|
||||
}
|
||||
|
||||
ss << cur_pos << DIR_SEPARATOR_STR;
|
||||
|
||||
SCP_string rootPath = ss.str();
|
||||
if (rootPath.size() + 1 >= CF_MAX_PATHNAME_LENGTH) {
|
||||
Error(LOCATION, "The length of mod directory path '%s' exceeds the maximum of %d!\n", rootPath.c_str(), CF_MAX_PATHNAME_LENGTH);
|
||||
}
|
||||
|
||||
// normalize the path to the native path format
|
||||
std::transform(rootPath.begin(), rootPath.end(), rootPath.begin(), normalize_directory_separator);
|
||||
|
||||
cf_root* root = cf_create_root();
|
||||
|
||||
strncpy(root->path, rootPath.c_str(), CF_MAX_PATHNAME_LENGTH-1);
|
||||
if (primary) {
|
||||
root->location_flags = basic_location | CF_LOCATION_TYPE_PRIMARY_MOD;
|
||||
} else {
|
||||
root->location_flags = basic_location | CF_LOCATION_TYPE_SECONDARY_MODS;
|
||||
}
|
||||
|
||||
root->roottype = CF_ROOTTYPE_PATH;
|
||||
cf_build_pack_list(root);
|
||||
|
||||
primary = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void cf_build_root_list(const char *cdrom_dir)
|
||||
void cf_build_root_list(const SCP_vector<cfile::ModRoot>& modRoots)
|
||||
{
|
||||
Num_roots = 0;
|
||||
Num_path_roots = 0;
|
||||
|
||||
cf_root *root = nullptr;
|
||||
auto has_mod = has_mod_location(modRoots);
|
||||
|
||||
if (os_is_legacy_mode())
|
||||
mprintf(("CFile root configuration:\n"));
|
||||
// Go through the roots specified and convert them to the internal data structure
|
||||
for (const auto& root : modRoots)
|
||||
{
|
||||
// =========================================================================
|
||||
#ifdef WIN32
|
||||
// Nothing to do here, Windows uses the current directory as the base
|
||||
#else
|
||||
cf_add_mod_roots(os_get_legacy_user_dir(), CF_LOCATION_ROOT_USER);
|
||||
|
||||
root = cf_create_root();
|
||||
strncpy(root->path, os_get_legacy_user_dir(), CF_MAX_PATHNAME_LENGTH - 1);
|
||||
|
||||
root->location_flags |= CF_LOCATION_ROOT_USER | CF_LOCATION_TYPE_ROOT;
|
||||
if (Cmdline_mod == nullptr || strlen(Cmdline_mod) <= 0) {
|
||||
// If there are no mods then the root is the primary mod
|
||||
root->location_flags |= CF_LOCATION_TYPE_PRIMARY_MOD;
|
||||
if (root.path.size() >= CF_MAX_PATHNAME_LENGTH - 1)
|
||||
{
|
||||
Error(LOCATION, "Mod root path '%s' is longer than the maximum %d!", root.path.c_str(), CF_MAX_PATHNAME_LENGTH - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// do we already have a slash? as in the case of a root directory install
|
||||
if ((strlen(root->path) < (CF_MAX_PATHNAME_LENGTH - 1)) && (root->path[strlen(root->path) - 1] != DIR_SEPARATOR_CHAR)) {
|
||||
strcat_s(root->path, DIR_SEPARATOR_STR); // put trailing backslash on for easier path construction
|
||||
}
|
||||
root->roottype = CF_ROOTTYPE_PATH;
|
||||
auto cfile_root = cf_create_root();
|
||||
memset(cfile_root, 0, sizeof(*cfile_root));
|
||||
|
||||
// Next, check any VP files under the current directory.
|
||||
cf_build_pack_list(root);
|
||||
strcpy_s(cfile_root->path, root.path.c_str());
|
||||
|
||||
if (root.user_location)
|
||||
{
|
||||
cfile_root->location_flags |= CF_LOCATION_ROOT_USER;
|
||||
} else {
|
||||
cfile_root->location_flags |= CF_LOCATION_ROOT_GAME;
|
||||
}
|
||||
|
||||
if (root.location_type == cfile::LocationType::SecondaryMod)
|
||||
{
|
||||
cfile_root->location_flags |= CF_LOCATION_TYPE_SECONDARY_MODS;
|
||||
} else if (root.location_type == cfile::LocationType::PrimaryMod)
|
||||
{
|
||||
cfile_root->location_flags |= CF_LOCATION_TYPE_PRIMARY_MOD;
|
||||
} else if (root.location_type == cfile::LocationType::GameRoot)
|
||||
{
|
||||
cfile_root->location_flags |= CF_LOCATION_TYPE_ROOT;
|
||||
if (!has_mod)
|
||||
{
|
||||
// If we do not have a mod then game roots are also primary mods
|
||||
cfile_root->location_flags |= CF_LOCATION_TYPE_PRIMARY_MOD;
|
||||
}
|
||||
} else {
|
||||
UNREACHABLE("Unknown root location type!");
|
||||
}
|
||||
|
||||
switch (root.type)
|
||||
{
|
||||
case cfile::ModRootType::Folder:
|
||||
cfile_root->roottype = CF_ROOTTYPE_PATH;
|
||||
break;
|
||||
case cfile::ModRootType::Package:
|
||||
cfile_root->roottype = CF_ROOTTYPE_PACK;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Unknown root type!");
|
||||
break;
|
||||
}
|
||||
|
||||
if (root.type == cfile::ModRootType::Folder)
|
||||
{
|
||||
size_t path_len = strlen(cfile_root->path);
|
||||
|
||||
// do we already have a slash? as in the case of a root directory install
|
||||
if ( (path_len < (CF_MAX_PATHNAME_LENGTH-1)) && (cfile_root->path[path_len-1] != DIR_SEPARATOR_CHAR) ) {
|
||||
strcat_s(cfile_root->path, DIR_SEPARATOR_STR); // put trailing backslash on for easier path construction
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
switch (root.type)
|
||||
{
|
||||
case cfile::ModRootType::Folder:
|
||||
mprintf((" Found root folder '%s'\n", root.path.c_str()));
|
||||
break;
|
||||
case cfile::ModRootType::Package: {
|
||||
uint chksum = 0;
|
||||
cf_chksum_pack(root.path.c_str(), &chksum);
|
||||
mprintf((" Found root pack '%s' with a checksum of 0x%08x\n", root.path.c_str(), chksum));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE("Unknown root type!");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
// =========================================================================
|
||||
}
|
||||
else if (!Cmdline_portable_mode)
|
||||
{
|
||||
// =========================================================================
|
||||
// now look for mods under the users HOME directory to use before system ones
|
||||
cf_add_mod_roots(Cfile_user_dir, CF_LOCATION_ROOT_USER);
|
||||
// =========================================================================
|
||||
|
||||
// =========================================================================
|
||||
// set users HOME directory as default for loading and saving files
|
||||
root = cf_create_root();
|
||||
strcpy_s(root->path, Cfile_user_dir);
|
||||
|
||||
root->location_flags |= CF_LOCATION_ROOT_USER | CF_LOCATION_TYPE_ROOT;
|
||||
if (Cmdline_mod == nullptr || strlen(Cmdline_mod) <= 0) {
|
||||
// If there are no mods then the root is the primary mod
|
||||
root->location_flags |= CF_LOCATION_TYPE_PRIMARY_MOD;
|
||||
}
|
||||
|
||||
// do we already have a slash? as in the case of a root directory install
|
||||
if ((strlen(root->path) < (CF_MAX_PATHNAME_LENGTH - 1)) && (root->path[strlen(root->path) - 1] != DIR_SEPARATOR_CHAR)) {
|
||||
strcat_s(root->path, DIR_SEPARATOR_STR); // put trailing backslash on for easier path construction
|
||||
}
|
||||
root->roottype = CF_ROOTTYPE_PATH;
|
||||
|
||||
// Next, check any VP files under the current directory.
|
||||
cf_build_pack_list(root);
|
||||
// =========================================================================
|
||||
}
|
||||
|
||||
char working_directory[CF_MAX_PATHNAME_LENGTH];
|
||||
|
||||
if ( !_getcwd(working_directory, CF_MAX_PATHNAME_LENGTH ) ) {
|
||||
Error(LOCATION, "Can't get current working directory -- %d", errno );
|
||||
}
|
||||
|
||||
cf_add_mod_roots(working_directory, CF_LOCATION_ROOT_GAME);
|
||||
|
||||
root = cf_create_root();
|
||||
|
||||
root->location_flags |= CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT;
|
||||
if (Cmdline_mod == nullptr || strlen(Cmdline_mod) <= 0) {
|
||||
// If there are no mods then the root is the primary mod
|
||||
root->location_flags |= CF_LOCATION_TYPE_PRIMARY_MOD;
|
||||
}
|
||||
|
||||
strcpy_s(root->path, working_directory);
|
||||
|
||||
size_t path_len = strlen(root->path);
|
||||
|
||||
// do we already have a slash? as in the case of a root directory install
|
||||
if ( (path_len < (CF_MAX_PATHNAME_LENGTH-1)) && (root->path[path_len-1] != DIR_SEPARATOR_CHAR) ) {
|
||||
strcat_s(root->path, DIR_SEPARATOR_STR); // put trailing backslash on for easier path construction
|
||||
}
|
||||
|
||||
root->roottype = CF_ROOTTYPE_PATH;
|
||||
|
||||
//======================================================
|
||||
// Next, check any VP files under the current directory.
|
||||
cf_build_pack_list(root);
|
||||
|
||||
|
||||
//======================================================
|
||||
// Check the real CD if one...
|
||||
if ( cdrom_dir && (strlen(cdrom_dir) < CF_MAX_PATHNAME_LENGTH) ) {
|
||||
root = cf_create_root();
|
||||
strcpy_s( root->path, cdrom_dir );
|
||||
root->roottype = CF_ROOTTYPE_PATH;
|
||||
|
||||
//======================================================
|
||||
// Next, check any VP files in the CD-ROM directory.
|
||||
cf_build_pack_list(root);
|
||||
|
||||
}
|
||||
|
||||
// The final root is the in-memory root
|
||||
root = cf_create_root();
|
||||
auto root = cf_create_root();
|
||||
root->location_flags = CF_LOCATION_ROOT_MEMORY | CF_LOCATION_TYPE_ROOT;
|
||||
memset(root->path, 0, sizeof(root->path));
|
||||
root->roottype = CF_ROOTTYPE_MEMORY;
|
||||
|
||||
}
|
||||
|
||||
// Given a lower case list of file extensions
|
||||
@@ -923,7 +657,7 @@ void cf_build_file_list()
|
||||
}
|
||||
|
||||
|
||||
void cf_build_secondary_filelist(const char *cdrom_dir)
|
||||
void cf_build_secondary_filelist(const SCP_vector<cfile::ModRoot>& modRoots)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -944,7 +678,7 @@ void cf_build_secondary_filelist(const char *cdrom_dir)
|
||||
mprintf(( "Building file index...\n" ));
|
||||
|
||||
// build the list of searchable roots
|
||||
cf_build_root_list(cdrom_dir);
|
||||
cf_build_root_list(modRoots);
|
||||
|
||||
// build the list of files themselves
|
||||
cf_build_file_list();
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "cfile/cfile.h"
|
||||
|
||||
// Builds a list of all the files
|
||||
void cf_build_secondary_filelist( const char *cdrom_path );
|
||||
void cf_build_secondary_filelist(const SCP_vector<cfile::ModRoot>& modRoots);
|
||||
void cf_free_secondary_filelist();
|
||||
|
||||
// Internal stuff
|
||||
|
||||
+396
-212
@@ -5,27 +5,27 @@
|
||||
* or otherwise commercially exploit the source or things you created based on the
|
||||
* source.
|
||||
*
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "camera/camera.h" //VIEWER_ZOOM_DEFAULT
|
||||
#include "cmdline/cmdline.h"
|
||||
#include "camera/camera.h" //VIEWER_ZOOM_DEFAULT
|
||||
#include "cfile/cfilesystem.h"
|
||||
#include "fireball/fireballs.h"
|
||||
#include "globalincs/linklist.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "globalincs/version.h"
|
||||
#include "graphics/shadows.h"
|
||||
#include "hud/hudconfig.h"
|
||||
#include "network/multi.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "parse/sexp.h"
|
||||
#include "globalincs/version.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "cfile/cfilesystem.h"
|
||||
#include "sound/speech.h"
|
||||
#include "sound/openal.h"
|
||||
#include "io/joy.h"
|
||||
#include "network/multi.h"
|
||||
#include "options/OptionsManager.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "parse/sexp.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "sound/openal.h"
|
||||
#include "sound/speech.h"
|
||||
#include "starfield/starfield.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
@@ -34,6 +34,9 @@
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#include "cfile/cfile.h"
|
||||
#include "cfile/cfilemod.h"
|
||||
|
||||
#ifdef SCP_UNIX
|
||||
#include "osapi/osapi.h"
|
||||
#include <dirent.h>
|
||||
@@ -71,23 +74,36 @@ enum class flag_output_type {
|
||||
Json_V1,
|
||||
};
|
||||
|
||||
enum class flag_input_type {
|
||||
Flags,
|
||||
Stdin_Json_V1,
|
||||
};
|
||||
|
||||
// variables
|
||||
class cmdline_parm {
|
||||
public:
|
||||
cmdline_parm *next, *prev;
|
||||
const char *name; // name of parameter, must start with '-' char
|
||||
const char *help; // help text for this parameter
|
||||
const bool stacks; // whether this arg stacks with each use or is replaced by newest use (should only be used for strings!!)
|
||||
char *args; // string value for parameter arguments (NULL if no arguments)
|
||||
int name_found; // true if parameter on command line, otherwise false
|
||||
const int arg_type; // from enum cmdline_arg_type; used for help
|
||||
cmdline_parm *next = nullptr, *prev = nullptr;
|
||||
const char* name; // name of parameter, must start with '-' char
|
||||
const char* help; // help text for this parameter
|
||||
const bool stacks; // whether this arg stacks with each use or is replaced by newest use (should only be used for
|
||||
// strings!!)
|
||||
SCP_string args; // string value for parameter arguments (empty if no arguments)
|
||||
bool name_found; // true if parameter on command line, otherwise false
|
||||
const int arg_type; // from enum cmdline_arg_type; used for help
|
||||
|
||||
cmdline_parm(const char *name, const char *help, const int arg_type, const bool stacks = false);
|
||||
~cmdline_parm();
|
||||
int found();
|
||||
cmdline_parm(const char* name, const char* help, const int arg_type, const bool stacks = false) noexcept;
|
||||
~cmdline_parm() noexcept;
|
||||
|
||||
cmdline_parm(const cmdline_parm& other) = delete;
|
||||
cmdline_parm& operator=(const cmdline_parm& other) = delete;
|
||||
|
||||
cmdline_parm(cmdline_parm&& other) = delete;
|
||||
cmdline_parm& operator=(cmdline_parm&& other) = delete;
|
||||
|
||||
bool found();
|
||||
int get_int();
|
||||
float get_float();
|
||||
char *str();
|
||||
SCP_string str();
|
||||
bool check_if_args_is_valid();
|
||||
bool has_param();
|
||||
};
|
||||
@@ -275,11 +291,11 @@ cmdline_parm mouse_coords("-coords", NULL, AT_NONE); // Cmdline_mouse_coords
|
||||
cmdline_parm timeout("-timeout", "Multiplayer network timeout (secs)", AT_INT); // Cmdline_timeout
|
||||
cmdline_parm bit32_arg("-32bit", "Deprecated", AT_NONE); // (only here for retail compatibility reasons, doesn't actually do anything)
|
||||
|
||||
char *Cmdline_connect_addr = NULL;
|
||||
char *Cmdline_game_name = NULL;
|
||||
char *Cmdline_game_password = NULL;
|
||||
char *Cmdline_rank_above = NULL;
|
||||
char *Cmdline_rank_below = NULL;
|
||||
SCP_string Cmdline_connect_addr;
|
||||
SCP_string Cmdline_game_name;
|
||||
SCP_string Cmdline_game_password;
|
||||
SCP_string Cmdline_rank_above;
|
||||
SCP_string Cmdline_rank_below;
|
||||
int Cmdline_cd_check = 1;
|
||||
int Cmdline_client_dodamage = 0;
|
||||
int Cmdline_closed_game = 0;
|
||||
@@ -331,13 +347,11 @@ cmdline_parm no_deferred_lighting_arg("-no_deferred", NULL, AT_NONE); // Cmdline
|
||||
cmdline_parm anisotropy_level_arg("-anisotropic_filter", NULL, AT_INT);
|
||||
|
||||
float Cmdline_clip_dist = Default_min_draw_distance;
|
||||
float Cmdline_fov = 0.75f;
|
||||
float Cmdline_ogl_spec = 80.0f;
|
||||
int Cmdline_ambient_factor = 128;
|
||||
int Cmdline_env = 1;
|
||||
int Cmdline_mipmap = 0;
|
||||
int Cmdline_glow = 1;
|
||||
int Cmdline_nomotiondebris = 0;
|
||||
int Cmdline_noscalevid = 0;
|
||||
int Cmdline_spec = 1;
|
||||
int Cmdline_no_emissive = 0;
|
||||
@@ -345,15 +359,8 @@ int Cmdline_normal = 1;
|
||||
int Cmdline_height = 1;
|
||||
int Cmdline_enable_3d_shockwave = 0;
|
||||
int Cmdline_softparticles = 0;
|
||||
int Cmdline_postprocess = 0;
|
||||
int Cmdline_bloom_intensity = 75;
|
||||
bool Cmdline_fxaa = false;
|
||||
int Cmdline_fxaa_preset = 6;
|
||||
extern int Fxaa_preset_last_frame;
|
||||
bool Cmdline_fb_explosions = 0;
|
||||
bool Cmdline_fb_thrusters = false;
|
||||
extern bool ls_force_off;
|
||||
int Cmdline_shadow_quality = 0;
|
||||
int Cmdline_no_deferred_lighting = 0;
|
||||
int Cmdline_aniso_level = 0;
|
||||
|
||||
@@ -401,7 +408,7 @@ int Cmdline_no_enhanced_sound = 0;
|
||||
// MOD related
|
||||
cmdline_parm mod_arg("-mod", "List of folders to overwrite/add-to the default data", AT_STRING, true); // Cmdline_mod -- DTP modsupport
|
||||
|
||||
char *Cmdline_mod = NULL; //DTP for mod argument
|
||||
SCP_vector<cfile::ModRoot> Cmdline_mod; // DTP for mod argument
|
||||
|
||||
// Multiplayer/Network related
|
||||
cmdline_parm almission_arg("-almission", "Autoload multiplayer mission", AT_STRING); // Cmdline_almission -- DTP for autoload Multi mission
|
||||
@@ -411,15 +418,19 @@ cmdline_parm missioncrcspew_arg("-missioncrcs", NULL, AT_STRING); // Cmdline_sp
|
||||
cmdline_parm tablecrcspew_arg("-tablecrcs", NULL, AT_STRING); // Cmdline_spew_table_crcs
|
||||
cmdline_parm objupd_arg("-cap_object_update", "Multiplayer object update cap (0-3)", AT_INT);
|
||||
|
||||
char *Cmdline_almission = NULL; //DTP for autoload multi mission.
|
||||
SCP_string Cmdline_almission; // DTP for autoload multi mission.
|
||||
int Cmdline_ingamejoin = 0;
|
||||
int Cmdline_mpnoreturn = 0;
|
||||
char *Cmdline_spew_mission_crcs = NULL;
|
||||
char *Cmdline_spew_table_crcs = NULL;
|
||||
SCP_string Cmdline_spew_mission_crcs;
|
||||
SCP_string Cmdline_spew_table_crcs;
|
||||
int Cmdline_objupd = 3; // client object updates on LAN by default
|
||||
|
||||
// Launcher related options
|
||||
cmdline_parm portable_mode("-portable_mode", NULL, AT_NONE);
|
||||
cmdline_parm
|
||||
stdin_flags("-stdin_flags",
|
||||
"Tells FSO to read flags from stdin. Parameter is format if data supplied. Possible values: json_v1",
|
||||
AT_STRING);
|
||||
|
||||
bool Cmdline_portable_mode = false;
|
||||
|
||||
@@ -451,7 +462,7 @@ int Cmdline_no_pbo = 0;
|
||||
int Cmdline_ati_color_swap = 0;
|
||||
int Cmdline_no_3d_sound = 0;
|
||||
int Cmdline_drawelements = 0;
|
||||
char* Cmdline_keyboard_layout = NULL;
|
||||
SCP_string Cmdline_keyboard_layout;
|
||||
bool Cmdline_gl_finish = false;
|
||||
bool Cmdline_no_geo_sdr_effects = false;
|
||||
bool Cmdline_set_cpu_affinity = false;
|
||||
@@ -488,8 +499,7 @@ cmdline_parm show_video_info("-show_video_info", NULL, AT_NONE); //Cmdline_show_
|
||||
cmdline_parm frame_profile_arg("-profile_frame_time", NULL, AT_NONE); //Cmdline_frame_profile
|
||||
cmdline_parm debug_window_arg("-debug_window", NULL, AT_NONE); // Cmdline_debug_window
|
||||
|
||||
|
||||
char *Cmdline_start_mission = NULL;
|
||||
SCP_string Cmdline_start_mission;
|
||||
int Cmdline_dis_collisions = 0;
|
||||
int Cmdline_dis_weapons = 0;
|
||||
bool Cmdline_output_sexp_info = false;
|
||||
@@ -504,8 +514,8 @@ int Cmdline_show_stats = 0;
|
||||
int Cmdline_save_render_targets = 0;
|
||||
int Cmdline_window = 0;
|
||||
int Cmdline_fullscreen_window = 0;
|
||||
char *Cmdline_res = 0;
|
||||
char *Cmdline_center_res = 0;
|
||||
SCP_string Cmdline_res;
|
||||
SCP_string Cmdline_center_res;
|
||||
int Cmdline_verify_vps = 0;
|
||||
int Cmdline_reparse_mainhall = 0;
|
||||
bool Cmdline_profile_write_file = false;
|
||||
@@ -556,8 +566,8 @@ void cmdline_debug_print_cmdline()
|
||||
|
||||
for (parmp = GET_FIRST(&Parm_list); parmp !=END_OF_LIST(&Parm_list); parmp = GET_NEXT(parmp) ) {
|
||||
if ( parmp->name_found ) {
|
||||
if ( parmp->args != NULL ) {
|
||||
mprintf(("\n %s %s", parmp->name, parmp->args));
|
||||
if (!parmp->args.empty()) {
|
||||
mprintf(("\n %s %s", parmp->name, parmp->args.c_str()));
|
||||
} else {
|
||||
mprintf(("\n %s", parmp->name));
|
||||
}
|
||||
@@ -663,14 +673,13 @@ char *drop_extra_chars(char *str)
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @brief Processes one argument for the given parameter
|
||||
*
|
||||
* @param param The parameter to check
|
||||
* @param argc The argument count
|
||||
* @param argc The argument values
|
||||
* @param argc The current index
|
||||
* @param argv The argument values
|
||||
* @param index The current index
|
||||
* @return @c true when an extra parameter was found, @c false otherwise
|
||||
*/
|
||||
bool parm_stuff_args(cmdline_parm *parm, int argc, char *argv[], int index)
|
||||
@@ -687,47 +696,17 @@ bool parm_stuff_args(cmdline_parm *parm, int argc, char *argv[], int index)
|
||||
}
|
||||
else
|
||||
{
|
||||
char* saved_args = NULL;
|
||||
|
||||
if (parm->args != NULL) {
|
||||
if (parm->stacks) {
|
||||
saved_args = parm->args;
|
||||
if (parm->stacks) {
|
||||
if (!parm->args.empty())
|
||||
{
|
||||
// Avoid adding the , at the start
|
||||
parm->args += ",";
|
||||
}
|
||||
else {
|
||||
delete[] parm->args;
|
||||
}
|
||||
|
||||
parm->args = NULL;
|
||||
}
|
||||
|
||||
size_t argsize = strlen(argv[index + 1]);
|
||||
size_t buffersize = argsize;
|
||||
|
||||
if (saved_args != NULL)
|
||||
{
|
||||
// Add one for the , separating args
|
||||
buffersize += strlen(saved_args) + 1;
|
||||
}
|
||||
|
||||
buffersize += 1; // Null-terminator
|
||||
|
||||
parm->args = new char[buffersize];
|
||||
memset(parm->args, 0, buffersize);
|
||||
|
||||
if (saved_args != NULL)
|
||||
{
|
||||
// saved args go first, then new arg
|
||||
strcpy_s(parm->args, buffersize, saved_args);
|
||||
// add a separator too, so that we can tell the args apart
|
||||
strcat_s(parm->args, buffersize, ",");
|
||||
// now the new arg
|
||||
strcat_s(parm->args, buffersize, argv[index + 1]);
|
||||
|
||||
delete[] saved_args;
|
||||
parm->args += argv[index + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy_s(parm->args, buffersize, argv[index + 1]);
|
||||
parm->args = argv[index + 1];
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -759,7 +738,7 @@ void os_parse_parms(int argc, char *argv[])
|
||||
for (parmp = GET_FIRST(&Parm_list); parmp != END_OF_LIST(&Parm_list); parmp = GET_NEXT(parmp)) {
|
||||
if (!stricmp(parmp->name, argv[i]))
|
||||
{
|
||||
parmp->name_found = 1;
|
||||
parmp->name_found = true;
|
||||
if (parm_stuff_args(parmp, argc, argv, i))
|
||||
{
|
||||
i++;
|
||||
@@ -940,11 +919,8 @@ bool has_cmdline_only_flag(int argc, char *argv[])
|
||||
static void reset_cmdline_parms()
|
||||
{
|
||||
for (cmdline_parm *parmp = GET_FIRST(&Parm_list); parmp != END_OF_LIST(&Parm_list); parmp = GET_NEXT(parmp)) {
|
||||
if (parmp->args != NULL) {
|
||||
delete[] parmp->args;
|
||||
parmp->args = NULL;
|
||||
}
|
||||
parmp->name_found = 0;
|
||||
parmp->args.clear();
|
||||
parmp->name_found = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1040,11 +1016,14 @@ void os_init_cmdline(int argc, char *argv[])
|
||||
* @param arg_type_ parameters arguement type (if any)
|
||||
* @param stacks_ can the parameter be stacked
|
||||
*/
|
||||
cmdline_parm::cmdline_parm(const char *name_, const char *help_, const int arg_type_, const bool stacks_):
|
||||
name(name_), help(help_), stacks(stacks_), arg_type(arg_type_)
|
||||
cmdline_parm::cmdline_parm(const char* name_, const char* help_, const int arg_type_, const bool stacks_) noexcept
|
||||
: name(name_), help(help_), stacks(stacks_), arg_type(arg_type_)
|
||||
{
|
||||
args = NULL;
|
||||
name_found = 0;
|
||||
if (stacks) {
|
||||
Assertion(arg_type == AT_STRING, "Only string arguments can stack!");
|
||||
}
|
||||
|
||||
name_found = false;
|
||||
|
||||
if (Parm_list_inited == 0) {
|
||||
Assertion(&Parm_list == this, "Coding error! 1st initialised cmdline_parm must be static Parm_list\n");
|
||||
@@ -1063,20 +1042,12 @@ cmdline_parm::cmdline_parm(const char *name_, const char *help_, const int arg_t
|
||||
|
||||
|
||||
// destructor - frees any allocated memory
|
||||
cmdline_parm::~cmdline_parm()
|
||||
{
|
||||
#ifndef FRED
|
||||
if (args) {
|
||||
delete [] args;
|
||||
args = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
cmdline_parm::~cmdline_parm() noexcept = default;
|
||||
|
||||
// checks if the objects args variable is valid
|
||||
// returns true if it is, shows an error box and returns false if not valid.
|
||||
bool cmdline_parm::check_if_args_is_valid() {
|
||||
if ( args == NULL ) {
|
||||
if (args.empty()) {
|
||||
Error(__FILE__, __LINE__,
|
||||
"Command line flag passed that requires an argument, but the argument is missing!\r\n"
|
||||
"The flag is '%s', make sure that you have an argument that follows it.\r\n"
|
||||
@@ -1090,10 +1061,7 @@ bool cmdline_parm::check_if_args_is_valid() {
|
||||
|
||||
|
||||
// returns - true if the parameter exists on the command line, otherwise false
|
||||
int cmdline_parm::found()
|
||||
{
|
||||
return name_found;
|
||||
}
|
||||
bool cmdline_parm::found() { return name_found; }
|
||||
|
||||
// returns - the interger representation for the parameter argument
|
||||
int cmdline_parm::get_int()
|
||||
@@ -1101,22 +1069,12 @@ int cmdline_parm::get_int()
|
||||
Assertion(arg_type == AT_INT, "Coding error! Cmdline arg (%s) called cmdline_parm::get_int() with invalid arg_type (%s)", name, cmdline_arg_types[arg_type]);
|
||||
check_if_args_is_valid();
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
if (stacks) {
|
||||
// first off, DON'T STACK NON-STRINGS!!
|
||||
Int3();
|
||||
|
||||
// secondly, we still need to get it right for the user's sake...
|
||||
char *moron = strstr(args, ",");
|
||||
|
||||
if ( moron && ((strlen(moron) + 1) < strlen(args)) ) {
|
||||
// we get the last arg, since it's the newest one
|
||||
offset = strlen(args) - strlen(moron) + 1;
|
||||
}
|
||||
try {
|
||||
return std::stoi(args);
|
||||
} catch (const std::exception& e) {
|
||||
Warning(LOCATION, "Failed to convert '%s' to a float for parameter '%s'!", args.c_str(), name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return atoi(args+offset);
|
||||
}
|
||||
|
||||
|
||||
@@ -1126,36 +1084,24 @@ float cmdline_parm::get_float()
|
||||
Assertion(arg_type == AT_FLOAT, "Coding error! Cmdline arg (%s) called cmdline_parm::get_float() with invalid arg_type (%s)", name, cmdline_arg_types[arg_type]);
|
||||
check_if_args_is_valid();
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
if (stacks) {
|
||||
// first off, DON'T STACK NON-STRINGS!!
|
||||
Int3();
|
||||
|
||||
// secondly, we still need to get it right for the user's sake
|
||||
char *moron = strstr(args, ",");
|
||||
|
||||
if ( moron && ((strlen(moron) + 1) < strlen(args)) ) {
|
||||
// we get the last arg, since it's the newest one
|
||||
offset = strlen(args) - strlen(moron) + 1;
|
||||
}
|
||||
try {
|
||||
return std::stof(args);
|
||||
} catch (const std::exception& e) {
|
||||
Warning(LOCATION, "Failed to convert '%s' to a float for parameter '%s'!", args.c_str(), name);
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return (float)atof(args+offset);
|
||||
}
|
||||
|
||||
|
||||
// returns - the string value for the parameter argument
|
||||
char *cmdline_parm::str()
|
||||
SCP_string cmdline_parm::str()
|
||||
{
|
||||
Assertion(arg_type == AT_STRING, "Coding error! Cmdline arg (%s) called cmdline_parm::str() with invalid arg_type (%s)", name, cmdline_arg_types[arg_type]);
|
||||
check_if_args_is_valid();
|
||||
|
||||
return args;
|
||||
}
|
||||
bool cmdline_parm::has_param() {
|
||||
return args != nullptr;
|
||||
}
|
||||
bool cmdline_parm::has_param() { return !args.empty(); }
|
||||
|
||||
#ifdef SCP_UNIX
|
||||
// Return a vector with all filesystem names of "parent/dir" relative to parent.
|
||||
@@ -1216,39 +1162,31 @@ static SCP_vector<SCP_string> unix_get_dir_names(const SCP_string& parent, const
|
||||
}
|
||||
|
||||
// For case sensitive filesystems (e.g. Linux/BSD) perform case-insensitive dir matches.
|
||||
static void handle_unix_modlist(char **modlist, size_t *len)
|
||||
static SCP_string handle_unix_modlist(const SCP_string& modlist)
|
||||
{
|
||||
// search filesystem for given paths
|
||||
SCP_vector<SCP_string> mod_paths;
|
||||
for (char *cur_mod = strtok(*modlist, ","); cur_mod != NULL; cur_mod = strtok(NULL, ","))
|
||||
{
|
||||
|
||||
SCP_stringstream iss(modlist);
|
||||
SCP_string cur_mod;
|
||||
while (std::getline(iss, cur_mod, ',')) {
|
||||
SCP_vector<SCP_string> this_mod_paths = unix_get_dir_names(".", cur_mod);
|
||||
// Ignore non-existing mods for unit tests
|
||||
if (!running_unittests && this_mod_paths.empty()) {
|
||||
ReleaseWarning(LOCATION, "Can't find mod '%s'. Ignoring.", cur_mod);
|
||||
ReleaseWarning(LOCATION, "Can't find mod '%s'. Ignoring.", cur_mod.c_str());
|
||||
}
|
||||
mod_paths.insert(mod_paths.end(), this_mod_paths.begin(), this_mod_paths.end());
|
||||
}
|
||||
|
||||
// create new char[] to replace modlist
|
||||
size_t total_len = 0;
|
||||
SCP_vector<SCP_string>::iterator ii, end = mod_paths.end();
|
||||
for (ii = mod_paths.begin(); ii != end; ++ii) {
|
||||
total_len += ii->length() + 1;
|
||||
}
|
||||
|
||||
char *new_modlist = new char[total_len + 1];
|
||||
memset(new_modlist, 0, total_len + 1);
|
||||
end = mod_paths.end();
|
||||
for (ii = mod_paths.begin(); ii != end; ++ii) {
|
||||
strcat_s(new_modlist, total_len + 1, ii->c_str());
|
||||
strcat_s(new_modlist, total_len + 1, ","); // replace later with NUL
|
||||
// create new string to replace modlist
|
||||
SCP_string new_modlist;
|
||||
for (const auto& path : mod_paths) {
|
||||
new_modlist += path;
|
||||
new_modlist += ","; // replace later with NUL
|
||||
}
|
||||
|
||||
// make the rest of the modlist manipulation unaware that anything happened here
|
||||
delete [] *modlist;
|
||||
*modlist = new_modlist;
|
||||
*len = total_len;
|
||||
return new_modlist;
|
||||
}
|
||||
#endif /* SCP_UNIX */
|
||||
|
||||
@@ -1306,6 +1244,7 @@ static json_t* json_get_v1() {
|
||||
json_array_append_new(caps_array, json_string("No D3D"));
|
||||
json_array_append_new(caps_array, json_string("New Sound"));
|
||||
json_array_append_new(caps_array, json_string("SDL"));
|
||||
json_array_append_new(caps_array, json_string("Stdin Flags: Json V1"));
|
||||
|
||||
json_object_set_new(root, "caps", caps_array);
|
||||
}
|
||||
@@ -1496,16 +1435,218 @@ static void write_flags() {
|
||||
case flag_output_type::Binary:
|
||||
write_flags_file();
|
||||
break;
|
||||
case flag_output_type::Json_V1:
|
||||
case flag_output_type::Json_V1: {
|
||||
json_t* root = json_get_v1();
|
||||
|
||||
json_dumpf(root, stdout, JSON_INDENT(4));
|
||||
json_decref(root);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SetCmdlineParams()
|
||||
static bool validate_version(json_t* rootEl)
|
||||
{
|
||||
const auto version_el = json_object_get(rootEl, "version");
|
||||
|
||||
if (version_el == nullptr) {
|
||||
printf("Version element is missing!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!json_is_number(version_el)) {
|
||||
printf("Version value is not a number!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (json_integer_value(version_el) != 1) {
|
||||
printf("Version value %" JSON_INTEGER_FORMAT " is not supported!\n", json_integer_value(version_el));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static cfile::ModRootType mapModRootType(const char* rootStr) {
|
||||
if (!stricmp(rootStr, "folder")) {
|
||||
return cfile::ModRootType::Folder;
|
||||
} else if (!stricmp(rootStr, "package")) {
|
||||
return cfile::ModRootType::Package;
|
||||
} else {
|
||||
return cfile::ModRootType::Unknown;
|
||||
}
|
||||
}
|
||||
static cfile::LocationType mapLocationType(const char* rootStr) {
|
||||
if (!stricmp(rootStr, "game_root")) {
|
||||
return cfile::LocationType::GameRoot;
|
||||
} else if (!stricmp(rootStr, "primary_mod")) {
|
||||
return cfile::LocationType::PrimaryMod;
|
||||
} else if (!stricmp(rootStr, "secondary_mod")) {
|
||||
return cfile::LocationType::SecondaryMod;
|
||||
} else {
|
||||
return cfile::LocationType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
static void stuff_mod_option(cmdline_parm* param, json_t* el)
|
||||
{
|
||||
if (!json_is_array(el))
|
||||
{
|
||||
printf("Value for option %s must be an array!", param->name);
|
||||
return;
|
||||
}
|
||||
|
||||
SCP_vector<cfile::ModRoot> roots;
|
||||
|
||||
size_t i;
|
||||
json_t* modObj;
|
||||
|
||||
json_array_foreach(el, i, modObj)
|
||||
{
|
||||
const auto typeEl = json_object_get(modObj, "type");
|
||||
const auto pathEl = json_object_get(modObj, "path");
|
||||
const auto locationEl = json_object_get(modObj, "location_type");
|
||||
|
||||
if (typeEl == nullptr || pathEl == nullptr || locationEl == nullptr)
|
||||
{
|
||||
printf("A required mod root element is missing: Type %p, Path %p, Location %p", typeEl, pathEl, locationEl);
|
||||
continue;
|
||||
}
|
||||
if (!json_is_string(typeEl) || !json_is_string(pathEl) || !json_is_string(locationEl))
|
||||
{
|
||||
printf("An element had a wrong type: Type %d, Path %d, Location %d", json_typeof(typeEl), json_typeof(pathEl), json_typeof(locationEl));
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto userEl = json_object_get(modObj, "user");
|
||||
|
||||
if (userEl != nullptr && !json_is_boolean(userEl))
|
||||
{
|
||||
printf("User element type is not a boolean!");
|
||||
continue;
|
||||
}
|
||||
|
||||
cfile::ModRoot root;
|
||||
root.type = mapModRootType(json_string_value(typeEl));
|
||||
root.path = json_string_value(pathEl);
|
||||
root.location_type = mapLocationType(json_string_value(locationEl));
|
||||
root.user_location = userEl == nullptr ? false : json_boolean_value(userEl);
|
||||
roots.push_back(root);
|
||||
}
|
||||
|
||||
Cmdline_mod = roots;
|
||||
}
|
||||
|
||||
static void stuff_json_option(cmdline_parm* param, json_t* el)
|
||||
{
|
||||
if (param == &mod_arg)
|
||||
{
|
||||
// The -mod parameter is handled specially since it is not a simple string
|
||||
stuff_mod_option(param, el);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (param->arg_type) {
|
||||
case AT_NONE:
|
||||
if (!json_is_boolean(el)) {
|
||||
printf("Value for option %s must be a boolean!", param->name);
|
||||
return;
|
||||
}
|
||||
param->name_found = json_is_true(el);
|
||||
break;
|
||||
case AT_INT:
|
||||
if (!json_is_number(el)) {
|
||||
printf("Value for option %s must be a number!", param->name);
|
||||
return;
|
||||
}
|
||||
param->name_found = true;
|
||||
param->args = std::to_string(json_integer_value(el));
|
||||
|
||||
break;
|
||||
case AT_FLOAT:
|
||||
if (!json_is_number(el)) {
|
||||
printf("Value for option %s must be a number!", param->name);
|
||||
return;
|
||||
}
|
||||
param->name_found = true;
|
||||
param->args = std::to_string(json_real_value(el));
|
||||
break;
|
||||
case AT_STRING:
|
||||
if (!json_is_string(el)) {
|
||||
printf("Value for option %s must be a string!", param->name);
|
||||
return;
|
||||
}
|
||||
param->name_found = true;
|
||||
param->args.assign(json_string_value(el), json_string_length(el));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Unknown arg type %d.", param->arg_type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_json_flags(json_t* rootEl)
|
||||
{
|
||||
if (!json_is_object(rootEl)) {
|
||||
printf("Root element must be an object!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!validate_version(rootEl)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto flagsEl = json_object_get(rootEl, "flags");
|
||||
|
||||
if (flagsEl == nullptr) {
|
||||
printf("Flags element is missing!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!json_is_object(flagsEl)) {
|
||||
printf("Flags element is not an object!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* flagName;
|
||||
json_t* flagValue;
|
||||
|
||||
json_object_foreach(flagsEl, flagName, flagValue)
|
||||
{
|
||||
for (auto parmp = GET_FIRST(&Parm_list); parmp != END_OF_LIST(&Parm_list); parmp = GET_NEXT(parmp)) {
|
||||
if (!stricmp(parmp->name, flagName)) {
|
||||
stuff_json_option(parmp, flagValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SetCmdlineParams(flag_input_type inputType);
|
||||
|
||||
static bool SetCmdlineParamsFromStdin(const SCP_string& format)
|
||||
{
|
||||
if (format != "json_v1") {
|
||||
// Invalid format!
|
||||
printf("INPUT TYPE '%s' IS NOT SUPPORTED!\n", format.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
json_error_t error;
|
||||
json_t* flagsEl = json_loadf(stdin, JSON_DISABLE_EOF_CHECK, &error);
|
||||
if (flagsEl == nullptr) {
|
||||
printf("Failed to parse JSON: %s(%d:%d): %s\n", error.source, error.line, error.column, error.text);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parse_json_flags(flagsEl)) {
|
||||
return false;
|
||||
}
|
||||
return SetCmdlineParams(flag_input_type::Stdin_Json_V1);
|
||||
}
|
||||
|
||||
static bool SetCmdlineParams(flag_input_type inputType)
|
||||
// Sets externed variables used for communication cmdline information
|
||||
{
|
||||
//getcwd(FreeSpace_Directory, 256); // set the directory to our fs2 root
|
||||
@@ -1517,6 +1658,10 @@ bool SetCmdlineParams()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inputType == flag_input_type::Flags && stdin_flags.found()) {
|
||||
return SetCmdlineParamsFromStdin(stdin_flags.str());
|
||||
}
|
||||
|
||||
if (no_fpscap.found())
|
||||
{
|
||||
Cmdline_NoFPSCap = 1;
|
||||
@@ -1548,8 +1693,8 @@ bool SetCmdlineParams()
|
||||
Cmdline_spew_mission_crcs = missioncrcspew_arg.str();
|
||||
|
||||
// strip off blank space at end if it's there
|
||||
if ( Cmdline_spew_mission_crcs[strlen(Cmdline_spew_mission_crcs)-1] == ' ' ) {
|
||||
Cmdline_spew_mission_crcs[strlen(Cmdline_spew_mission_crcs)-1] = '\0';
|
||||
if (Cmdline_spew_mission_crcs.back() == ' ') {
|
||||
Cmdline_spew_mission_crcs.resize(Cmdline_spew_mission_crcs.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1557,8 +1702,8 @@ bool SetCmdlineParams()
|
||||
Cmdline_spew_table_crcs = tablecrcspew_arg.str();
|
||||
|
||||
// strip off blank space at end if it's there
|
||||
if ( Cmdline_spew_table_crcs[strlen(Cmdline_spew_table_crcs)-1] == ' ' ) {
|
||||
Cmdline_spew_table_crcs[strlen(Cmdline_spew_table_crcs)-1] = '\0';
|
||||
if (Cmdline_spew_table_crcs.back() == ' ') {
|
||||
Cmdline_spew_table_crcs.resize(Cmdline_spew_table_crcs.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1624,8 +1769,8 @@ bool SetCmdlineParams()
|
||||
Cmdline_game_name = gamename_arg.str();
|
||||
|
||||
// be sure that this string fits in our limits
|
||||
if ( strlen(Cmdline_game_name) > MAX_GAMENAME_LEN ) {
|
||||
Cmdline_game_name[MAX_GAMENAME_LEN-1] = '\0';
|
||||
if (Cmdline_game_name.size() > MAX_GAMENAME_LEN - 1) {
|
||||
Cmdline_game_name.resize(MAX_GAMENAME_LEN - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1634,8 +1779,8 @@ bool SetCmdlineParams()
|
||||
Cmdline_game_password = gamepassword_arg.str();
|
||||
|
||||
// be sure that this string fits in our limits
|
||||
if ( strlen(Cmdline_game_name) > MAX_PASSWD_LEN ) {
|
||||
Cmdline_game_name[MAX_PASSWD_LEN-1] = '\0';
|
||||
if (Cmdline_game_password.size() > MAX_GAMENAME_LEN - 1) {
|
||||
Cmdline_game_password.resize(MAX_GAMENAME_LEN - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1685,18 +1830,33 @@ bool SetCmdlineParams()
|
||||
}
|
||||
|
||||
// d3d windowed
|
||||
if(window_arg.found()){
|
||||
if(window_arg.found()) {
|
||||
// We need to set both values since we don't know if we are going to use the new config system
|
||||
options::OptionsManager::instance()->setOverride("Graphics.WindowMode", "0");
|
||||
Cmdline_window = 1;
|
||||
}
|
||||
|
||||
if ( fullscreen_window_arg.found( ) )
|
||||
{
|
||||
options::OptionsManager::instance()->setOverride("Graphics.WindowMode", "1");
|
||||
Cmdline_fullscreen_window = 1;
|
||||
Cmdline_window = 0; /* Make sure no-one sets both */
|
||||
}
|
||||
|
||||
if(res_arg.found()){
|
||||
Cmdline_res = res_arg.str();
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
if (sscanf(Cmdline_res.c_str(), "%dx%d", &width, &height) == 2) {
|
||||
SCP_string override;
|
||||
sprintf(override, R"({"width":%d,"height":%d})", width, height);
|
||||
options::OptionsManager::instance()->setOverride("Graphics.Resolution", override);
|
||||
} else {
|
||||
mprintf(("Failed to parse -res parameter \"%s\". Must be in format \"<width>x<height>\".",
|
||||
Cmdline_res.c_str()));
|
||||
}
|
||||
}
|
||||
if(center_res_arg.found()){
|
||||
Cmdline_center_res = center_res_arg.str();
|
||||
@@ -1728,35 +1888,32 @@ bool SetCmdlineParams()
|
||||
Cmdline_noparseerrors = 1;
|
||||
}
|
||||
|
||||
|
||||
if(mod_arg.found() ) {
|
||||
Cmdline_mod = mod_arg.str();
|
||||
// Legacy mod handling is handled here
|
||||
if (inputType == flag_input_type::Flags && mod_arg.found()) {
|
||||
auto modCmdline = mod_arg.str();
|
||||
|
||||
// strip off blank space it it's there
|
||||
if ( Cmdline_mod[strlen(Cmdline_mod)-1] == ' ' ) {
|
||||
Cmdline_mod[strlen(Cmdline_mod)-1] = '\0';
|
||||
if (modCmdline.back() == ' ') {
|
||||
modCmdline.resize(modCmdline.size() - 1);
|
||||
}
|
||||
|
||||
// Ok - mod stacking support
|
||||
size_t len = strlen(Cmdline_mod);
|
||||
char *modlist = new char[len+2];
|
||||
memset( modlist, 0, len + 2 );
|
||||
strcpy_s(modlist, len+2, Cmdline_mod);
|
||||
|
||||
#ifdef SCP_UNIX
|
||||
// handle case-insensitive searching
|
||||
handle_unix_modlist(&modlist, &len);
|
||||
modCmdline = handle_unix_modlist(modCmdline);
|
||||
#endif
|
||||
|
||||
// null terminate each individual
|
||||
for (size_t i = 0; i < len; i++)
|
||||
for (auto& ch : modCmdline)
|
||||
{
|
||||
if (modlist[i] == ',')
|
||||
modlist[i] = '\0';
|
||||
if (ch == ',')
|
||||
ch = '\0';
|
||||
}
|
||||
|
||||
//copy over - we don't have to delete[] Cmdline_mod because it's a pointer to an automatic global char*
|
||||
Cmdline_mod = modlist;
|
||||
|
||||
char whee[MAX_PATH_LEN];
|
||||
memset(whee, 0, sizeof(whee));
|
||||
_getcwd(whee, MAX_PATH_LEN-1);
|
||||
|
||||
Cmdline_mod = cfile::getModRootsFromModCmdline(whee, modCmdline.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -1776,7 +1933,7 @@ bool SetCmdlineParams()
|
||||
}
|
||||
|
||||
if ( nomotiondebris_arg.found() ) {
|
||||
Cmdline_nomotiondebris = 1;
|
||||
Motion_debris_enabled = false;
|
||||
}
|
||||
|
||||
if( mipmap_arg.found() ) {
|
||||
@@ -1788,11 +1945,11 @@ bool SetCmdlineParams()
|
||||
}
|
||||
|
||||
if ( fov_arg.found() ) {
|
||||
Cmdline_fov = fov_arg.get_float();
|
||||
if (Cmdline_fov > 0.1) {
|
||||
VIEWER_ZOOM_DEFAULT = Cmdline_fov;
|
||||
auto val = fov_arg.get_float();
|
||||
if (val > 0.1) {
|
||||
VIEWER_ZOOM_DEFAULT = val;
|
||||
} else {
|
||||
VIEWER_ZOOM_DEFAULT = Cmdline_fov = 0.75f;
|
||||
VIEWER_ZOOM_DEFAULT = 0.75f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1806,7 +1963,7 @@ bool SetCmdlineParams()
|
||||
}
|
||||
|
||||
if ( use_3dwarp.found() ) {
|
||||
Cmdline_3dwarp = 1;
|
||||
Fireball_use_3d_warp = true;
|
||||
}
|
||||
|
||||
if ( use_warp_flash.found() ) {
|
||||
@@ -1849,7 +2006,7 @@ bool SetCmdlineParams()
|
||||
|
||||
if(no_vsync_arg.found() )
|
||||
{
|
||||
Cmdline_no_vsync = 1;
|
||||
Gr_enable_vsync = false;
|
||||
}
|
||||
|
||||
if ( normal_arg.found() ) {
|
||||
@@ -1861,13 +2018,20 @@ bool SetCmdlineParams()
|
||||
}
|
||||
|
||||
if (fxaa_arg.found() ) {
|
||||
Cmdline_fxaa = true;
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
|
||||
|
||||
if (fxaa_preset_arg.found()) {
|
||||
Cmdline_fxaa_preset = fxaa_preset_arg.get_int();
|
||||
auto val = fxaa_preset_arg.get_int();
|
||||
if (val > 6) {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_High;
|
||||
} else if (val > 3) {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
|
||||
} else {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Low;
|
||||
}
|
||||
}
|
||||
|
||||
Fxaa_preset_last_frame = Cmdline_fxaa_preset;
|
||||
Gr_aa_mode_last_frame = Gr_aa_mode;
|
||||
}
|
||||
|
||||
if ( glow_arg.found() )
|
||||
@@ -2004,17 +2168,17 @@ bool SetCmdlineParams()
|
||||
|
||||
if ( fb_explosions_arg.found() )
|
||||
{
|
||||
Cmdline_fb_explosions = 1;
|
||||
Gr_framebuffer_effects.set(FramebufferEffects::Shockwaves, true);
|
||||
}
|
||||
|
||||
if (fb_thrusters_arg.found())
|
||||
{
|
||||
Cmdline_fb_thrusters = true;
|
||||
Gr_framebuffer_effects.set(FramebufferEffects::Thrusters, true);
|
||||
}
|
||||
|
||||
if ( postprocess_arg.found() )
|
||||
{
|
||||
Cmdline_postprocess = 1;
|
||||
Gr_post_processing_enabled = true;
|
||||
}
|
||||
|
||||
if ( bloom_intensity_arg.found() )
|
||||
@@ -2034,12 +2198,32 @@ bool SetCmdlineParams()
|
||||
|
||||
if( enable_shadows_arg.found() )
|
||||
{
|
||||
Cmdline_shadow_quality = 2;
|
||||
Shadow_quality = ShadowQuality::Medium;
|
||||
}
|
||||
|
||||
if( shadow_quality_arg.found() )
|
||||
{
|
||||
Cmdline_shadow_quality = shadow_quality_arg.get_int();
|
||||
switch (shadow_quality_arg.get_int()) {
|
||||
case 0:
|
||||
Shadow_quality = ShadowQuality::Disabled;
|
||||
break;
|
||||
case 1:
|
||||
Shadow_quality = ShadowQuality::Low;
|
||||
break;
|
||||
case 2:
|
||||
Shadow_quality = ShadowQuality::Medium;
|
||||
break;
|
||||
case 3:
|
||||
Shadow_quality = ShadowQuality::High;
|
||||
break;
|
||||
case 4:
|
||||
Shadow_quality = ShadowQuality::Ultra;
|
||||
break;
|
||||
default:
|
||||
mprintf(("Invalid shadow quality %d. Disabling shadows...\n", shadow_quality_arg.get_int()));
|
||||
Shadow_quality = ShadowQuality::Disabled;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( no_deferred_lighting_arg.found() )
|
||||
@@ -2153,7 +2337,7 @@ int parse_cmdline(int argc, char *argv[])
|
||||
// --------------- Kazan -------------
|
||||
// If you're looking for the list of if (someparam.found()) { cmdline_someparam = something; } look above at this function
|
||||
// I did this because of fred2_parse_cmdline()
|
||||
return SetCmdlineParams();
|
||||
return SetCmdlineParams(flag_input_type::Flags);
|
||||
}
|
||||
|
||||
const char * get_param_desc(const char *flag_name)
|
||||
|
||||
+18
-21
@@ -12,6 +12,7 @@
|
||||
#ifndef FS_CMDLINE_HEADER_FILE
|
||||
#define FS_CMDLINE_HEADER_FILE
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
int parse_cmdline(int argc, char *argv[]);
|
||||
|
||||
@@ -24,11 +25,11 @@ int parse_cmdline(int argc, char *argv[]);
|
||||
|
||||
|
||||
// RETAIL OPTIONS ----------------------------------------------
|
||||
extern char *Cmdline_connect_addr;
|
||||
extern char *Cmdline_game_name;
|
||||
extern char *Cmdline_game_password;
|
||||
extern char *Cmdline_rank_above;
|
||||
extern char *Cmdline_rank_below;
|
||||
extern SCP_string Cmdline_connect_addr;
|
||||
extern SCP_string Cmdline_game_name;
|
||||
extern SCP_string Cmdline_game_password;
|
||||
extern SCP_string Cmdline_rank_above;
|
||||
extern SCP_string Cmdline_rank_below;
|
||||
extern int Cmdline_cd_check;
|
||||
extern int Cmdline_client_dodamage;
|
||||
extern int Cmdline_closed_game;
|
||||
@@ -46,8 +47,8 @@ extern int Cmdline_timeout;
|
||||
extern int Cmdline_use_last_pilot;
|
||||
extern int Cmdline_window;
|
||||
extern int Cmdline_fullscreen_window;
|
||||
extern char *Cmdline_res;
|
||||
extern char *Cmdline_center_res;
|
||||
extern SCP_string Cmdline_res;
|
||||
extern SCP_string Cmdline_center_res;
|
||||
|
||||
|
||||
// FSO OPTIONS -------------------------------------------------
|
||||
@@ -55,7 +56,6 @@ extern char *Cmdline_center_res;
|
||||
// Graphics related
|
||||
extern double specular_exponent_value;
|
||||
extern float Cmdline_clip_dist;
|
||||
extern float Cmdline_fov;
|
||||
extern float Cmdline_ogl_spec;
|
||||
extern float static_light_factor;
|
||||
extern float static_point_factor;
|
||||
@@ -63,20 +63,13 @@ extern float static_tube_factor;
|
||||
extern int Cmdline_ambient_factor;
|
||||
extern int Cmdline_env;
|
||||
extern int Cmdline_glow;
|
||||
extern int Cmdline_nomotiondebris;
|
||||
extern int Cmdline_noscalevid; // disables fit-to-window for movies - taylor
|
||||
extern int Cmdline_spec;
|
||||
extern int Cmdline_normal;
|
||||
extern int Cmdline_height;
|
||||
extern int Cmdline_enable_3d_shockwave;
|
||||
extern int Cmdline_softparticles;
|
||||
extern int Cmdline_postprocess;
|
||||
extern int Cmdline_bloom_intensity;
|
||||
extern bool Cmdline_fxaa;
|
||||
extern int Cmdline_fxaa_preset;
|
||||
extern bool Cmdline_fb_explosions;
|
||||
extern bool Cmdline_fb_thrusters;
|
||||
extern int Cmdline_shadow_quality;
|
||||
extern int Cmdline_no_deferred_lighting;
|
||||
extern int Cmdline_no_emissive;
|
||||
extern int Cmdline_aniso_level;
|
||||
@@ -104,14 +97,18 @@ extern int Cmdline_stretch_menu;
|
||||
extern int Cmdline_voice_recognition;
|
||||
extern int Cmdline_no_enhanced_sound;
|
||||
|
||||
namespace cfile {
|
||||
struct ModRoot;
|
||||
} // namespace cfile
|
||||
|
||||
// MOD related
|
||||
extern char *Cmdline_mod; // DTP for mod support
|
||||
extern SCP_vector<cfile::ModRoot> Cmdline_mod; // DTP for mod support
|
||||
// Multiplayer/Network related
|
||||
extern char *Cmdline_almission; // DTP for autoload mission (for multi only)
|
||||
extern SCP_string Cmdline_almission; // DTP for autoload mission (for multi only)
|
||||
extern int Cmdline_ingamejoin;
|
||||
extern int Cmdline_mpnoreturn;
|
||||
extern char *Cmdline_spew_mission_crcs;
|
||||
extern char *Cmdline_spew_table_crcs;
|
||||
extern SCP_string Cmdline_spew_mission_crcs;
|
||||
extern SCP_string Cmdline_spew_table_crcs;
|
||||
extern int Cmdline_objupd;
|
||||
|
||||
// Launcher related options
|
||||
@@ -127,7 +124,7 @@ extern int Cmdline_mipmap;
|
||||
extern int Cmdline_ati_color_swap;
|
||||
extern int Cmdline_no_3d_sound;
|
||||
extern int Cmdline_drawelements;
|
||||
extern char* Cmdline_keyboard_layout;
|
||||
extern SCP_string Cmdline_keyboard_layout;
|
||||
extern bool Cmdline_gl_finish;
|
||||
extern bool Cmdline_no_geo_sdr_effects;
|
||||
extern bool Cmdline_set_cpu_affinity;
|
||||
@@ -138,7 +135,7 @@ extern bool Cmdline_alternate_registry_path;
|
||||
#endif
|
||||
|
||||
// Developer/Testing related
|
||||
extern char *Cmdline_start_mission;
|
||||
extern SCP_string Cmdline_start_mission;
|
||||
extern int Cmdline_dis_collisions;
|
||||
extern int Cmdline_dis_weapons;
|
||||
extern bool Cmdline_output_sexp_info;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"version": 1,
|
||||
"flags": {
|
||||
"-bool_option": false,
|
||||
"-number_option": 5.8,
|
||||
"-string_option": "asdf",
|
||||
"-mod": [
|
||||
{
|
||||
"type": "folder",
|
||||
"path": "/home/fso/mediavps",
|
||||
"user": true,
|
||||
"location_type": "primary_mod"
|
||||
},
|
||||
{
|
||||
"type": "folder",
|
||||
"path": "/home/fso",
|
||||
"user": true,
|
||||
"location_type": "game_root"
|
||||
},
|
||||
{
|
||||
"type": "folder",
|
||||
"path": "/path/to/fso/mediavps",
|
||||
"user": false,
|
||||
"location_type": "primary_mod"
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"path": "/path/to/fso/mediavps/MVPS-Z.vp",
|
||||
"location_type": "primary_mod"
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"path": "/path/to/fso/mediavps/MVPS-X.vp",
|
||||
"location_type": "primary_mod"
|
||||
},
|
||||
{
|
||||
"type": "folder",
|
||||
"path": "/path/to/fso",
|
||||
"location_type": "game_root"
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"path": "/path/to/fso/retail.vp",
|
||||
"location_type": "game_root"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -504,7 +504,9 @@ char *translate_key(char *key)
|
||||
// both key and joystick button are mapped to this control
|
||||
if ((key_code >= 0 ) && (joy_code >= 0) ) {
|
||||
strcpy_s(text, key_text);
|
||||
strcat_s(text, " or ");
|
||||
strcat_s(text, " ");
|
||||
strcat_s(text, XSTR("or", 1638));
|
||||
strcat_s(text, " ");
|
||||
strcat_s(text, joy_text);
|
||||
}
|
||||
// if we only have one
|
||||
|
||||
@@ -21,15 +21,7 @@ typedef struct cutscene_info
|
||||
bool viewable;
|
||||
} cutscene_info;
|
||||
|
||||
#if SCP_COMPILER_IS_GNU
|
||||
#pragma GCC diagnostic push
|
||||
// This suppresses a GCC bug where it thinks that the Cutscenes from the enum class below shadows a global variable
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
extern SCP_vector<cutscene_info> Cutscenes;
|
||||
#if SCP_COMPILER_IS_GNU
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
// initializa table data
|
||||
void cutscene_init();
|
||||
|
||||
@@ -974,8 +974,6 @@ int debris_check_collision(object *pdebris, object *other_obj, vec3d *hitpos, co
|
||||
|
||||
// SET PHYSICS PARAMETERS
|
||||
// already have (hitpos - heavy) and light_cm_pos
|
||||
// get heavy cm pos - already have light_cm_pos
|
||||
debris_hit_info->heavy_collision_cm_pos = zero;
|
||||
|
||||
// get r_heavy and r_light
|
||||
debris_hit_info->r_heavy = debris_hit_info->hit_pos;
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
||||
*
|
||||
* All source code herein is the property of Volition, Inc. You may not sell
|
||||
* or otherwise commercially exploit the source or things you created based on the
|
||||
* All source code herein is the property of Volition, Inc. You may not sell
|
||||
* or otherwise commercially exploit the source or things you created based on the
|
||||
* source.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include "fireball/fireballs.h"
|
||||
#include "asteroid/asteroid.h"
|
||||
#include "cmdline/cmdline.h"
|
||||
#include "fireball/fireballs.h"
|
||||
#include "gamesnd/gamesnd.h"
|
||||
#include "graphics/tmapper.h"
|
||||
#include "localization/localize.h"
|
||||
#include "model/model.h"
|
||||
#include "object/object.h"
|
||||
#include "options/Option.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "render/3d.h"
|
||||
#include "render/batching.h"
|
||||
@@ -46,6 +45,15 @@ int Num_fireball_types = 0;
|
||||
bool fireballs_inited = false;
|
||||
bool fireballs_parsed = false;
|
||||
|
||||
bool Fireball_use_3d_warp = false;
|
||||
|
||||
static auto WarpOption = options::OptionBuilder<bool>("Graphics.3dWarp", "3D Warp", "Use a 3D model for warp effects")
|
||||
.category("Graphics")
|
||||
.default_val(true)
|
||||
.level(options::ExpertLevel::Advanced)
|
||||
.bind_to(&Fireball_use_3d_warp)
|
||||
.importance(65)
|
||||
.finish();
|
||||
/**
|
||||
* Play warp in sound for warp effect
|
||||
*/
|
||||
|
||||
@@ -137,4 +137,6 @@ float fireball_wormhole_intensity( fireball *fb );
|
||||
// Goober5000
|
||||
extern int Knossos_warp_ani_used;
|
||||
|
||||
extern bool Fireball_use_3d_warp;
|
||||
|
||||
#endif /* _FIREBALLS_H */
|
||||
@@ -100,7 +100,7 @@ void warpin_queue_render(model_draw_list *scene, object *obj, matrix *orient, ve
|
||||
}
|
||||
}
|
||||
|
||||
if ( (warp_model_id >= 0) && (warp_3d || Cmdline_3dwarp) ) {
|
||||
if ( (warp_model_id >= 0) && (warp_3d || Fireball_use_3d_warp) ) {
|
||||
model_render_params render_info;
|
||||
|
||||
float scale = radius / 25.0f;
|
||||
@@ -137,7 +137,7 @@ void warpin_queue_render(model_draw_list *scene, object *obj, matrix *orient, ve
|
||||
vm_vec_scale_add2( &vecs[3], &orient->vec.rvec, -radius );
|
||||
vm_vec_scale_add2( &vecs[3], &orient->vec.fvec, Grid_depth );
|
||||
|
||||
// vm_vec_scale_add( &vecs[4], ¢er, &orient->vec.fvec, -Grid_depth );
|
||||
// vm_vec_scale_add( &vecs[4], center, &orient->vec.fvec, -Grid_depth );
|
||||
vecs[4] = center;
|
||||
|
||||
verts[0].texture_position.u = 0.01f;
|
||||
|
||||
@@ -1678,7 +1678,7 @@ void fs2netd_spew_table_checksums(const char *outfile)
|
||||
char description[512] = { 0 };
|
||||
char filename[65] = { 0 };
|
||||
size_t offset = 0;
|
||||
char *p = NULL;
|
||||
const char *p = nullptr;
|
||||
|
||||
if ( Table_valid_status.empty() ) {
|
||||
return;
|
||||
@@ -1693,7 +1693,7 @@ void fs2netd_spew_table_checksums(const char *outfile)
|
||||
return;
|
||||
}
|
||||
|
||||
p = Cmdline_spew_table_crcs;
|
||||
p = Cmdline_spew_table_crcs.c_str();
|
||||
|
||||
while (*p && (offset < sizeof(description))) {
|
||||
if (*p == '"' && offset < sizeof(description)-1) {
|
||||
|
||||
@@ -246,7 +246,7 @@ enum class GameSounds {
|
||||
/**
|
||||
* Interface sounds
|
||||
*/
|
||||
enum InterfaceSounds {
|
||||
enum class InterfaceSounds {
|
||||
IFACE_MOUSE_CLICK =0, //!< mouse click
|
||||
ICON_PICKUP =1, //!< pick up a ship icon (Empty in Retail)
|
||||
ICON_DROP_ON_WING =2, //!< drop a ship icon on a wing slot
|
||||
|
||||
@@ -125,8 +125,17 @@ class flagset {
|
||||
|
||||
void from_u64(std::uint64_t num) { values = (unsigned long) num; }
|
||||
std::uint64_t to_u64() const { return (std::uint64_t) values.to_ulong(); }
|
||||
|
||||
size_t hash() const { return std::hash<std::bitset<SIZE>>()(values); }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <typename T, size_t N>
|
||||
struct hash<::flagset<T, N>> {
|
||||
size_t operator()(const ::flagset<T, N>& val) { return val.hash(); }
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
#define FLAG_LIST(Type) enum class Type : size_t
|
||||
|
||||
template<typename TEnum, size_t SIZE>
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
|
||||
/* SCP_vm_allocator - maintained by portej05 (i.e. please don't patch this one yourself!) */
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
template< typename T >
|
||||
using SCP_vector = std::vector< T, std::allocator< T > >;
|
||||
@@ -35,7 +35,41 @@ using SCP_queue = std::queue< T, std::deque< T, std::allocator< T > > >;
|
||||
template< typename T >
|
||||
using SCP_deque = std::deque< T, std::allocator< T > >;
|
||||
|
||||
template< typename Key, typename T, typename Hash = std::hash<Key>, typename KeyEqual = std::equal_to<Key> >
|
||||
using SCP_unordered_map = std::unordered_map< Key, T, Hash, KeyEqual, std::allocator< std::pair<const Key, T> > >;
|
||||
#if __cplusplus < 201402L
|
||||
template <class T, bool>
|
||||
struct enum_hasher_util {
|
||||
inline size_t operator()(const T& elem) { return std::hash<T>()(elem); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct enum_hasher_util<T, true> {
|
||||
inline size_t operator()(const T& elem)
|
||||
{
|
||||
typedef typename std::underlying_type<T>::type enumType;
|
||||
return std::hash<enumType>()(static_cast<enumType>(elem));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An enum class compatible hashing class
|
||||
*
|
||||
* This is the same as std::hash except that is can handle enum classes properly using their underlying type. This is
|
||||
* only required for C++ < 14 since after that the standard mandates that enum classes should be hashable.
|
||||
*
|
||||
* @tparam T The type that should be hashed
|
||||
*/
|
||||
template <class T>
|
||||
struct SCP_hash {
|
||||
inline size_t operator()(const T& elem) const { return enum_hasher_util<T, std::is_enum<T>::value>()(elem); }
|
||||
};
|
||||
|
||||
#else
|
||||
// No need for the special hash class so just use the standard hash
|
||||
template <typename T>
|
||||
using SCP_hash = std::hash<T>;
|
||||
#endif
|
||||
|
||||
template <typename Key, typename T, typename Hash = SCP_hash<Key>, typename KeyEqual = std::equal_to<Key>>
|
||||
using SCP_unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, std::allocator<std::pair<const Key, T>>>;
|
||||
|
||||
#endif // _VMALLOCATOR_H_INCLUDED_
|
||||
+294
-51
@@ -36,6 +36,8 @@
|
||||
#include "graphics/util/UniformBufferManager.h"
|
||||
#include "io/keycontrol.h" // m!m
|
||||
#include "io/timer.h"
|
||||
#include "libs/jansson.h"
|
||||
#include "options/Option.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "popup/popup.h"
|
||||
@@ -103,6 +105,239 @@ float Gr_save_menu_zoomed_offset_X = 0.0f, Gr_save_menu_zoomed_offset_Y = 0.0f;
|
||||
|
||||
bool Save_custom_screen_size;
|
||||
|
||||
static int videodisplay_deserializer(const json_t* value)
|
||||
{
|
||||
int id;
|
||||
|
||||
json_error_t err;
|
||||
if (json_unpack_ex((json_t*)value, &err, 0, "i", &id) != 0) {
|
||||
throw json_exception(err);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
static json_t* videodisplay_serializer(int value) { return json_pack("i", value); }
|
||||
static SCP_vector<int> videodisplay_enumerator()
|
||||
{
|
||||
SCP_vector<int> vals;
|
||||
for (int i = 0; i < SDL_GetNumVideoDisplays(); ++i) {
|
||||
vals.push_back(i);
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
static SCP_string videodisplay_display(int id)
|
||||
{
|
||||
SCP_string out;
|
||||
sprintf(out, "(%d) %s", id + 1, SDL_GetDisplayName(id));
|
||||
return out;
|
||||
}
|
||||
static bool videodisplay_change(int display, bool initial)
|
||||
{
|
||||
if (initial) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto window = os::getSDLMainWindow();
|
||||
if (window == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(display), SDL_WINDOWPOS_CENTERED_DISPLAY(display));
|
||||
return true;
|
||||
}
|
||||
static auto VideoDisplayOption =
|
||||
options::OptionBuilder<int>("Graphics.Display", "Primary display", "The display used for rendering.")
|
||||
.category("Graphics")
|
||||
.level(options::ExpertLevel::Beginner)
|
||||
.deserializer(videodisplay_deserializer)
|
||||
.serializer(videodisplay_serializer)
|
||||
.enumerator(videodisplay_enumerator)
|
||||
.display(videodisplay_display)
|
||||
.default_val(0)
|
||||
.change_listener(videodisplay_change)
|
||||
.importance(99)
|
||||
.finish();
|
||||
|
||||
struct ResolutionInfo {
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
ResolutionInfo(uint32_t _width, uint32_t _height) : width(_width), height(_height) {}
|
||||
ResolutionInfo() = default;
|
||||
friend bool operator==(const ResolutionInfo& lhs, const ResolutionInfo& rhs)
|
||||
{
|
||||
return lhs.width == rhs.width && lhs.height == rhs.height;
|
||||
}
|
||||
friend bool operator!=(const ResolutionInfo& lhs, const ResolutionInfo& rhs) { return !(rhs == lhs); }
|
||||
};
|
||||
|
||||
static ResolutionInfo resolution_deserializer(const json_t* el)
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
|
||||
json_error_t err;
|
||||
if (json_unpack_ex((json_t*)el, &err, 0, "{s:i, s:i}", "width", &width, "height", &height) != 0) {
|
||||
throw json_exception(err);
|
||||
}
|
||||
|
||||
return {(uint32_t)width, (uint32_t)height};
|
||||
}
|
||||
static json_t* resolution_serializer(const ResolutionInfo& value)
|
||||
{
|
||||
return json_pack("{s:i, s:i}", "width", value.width, "height", value.height);
|
||||
}
|
||||
static SCP_vector<ResolutionInfo> resolution_enumerator()
|
||||
{
|
||||
SCP_vector<ResolutionInfo> out;
|
||||
auto display = VideoDisplayOption->getValue();
|
||||
for (auto i = 0; i < SDL_GetNumDisplayModes(display); ++i) {
|
||||
SDL_DisplayMode mode;
|
||||
if (SDL_GetDisplayMode(display, i, &mode) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto res = ResolutionInfo(mode.w, mode.h);
|
||||
if (std::find(out.begin(), out.end(), res) == out.end()) {
|
||||
out.push_back(res);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
static SCP_string resolution_display(const ResolutionInfo& info)
|
||||
{
|
||||
SCP_string str;
|
||||
sprintf(str, "%dx%d", info.width, info.height);
|
||||
return str;
|
||||
}
|
||||
static ResolutionInfo resolution_default()
|
||||
{
|
||||
SDL_DisplayMode mode;
|
||||
if (SDL_GetDesktopDisplayMode(VideoDisplayOption->getValue(), &mode) != 0) {
|
||||
return {};
|
||||
}
|
||||
return ResolutionInfo(mode.w, mode.h);
|
||||
}
|
||||
static bool resolution_change(const ResolutionInfo& /*info*/, bool initial)
|
||||
{
|
||||
if (initial) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
// The following code should change the size of the window properly but FSO currently can't handle that
|
||||
/*
|
||||
auto window = os::getSDLMainWindow();
|
||||
if (window == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto display = VideoDisplayOption->getValue();
|
||||
if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
|
||||
SDL_DisplayMode target;
|
||||
target.w = info.width;
|
||||
target.h = info.height;
|
||||
target.format = 0; // don't care
|
||||
target.refresh_rate = 0; // don't care
|
||||
target.driverdata = 0; // initialize to 0
|
||||
|
||||
SDL_DisplayMode closest;
|
||||
if (SDL_GetClosestDisplayMode(display, &target, &closest) == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_SetWindowDisplayMode(window, &closest);
|
||||
} else {
|
||||
SDL_SetWindowSize(window, info.width, info.height);
|
||||
// Recenter the window
|
||||
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(display), SDL_WINDOWPOS_CENTERED_DISPLAY(display));
|
||||
}
|
||||
*/
|
||||
}
|
||||
static auto ResolutionOption =
|
||||
options::OptionBuilder<ResolutionInfo>("Graphics.Resolution", "Resolution", "The rendering resolution.")
|
||||
.category("Graphics")
|
||||
.level(options::ExpertLevel::Beginner)
|
||||
.deserializer(resolution_deserializer)
|
||||
.serializer(resolution_serializer)
|
||||
.enumerator(resolution_enumerator)
|
||||
.display(resolution_display)
|
||||
.default_func(resolution_default)
|
||||
.change_listener(resolution_change)
|
||||
.importance(100)
|
||||
.finish();
|
||||
|
||||
bool Gr_enable_soft_particles = false;
|
||||
|
||||
static auto SoftParticlesOption = options::OptionBuilder<bool>("Graphics.SoftParticles", "Soft Particles",
|
||||
"Enable or disable soft particle rendering.")
|
||||
.category("Graphics")
|
||||
.level(options::ExpertLevel::Advanced)
|
||||
.default_val(true)
|
||||
.bind_to_once(&Gr_enable_soft_particles)
|
||||
.importance(68)
|
||||
.finish();
|
||||
|
||||
flagset<FramebufferEffects> Gr_framebuffer_effects;
|
||||
|
||||
static auto FramebufferEffectsOption =
|
||||
options::OptionBuilder<flagset<FramebufferEffects>>(
|
||||
"Graphics.FramebufferEffects", "Framebuffer effects",
|
||||
"Controls which framebuffer effects will be applied to the scene.")
|
||||
.category("Graphics")
|
||||
.level(options::ExpertLevel::Advanced)
|
||||
.values({{{}, "None"},
|
||||
{{FramebufferEffects::Shockwaves}, "Shockwaves"},
|
||||
{{FramebufferEffects::Thrusters}, "Thrusters"},
|
||||
{{FramebufferEffects::Shockwaves, FramebufferEffects::Thrusters}, "All"}})
|
||||
.default_val({FramebufferEffects::Shockwaves, FramebufferEffects::Thrusters})
|
||||
.bind_to_once(&Gr_framebuffer_effects)
|
||||
.importance(77)
|
||||
.finish();
|
||||
|
||||
AntiAliasMode Gr_aa_mode = AntiAliasMode::None;
|
||||
AntiAliasMode Gr_aa_mode_last_frame = AntiAliasMode::None;
|
||||
|
||||
static auto AAOption = options::OptionBuilder<AntiAliasMode>("Graphics.AAMode", "Anti Aliasing",
|
||||
"Controls the anti aliasing mode of the engine.")
|
||||
.category("Graphics")
|
||||
.level(options::ExpertLevel::Advanced)
|
||||
.values({{AntiAliasMode::None, "None"},
|
||||
{AntiAliasMode::FXAA_Low, "FXAA Low"},
|
||||
{AntiAliasMode::FXAA_Medium, "FXAA Medium"},
|
||||
{AntiAliasMode::FXAA_High, "FXAA High"}})
|
||||
.default_val(AntiAliasMode::None)
|
||||
.bind_to(&Gr_aa_mode)
|
||||
.importance(79)
|
||||
.finish();
|
||||
|
||||
bool gr_is_fxaa_mode(AntiAliasMode mode)
|
||||
{
|
||||
return mode == AntiAliasMode::FXAA_Low || mode == AntiAliasMode::FXAA_Medium || mode == AntiAliasMode::FXAA_High;
|
||||
}
|
||||
|
||||
bool Gr_post_processing_enabled = false;
|
||||
|
||||
static auto PostProcessOption =
|
||||
options::OptionBuilder<bool>("Graphis.PostProcessing", "Post processing",
|
||||
"Controls whether post processing is enabled in the engine")
|
||||
.category("Graphics")
|
||||
.level(options::ExpertLevel::Advanced)
|
||||
.default_val(false)
|
||||
.bind_to_once(&Gr_post_processing_enabled)
|
||||
.importance(69)
|
||||
.finish();
|
||||
|
||||
bool Gr_enable_vsync = true;
|
||||
|
||||
static auto VSyncOption = options::OptionBuilder<bool>("Graphis.VSync", "Vertical Sync",
|
||||
"Controls how the engine does vertical synchronization")
|
||||
.category("Graphics")
|
||||
.level(options::ExpertLevel::Advanced)
|
||||
.default_val(true)
|
||||
.bind_to_once(&Gr_enable_vsync)
|
||||
.importance(70)
|
||||
.finish();
|
||||
|
||||
// Forward definitions
|
||||
static void uniform_buffer_managers_init();
|
||||
static void uniform_buffer_managers_deinit();
|
||||
@@ -953,75 +1188,83 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot continue without this, quit, but try to help the user out first
|
||||
ptr = os_config_read_string(NULL, NOX("VideocardFs2open"), NULL);
|
||||
if (Using_in_game_options) {
|
||||
auto res = ResolutionOption->getValue();
|
||||
width = res.width;
|
||||
height = res.height;
|
||||
} else {
|
||||
// We cannot continue without this, quit, but try to help the user out first
|
||||
ptr = os_config_read_string(nullptr, NOX("VideocardFs2open"), nullptr);
|
||||
|
||||
// if we don't have a config string then construct one, using OpenGL 1024x768 32-bit as the default
|
||||
if (ptr == NULL) {
|
||||
// If we don't have a display mode, use SDL to get default settings
|
||||
// We need to initialize SDL to do this
|
||||
// if we don't have a config string then construct one, using OpenGL 1024x768 32-bit as the default
|
||||
if (ptr == nullptr) {
|
||||
// If we don't have a display mode, use SDL to get default settings
|
||||
// We need to initialize SDL to do this
|
||||
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) == 0)
|
||||
{
|
||||
int display = static_cast<int>(os_config_read_uint("Video", "Display", 0));
|
||||
SDL_DisplayMode displayMode;
|
||||
if (SDL_GetDesktopDisplayMode(display, &displayMode) == 0)
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) == 0)
|
||||
{
|
||||
width = displayMode.w;
|
||||
height = displayMode.h;
|
||||
int sdlBits = SDL_BITSPERPIXEL(displayMode.format);
|
||||
auto display = static_cast<int>(os_config_read_uint("Video", "Display", 0));
|
||||
SDL_DisplayMode displayMode;
|
||||
if (SDL_GetDesktopDisplayMode(display, &displayMode) == 0)
|
||||
{
|
||||
width = displayMode.w;
|
||||
height = displayMode.h;
|
||||
int sdlBits = SDL_BITSPERPIXEL(displayMode.format);
|
||||
|
||||
if (SDL_ISPIXELFORMAT_ALPHA(displayMode.format))
|
||||
{
|
||||
depth = sdlBits;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fix a few values
|
||||
if (sdlBits == 24)
|
||||
{
|
||||
depth = 32;
|
||||
}
|
||||
else if (sdlBits == 15)
|
||||
{
|
||||
depth = 16;
|
||||
}
|
||||
else
|
||||
if (SDL_ISPIXELFORMAT_ALPHA(displayMode.format))
|
||||
{
|
||||
depth = sdlBits;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fix a few values
|
||||
if (sdlBits == 24)
|
||||
{
|
||||
depth = 32;
|
||||
}
|
||||
else if (sdlBits == 15)
|
||||
{
|
||||
depth = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
depth = sdlBits;
|
||||
}
|
||||
}
|
||||
|
||||
SCP_string videomode;
|
||||
sprintf(videomode, "OGL -(%dx%d)x%d bit", width, height, depth);
|
||||
|
||||
os_config_write_string(nullptr, NOX("VideocardFs2open"), videomode.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Assert(ptr != nullptr);
|
||||
|
||||
SCP_string videomode;
|
||||
sprintf(videomode, "OGL -(%dx%d)x%d bit", width, height, depth);
|
||||
|
||||
os_config_write_string(NULL, NOX("VideocardFs2open"), videomode.c_str());
|
||||
// NOTE: The "ptr+5" is to skip over the initial "????-" in the video string.
|
||||
// If the format of that string changes you'll have to change this too!!!
|
||||
if (sscanf(ptr + 5, "(%dx%d)x%d ", &width, &height, &depth) != 3) {
|
||||
Error(LOCATION, "Can't understand 'VideocardFs2open' config entry!");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Assert(ptr != NULL);
|
||||
|
||||
// NOTE: The "ptr+5" is to skip over the initial "????-" in the video string.
|
||||
// If the format of that string changes you'll have to change this too!!!
|
||||
if (sscanf(ptr + 5, "(%dx%d)x%d ", &width, &height, &depth) != 3) {
|
||||
Error(LOCATION, "Can't understand 'VideocardFs2open' config entry!");
|
||||
if (!Cmdline_res.empty()) {
|
||||
int tmp_width = 0;
|
||||
int tmp_height = 0;
|
||||
|
||||
if ( sscanf(Cmdline_res.c_str(), "%dx%d", &tmp_width, &tmp_height) == 2 ) {
|
||||
width = tmp_width;
|
||||
height = tmp_height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Cmdline_res != NULL) {
|
||||
int tmp_width = 0;
|
||||
int tmp_height = 0;
|
||||
|
||||
if ( sscanf(Cmdline_res, "%dx%d", &tmp_width, &tmp_height) == 2 ) {
|
||||
width = tmp_width;
|
||||
height = tmp_height;
|
||||
}
|
||||
Gr_enable_soft_particles = Cmdline_softparticles != 0;
|
||||
}
|
||||
if (Cmdline_center_res != NULL) {
|
||||
if (!Cmdline_center_res.empty()) {
|
||||
int tmp_center_width = 0;
|
||||
int tmp_center_height = 0;
|
||||
|
||||
if ( (sscanf(Cmdline_center_res, "%dx%d", &tmp_center_width, &tmp_center_height) == 2) && (tmp_center_width > 0) && (tmp_center_height > 0) ) {
|
||||
if ( (sscanf(Cmdline_center_res.c_str(), "%dx%d", &tmp_center_width, &tmp_center_height) == 2) && (tmp_center_width > 0) && (tmp_center_height > 0) ) {
|
||||
center_aspect_ratio = (float)tmp_center_width / (float)tmp_center_height;
|
||||
}
|
||||
}
|
||||
|
||||
+25
-4
@@ -12,15 +12,15 @@
|
||||
#ifndef _GRAPHICS_H
|
||||
#define _GRAPHICS_H
|
||||
|
||||
#include "graphics/grinternal.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "bmpman/bmpman.h"
|
||||
#include "cfile/cfile.h"
|
||||
#include "globalincs/flagset.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "graphics/grinternal.h"
|
||||
#include "graphics/tmapper.h"
|
||||
#include "cfile/cfile.h"
|
||||
#include "math/vecmat.h"
|
||||
#include "io/cursor.h"
|
||||
#include "math/vecmat.h"
|
||||
#include "osapi/osapi.h"
|
||||
|
||||
// Forward definition
|
||||
namespace graphics {
|
||||
@@ -40,6 +40,26 @@ extern int Gr_inited;
|
||||
extern int gr_zbuffering, gr_zbuffering_mode;
|
||||
extern int gr_global_zbuffering;
|
||||
|
||||
extern bool Gr_enable_soft_particles;
|
||||
|
||||
FLAG_LIST(FramebufferEffects){Thrusters = 0, Shockwaves, NUM_VALUES};
|
||||
extern flagset<FramebufferEffects> Gr_framebuffer_effects;
|
||||
|
||||
enum class AntiAliasMode {
|
||||
None,
|
||||
FXAA_Low,
|
||||
FXAA_Medium,
|
||||
FXAA_High
|
||||
};
|
||||
extern AntiAliasMode Gr_aa_mode;
|
||||
extern AntiAliasMode Gr_aa_mode_last_frame;
|
||||
|
||||
bool gr_is_fxaa_mode(AntiAliasMode mode);
|
||||
|
||||
extern bool Gr_post_processing_enabled;
|
||||
|
||||
extern bool Gr_enable_vsync;
|
||||
|
||||
class material;
|
||||
class model_material;
|
||||
class particle_material;
|
||||
@@ -294,6 +314,7 @@ typedef enum gr_capability {
|
||||
enum class gr_property {
|
||||
UNIFORM_BUFFER_OFFSET_ALIGNMENT,
|
||||
UNIFORM_BUFFER_MAX_SIZE,
|
||||
MAX_ANISOTROPY
|
||||
};
|
||||
|
||||
// stencil buffering stuff
|
||||
|
||||
+67
-69
@@ -667,7 +667,7 @@ float batch_add_laser(int texture, vec3d *p0, float width1, vec3d *p1, float wid
|
||||
batch_item *item = NULL;
|
||||
SCP_map<int, batch_item>::iterator it = geometry_map.find(texture);
|
||||
|
||||
if ( !geometry_map.empty() && it != geometry_map.end() ) {
|
||||
if (it != geometry_map.end()) {
|
||||
item = &it->second;
|
||||
} else {
|
||||
item = &geometry_map[texture];
|
||||
@@ -690,12 +690,12 @@ int batch_add_bitmap(int texture, int tmap_flags, vertex *pnt, int orient, float
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( tmap_flags & TMAP_FLAG_SOFT_QUAD && !Cmdline_softparticles ) {
|
||||
if ( tmap_flags & TMAP_FLAG_SOFT_QUAD && !Gr_enable_soft_particles ) {
|
||||
// don't render this as a soft particle if we don't support soft particles
|
||||
tmap_flags &= ~(TMAP_FLAG_SOFT_QUAD);
|
||||
}
|
||||
|
||||
if ( Cmdline_softparticles && !Cmdline_no_geo_sdr_effects && (tmap_flags & TMAP_FLAG_VERTEX_GEN) ) {
|
||||
if ( Gr_enable_soft_particles && !Cmdline_no_geo_sdr_effects && (tmap_flags & TMAP_FLAG_VERTEX_GEN) ) {
|
||||
geometry_batch_add_bitmap(texture, tmap_flags, pnt, orient, rad, alpha, depth);
|
||||
return 0;
|
||||
} else if ( tmap_flags & TMAP_FLAG_VERTEX_GEN ) {
|
||||
@@ -706,7 +706,7 @@ int batch_add_bitmap(int texture, int tmap_flags, vertex *pnt, int orient, float
|
||||
|
||||
SCP_map<int, batch_item>::iterator it = geometry_map.find(texture);
|
||||
|
||||
if ( !geometry_map.empty() && it != geometry_map.end() ) {
|
||||
if (it != geometry_map.end()) {
|
||||
item = &it->second;
|
||||
} else {
|
||||
item = &geometry_map[texture];
|
||||
@@ -735,7 +735,7 @@ int geometry_batch_add_bitmap(int texture, int tmap_flags, vertex *pnt, int orie
|
||||
g_sdr_batch_item *item = NULL;
|
||||
SCP_map<int, g_sdr_batch_item>::iterator it = geometry_shader_map.find(texture);
|
||||
|
||||
if ( !geometry_shader_map.empty() && it != geometry_shader_map.end() ) {
|
||||
if (it != geometry_shader_map.end()) {
|
||||
item = &it->second;
|
||||
} else {
|
||||
item = &geometry_shader_map[texture];
|
||||
@@ -759,7 +759,7 @@ int batch_add_bitmap_rotated(int texture, int tmap_flags, vertex *pnt, float ang
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( tmap_flags & TMAP_FLAG_SOFT_QUAD && !Cmdline_softparticles ) {
|
||||
if ( tmap_flags & TMAP_FLAG_SOFT_QUAD && !Gr_enable_soft_particles ) {
|
||||
// don't render this as a soft particle if we don't support soft particles
|
||||
tmap_flags &= ~(TMAP_FLAG_SOFT_QUAD);
|
||||
}
|
||||
@@ -768,7 +768,7 @@ int batch_add_bitmap_rotated(int texture, int tmap_flags, vertex *pnt, float ang
|
||||
|
||||
SCP_map<int, batch_item>::iterator it = geometry_map.find(texture);
|
||||
|
||||
if ( !geometry_map.empty() && it != geometry_map.end() ) {
|
||||
if (it != geometry_map.end()) {
|
||||
item = &it->second;
|
||||
} else {
|
||||
item = &geometry_map[texture];
|
||||
@@ -798,7 +798,7 @@ int batch_add_tri(int texture, int tmap_flags, vertex *verts, float alpha)
|
||||
|
||||
SCP_map<int, batch_item>::iterator it = geometry_map.find(texture);
|
||||
|
||||
if ( !geometry_map.empty() && it != geometry_map.end() ) {
|
||||
if (it != geometry_map.end()) {
|
||||
item = &it->second;
|
||||
} else {
|
||||
item = &geometry_map[texture];
|
||||
@@ -828,7 +828,7 @@ int batch_add_quad(int texture, int tmap_flags, vertex *verts, float alpha)
|
||||
|
||||
SCP_map<int, batch_item>::iterator it = geometry_map.find(texture);
|
||||
|
||||
if ( !geometry_map.empty() && it != geometry_map.end() ) {
|
||||
if (it != geometry_map.end()) {
|
||||
item = &it->second;
|
||||
} else {
|
||||
item = &geometry_map[texture];
|
||||
@@ -908,7 +908,7 @@ int batch_add_polygon(int texture, int tmap_flags, vec3d *pos, matrix *orient, f
|
||||
|
||||
SCP_map<int, batch_item>::iterator it = geometry_map.find(texture);
|
||||
|
||||
if ( !geometry_map.empty() && it != geometry_map.end() ) {
|
||||
if (it != geometry_map.end()) {
|
||||
item = &it->second;
|
||||
} else {
|
||||
item = &geometry_map[texture];
|
||||
@@ -938,7 +938,7 @@ int batch_add_beam(int texture, int tmap_flags, vec3d *start, vec3d *end, float
|
||||
|
||||
SCP_map<int, batch_item>::iterator it = geometry_map.find(texture);
|
||||
|
||||
if ( !geometry_map.empty() && it != geometry_map.end() ) {
|
||||
if (it != geometry_map.end()) {
|
||||
item = &it->second;
|
||||
} else {
|
||||
item = &geometry_map[texture];
|
||||
@@ -959,102 +959,102 @@ int batch_add_beam(int texture, int tmap_flags, vec3d *start, vec3d *end, float
|
||||
|
||||
void batch_render_lasers(int buffer_handle)
|
||||
{
|
||||
for (SCP_map<int, batch_item>::iterator bi = geometry_map.begin(); bi != geometry_map.end(); ++bi) {
|
||||
for (auto &bi : geometry_map) {
|
||||
|
||||
if ( !bi->second.laser )
|
||||
if ( !bi.second.laser )
|
||||
continue;
|
||||
|
||||
if ( !bi->second.batch.need_to_render() )
|
||||
if ( !bi.second.batch.need_to_render() )
|
||||
continue;
|
||||
|
||||
Assert( bi->second.texture >= 0 );
|
||||
gr_set_bitmap(bi->second.texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.99999f);
|
||||
Assert( bi.second.texture >= 0 );
|
||||
gr_set_bitmap(bi.second.texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.99999f);
|
||||
if ( buffer_handle >= 0 ) {
|
||||
bi->second.batch.render_buffer(buffer_handle, TMAP_FLAG_TEXTURED | TMAP_FLAG_XPARENT | TMAP_HTL_3D_UNLIT | TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD | TMAP_FLAG_CORRECT | TMAP_FLAG_EMISSIVE);
|
||||
bi.second.batch.render_buffer(buffer_handle, TMAP_FLAG_TEXTURED | TMAP_FLAG_XPARENT | TMAP_HTL_3D_UNLIT | TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD | TMAP_FLAG_CORRECT | TMAP_FLAG_EMISSIVE);
|
||||
} else {
|
||||
bi->second.batch.render(TMAP_FLAG_TEXTURED | TMAP_FLAG_XPARENT | TMAP_HTL_3D_UNLIT | TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD | TMAP_FLAG_CORRECT | TMAP_FLAG_EMISSIVE);
|
||||
bi.second.batch.render(TMAP_FLAG_TEXTURED | TMAP_FLAG_XPARENT | TMAP_HTL_3D_UNLIT | TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD | TMAP_FLAG_CORRECT | TMAP_FLAG_EMISSIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void batch_load_buffer_lasers(effect_vertex* buffer, int *n_verts)
|
||||
{
|
||||
for (SCP_map<int, batch_item>::iterator bi = geometry_map.begin(); bi != geometry_map.end(); ++bi) {
|
||||
for (auto &bi : geometry_map) {
|
||||
|
||||
if ( !bi->second.laser )
|
||||
if ( !bi.second.laser )
|
||||
continue;
|
||||
|
||||
if ( !bi->second.batch.need_to_render() )
|
||||
if ( !bi.second.batch.need_to_render() )
|
||||
continue;
|
||||
|
||||
Assert( bi->second.texture >= 0 );
|
||||
bi->second.batch.load_buffer(buffer, n_verts);
|
||||
Assert( bi.second.texture >= 0 );
|
||||
bi.second.batch.load_buffer(buffer, n_verts);
|
||||
}
|
||||
}
|
||||
|
||||
void batch_render_geometry_map_bitmaps(int buffer_handle)
|
||||
{
|
||||
for (SCP_map<int, batch_item>::iterator bi = geometry_map.begin(); bi != geometry_map.end(); ++bi) {
|
||||
for (auto &bi : geometry_map) {
|
||||
|
||||
if ( bi->second.laser )
|
||||
if ( bi.second.laser )
|
||||
continue;
|
||||
|
||||
if ( !bi->second.batch.need_to_render() )
|
||||
if ( !bi.second.batch.need_to_render() )
|
||||
continue;
|
||||
|
||||
Assert( bi->second.texture >= 0 );
|
||||
gr_set_bitmap(bi->second.texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, bi->second.alpha);
|
||||
Assert( bi.second.texture >= 0 );
|
||||
gr_set_bitmap(bi.second.texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, bi.second.alpha);
|
||||
if ( buffer_handle >= 0 ) {
|
||||
bi->second.batch.render_buffer(buffer_handle, bi->second.tmap_flags);
|
||||
bi.second.batch.render_buffer(buffer_handle, bi.second.tmap_flags);
|
||||
} else {
|
||||
bi->second.batch.render( bi->second.tmap_flags);
|
||||
bi.second.batch.render( bi.second.tmap_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void batch_render_geometry_shader_map_bitmaps(int buffer_handle)
|
||||
{
|
||||
for (SCP_map<int, g_sdr_batch_item>::iterator bi = geometry_shader_map.begin(); bi != geometry_shader_map.end(); ++bi) {
|
||||
for (auto &bi : geometry_shader_map) {
|
||||
|
||||
if ( bi->second.laser )
|
||||
if ( bi.second.laser )
|
||||
continue;
|
||||
|
||||
if ( !bi->second.batch.need_to_render() )
|
||||
if ( !bi.second.batch.need_to_render() )
|
||||
continue;
|
||||
|
||||
Assert( bi->second.texture >= 0 );
|
||||
gr_set_bitmap(bi->second.texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, bi->second.alpha);
|
||||
bi->second.batch.render_buffer(buffer_handle, bi->second.tmap_flags);
|
||||
Assert( bi.second.texture >= 0 );
|
||||
gr_set_bitmap(bi.second.texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, bi.second.alpha);
|
||||
bi.second.batch.render_buffer(buffer_handle, bi.second.tmap_flags);
|
||||
}
|
||||
}
|
||||
|
||||
void batch_load_buffer_geometry_map_bitmaps(effect_vertex* buffer, int *n_verts)
|
||||
{
|
||||
for (SCP_map<int, batch_item>::iterator bi = geometry_map.begin(); bi != geometry_map.end(); ++bi) {
|
||||
for (auto &bi : geometry_map) {
|
||||
|
||||
if ( bi->second.laser )
|
||||
if ( bi.second.laser )
|
||||
continue;
|
||||
|
||||
if ( !bi->second.batch.need_to_render() )
|
||||
if ( !bi.second.batch.need_to_render() )
|
||||
continue;
|
||||
|
||||
Assert( bi->second.texture >= 0 );
|
||||
bi->second.batch.load_buffer(buffer, n_verts);
|
||||
Assert( bi.second.texture >= 0 );
|
||||
bi.second.batch.load_buffer(buffer, n_verts);
|
||||
}
|
||||
}
|
||||
|
||||
void batch_load_buffer_geometry_shader_map_bitmaps(particle_pnt* buffer, size_t *n_verts)
|
||||
{
|
||||
for (SCP_map<int, g_sdr_batch_item>::iterator bi = geometry_shader_map.begin(); bi != geometry_shader_map.end(); ++bi) {
|
||||
for (auto &bi : geometry_shader_map) {
|
||||
|
||||
if ( bi->second.laser )
|
||||
if ( bi.second.laser )
|
||||
continue;
|
||||
|
||||
if ( !bi->second.batch.need_to_render() )
|
||||
if ( !bi.second.batch.need_to_render() )
|
||||
continue;
|
||||
|
||||
Assert( bi->second.texture >= 0 );
|
||||
bi->second.batch.load_buffer(buffer, n_verts);
|
||||
Assert( bi.second.texture >= 0 );
|
||||
bi.second.batch.load_buffer(buffer, n_verts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1145,7 +1145,7 @@ int distortion_add_bitmap_rotated(int texture, int tmap_flags, vertex *pnt, floa
|
||||
|
||||
SCP_map<int, batch_item>::iterator it = distortion_map.find(texture);
|
||||
|
||||
if ( !distortion_map.empty() && it != distortion_map.end() ) {
|
||||
if (it != distortion_map.end()) {
|
||||
item = &it->second;
|
||||
} else {
|
||||
item = &distortion_map[texture];
|
||||
@@ -1175,7 +1175,7 @@ int distortion_add_beam(int texture, int tmap_flags, vec3d *start, vec3d *end, f
|
||||
|
||||
SCP_map<int, batch_item>::iterator it = distortion_map.find(texture);
|
||||
|
||||
if ( !distortion_map.empty() && it != distortion_map.end() ) {
|
||||
if (it != distortion_map.end()) {
|
||||
item = &it->second;
|
||||
} else {
|
||||
item = &distortion_map[texture];
|
||||
@@ -1196,54 +1196,53 @@ int distortion_add_beam(int texture, int tmap_flags, vec3d *start, vec3d *end, f
|
||||
|
||||
void batch_render_distortion_map_bitmaps(int buffer_handle)
|
||||
{
|
||||
for (SCP_map<int,batch_item>::iterator bi = distortion_map.begin(); bi != distortion_map.end(); ++bi) {
|
||||
for (auto &bi : distortion_map) {
|
||||
|
||||
if ( bi->second.laser )
|
||||
if ( bi.second.laser )
|
||||
continue;
|
||||
|
||||
if ( !bi->second.batch.need_to_render() )
|
||||
if ( !bi.second.batch.need_to_render() )
|
||||
continue;
|
||||
|
||||
Assert( bi->second.texture >= 0 );
|
||||
gr_set_bitmap(bi->second.texture, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, bi->second.alpha);
|
||||
Assert( bi.second.texture >= 0 );
|
||||
gr_set_bitmap(bi.second.texture, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, bi.second.alpha);
|
||||
|
||||
if ( buffer_handle >= 0 ) {
|
||||
bi->second.batch.render_buffer(buffer_handle, bi->second.tmap_flags);
|
||||
bi.second.batch.render_buffer(buffer_handle, bi.second.tmap_flags);
|
||||
} else {
|
||||
bi->second.batch.render( bi->second.tmap_flags);
|
||||
bi.second.batch.render( bi.second.tmap_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void batch_load_buffer_distortion_map_bitmaps(effect_vertex* buffer, int *n_verts)
|
||||
{
|
||||
for (SCP_map<int, batch_item>::iterator bi = distortion_map.begin(); bi != distortion_map.end(); ++bi) {
|
||||
for (auto &bi : distortion_map) {
|
||||
|
||||
if ( bi->second.laser )
|
||||
if ( bi.second.laser )
|
||||
continue;
|
||||
|
||||
if ( !bi->second.batch.need_to_render() )
|
||||
if ( !bi.second.batch.need_to_render() )
|
||||
continue;
|
||||
|
||||
Assert( bi->second.texture >= 0 );
|
||||
bi->second.batch.load_buffer(buffer, n_verts);
|
||||
Assert( bi.second.texture >= 0 );
|
||||
bi.second.batch.load_buffer(buffer, n_verts);
|
||||
}
|
||||
}
|
||||
|
||||
int batch_get_size()
|
||||
{
|
||||
int n_to_render = 0;
|
||||
SCP_map<int, batch_item>::iterator bi;
|
||||
|
||||
for (bi = geometry_map.begin(); bi != geometry_map.end(); ++bi) {
|
||||
n_to_render += bi->second.batch.need_to_render();
|
||||
for (auto &bi : geometry_map) {
|
||||
n_to_render += bi.second.batch.need_to_render();
|
||||
}
|
||||
|
||||
for (bi = distortion_map.begin(); bi != distortion_map.end(); ++bi) {
|
||||
if ( bi->second.laser )
|
||||
for (auto &bi : distortion_map) {
|
||||
if ( bi.second.laser )
|
||||
continue;
|
||||
|
||||
n_to_render += bi->second.batch.need_to_render();
|
||||
n_to_render += bi.second.batch.need_to_render();
|
||||
}
|
||||
|
||||
return n_to_render * 3;
|
||||
@@ -1252,10 +1251,9 @@ int batch_get_size()
|
||||
size_t geometry_batch_get_size()
|
||||
{
|
||||
size_t n_to_render = 0;
|
||||
SCP_map<int, g_sdr_batch_item>::iterator bi;
|
||||
|
||||
for (bi = geometry_shader_map.begin(); bi != geometry_shader_map.end(); ++bi) {
|
||||
n_to_render += bi->second.batch.need_to_render();
|
||||
for (auto &bi : geometry_shader_map) {
|
||||
n_to_render += bi.second.batch.need_to_render();
|
||||
}
|
||||
|
||||
return n_to_render;
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "gropengl.h"
|
||||
#include "gropenglbmpman.h"
|
||||
#include "gropengldeferred.h"
|
||||
#include "gropengldraw.h"
|
||||
#include "gropenglpostprocessing.h"
|
||||
#include "gropenglquery.h"
|
||||
#include "gropenglshader.h"
|
||||
#include "gropenglstate.h"
|
||||
#include "gropenglsync.h"
|
||||
#include "gropengltexture.h"
|
||||
#include "gropengltnl.h"
|
||||
#include "bmpman/bmpman.h"
|
||||
#include "cfile/cfile.h"
|
||||
#include "cmdline/cmdline.h"
|
||||
@@ -10,32 +18,22 @@
|
||||
#include "debugconsole/console.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "graphics/2d.h"
|
||||
#include "graphics/line.h"
|
||||
#include "graphics/matrix.h"
|
||||
#include "graphics/paths/PathRenderer.h"
|
||||
#include "gropengl.h"
|
||||
#include "gropenglbmpman.h"
|
||||
#include "gropengldraw.h"
|
||||
#include "gropenglpostprocessing.h"
|
||||
#include "gropenglquery.h"
|
||||
#include "gropenglsync.h"
|
||||
#include "gropenglshader.h"
|
||||
#include "gropenglstate.h"
|
||||
#include "gropengltexture.h"
|
||||
#include "gropengltnl.h"
|
||||
#include "gropengldeferred.h"
|
||||
#include "graphics/line.h"
|
||||
#include "io/mouse.h"
|
||||
#include "io/timer.h"
|
||||
#include "libs/renderdoc/renderdoc.h"
|
||||
#include "math/floating.h"
|
||||
#include "model/model.h"
|
||||
#include "nebula/neb.h"
|
||||
#include "libs/renderdoc/renderdoc.h"
|
||||
#include "options/Option.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "osapi/osregistry.h"
|
||||
#include "render/3d.h"
|
||||
#include "popup/popup.h"
|
||||
#include "tracing/tracing.h"
|
||||
#include "pngutils/pngutils.h"
|
||||
#include "popup/popup.h"
|
||||
#include "render/3d.h"
|
||||
#include "tracing/tracing.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
@@ -99,6 +97,33 @@ static std::unique_ptr<os::OpenGLContext> GL_context = nullptr;
|
||||
static std::unique_ptr<os::GraphicsOperations> graphic_operations = nullptr;
|
||||
static os::Viewport* current_viewport = nullptr;
|
||||
|
||||
static bool mode_change_func(os::ViewportState state, bool initial)
|
||||
{
|
||||
if (initial) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto window = os::getMainViewport();
|
||||
if (window == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
window->setState(state);
|
||||
|
||||
return true;
|
||||
}
|
||||
static auto WindowModeOption = options::OptionBuilder<os::ViewportState>("Graphics.WindowMode", "Window Mode",
|
||||
"Controls how the game window is created.")
|
||||
.category("Graphics")
|
||||
.level(options::ExpertLevel::Beginner)
|
||||
.values({{os::ViewportState::Fullscreen, "Fullscreen"},
|
||||
{os::ViewportState::Borderless, "Borderless"},
|
||||
{os::ViewportState::Windowed, "Windowed"}})
|
||||
.importance(98)
|
||||
.default_val(os::ViewportState::Fullscreen)
|
||||
.change_listener(mode_change_func)
|
||||
.finish();
|
||||
|
||||
void opengl_go_fullscreen()
|
||||
{
|
||||
if (Cmdline_fullscreen_window || Cmdline_window || GL_fullscreen || Fred_running)
|
||||
@@ -854,16 +879,16 @@ int opengl_check_for_errors(const char *err_at)
|
||||
return num_errors;
|
||||
}
|
||||
|
||||
void opengl_set_vsync(int status)
|
||||
void opengl_set_vsync(bool enable)
|
||||
{
|
||||
if ( (status < 0) || (status > 1) ) {
|
||||
Int3();
|
||||
return;
|
||||
if (enable) {
|
||||
// Try to use adaptive vsync when supported
|
||||
if (!GL_context->setSwapInterval(-1)) {
|
||||
GL_context->setSwapInterval(1);
|
||||
}
|
||||
} else {
|
||||
GL_context->setSwapInterval(0);
|
||||
}
|
||||
|
||||
GL_context->setSwapInterval(status);
|
||||
|
||||
GL_CHECK_FOR_ERRORS("end of set_vsync()");
|
||||
}
|
||||
|
||||
std::unique_ptr<os::Viewport> gr_opengl_create_viewport(const os::ViewPortProperties& props) {
|
||||
@@ -1021,10 +1046,24 @@ int opengl_init_display_device()
|
||||
attrs.title = Window_title;
|
||||
}
|
||||
|
||||
if (!Cmdline_window && ! Cmdline_fullscreen_window) {
|
||||
attrs.flags.set(os::ViewPortFlags::Fullscreen);
|
||||
} else if (Cmdline_fullscreen_window) {
|
||||
attrs.flags.set(os::ViewPortFlags::Borderless);
|
||||
if (Using_in_game_options) {
|
||||
switch (WindowModeOption->getValue()) {
|
||||
case os::ViewportState::Windowed:
|
||||
// That's the default
|
||||
break;
|
||||
case os::ViewportState::Borderless:
|
||||
attrs.flags.set(os::ViewPortFlags::Borderless);
|
||||
break;
|
||||
case os::ViewportState::Fullscreen:
|
||||
attrs.flags.set(os::ViewPortFlags::Fullscreen);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!Cmdline_window && !Cmdline_fullscreen_window) {
|
||||
attrs.flags.set(os::ViewPortFlags::Fullscreen);
|
||||
} else if (Cmdline_fullscreen_window) {
|
||||
attrs.flags.set(os::ViewPortFlags::Borderless);
|
||||
}
|
||||
}
|
||||
|
||||
auto viewport = gr_opengl_create_viewport(attrs);
|
||||
@@ -1497,7 +1536,7 @@ bool gr_opengl_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps)
|
||||
opengl_post_process_init();
|
||||
|
||||
// must be called after extensions are setup
|
||||
opengl_set_vsync( !Cmdline_no_vsync );
|
||||
opengl_set_vsync(Gr_enable_vsync);
|
||||
|
||||
gr_reset_matrices();
|
||||
gr_setup_viewport();
|
||||
@@ -1541,7 +1580,7 @@ bool gr_opengl_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps)
|
||||
|
||||
mprintf(( " Can use compressed textures: %s\n", Use_compressed_textures ? NOX("YES") : NOX("NO") ));
|
||||
mprintf(( " Texture compression available: %s\n", Texture_compression_available ? NOX("YES") : NOX("NO") ));
|
||||
mprintf(( " Post-processing enabled: %s\n", (Cmdline_postprocess) ? "YES" : "NO"));
|
||||
mprintf(( " Post-processing enabled: %s\n", (Gr_post_processing_enabled) ? "YES" : "NO"));
|
||||
mprintf(( " Using %s texture filter.\n", (GL_mipmap_filter) ? NOX("trilinear") : NOX("bilinear") ));
|
||||
|
||||
mprintf(( " OpenGL Shader Version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION) ));
|
||||
@@ -1572,9 +1611,9 @@ bool gr_opengl_is_capable(gr_capability capability)
|
||||
return Cmdline_height ? true : false;
|
||||
case CAPABILITY_SOFT_PARTICLES:
|
||||
case CAPABILITY_DISTORTION:
|
||||
return Cmdline_softparticles && !Cmdline_no_fbo;
|
||||
return Gr_enable_soft_particles && !Cmdline_no_fbo;
|
||||
case CAPABILITY_POST_PROCESSING:
|
||||
return Cmdline_postprocess && !Cmdline_no_fbo;
|
||||
return Gr_post_processing_enabled && !Cmdline_no_fbo;
|
||||
case CAPABILITY_DEFERRED_LIGHTING:
|
||||
return !Cmdline_no_fbo && !Cmdline_no_deferred_lighting;
|
||||
case CAPABILITY_SHADOWS:
|
||||
@@ -1592,16 +1631,20 @@ bool gr_opengl_is_capable(gr_capability capability)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gr_opengl_get_property(gr_property prop, void* dest) {
|
||||
switch(prop) {
|
||||
case gr_property::UNIFORM_BUFFER_OFFSET_ALIGNMENT:
|
||||
*((int*)dest) = GL_state.Constants.GetUniformBufferOffsetAlignment();
|
||||
return true;
|
||||
case gr_property::UNIFORM_BUFFER_MAX_SIZE:
|
||||
*((int*)dest) = GL_state.Constants.GetMaxUniformBlockSize();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
bool gr_opengl_get_property(gr_property prop, void* dest)
|
||||
{
|
||||
switch (prop) {
|
||||
case gr_property::UNIFORM_BUFFER_OFFSET_ALIGNMENT:
|
||||
*((int*)dest) = GL_state.Constants.GetUniformBufferOffsetAlignment();
|
||||
return true;
|
||||
case gr_property::UNIFORM_BUFFER_MAX_SIZE:
|
||||
*((int*)dest) = GL_state.Constants.GetMaxUniformBlockSize();
|
||||
return true;
|
||||
case gr_property::MAX_ANISOTROPY:
|
||||
*((float*)dest) = GL_state.Constants.GetMaxAnisotropy();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1679,7 +1722,8 @@ DCF(ogl_anisotropy, "toggles anisotropic filtering")
|
||||
|
||||
if (dc_optional_string_either("help", "--help")) {
|
||||
dc_printf("Sets OpenGL anisotropic filtering level.\n");
|
||||
dc_printf("GL_anisotropy [int] Valid values are 0 to %i. 0 turns off anisotropic filtering.\n", (int)opengl_get_max_anisotropy());
|
||||
dc_printf("GL_anisotropy [int] Valid values are 0 to %i. 0 turns off anisotropic filtering.\n",
|
||||
(int)GL_state.Constants.GetMaxAnisotropy());
|
||||
process = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ void gr_opengl_deferred_lighting_finish()
|
||||
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_normal_texture);
|
||||
GL_state.Texture.Enable(2, GL_TEXTURE_2D, Scene_position_texture);
|
||||
GL_state.Texture.Enable(3, GL_TEXTURE_2D, Scene_specular_texture);
|
||||
if (Cmdline_shadow_quality) {
|
||||
if (Shadow_quality != ShadowQuality::Disabled) {
|
||||
GL_state.Texture.Enable(4, GL_TEXTURE_2D_ARRAY, Shadow_map_texture);
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ void gr_opengl_deferred_lighting_finish()
|
||||
GR_DEBUG_SCOPE("Build buffer data");
|
||||
|
||||
auto header = uniformAligner.getHeader<deferred_global_data>();
|
||||
if (Cmdline_shadow_quality) {
|
||||
if (Shadow_quality != ShadowQuality::Disabled) {
|
||||
// Avoid this overhead when we are not going to use these values
|
||||
header->shadow_mv_matrix = Shadow_view_matrix;
|
||||
for (size_t i = 0; i < MAX_SHADOW_CASCADES; ++i) {
|
||||
@@ -173,7 +173,7 @@ void gr_opengl_deferred_lighting_finish()
|
||||
|
||||
switch (l.type) {
|
||||
case Light_Type::Directional:
|
||||
if (Cmdline_shadow_quality) {
|
||||
if (Shadow_quality != ShadowQuality::Disabled) {
|
||||
light_data->enable_shadows = first_directional ? 1 : 0;
|
||||
}
|
||||
vec4 light_dir;
|
||||
|
||||
@@ -88,9 +88,9 @@ void opengl_setup_scene_textures()
|
||||
Scene_texture_initialized = 0;
|
||||
|
||||
if ( Cmdline_no_fbo ) {
|
||||
Cmdline_postprocess = 0;
|
||||
Cmdline_softparticles = 0;
|
||||
Cmdline_fb_explosions = 0;
|
||||
Gr_post_processing_enabled = false;
|
||||
Gr_enable_soft_particles = false;
|
||||
Gr_framebuffer_effects = {};
|
||||
|
||||
Scene_ldr_texture = 0;
|
||||
Scene_color_texture = 0;
|
||||
@@ -338,13 +338,13 @@ void opengl_setup_scene_textures()
|
||||
//glDeleteTextures(1, &Scene_fxaa_output_texture);
|
||||
//Scene_fxaa_output_texture = 0;
|
||||
|
||||
Cmdline_postprocess = 0;
|
||||
Cmdline_softparticles = 0;
|
||||
Gr_post_processing_enabled = false;
|
||||
Gr_enable_soft_particles = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//Setup thruster distortion framebuffer
|
||||
if (Cmdline_fb_thrusters || Cmdline_fb_explosions)
|
||||
if (Gr_framebuffer_effects.any_set())
|
||||
{
|
||||
glGenFramebuffers(1, &Distortion_framebuffer);
|
||||
GL_state.BindFrameBuffer(Distortion_framebuffer);
|
||||
@@ -392,8 +392,8 @@ void opengl_setup_scene_textures()
|
||||
Scene_color_texture = 0;
|
||||
Scene_depth_texture = 0;
|
||||
|
||||
Cmdline_postprocess = 0;
|
||||
Cmdline_softparticles = 0;
|
||||
Gr_post_processing_enabled = false;
|
||||
Gr_enable_soft_particles = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -510,7 +510,7 @@ void gr_opengl_scene_texture_begin()
|
||||
|
||||
Scene_framebuffer_in_frame = true;
|
||||
|
||||
if ( Cmdline_postprocess && !PostProcessing_override ) {
|
||||
if ( Gr_post_processing_enabled && !PostProcessing_override ) {
|
||||
High_dynamic_range = true;
|
||||
}
|
||||
}
|
||||
@@ -533,7 +533,7 @@ void gr_opengl_scene_texture_end()
|
||||
time_buffer = 0.0f;
|
||||
}
|
||||
|
||||
if ( Cmdline_postprocess && !PostProcessing_override ) {
|
||||
if ( Gr_post_processing_enabled && !PostProcessing_override ) {
|
||||
gr_post_process_end();
|
||||
} else {
|
||||
GR_DEBUG_SCOPE("Draw scene texture");
|
||||
|
||||
@@ -21,10 +21,8 @@ extern bool PostProcessing_override;
|
||||
extern int opengl_check_framebuffer();
|
||||
|
||||
//Needed to track where the FXAA shaders are
|
||||
size_t fxaa_shader_id;
|
||||
//In case we don't find the shaders at all, this override is needed
|
||||
bool fxaa_unavailable = false;
|
||||
int Fxaa_preset_last_frame;
|
||||
bool zbuffer_saved = false;
|
||||
|
||||
// lightshaft parameters
|
||||
@@ -227,8 +225,11 @@ void gr_opengl_post_process_begin()
|
||||
}
|
||||
|
||||
void recompile_fxaa_shader() {
|
||||
if (!gr_is_fxaa_mode(Gr_aa_mode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mprintf(("Recompiling FXAA shader with preset %d\n", Cmdline_fxaa_preset));
|
||||
mprintf(("Recompiling FXAA shader...\n"));
|
||||
|
||||
// start recompile by grabbing deleting the current shader we have, assuming it's already created
|
||||
opengl_delete_shader( gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_FXAA, 0) );
|
||||
@@ -236,7 +237,7 @@ void recompile_fxaa_shader() {
|
||||
// then recreate it again. shader loading code will be updated with the new FXAA presets
|
||||
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_FXAA, 0);
|
||||
|
||||
Fxaa_preset_last_frame = Cmdline_fxaa_preset;
|
||||
Gr_aa_mode_last_frame = Gr_aa_mode;
|
||||
}
|
||||
|
||||
void opengl_post_pass_fxaa() {
|
||||
@@ -244,7 +245,7 @@ void opengl_post_pass_fxaa() {
|
||||
TRACE_SCOPE(tracing::FXAA);
|
||||
|
||||
//If the preset changed, recompile the shader
|
||||
if (Fxaa_preset_last_frame != Cmdline_fxaa_preset) {
|
||||
if (Gr_aa_mode_last_frame != Gr_aa_mode) {
|
||||
recompile_fxaa_shader();
|
||||
}
|
||||
|
||||
@@ -362,7 +363,7 @@ void gr_opengl_post_process_end()
|
||||
opengl_post_pass_tonemap();
|
||||
|
||||
// Do FXAA
|
||||
if (Cmdline_fxaa && !fxaa_unavailable && !GL_rendering_to_texture) {
|
||||
if (gr_is_fxaa_mode(Gr_aa_mode) && !fxaa_unavailable && !GL_rendering_to_texture) {
|
||||
opengl_post_pass_fxaa();
|
||||
}
|
||||
|
||||
@@ -728,63 +729,26 @@ void opengl_post_shader_header(SCP_stringstream &sflags, shader_type shader_t, i
|
||||
sflags << "#define FXAA_GATHER4_ALPHA 1\n";
|
||||
}
|
||||
|
||||
switch (Cmdline_fxaa_preset) {
|
||||
case 0:
|
||||
switch (Gr_aa_mode) {
|
||||
case AntiAliasMode::None:
|
||||
sflags << "#define FXAA_QUALITY_PRESET 10\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/6.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/12.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
|
||||
break;
|
||||
case 1:
|
||||
sflags << "#define FXAA_QUALITY_PRESET 11\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/7.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/14.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
|
||||
break;
|
||||
case 2:
|
||||
case AntiAliasMode::FXAA_Low:
|
||||
sflags << "#define FXAA_QUALITY_PRESET 12\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/8.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/16.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
|
||||
break;
|
||||
case 3:
|
||||
sflags << "#define FXAA_QUALITY_PRESET 13\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/9.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/18.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
|
||||
break;
|
||||
case 4:
|
||||
sflags << "#define FXAA_QUALITY_PRESET 14\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/10.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/20.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
|
||||
break;
|
||||
case 5:
|
||||
sflags << "#define FXAA_QUALITY_PRESET 25\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/11.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/22.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
|
||||
break;
|
||||
case 6:
|
||||
case AntiAliasMode::FXAA_Medium:
|
||||
sflags << "#define FXAA_QUALITY_PRESET 26\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/12.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/24.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
|
||||
break;
|
||||
case 7:
|
||||
sflags << "#define FXAA_PC 1\n";
|
||||
sflags << "#define FXAA_QUALITY_PRESET 27\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/13.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/26.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
|
||||
break;
|
||||
case 8:
|
||||
sflags << "#define FXAA_QUALITY_PRESET 28\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/14.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/28.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
|
||||
break;
|
||||
case 9:
|
||||
case AntiAliasMode::FXAA_High:
|
||||
sflags << "#define FXAA_QUALITY_PRESET 39\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/15.0)\n";
|
||||
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/32.0)\n";
|
||||
@@ -821,7 +785,7 @@ bool opengl_post_init_shaders()
|
||||
|
||||
if ( gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_FXAA, 0) < 0 ||
|
||||
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_FXAA_PREPASS, 0) < 0 ) {
|
||||
Cmdline_fxaa = false;
|
||||
Gr_aa_mode = AntiAliasMode::None;
|
||||
fxaa_unavailable = true;
|
||||
mprintf(("Error while compiling FXAA shaders. FXAA will be unavailable.\n"));
|
||||
}
|
||||
@@ -918,11 +882,11 @@ void opengl_post_process_init()
|
||||
//We need to read the tbl first. This is mostly for FRED's benefit, as otherwise the list of post effects for the sexp doesn't get updated.
|
||||
if ( !opengl_post_init_table() ) {
|
||||
mprintf((" Unable to read post-processing table! Disabling post-processing...\n\n"));
|
||||
Cmdline_postprocess = 0;
|
||||
Gr_post_processing_enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !Cmdline_postprocess ) {
|
||||
if ( !Gr_post_processing_enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -931,19 +895,19 @@ void opengl_post_process_init()
|
||||
}
|
||||
|
||||
if ( Cmdline_no_fbo ) {
|
||||
Cmdline_postprocess = 0;
|
||||
Gr_post_processing_enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !opengl_post_init_shaders() ) {
|
||||
mprintf((" Unable to initialize post-processing shaders! Disabling post-processing...\n\n"));
|
||||
Cmdline_postprocess = 0;
|
||||
Gr_post_processing_enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !opengl_post_init_framebuffer() ) {
|
||||
mprintf((" Unable to initialize post-processing framebuffer! Disabling post-processing...\n\n"));
|
||||
Cmdline_postprocess = 0;
|
||||
Gr_post_processing_enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -883,20 +883,6 @@ static void opengl_purge_shader_cache_type(const char* ext) {
|
||||
SCP_string filter("*.");
|
||||
filter += ext;
|
||||
|
||||
// Previously the cache files were stored in the mod directory. Since we have a better system now, those files
|
||||
// should be cleaned out. This is only needed if we have a mod directory since otherwise we would delete the actual
|
||||
// cache files
|
||||
if (Cmdline_mod != nullptr && strlen(Cmdline_mod) > 0) {
|
||||
SCP_vector<SCP_string> cache_files;
|
||||
cf_get_file_list(cache_files, CF_TYPE_CACHE, filter.c_str(), CF_SORT_NONE, nullptr,
|
||||
CF_LOCATION_TYPE_PRIMARY_MOD | CF_LOCATION_TYPE_SECONDARY_MODS);
|
||||
|
||||
for (auto& file : cache_files) {
|
||||
cf_delete((file + "." + ext).c_str(), CF_TYPE_CACHE,
|
||||
CF_LOCATION_TYPE_PRIMARY_MOD | CF_LOCATION_TYPE_SECONDARY_MODS);
|
||||
}
|
||||
}
|
||||
|
||||
SCP_vector<SCP_string> cache_files;
|
||||
SCP_vector<file_list_info> file_info;
|
||||
cf_get_file_list(cache_files, CF_TYPE_CACHE, filter.c_str(), CF_SORT_NONE, &file_info,
|
||||
|
||||
@@ -718,10 +718,8 @@ void opengl_array_state::VertexAttribPointer(GLuint index, GLint size, GLenum ty
|
||||
|
||||
void opengl_array_state::ResetVertexAttribs()
|
||||
{
|
||||
SCP_map<GLuint, opengl_vertex_attrib_unit>::iterator it;
|
||||
|
||||
for ( it = vertex_attrib_units.begin(); it != vertex_attrib_units.end(); ++it ) {
|
||||
DisableVertexAttrib(it->first);
|
||||
for (auto &it : vertex_attrib_units) {
|
||||
DisableVertexAttrib(it.first);
|
||||
}
|
||||
|
||||
vertex_attrib_units.clear();
|
||||
@@ -733,19 +731,17 @@ void opengl_array_state::BindPointersBegin()
|
||||
client_texture_units[i].used_for_draw = false;
|
||||
}
|
||||
|
||||
SCP_map<GLuint, opengl_vertex_attrib_unit>::iterator it;
|
||||
|
||||
for (it = vertex_attrib_units.begin(); it != vertex_attrib_units.end(); ++it) {
|
||||
it->second.used_for_draw = false;
|
||||
for (auto &it : vertex_attrib_units) {
|
||||
it.second.used_for_draw = false;
|
||||
}
|
||||
}
|
||||
|
||||
void opengl_array_state::BindPointersEnd()
|
||||
{
|
||||
SCP_map<GLuint, opengl_vertex_attrib_unit>::iterator it;
|
||||
|
||||
for (it = vertex_attrib_units.begin(); it != vertex_attrib_units.end(); ++it) {
|
||||
if ( !it->second.used_for_draw ) DisableVertexAttrib(it->first);
|
||||
for (auto &it : vertex_attrib_units) {
|
||||
if (!it.second.used_for_draw) {
|
||||
DisableVertexAttrib(it.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,10 +760,8 @@ void opengl_array_state::BindArrayBuffer(GLuint id)
|
||||
client_texture_units[i].reset_ptr = true;
|
||||
}
|
||||
|
||||
SCP_map<GLuint,opengl_vertex_attrib_unit>::iterator it;
|
||||
|
||||
for ( it = vertex_attrib_units.begin(); it != vertex_attrib_units.end(); ++it ) {
|
||||
it->second.reset_ptr = true;
|
||||
for (auto &it : vertex_attrib_units) {
|
||||
it.second.reset_ptr = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -849,6 +843,12 @@ void opengl_constant_state::init() {
|
||||
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uniform_buffer_offset_alignment);
|
||||
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &_max_uniform_block_size);
|
||||
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &_max_uniform_block_bindings);
|
||||
|
||||
if (GLAD_GL_EXT_texture_filter_anisotropic) {
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &_max_anisotropy);
|
||||
} else {
|
||||
_max_anisotropy = -1.0f;
|
||||
}
|
||||
}
|
||||
GLint opengl_constant_state::GetUniformBufferOffsetAlignment() {
|
||||
return _uniform_buffer_offset_alignment;
|
||||
@@ -859,6 +859,9 @@ GLint opengl_constant_state::GetMaxUniformBlockSize() {
|
||||
GLint opengl_constant_state::GetMaxUniformBlockBindings() {
|
||||
return _max_uniform_block_bindings;
|
||||
}
|
||||
GLfloat opengl_constant_state::GetMaxAnisotropy() {
|
||||
return _max_anisotropy;
|
||||
}
|
||||
|
||||
void gr_opengl_clear_states()
|
||||
{
|
||||
|
||||
@@ -178,6 +178,7 @@ class opengl_constant_state {
|
||||
GLint _uniform_buffer_offset_alignment;
|
||||
GLint _max_uniform_block_size;
|
||||
GLint _max_uniform_block_bindings;
|
||||
GLfloat _max_anisotropy;
|
||||
|
||||
public:
|
||||
opengl_constant_state();
|
||||
@@ -189,6 +190,8 @@ class opengl_constant_state {
|
||||
GLint GetMaxUniformBlockSize();
|
||||
|
||||
GLint GetMaxUniformBlockBindings();
|
||||
|
||||
GLfloat GetMaxAnisotropy();
|
||||
};
|
||||
|
||||
class opengl_state
|
||||
|
||||
@@ -12,15 +12,16 @@
|
||||
#endif
|
||||
|
||||
#define BMPMAN_INTERNAL
|
||||
#include "gropenglstate.h"
|
||||
#include "gropengltexture.h"
|
||||
#include "bmpman/bm_internal.h"
|
||||
#include "bmpman/bmpman.h"
|
||||
#include "cmdline/cmdline.h"
|
||||
#include "ddsutils/ddsutils.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "graphics/grinternal.h"
|
||||
#include "gropenglstate.h"
|
||||
#include "gropengltexture.h"
|
||||
#include "math/vecmat.h"
|
||||
#include "options/Option.h"
|
||||
#include "osapi/osregistry.h"
|
||||
|
||||
matrix4 GL_texture_matrix;
|
||||
@@ -35,7 +36,6 @@ int GL_last_detail = -1;
|
||||
GLint GL_supported_texture_units = 2;
|
||||
int GL_should_preload = 0;
|
||||
GLfloat GL_anisotropy = 1.0f;
|
||||
GLfloat GL_max_anisotropy = -1.0f;
|
||||
int GL_mipmap_filter = 0;
|
||||
GLenum GL_texture_target = GL_TEXTURE_2D;
|
||||
GLenum GL_texture_face = GL_TEXTURE_2D;
|
||||
@@ -49,6 +49,57 @@ extern int CLOAKMAP;
|
||||
extern int ENVMAP;
|
||||
extern int Interp_multitex_cloakmap;
|
||||
|
||||
static SCP_vector<float> anisotropic_value_enumerator()
|
||||
{
|
||||
float max;
|
||||
if (!gr_get_property(gr_property::MAX_ANISOTROPY, &max)) {
|
||||
return SCP_vector<float>();
|
||||
}
|
||||
|
||||
if (max <= 2.0f) {
|
||||
return SCP_vector<float>();
|
||||
}
|
||||
|
||||
SCP_vector<float> out;
|
||||
|
||||
// We assume here that the anisotropy levels are powers of two...
|
||||
float current = 1.0f;
|
||||
while (current <= max) {
|
||||
out.push_back(current);
|
||||
current *= 2.0f;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
static SCP_string anisotropic_display(float val)
|
||||
{
|
||||
if (val < 2.0f) {
|
||||
return "None";
|
||||
}
|
||||
|
||||
SCP_string out;
|
||||
sprintf(out, "%.0fx", val);
|
||||
return out;
|
||||
}
|
||||
static float anisotropic_default()
|
||||
{
|
||||
float max;
|
||||
if (!gr_get_property(gr_property::MAX_ANISOTROPY, &max)) {
|
||||
return 1.0f;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
static auto AnisotropyOption =
|
||||
options::OptionBuilder<float>("Graphics.Anisotropy", "Anisotropic filtering",
|
||||
"Controls the amount of anisotropic filtering of the textures.")
|
||||
.enumerator(anisotropic_value_enumerator)
|
||||
.category("Graphics")
|
||||
.display(anisotropic_display)
|
||||
.default_func(anisotropic_default)
|
||||
.level(options::ExpertLevel::Advanced)
|
||||
.importance(78)
|
||||
.finish();
|
||||
|
||||
// forward declarations
|
||||
int opengl_free_texture(tcache_slot_opengl *t);
|
||||
@@ -72,22 +123,6 @@ void opengl_set_modulate_tex_env()
|
||||
GL_CHECK_FOR_ERRORS("end of set_modulate_tex_env()");
|
||||
}
|
||||
|
||||
GLfloat opengl_get_max_anisotropy()
|
||||
{
|
||||
if ( !GLAD_GL_EXT_texture_filter_anisotropic ) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if ( GL_max_anisotropy < 0.0f ) {
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &GL_max_anisotropy);
|
||||
}
|
||||
|
||||
// the spec says that it should be a minimum of 2.0
|
||||
Assert( GL_max_anisotropy >= 2.0f );
|
||||
|
||||
return GL_max_anisotropy;
|
||||
}
|
||||
|
||||
void opengl_set_texture_target( GLenum target )
|
||||
{
|
||||
GL_texture_target = target;
|
||||
@@ -131,13 +166,17 @@ void opengl_tcache_init()
|
||||
// anisotropy
|
||||
if ( GLAD_GL_EXT_texture_filter_anisotropic ) {
|
||||
// set max value first thing
|
||||
GL_anisotropy = opengl_get_max_anisotropy();
|
||||
GL_anisotropy = GL_state.Constants.GetMaxAnisotropy();
|
||||
|
||||
// now for the user setting
|
||||
if (Cmdline_aniso_level != 0)
|
||||
GL_anisotropy = (GLfloat) Cmdline_aniso_level;
|
||||
if (Using_in_game_options) {
|
||||
GL_anisotropy = AnisotropyOption->getValue();
|
||||
} else {
|
||||
// now for the user setting
|
||||
if (Cmdline_aniso_level != 0)
|
||||
GL_anisotropy = (GLfloat)Cmdline_aniso_level;
|
||||
}
|
||||
|
||||
CLAMP(GL_anisotropy, 1.0f, GL_max_anisotropy);
|
||||
CLAMP(GL_anisotropy, 1.0f, GL_state.Constants.GetMaxAnisotropy());
|
||||
}
|
||||
|
||||
Assert( GL_supported_texture_units >= 2 );
|
||||
|
||||
@@ -80,7 +80,6 @@ void opengl_tcache_frame();
|
||||
void opengl_set_additive_tex_env();
|
||||
void opengl_set_modulate_tex_env();
|
||||
void opengl_preload_init();
|
||||
GLfloat opengl_get_max_anisotropy();
|
||||
void opengl_kill_render_target(bitmap_slot* slot);
|
||||
int opengl_make_render_target(int handle, int *w, int *h, int *bpp, int *mm_lvl, int flags);
|
||||
int opengl_set_render_target(int slot, int face = -1, int is_static = 0);
|
||||
|
||||
@@ -12,28 +12,29 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "ShaderProgram.h"
|
||||
#include "gropengldeferred.h"
|
||||
#include "gropengldraw.h"
|
||||
#include "gropenglshader.h"
|
||||
#include "gropenglstate.h"
|
||||
#include "gropengltexture.h"
|
||||
#include "gropengltnl.h"
|
||||
#include "cmdline/cmdline.h"
|
||||
#include "def_files/def_files.h"
|
||||
#include "globalincs/alphacolors.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "graphics/2d.h"
|
||||
#include "graphics/matrix.h"
|
||||
#include "graphics/grinternal.h"
|
||||
#include "gropengldraw.h"
|
||||
#include "gropenglshader.h"
|
||||
#include "gropenglstate.h"
|
||||
#include "gropengltexture.h"
|
||||
#include "gropengldeferred.h"
|
||||
#include "gropengltnl.h"
|
||||
#include "graphics/light.h"
|
||||
#include "graphics/material.h"
|
||||
#include "graphics/matrix.h"
|
||||
#include "graphics/shadows.h"
|
||||
#include "lighting/lighting.h"
|
||||
#include "math/vecmat.h"
|
||||
#include "options/Option.h"
|
||||
#include "particle/particle.h"
|
||||
#include "render/3d.h"
|
||||
#include "weapon/trails.h"
|
||||
#include "particle/particle.h"
|
||||
#include "graphics/shadows.h"
|
||||
#include "graphics/material.h"
|
||||
#include "graphics/light.h"
|
||||
#include "ShaderProgram.h"
|
||||
|
||||
extern int GLOWMAP;
|
||||
extern int CLOAKMAP;
|
||||
@@ -61,6 +62,7 @@ GLint GL_max_elements_indices = 4096;
|
||||
GLuint Shadow_map_texture = 0;
|
||||
GLuint Shadow_map_depth_texture = 0;
|
||||
GLuint shadow_fbo = 0;
|
||||
int Shadow_texture_size = 0;
|
||||
bool Rendering_to_shadow_map = false;
|
||||
|
||||
int Transform_buffer_handle = -1;
|
||||
@@ -455,6 +457,7 @@ static bool opengl_init_shadow_framebuffer(int size, GLenum color_format)
|
||||
if (status == GL_FRAMEBUFFER_COMPLETE) {
|
||||
// Everything is fine
|
||||
mprintf(("Shadow framebuffer created successfully.\n"));
|
||||
Shadow_texture_size = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -491,13 +494,30 @@ void opengl_tnl_init()
|
||||
{
|
||||
Transform_buffer_handle = opengl_create_texture_buffer_object();
|
||||
|
||||
if (Cmdline_shadow_quality) {
|
||||
int size = (Cmdline_shadow_quality == 2 ? 1024 : 512);
|
||||
if (Shadow_quality != ShadowQuality::Disabled) {
|
||||
int size;
|
||||
switch (Shadow_quality) {
|
||||
case ShadowQuality::Low:
|
||||
size = 512;
|
||||
break;
|
||||
case ShadowQuality::Medium:
|
||||
size = 1024;
|
||||
break;
|
||||
case ShadowQuality::High:
|
||||
size = 2048;
|
||||
break;
|
||||
case ShadowQuality::Ultra:
|
||||
size = 4096;
|
||||
break;
|
||||
default:
|
||||
size = 256;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!opengl_init_shadow_framebuffer(size, GL_RGBA32F)) {
|
||||
if (!opengl_init_shadow_framebuffer(size, GL_RGBA16F)) {
|
||||
mprintf(("Failed to create either 32 or 16-bit color shadow framebuffer. Disabling shadow support.\n"));
|
||||
Cmdline_shadow_quality = 0;
|
||||
Shadow_quality = ShadowQuality::Disabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -628,7 +648,7 @@ extern bool gr_htl_projection_matrix_set;
|
||||
|
||||
void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light_orient)
|
||||
{
|
||||
if ( !Cmdline_shadow_quality )
|
||||
if (Shadow_quality == ShadowQuality::Disabled)
|
||||
return;
|
||||
|
||||
GL_state.PushFramebufferState();
|
||||
@@ -652,8 +672,7 @@ void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light
|
||||
|
||||
*shadow_view_matrix = gr_view_matrix;
|
||||
|
||||
int size = (Cmdline_shadow_quality == 2 ? 1024 : 512);
|
||||
glViewport(0, 0, size, size);
|
||||
glViewport(0, 0, Shadow_texture_size, Shadow_texture_size);
|
||||
}
|
||||
|
||||
void gr_opengl_shadow_map_end()
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) Freespace Open 2013. All rights reserved.
|
||||
*
|
||||
* All source code herein is the property of Freespace Open. You may not sell
|
||||
* or otherwise commercially exploit the source or things you created based on the
|
||||
* All source code herein is the property of Freespace Open. You may not sell
|
||||
* or otherwise commercially exploit the source or things you created based on the
|
||||
* source.
|
||||
*
|
||||
*/
|
||||
*/
|
||||
|
||||
#include "graphics/shadows.h"
|
||||
#include "asteroid/asteroid.h"
|
||||
#include "cmdline/cmdline.h"
|
||||
#include "debris/debris.h"
|
||||
#include "graphics/shadows.h"
|
||||
#include "graphics/matrix.h"
|
||||
#include "lighting/lighting.h"
|
||||
#include "math/vecmat.h"
|
||||
#include "model/model.h"
|
||||
#include "model/modelrender.h"
|
||||
#include "options/Option.h"
|
||||
#include "render/3d.h"
|
||||
#include "tracing/tracing.h"
|
||||
|
||||
@@ -27,6 +28,27 @@ float Shadow_cascade_distances[MAX_SHADOW_CASCADES];
|
||||
|
||||
light_frustum_info Shadow_frustums[MAX_SHADOW_CASCADES];
|
||||
|
||||
ShadowQuality Shadow_quality;
|
||||
|
||||
auto ShadowQualityOption =
|
||||
options::OptionBuilder<ShadowQuality>("Graphics.Shadows", "Shadow Quality", "The quality of the shadows")
|
||||
.values({{ShadowQuality::Disabled, "Disabled"},
|
||||
{ShadowQuality::Low, "Low"},
|
||||
{ShadowQuality::Medium, "Medium"},
|
||||
{ShadowQuality::High, "High"},
|
||||
{ShadowQuality::Ultra, "Ultra"}})
|
||||
.change_listener([](ShadowQuality val, bool initial) {
|
||||
if (initial) {
|
||||
Shadow_quality = val;
|
||||
}
|
||||
return initial; // No dynamic changes implemented
|
||||
})
|
||||
.level(options::ExpertLevel::Advanced)
|
||||
.category("Graphics")
|
||||
.default_val(ShadowQuality::Disabled)
|
||||
.importance(80)
|
||||
.finish();
|
||||
|
||||
bool shadows_obj_in_frustum(object *objp, matrix *light_orient, vec3d *min, vec3d *max)
|
||||
{
|
||||
vec3d pos, pos_rot;
|
||||
@@ -402,7 +424,7 @@ void shadows_render_all(float fov, matrix *eye_orient, vec3d *eye_pos)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Cmdline_shadow_quality) {
|
||||
if (Shadow_quality == ShadowQuality::Disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@ struct light_frustum_info
|
||||
float start_dist;
|
||||
};
|
||||
|
||||
enum class ShadowQuality { Disabled = 0, Low = 1, Medium = 2, High = 3, Ultra = 4 };
|
||||
|
||||
extern ShadowQuality Shadow_quality;
|
||||
|
||||
extern matrix4 Shadow_view_matrix;
|
||||
extern matrix4 Shadow_proj_matrix[MAX_SHADOW_CASCADES];
|
||||
extern float Shadow_cascade_distances[MAX_SHADOW_CASCADES];
|
||||
@@ -36,4 +40,4 @@ void shadows_render_all(float fov, matrix *eye_orient, vec3d *eye_pos);
|
||||
matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, float fov, float aspect, float veryneardist, float neardist, float middist, float fardist);
|
||||
void shadows_end_render();
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+43
-9
@@ -5,7 +5,7 @@
|
||||
* or otherwise commercially exploit the source or things you created based on the
|
||||
* source.
|
||||
*
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
#include "ai/aigoals.h"
|
||||
@@ -23,9 +23,10 @@
|
||||
#include "hud/hudets.h"
|
||||
#include "hud/hudlock.h"
|
||||
#include "hud/hudmessage.h"
|
||||
#include "hud/hudnavigation.h" //kazan
|
||||
#include "hud/hudnavigation.h" //kazan
|
||||
#include "hud/hudobserver.h"
|
||||
#include "hud/hudreticle.h"
|
||||
#include "hud/hudscripting.h"
|
||||
#include "hud/hudshield.h"
|
||||
#include "hud/hudsquadmsg.h"
|
||||
#include "hud/hudtarget.h"
|
||||
@@ -50,6 +51,7 @@
|
||||
#include "radar/radar.h"
|
||||
#include "radar/radarsetup.h"
|
||||
#include "render/3d.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "ship/ship.h"
|
||||
#include "starfield/supernova.h"
|
||||
#include "weapon/emp.h"
|
||||
@@ -246,7 +248,7 @@ static int Damage_flash_timer;
|
||||
|
||||
HudGauge::HudGauge():
|
||||
base_w(0), base_h(0), gauge_config(-1), font_num(font::FONT1), lock_color(false), sexp_lock_color(false), reticle_follow(false),
|
||||
active(false), off_by_default(false), sexp_override(false), pop_up(false), disabled_views(0), custom_gauge(false),
|
||||
active(false), off_by_default(false), sexp_override(false), pop_up(false), disabled_views(0), only_render_in_chase_view(false), custom_gauge(false),
|
||||
texture_target(-1), canvas_w(-1), canvas_h(-1), target_w(-1), target_h(-1)
|
||||
{
|
||||
position[0] = 0;
|
||||
@@ -271,7 +273,7 @@ texture_target(-1), canvas_w(-1), canvas_h(-1), target_w(-1), target_h(-1)
|
||||
HudGauge::HudGauge(int _gauge_object, int _gauge_config, bool _slew, bool _message, int _disabled_views, int r, int g, int b):
|
||||
base_w(0), base_h(0), gauge_config(_gauge_config), gauge_object(_gauge_object), font_num(font::FONT1), lock_color(false), sexp_lock_color(false),
|
||||
reticle_follow(_slew), active(false), off_by_default(false), sexp_override(false), pop_up(false), message_gauge(_message),
|
||||
disabled_views(_disabled_views), custom_gauge(false), textoffset_x(0), textoffset_y(0), texture_target(-1),
|
||||
disabled_views(_disabled_views), only_render_in_chase_view(false), custom_gauge(false), textoffset_x(0), textoffset_y(0), texture_target(-1),
|
||||
canvas_w(-1), canvas_h(-1), target_w(-1), target_h(-1)
|
||||
{
|
||||
Assert(gauge_config <= NUM_HUD_GAUGES && gauge_config >= 0);
|
||||
@@ -307,8 +309,8 @@ canvas_w(-1), canvas_h(-1), target_w(-1), target_h(-1)
|
||||
HudGauge::HudGauge(int _gauge_config, bool _slew, int r, int g, int b, char* _custom_name, char* _custom_text, char* frame_fname, int txtoffset_x, int txtoffset_y):
|
||||
base_w(0), base_h(0), gauge_config(_gauge_config), gauge_object(HUD_OBJECT_CUSTOM), font_num(font::FONT1), lock_color(false), sexp_lock_color(false),
|
||||
reticle_follow(_slew), active(false), off_by_default(false), sexp_override(false), pop_up(false), message_gauge(false),
|
||||
disabled_views(VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY), custom_gauge(true), textoffset_x(txtoffset_x),
|
||||
textoffset_y(txtoffset_y), texture_target(-1), canvas_w(-1), canvas_h(-1), target_w(-1), target_h(-1)
|
||||
disabled_views(VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY), only_render_in_chase_view(false), custom_gauge(true), textoffset_x(txtoffset_x),
|
||||
textoffset_y(txtoffset_y), texture_target(-1), canvas_w(-1), canvas_h(-1), target_w(-1), target_h(-1)
|
||||
{
|
||||
position[0] = 0;
|
||||
position[1] = 0;
|
||||
@@ -546,6 +548,11 @@ void HudGauge::initRenderStatus(bool do_render)
|
||||
off_by_default = !do_render;
|
||||
}
|
||||
|
||||
void HudGauge::initChase_view_only(bool chase_view_only)
|
||||
{
|
||||
only_render_in_chase_view = chase_view_only;
|
||||
}
|
||||
|
||||
bool HudGauge::isOffbyDefault()
|
||||
{
|
||||
return off_by_default;
|
||||
@@ -917,7 +924,7 @@ void HudGauge::renderRect(int x, int y, int w, int h)
|
||||
gr_reset_screen_scale();
|
||||
}
|
||||
|
||||
void HudGauge::renderCircle(int x, int y, int diameter)
|
||||
void HudGauge::renderCircle(int x, int y, int diameter, bool filled)
|
||||
{
|
||||
int nx = 0, ny = 0;
|
||||
|
||||
@@ -935,8 +942,12 @@ void HudGauge::renderCircle(int x, int y, int diameter)
|
||||
gr_set_screen_scale(base_w, base_h);
|
||||
}
|
||||
}
|
||||
if (filled) {
|
||||
gr_circle(x + nx, y + ny, diameter);
|
||||
} else {
|
||||
gr_unfilled_circle(x + nx, y + ny, diameter);
|
||||
}
|
||||
|
||||
gr_circle(x+nx, y+ny, diameter);
|
||||
gr_reset_screen_scale();
|
||||
}
|
||||
|
||||
@@ -1082,6 +1093,10 @@ bool HudGauge::canRender()
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((Viewer_mode & (VM_CHASE)) == 0 && only_render_in_chase_view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(pop_up) {
|
||||
if(!popUpActive()) {
|
||||
return false;
|
||||
@@ -1222,6 +1237,25 @@ void HUD_init()
|
||||
default_hud_gauges[j]->resetTimers();
|
||||
default_hud_gauges[j]->updateSexpOverride(false);
|
||||
}
|
||||
|
||||
Script_system.OnStateDestroy.add(hud_scripting_close);
|
||||
}
|
||||
|
||||
void hud_scripting_close(lua_State*) {
|
||||
// Clean up Lua references so that we don't have dangling references on to the lua state in the HUD gauges
|
||||
for (auto it = Ship_info.begin(); it != Ship_info.end(); ++it) {
|
||||
for (const auto& gauge : it->hud_gauges) {
|
||||
if (gauge->getObjectType() == HUD_OBJECT_SCRIPTING) {
|
||||
static_cast<HudGaugeScripting*>(gauge.get())->setRenderFunction(luacpp::LuaFunction());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& gauge : default_hud_gauges) {
|
||||
if (gauge->getObjectType() == HUD_OBJECT_SCRIPTING) {
|
||||
static_cast<HudGaugeScripting*>(gauge.get())->setRenderFunction(luacpp::LuaFunction());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3911,7 +3945,7 @@ void HudGaugeSupernova::render(float /*frametime*/)
|
||||
if (Lcl_pl) {
|
||||
renderPrintf(position[0], position[1], "Wybuch supernowej: %.2f s", time_left);
|
||||
} else {
|
||||
renderPrintf(position[0], position[1], "Supernova Warning: %.2f s", time_left);
|
||||
renderPrintf(position[0], position[1], XSTR( "Supernova Warning: %.2f s", 1639), time_left);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -110,6 +110,7 @@ extern int Hud_max_targeting_range;
|
||||
|
||||
void HUD_init_colors();
|
||||
void HUD_init();
|
||||
void hud_scripting_close(lua_State*);
|
||||
void hud_close();
|
||||
void hud_level_close();
|
||||
void hud_update_frame(float frametime); // updates hud systems not dependant on rendering
|
||||
@@ -223,6 +224,7 @@ protected:
|
||||
int flash_duration;
|
||||
int flash_next;
|
||||
bool flash_status;
|
||||
bool only_render_in_chase_view;
|
||||
|
||||
// custom gauge specific stuff
|
||||
bool custom_gauge;
|
||||
@@ -268,7 +270,7 @@ public:
|
||||
void updateActive(bool show);
|
||||
void updatePopUp(bool pop_up_flag);
|
||||
void updateSexpOverride(bool sexp);
|
||||
|
||||
void initChase_view_only(bool chase_view_only);
|
||||
|
||||
// SEXP interfacing functions
|
||||
// For flashing gauges in training missions
|
||||
@@ -316,7 +318,7 @@ public:
|
||||
void renderLine(int x1, int y1, int x2, int y2);
|
||||
void renderGradientLine(int x1, int y1, int x2, int y2);
|
||||
void renderRect(int x, int y, int w, int h);
|
||||
void renderCircle(int x, int y, int diameter);
|
||||
void renderCircle(int x, int y, int diameter, bool filled = true);
|
||||
|
||||
void unsize(int *x, int *y);
|
||||
void unsize(float *x, float *y);
|
||||
|
||||
@@ -95,6 +95,32 @@ void parse_ssm(const char *filename)
|
||||
s.max_count = -1;
|
||||
}
|
||||
|
||||
if (optional_string("+WarpEffect:")) {
|
||||
int temp;
|
||||
if (stuff_int_optional(&temp) != 2) {
|
||||
// We have a string to parse instead.
|
||||
char unique_id[NAME_LENGTH];
|
||||
memset(unique_id, 0, NAME_LENGTH);
|
||||
stuff_string(unique_id, F_NAME, NAME_LENGTH);
|
||||
int fireball_type = fireball_info_lookup(unique_id);
|
||||
if (fireball_type < 0) {
|
||||
error_display(1, "Unknown fireball [%s] to use as warp effect for SSM strike [%s]", unique_id, s.name);
|
||||
s.fireball_idx = FIREBALL_WARP;
|
||||
} else {
|
||||
s.fireball_idx = fireball_type;
|
||||
}
|
||||
} else {
|
||||
if ((temp < 0) || (temp >= Num_fireball_types)) {
|
||||
error_display(1, "Fireball index [%d] out of range (should be 0-%d) for SSM strike [%s]", temp, Num_fireball_types - 1, s.name);
|
||||
s.fireball_idx = FIREBALL_WARP;
|
||||
} else {
|
||||
s.fireball_idx = temp;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.fireball_idx = FIREBALL_WARP;
|
||||
}
|
||||
|
||||
required_string("+WarpRadius:");
|
||||
stuff_float(&s.warp_radius);
|
||||
|
||||
@@ -422,7 +448,7 @@ void ssm_process()
|
||||
vm_vec_sub(&temp, &moveup->sinfo.target->pos, &moveup->sinfo.start_pos[idx]);
|
||||
vm_vec_normalize(&temp);
|
||||
vm_vector_2_matrix(&orient, &temp, NULL, NULL);
|
||||
moveup->fireballs[idx] = fireball_create(&moveup->sinfo.start_pos[idx], FIREBALL_WARP, FIREBALL_WARP_EFFECT, -1, si->warp_radius, false, &vmd_zero_vector, si->warp_time, 0, &orient);
|
||||
moveup->fireballs[idx] = fireball_create(&moveup->sinfo.start_pos[idx], si->fireball_idx, FIREBALL_WARP_EFFECT, -1, si->warp_radius, false, &vmd_zero_vector, si->warp_time, 0, &orient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ typedef struct ssm_info {
|
||||
int count; // # of missiles in this type of strike
|
||||
int max_count; // Maximum # of missiles in this type of strike (-1 for no variance)
|
||||
int weapon_info_index; // missile type
|
||||
int fireball_idx; // Type of fireball to use for warp effect (defaults to FIREBALL_WARP)
|
||||
float warp_radius; // radius of associated warp effect
|
||||
float warp_time; // how long the warp effect lasts
|
||||
float radius; // radius around the target ship
|
||||
@@ -49,7 +50,7 @@ typedef struct ssm_firing_info {
|
||||
SCP_vector<vec3d> start_pos; // start positions
|
||||
|
||||
int count; // The ssm_info count may be variable; this stores the actual number of projectiles for this strike.
|
||||
size_t ssm_index; // index info ssm_info array
|
||||
size_t ssm_index; // index into ssm_info array
|
||||
class object* target; // target for the strike
|
||||
int ssm_team; // team that fired the ssm.
|
||||
float duration; // how far into the warp effect to fire
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "hud/hudmessage.h"
|
||||
#include "hud/hudparse.h" //Duh.
|
||||
#include "hud/hudreticle.h"
|
||||
#include "hud/hudscripting.h"
|
||||
#include "hud/hudshield.h"
|
||||
#include "hud/hudsquadmsg.h"
|
||||
#include "hud/hudtarget.h"
|
||||
@@ -49,6 +50,8 @@ int Force_scaling_above_res_global[2] = {-1, -1};
|
||||
|
||||
int Hud_font = -1;
|
||||
|
||||
bool Chase_view_only_ex = false;
|
||||
|
||||
//WARNING: If you add gauges to this array, make sure to bump num_default_gauges!
|
||||
int num_default_gauges = 42;
|
||||
static int retail_gauges[] = {
|
||||
@@ -173,6 +176,7 @@ void parse_hud_gauges_tbl(const char *filename)
|
||||
color *hud_clr_p = NULL;
|
||||
color *ship_clr_p = NULL;
|
||||
bool scale_gauge = true;
|
||||
bool chase_view_only = false;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -195,6 +199,11 @@ void parse_hud_gauges_tbl(const char *filename)
|
||||
Hud_font = font::parse_font();
|
||||
}
|
||||
|
||||
if (optional_string("$Chase View Only:")) {
|
||||
stuff_boolean(&chase_view_only);
|
||||
Chase_view_only_ex = chase_view_only;
|
||||
}
|
||||
|
||||
if(optional_string("$Max Directives:")) {
|
||||
stuff_int(&Max_directives);
|
||||
}
|
||||
@@ -309,6 +318,10 @@ void parse_hud_gauges_tbl(const char *filename)
|
||||
if (optional_string("$Font:")) {
|
||||
ship_font = font::parse_font();
|
||||
}
|
||||
|
||||
if (optional_string("$Chase View Only:")) {
|
||||
stuff_boolean(&chase_view_only);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// can't find ship class. move on.
|
||||
@@ -344,6 +357,9 @@ void parse_hud_gauges_tbl(const char *filename)
|
||||
if (optional_string("$Font:")) {
|
||||
ship_font = font::parse_font();
|
||||
}
|
||||
if (optional_string("$Chase View Only:")) {
|
||||
stuff_boolean(&chase_view_only);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// No particular ship. -1 for default HUD configuration.
|
||||
@@ -449,6 +465,7 @@ void parse_hud_gauges_tbl(const char *filename)
|
||||
memcpy(settings.force_scaling_above_res, force_scaling_above_res, sizeof(settings.force_scaling_above_res));
|
||||
settings.ship_idx = &ship_classes;
|
||||
settings.use_clr = use_clr_p;
|
||||
settings.chase_view_only = chase_view_only;
|
||||
|
||||
// if "default" is specified, then the base resolution is {-1, -1},
|
||||
// indicating GR_640 or GR_1024 to the handlers. otherwise, change it
|
||||
@@ -898,6 +915,9 @@ int parse_gauge_type()
|
||||
|
||||
if ( optional_string("+Secondary Weapons:") )
|
||||
return HUD_OBJECT_SECONDARY_WEAPONS;
|
||||
|
||||
if ( optional_string("+Scripted Gauge:") )
|
||||
return HUD_OBJECT_SCRIPTING;
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -1078,6 +1098,9 @@ void load_gauge(int gauge, gauge_settings* settings)
|
||||
case HUD_OBJECT_SECONDARY_WEAPONS:
|
||||
load_gauge_secondary_weapons(settings);
|
||||
break;
|
||||
case HUD_OBJECT_SCRIPTING:
|
||||
load_gauge_scripting(settings);
|
||||
break;
|
||||
default:
|
||||
// It's either -1, indicating we're ignoring a parse error, or it's a coding error.
|
||||
Assertion(gauge == -1, "Invalid value '%d' passed to load_gauge(); get a coder!\n", gauge);
|
||||
@@ -1262,6 +1285,10 @@ std::unique_ptr<T> gauge_load_common(gauge_settings* settings, T* preAllocated =
|
||||
}
|
||||
}
|
||||
|
||||
if (optional_string("Chase View Only:")) {
|
||||
stuff_boolean(&settings->chase_view_only);
|
||||
}
|
||||
|
||||
if (settings->set_position) {
|
||||
if(optional_string("Slew:")) {
|
||||
stuff_boolean(&settings->slew);
|
||||
@@ -1277,6 +1304,7 @@ std::unique_ptr<T> gauge_load_common(gauge_settings* settings, T* preAllocated =
|
||||
|
||||
instance->initBaseResolution(settings->base_res[0], settings->base_res[1]);
|
||||
instance->initFont(settings->font_num);
|
||||
instance->initChase_view_only(settings->chase_view_only);
|
||||
if (settings->set_position) {
|
||||
instance->initPosition(settings->coords[0], settings->coords[1]);
|
||||
instance->initSlew(settings->slew);
|
||||
@@ -1401,6 +1429,10 @@ void load_gauge_custom(gauge_settings* settings)
|
||||
}
|
||||
}
|
||||
|
||||
if (optional_string("Chase View Only:")) {
|
||||
stuff_boolean(&settings->chase_view_only);
|
||||
}
|
||||
|
||||
required_string("Name:");
|
||||
stuff_string(name, F_NAME, MAX_FILENAME_LEN);
|
||||
|
||||
@@ -1452,6 +1484,7 @@ void load_gauge_custom(gauge_settings* settings)
|
||||
hud_gauge->updateColor(colors[0], colors[1], colors[2]);
|
||||
hud_gauge->lockConfigColor(lock_color);
|
||||
hud_gauge->initCockpitTarget(display_name, display_offset[0], display_offset[1], display_size[0], display_size[1], canvas_size[0], canvas_size[1]);
|
||||
hud_gauge->initChase_view_only(settings->chase_view_only);
|
||||
|
||||
gauge_assign_common(settings, std::move(hud_gauge));
|
||||
}
|
||||
@@ -2964,6 +2997,7 @@ void load_gauge_radar_dradis(gauge_settings* settings)
|
||||
hud_gauge->initCockpitTarget(display_name, display_offset[0], display_offset[1], display_size[0], display_size[1], canvas_size[0], canvas_size[1]);
|
||||
hud_gauge->initFont(settings->font_num);
|
||||
hud_gauge->initSound(loop_snd, loop_snd_volume, arrival_beep_snd, departure_beep_snd, stealth_arrival_snd, stealth_departure_snd, arrival_beep_delay, departure_beep_delay);
|
||||
hud_gauge->initChase_view_only(settings->chase_view_only);
|
||||
|
||||
gauge_assign_common(settings, std::move(hud_gauge));
|
||||
}
|
||||
@@ -5192,3 +5226,15 @@ void load_gauge_secondary_weapons(gauge_settings* settings)
|
||||
|
||||
gauge_assign_common(settings, std::move(hud_gauge));
|
||||
}
|
||||
|
||||
void load_gauge_scripting(gauge_settings* settings) {
|
||||
auto hud_gauge = gauge_load_common<HudGaugeScripting>(settings);
|
||||
|
||||
required_string("Name:");
|
||||
SCP_string name;
|
||||
stuff_string(name, F_NAME);
|
||||
|
||||
hud_gauge->initName(name);
|
||||
|
||||
gauge_assign_common(settings, std::move(hud_gauge));
|
||||
}
|
||||
+6
-1
@@ -23,6 +23,7 @@ extern int Hud_reticle_style;
|
||||
extern bool Scale_retail_gauges;
|
||||
extern int Force_scaling_above_res_global[2];
|
||||
extern int Hud_font;
|
||||
extern bool Chase_view_only_ex;
|
||||
|
||||
typedef struct gauge_settings {
|
||||
int base_res[2];
|
||||
@@ -38,8 +39,9 @@ typedef struct gauge_settings {
|
||||
bool set_position;
|
||||
bool set_colour;
|
||||
bool slew;
|
||||
bool chase_view_only;
|
||||
|
||||
gauge_settings() : font_num(Hud_font), scale_gauge(Scale_retail_gauges), ship_idx(nullptr), use_clr(nullptr), use_coords(false), set_position(true), set_colour(true), slew(false) {
|
||||
gauge_settings() : font_num(Hud_font), scale_gauge(Scale_retail_gauges), ship_idx(nullptr), use_clr(nullptr), use_coords(false), set_position(true), set_colour(true), slew(false), chase_view_only(Chase_view_only_ex) {
|
||||
base_res[0] = -1;
|
||||
base_res[1] = -1;
|
||||
memcpy(force_scaling_above_res, Force_scaling_above_res_global, sizeof(force_scaling_above_res));
|
||||
@@ -230,4 +232,7 @@ void load_gauge_primary_weapons(gauge_settings* settings);
|
||||
#define HUD_OBJECT_SECONDARY_WEAPONS 55
|
||||
void load_gauge_secondary_weapons(gauge_settings* settings);
|
||||
|
||||
#define HUD_OBJECT_SCRIPTING 56
|
||||
void load_gauge_scripting(gauge_settings* settings);
|
||||
|
||||
#endif // _HUDPARSE_H
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
//
|
||||
|
||||
#include "hud/hudscripting.h"
|
||||
|
||||
#include "parse/parselo.h"
|
||||
|
||||
#include "scripting/api/objs/hudgauge.h"
|
||||
|
||||
HudGaugeScripting::HudGaugeScripting() :
|
||||
HudGauge(HUD_OBJECT_SCRIPTING,
|
||||
HUD_CENTER_RETICLE,
|
||||
true,
|
||||
false,
|
||||
(VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_TOPDOWN | VM_OTHER_SHIP),
|
||||
255,
|
||||
255,
|
||||
255) {
|
||||
}
|
||||
|
||||
void HudGaugeScripting::render(float /*frametime*/) {
|
||||
using namespace scripting::api;
|
||||
|
||||
if (!_renderFunction.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_renderFunction.call({ luacpp::LuaValue::createValue(_renderFunction.getLuaState(),
|
||||
l_HudGaugeDrawFuncs.Set(this)) });
|
||||
}
|
||||
|
||||
void HudGaugeScripting::initName(SCP_string name) {
|
||||
if (name.size() > NAME_LENGTH - 1) {
|
||||
error_display(0,
|
||||
"Name \"%s\" is too long. May not be longer than %d! Name will be truncated",
|
||||
name.c_str(),
|
||||
NAME_LENGTH - 1);
|
||||
name.resize(NAME_LENGTH - 1);
|
||||
}
|
||||
strcpy(custom_name, name.c_str());
|
||||
}
|
||||
const luacpp::LuaFunction& HudGaugeScripting::getRenderFunction() const {
|
||||
return _renderFunction;
|
||||
}
|
||||
void HudGaugeScripting::setRenderFunction(const luacpp::LuaFunction& renderFunction) {
|
||||
_renderFunction = renderFunction;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "hud/hud.h"
|
||||
#include "scripting/lua/LuaFunction.h"
|
||||
|
||||
class HudGaugeScripting: public HudGauge {
|
||||
luacpp::LuaFunction _renderFunction;
|
||||
public:
|
||||
HudGaugeScripting();
|
||||
|
||||
void render(float frametime) override;
|
||||
|
||||
void initName(SCP_string name);
|
||||
|
||||
const luacpp::LuaFunction& getRenderFunction() const;
|
||||
void setRenderFunction(const luacpp::LuaFunction& renderFunction);
|
||||
};
|
||||
@@ -6338,6 +6338,8 @@ void HudGaugeOffscreen::calculatePosition(vertex* target_point, vec3d *tpos, vec
|
||||
if (eye_vertex->flags&PF_OVERFLOW) {
|
||||
Int3(); // This is unlikely to happen, but can if a clip goes through the player's eye.
|
||||
Player_ai->target_objnum = -1;
|
||||
if (!in_frame)
|
||||
g3_end_frame();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6398,6 +6400,8 @@ void HudGaugeOffscreen::calculatePosition(vertex* target_point, vec3d *tpos, vec
|
||||
|
||||
} else {
|
||||
Int3();
|
||||
if (!in_frame)
|
||||
g3_end_frame();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1435,10 +1435,9 @@ void HudGaugeExtraTargetData::render(float /*frametime*/)
|
||||
// Print out current orders if the targeted ship is friendly
|
||||
// AL 12-26-97: only show orders and time to target for friendly ships
|
||||
// Backslash: actually let's consult the IFF table. Maybe we want to show orders for certain teams, or hide orders for friendlies
|
||||
if (((Player_ship->team == target_shipp->team) ||
|
||||
((Iff_info[target_shipp->team].flags & IFFF_ORDERS_SHOWN) &&
|
||||
!(Iff_info[target_shipp->team].flags & IFFF_ORDERS_HIDDEN)))
|
||||
&& Ship_info[target_shipp->ship_info_index].is_flyable()) {
|
||||
if ( ((Player_ship->team == target_shipp->team) ||
|
||||
((Iff_info[target_shipp->team].flags & IFFF_ORDERS_SHOWN) && !(Iff_info[target_shipp->team].flags & IFFF_ORDERS_HIDDEN)))
|
||||
&& Ship_info[target_shipp->ship_info_index].is_flyable() ) {
|
||||
extra_data_shown = 1;
|
||||
auto orders = ship_return_orders(target_shipp);
|
||||
if (!orders.empty()) {
|
||||
|
||||
+117
-37
@@ -1,6 +1,8 @@
|
||||
#include "io/joy.h"
|
||||
#include "io/joy_ff.h"
|
||||
#include "io/timer.h"
|
||||
#include "mod_table/mod_table.h"
|
||||
#include "options/Option.h"
|
||||
#include "osapi/osapi.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -8,10 +10,13 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "libs/jansson.h"
|
||||
|
||||
using namespace io::joystick;
|
||||
using namespace os::events;
|
||||
|
||||
namespace {
|
||||
|
||||
typedef std::unique_ptr<Joystick> JoystickPtr;
|
||||
|
||||
SCP_vector<JoystickPtr> joysticks;
|
||||
@@ -59,7 +64,7 @@ HatPosition hatBtnToEnum(int in) {
|
||||
}
|
||||
};
|
||||
|
||||
SCP_string getJoystickGUID(SDL_Joystick *stick)
|
||||
SCP_string getJoystickGUID(SDL_Joystick* stick)
|
||||
{
|
||||
auto guid = SDL_JoystickGetGUID(stick);
|
||||
|
||||
@@ -79,16 +84,14 @@ SCP_string getJoystickGUID(SDL_Joystick *stick)
|
||||
return joystickGUID;
|
||||
}
|
||||
|
||||
bool isCurrentJoystick(Joystick* testStick) {
|
||||
auto currentGUID = os_config_read_string(nullptr, "CurrentJoystickGUID", nullptr);
|
||||
auto currentId = (int)os_config_read_uint(nullptr, "CurrentJoystick", 0);
|
||||
|
||||
if ((currentGUID == nullptr) || !strcmp(currentGUID, "")) {
|
||||
bool joystickMatchesGuid(Joystick* testStick, const SCP_string& guid, int id)
|
||||
{
|
||||
if (guid.empty()) {
|
||||
// Only use the id
|
||||
return currentId == testStick->getDeviceId();
|
||||
return id == testStick->getDeviceId();
|
||||
}
|
||||
|
||||
SCP_string guidStr(currentGUID);
|
||||
SCP_string guidStr(guid);
|
||||
std::transform(begin(guidStr), end(guidStr), begin(guidStr), [](char c) { return (char)::toupper(c); });
|
||||
|
||||
if (testStick->getGUID() != guidStr) {
|
||||
@@ -113,26 +116,73 @@ bool isCurrentJoystick(Joystick* testStick) {
|
||||
}
|
||||
|
||||
// Multiple sticks -> check if the device id is the same
|
||||
return testStick->getDeviceId() == currentId;
|
||||
return testStick->getDeviceId() == id;
|
||||
}
|
||||
|
||||
void enumerateJoysticks(SCP_vector<JoystickPtr>& outVec)
|
||||
bool isCurrentJoystick(Joystick* testStick)
|
||||
{
|
||||
auto num = SDL_NumJoysticks();
|
||||
outVec.clear();
|
||||
outVec.reserve(static_cast<size_t>(num));
|
||||
auto currentGUID = os_config_read_string(nullptr, "CurrentJoystickGUID", nullptr);
|
||||
auto currentId = (int)os_config_read_uint(nullptr, "CurrentJoystick", 0);
|
||||
|
||||
mprintf(("Printing joystick info:\n"));
|
||||
|
||||
for (auto i = 0; i < num; ++i)
|
||||
{
|
||||
auto ptr = JoystickPtr(new Joystick(i));
|
||||
ptr->printInfo();
|
||||
|
||||
outVec.push_back(std::move(ptr));
|
||||
}
|
||||
return joystickMatchesGuid(testStick, currentGUID, currentId);
|
||||
}
|
||||
|
||||
Joystick* joystick_deserialize(const json_t* value)
|
||||
{
|
||||
const char* guid;
|
||||
int id;
|
||||
|
||||
json_error_t err;
|
||||
if (json_unpack_ex((json_t*)value, &err, 0, "{s:s, s:i}", "guid", &guid, "id", &id) != 0) {
|
||||
throw json_exception(err);
|
||||
}
|
||||
|
||||
for (auto& test_stick : joysticks) {
|
||||
if (joystickMatchesGuid(test_stick.get(), guid, id)) {
|
||||
return test_stick.get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
json_t* joystick_serializer(Joystick* joystick)
|
||||
{
|
||||
if (joystick == nullptr) {
|
||||
return json_null();
|
||||
}
|
||||
|
||||
return json_pack("{sssi}", "guid", joystick->getGUID().c_str(), "id", joystick->getID());
|
||||
}
|
||||
|
||||
SCP_string joystick_display(Joystick* stick)
|
||||
{
|
||||
if (stick == nullptr) {
|
||||
return "None";
|
||||
}
|
||||
return stick->getName();
|
||||
}
|
||||
|
||||
SCP_vector<Joystick*> joystick_enumerator()
|
||||
{
|
||||
SCP_vector<Joystick*> out;
|
||||
out.push_back(nullptr);
|
||||
for (auto& stick : joysticks) {
|
||||
out.push_back(stick.get());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
auto JoystickOption = options::OptionBuilder<Joystick*>("Input.Joystick", "Joystick", "The current joystick.")
|
||||
.category("Input")
|
||||
.deserializer(joystick_deserialize)
|
||||
.serializer(joystick_serializer)
|
||||
.display(joystick_display)
|
||||
.enumerator(joystick_enumerator)
|
||||
.level(options::ExpertLevel::Beginner)
|
||||
.default_val(nullptr)
|
||||
.finish();
|
||||
|
||||
HatPosition convertSDLHat(int val)
|
||||
{
|
||||
switch (val)
|
||||
@@ -160,6 +210,22 @@ HatPosition convertSDLHat(int val)
|
||||
}
|
||||
}
|
||||
|
||||
void enumerateJoysticks(SCP_vector<JoystickPtr>& outVec)
|
||||
{
|
||||
auto num = SDL_NumJoysticks();
|
||||
outVec.clear();
|
||||
outVec.reserve(static_cast<size_t>(num));
|
||||
|
||||
mprintf(("Printing joystick info:\n"));
|
||||
|
||||
for (auto i = 0; i < num; ++i) {
|
||||
auto ptr = JoystickPtr(new Joystick(i));
|
||||
ptr->printInfo();
|
||||
|
||||
outVec.push_back(std::move(ptr));
|
||||
}
|
||||
}
|
||||
|
||||
void setCurrentJoystick(Joystick *stick)
|
||||
{
|
||||
currentJoystick = stick;
|
||||
@@ -286,10 +352,18 @@ bool device_event_handler(const SDL_Event &evt)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isCurrentJoystick(addedStick))
|
||||
{
|
||||
// found our wanted stick!
|
||||
setCurrentJoystick(addedStick);
|
||||
if (Using_in_game_options) {
|
||||
auto value = JoystickOption->getValue();
|
||||
|
||||
// Since the new stick has been added to the joystick list, the option will return the same pointer here
|
||||
if (value == addedStick) {
|
||||
setCurrentJoystick(addedStick);
|
||||
}
|
||||
} else {
|
||||
if (isCurrentJoystick(addedStick)) {
|
||||
// found our wanted stick!
|
||||
setCurrentJoystick(addedStick);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (evtType == SDL_JOYDEVICEREMOVED)
|
||||
@@ -746,19 +820,25 @@ namespace joystick
|
||||
addEventListener(SDL_JOYDEVICEREMOVED, DEFAULT_LISTENER_WEIGHT, device_event_handler);
|
||||
|
||||
// Search for the correct stick
|
||||
for (auto& stick : joysticks) {
|
||||
if (isCurrentJoystick(stick.get())) {
|
||||
// Joystick found
|
||||
setCurrentJoystick(stick.get());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Using_in_game_options) {
|
||||
// The new options system is in use
|
||||
setCurrentJoystick(JoystickOption->getValue());
|
||||
} else {
|
||||
// Fall back to the legacy config values
|
||||
for (auto& stick : joysticks) {
|
||||
if (isCurrentJoystick(stick.get())) {
|
||||
// Joystick found
|
||||
setCurrentJoystick(stick.get());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentJoystick == nullptr) {
|
||||
mprintf((" No joystick is being used.\n"));
|
||||
}
|
||||
if (currentJoystick == nullptr) {
|
||||
mprintf((" No joystick is being used.\n"));
|
||||
}
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
initialized = true;
|
||||
|
||||
joy_ff_init();
|
||||
|
||||
|
||||
+49
-14
@@ -1,21 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
||||
*
|
||||
* All source code herein is the property of Volition, Inc. You may not sell
|
||||
* or otherwise commercially exploit the source or things you created based on the
|
||||
* All source code herein is the property of Volition, Inc. You may not sell
|
||||
* or otherwise commercially exploit the source or things you created based on the
|
||||
* source.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "math/vecmat.h"
|
||||
#include "osapi/osregistry.h"
|
||||
#include "io/joy_ff.h"
|
||||
#include "io/joy.h"
|
||||
#include "io/joy_ff.h"
|
||||
#include "math/vecmat.h"
|
||||
#include "mod_table/mod_table.h"
|
||||
#include "options/Option.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "osapi/osregistry.h"
|
||||
|
||||
#include "SDL_haptic.h"
|
||||
|
||||
@@ -27,7 +26,7 @@
|
||||
static int Joy_ff_enabled = 0;
|
||||
static SDL_Haptic *haptic = NULL;
|
||||
static int joy_ff_handling_scaler = 0;
|
||||
static int Joy_ff_directional_hit_effect_enabled = 1;
|
||||
static bool Joy_ff_directional_hit_effect_enabled = true;
|
||||
|
||||
typedef struct {
|
||||
SDL_HapticEffect eff;
|
||||
@@ -47,6 +46,28 @@ static haptic_effect_t pDeathroll1;
|
||||
static haptic_effect_t pDeathroll2;
|
||||
static haptic_effect_t pExplode;
|
||||
|
||||
static auto ForceFeedbackOption =
|
||||
options::OptionBuilder<bool>("Input.ForceFeedback", "Force Feedback", "Enable or disable force feedback.")
|
||||
.category("Input")
|
||||
.level(options::ExpertLevel::Beginner)
|
||||
.default_val(false)
|
||||
.finish();
|
||||
|
||||
static auto HitEffectOption =
|
||||
options::OptionBuilder<bool>("Input.HitEffect", "Directional Hit", "Enable or disable the directional hit effect.")
|
||||
.category("Input")
|
||||
.level(options::ExpertLevel::Beginner)
|
||||
.default_val(false)
|
||||
.finish();
|
||||
|
||||
static auto ForceFeedbackStrength = options::OptionBuilder<float>("Input.FFStrength", "Force Feedback Strength",
|
||||
"The relative strength of Force Feedback effects.")
|
||||
.category("Input")
|
||||
.level(options::ExpertLevel::Beginner)
|
||||
.range(0, 100)
|
||||
.default_val(100)
|
||||
.finish();
|
||||
|
||||
static int joy_ff_create_effects();
|
||||
static int joy_ff_has_valid_effects();
|
||||
static int joy_ff_effect_playing(haptic_effect_t *eff);
|
||||
@@ -82,9 +103,13 @@ static void print_haptic_support() {
|
||||
|
||||
int joy_ff_init()
|
||||
{
|
||||
int ff_enabled = 0;
|
||||
bool ff_enabled;
|
||||
|
||||
ff_enabled = os_config_read_uint(NULL, "EnableJoystickFF", 1);
|
||||
if (Using_in_game_options) {
|
||||
ff_enabled = ForceFeedbackOption->getValue();
|
||||
} else {
|
||||
ff_enabled = os_config_read_uint(nullptr, "EnableJoystickFF", 1) != 0;
|
||||
}
|
||||
|
||||
if ( !ff_enabled || io::joystick::getCurrentJoystick() == nullptr) {
|
||||
return 0;
|
||||
@@ -137,10 +162,20 @@ int joy_ff_init()
|
||||
return -1;
|
||||
}
|
||||
|
||||
Joy_ff_directional_hit_effect_enabled = os_config_read_uint(NULL, "EnableHitEffect", 1);
|
||||
if (Using_in_game_options) {
|
||||
Joy_ff_directional_hit_effect_enabled = HitEffectOption->getValue();
|
||||
} else {
|
||||
Joy_ff_directional_hit_effect_enabled = os_config_read_uint(nullptr, "EnableHitEffect", 1) != 0;
|
||||
}
|
||||
// ForceFeedback.Strength lets the user specify how strong the effects should be. This uses SDL_HapticSetGain which
|
||||
// needs to be supported by the haptic device
|
||||
int ff_strength = os_config_read_uint("ForceFeedback", "Strength", 100);
|
||||
int ff_strength;
|
||||
|
||||
if (Using_in_game_options) {
|
||||
ff_strength = (int)ForceFeedbackStrength->getValue();
|
||||
} else {
|
||||
ff_strength = os_config_read_uint("ForceFeedback", "Strength", 100);
|
||||
}
|
||||
CLAMP(ff_strength, 0, 100);
|
||||
if (SDL_HapticQuery(haptic) & SDL_HAPTIC_GAIN) {
|
||||
SDL_HapticSetGain(haptic, ff_strength);
|
||||
|
||||
+26
-37
@@ -405,13 +405,13 @@ void labviewer_render_model(float frametime)
|
||||
|
||||
if (Lab_selected_object != -1) {
|
||||
auto lab_render_light_save = Lab_render_without_light;
|
||||
auto lab_debris_override_save = Cmdline_nomotiondebris;
|
||||
auto lab_debris_override_save = Motion_debris_enabled;
|
||||
auto lab_envmap_override_save = Envmap_override;
|
||||
auto lab_emissive_light_save = Cmdline_no_emissive;
|
||||
|
||||
if (Lab_selected_mission == "None") {
|
||||
Lab_render_without_light = true;
|
||||
Cmdline_nomotiondebris = 1;
|
||||
Motion_debris_enabled = true;
|
||||
}
|
||||
|
||||
Cmdline_no_emissive = !Lab_emissive_light_override;
|
||||
@@ -464,7 +464,7 @@ void labviewer_render_model(float frametime)
|
||||
Trail_render_override = false;
|
||||
|
||||
Lab_render_without_light = lab_render_light_save;
|
||||
Cmdline_nomotiondebris = lab_debris_override_save;
|
||||
Motion_debris_enabled = lab_debris_override_save;
|
||||
Envmap_override = lab_envmap_override_save;
|
||||
Cmdline_no_emissive = lab_emissive_light_save;
|
||||
|
||||
@@ -548,10 +548,27 @@ void labviewer_do_render(float frametime)
|
||||
}
|
||||
|
||||
// Print FXAA preset
|
||||
if (Cmdline_fxaa && !PostProcessing_override)
|
||||
if (gr_is_fxaa_mode(Gr_aa_mode) && !PostProcessing_override) {
|
||||
const char* fxaa_mode;
|
||||
switch (Gr_aa_mode) {
|
||||
case AntiAliasMode::FXAA_Low:
|
||||
fxaa_mode = "Low";
|
||||
break;
|
||||
case AntiAliasMode::FXAA_Medium:
|
||||
fxaa_mode = "Medium";
|
||||
break;
|
||||
case AntiAliasMode::FXAA_High:
|
||||
fxaa_mode = "High";
|
||||
break;
|
||||
default:
|
||||
fxaa_mode = "None";
|
||||
break;
|
||||
}
|
||||
|
||||
gr_printf_no_resize(gr_screen.center_offset_x + 2,
|
||||
gr_screen.center_offset_y + gr_screen.center_h - (gr_get_font_height() * 2) - 3,
|
||||
"FXAA Preset: %i", Cmdline_fxaa_preset);
|
||||
"FXAA Preset: %s", fxaa_mode);
|
||||
}
|
||||
|
||||
// Print current Team Color setting, if any
|
||||
if (Lab_team_color != "<none>")
|
||||
@@ -1140,7 +1157,7 @@ void labviewer_make_render_options_window(Button* /*caller*/)
|
||||
ADD_RENDER_FLAG("Show Damage Lightning", Lab_viewer_flags, LAB_FLAG_LIGHTNING_ARCS);
|
||||
ADD_RENDER_BOOL("Rotate Subsystems", Lab_rotate_subobjects);
|
||||
|
||||
if (Cmdline_postprocess) {
|
||||
if (Gr_post_processing_enabled) {
|
||||
ADD_RENDER_BOOL("Hide Post Processing", PostProcessing_override);
|
||||
}
|
||||
|
||||
@@ -2270,43 +2287,15 @@ void lab_do_frame(float frametime)
|
||||
// Adjust FXAA presets
|
||||
case KEY_0:
|
||||
if (!PostProcessing_override)
|
||||
Cmdline_fxaa_preset = 0;
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Low;
|
||||
break;
|
||||
case KEY_1:
|
||||
if (!PostProcessing_override)
|
||||
Cmdline_fxaa_preset = 1;
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
|
||||
break;
|
||||
case KEY_2:
|
||||
if (!PostProcessing_override)
|
||||
Cmdline_fxaa_preset = 2;
|
||||
break;
|
||||
case KEY_3:
|
||||
if (!PostProcessing_override)
|
||||
Cmdline_fxaa_preset = 3;
|
||||
break;
|
||||
case KEY_4:
|
||||
if (!PostProcessing_override)
|
||||
Cmdline_fxaa_preset = 4;
|
||||
break;
|
||||
case KEY_5:
|
||||
if (!PostProcessing_override)
|
||||
Cmdline_fxaa_preset = 5;
|
||||
break;
|
||||
case KEY_6:
|
||||
if (!PostProcessing_override)
|
||||
Cmdline_fxaa_preset = 6;
|
||||
break;
|
||||
case KEY_7:
|
||||
if (!PostProcessing_override)
|
||||
Cmdline_fxaa_preset = 7;
|
||||
break;
|
||||
case KEY_8:
|
||||
if (!PostProcessing_override)
|
||||
Cmdline_fxaa_preset = 8;
|
||||
break;
|
||||
case KEY_9:
|
||||
if (!PostProcessing_override)
|
||||
Cmdline_fxaa_preset = 9;
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_High;
|
||||
break;
|
||||
|
||||
case KEY_T:
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
|
||||
#include "libs/jansson.h"
|
||||
#include "jansson.h"
|
||||
#include "parse/parselo.h"
|
||||
|
||||
namespace {
|
||||
int json_write_callback(const char *buffer, size_t size, void *data) {
|
||||
@@ -17,6 +19,12 @@ size_t json_read_callback(void *buffer, size_t buflen, void *data) {
|
||||
|
||||
return (size_t)cfread(buffer, 1, (int)buflen, fp);
|
||||
}
|
||||
SCP_string format_error(const json_error_t& err)
|
||||
{
|
||||
SCP_string out;
|
||||
sprintf(out, "%s(%d:%d): %s", err.source, err.line, err.column, err.text);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
int json_dump_cfile(const json_t* json, CFILE* file, size_t flags) {
|
||||
@@ -25,3 +33,23 @@ int json_dump_cfile(const json_t* json, CFILE* file, size_t flags) {
|
||||
json_t* json_load_cfile(CFILE* cfile, size_t flags, json_error_t* error) {
|
||||
return json_load_callback(json_read_callback, cfile, flags, error);
|
||||
}
|
||||
SCP_string json_dump_string(const json_t* json, size_t flags)
|
||||
{
|
||||
auto buf = json_dumps(json, flags);
|
||||
|
||||
if (buf == nullptr) {
|
||||
return SCP_string();
|
||||
}
|
||||
|
||||
SCP_string out(buf);
|
||||
free(buf);
|
||||
|
||||
return out;
|
||||
}
|
||||
SCP_string json_dump_string_new(json_t* json, size_t flags) {
|
||||
auto str = json_dump_string(json, flags);
|
||||
json_decref(json);
|
||||
return str;
|
||||
}
|
||||
|
||||
json_exception::json_exception(const json_error_t& error) : std::runtime_error(format_error(error)) {}
|
||||
+24
-1
@@ -1,9 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "cfile/cfile.h"
|
||||
|
||||
#include <jansson.h>
|
||||
#include <stdexcept>
|
||||
|
||||
int json_dump_cfile(const json_t *json, CFILE* file, size_t flags);
|
||||
|
||||
json_t *json_load_cfile(CFILE* cfile, size_t flags, json_error_t *error);
|
||||
|
||||
SCP_string json_dump_string(const json_t* json, size_t flags);
|
||||
|
||||
SCP_string json_dump_string_new(json_t* json, size_t flagse);
|
||||
|
||||
// This allows using std::unique_ptr with json_t values
|
||||
namespace std {
|
||||
template <>
|
||||
class default_delete<json_t> {
|
||||
public:
|
||||
void operator()(json_t* ptr) const
|
||||
{
|
||||
if (ptr) {
|
||||
json_decref(ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
}; // namespace std
|
||||
|
||||
class json_exception : public std::runtime_error {
|
||||
public:
|
||||
explicit json_exception(const json_error_t& error);
|
||||
};
|
||||
@@ -56,7 +56,7 @@ int Lcl_english = 1;
|
||||
// the english version (in the code) to a foreign version (in the table). Thus, if you
|
||||
// add a new string to the code, you must assign it a new index. Use the number below for
|
||||
// that index and increase the number below by one.
|
||||
#define XSTR_SIZE 1638
|
||||
#define XSTR_SIZE 1641
|
||||
|
||||
|
||||
// struct to allow for strings.tbl-determined x offset
|
||||
|
||||
@@ -352,7 +352,7 @@ void player_select_do()
|
||||
// Goober5000 - display a popup warning about problems in the mod
|
||||
if ((Global_warning_count > 10 || Global_error_count > 0) && !Startup_warning_dialog_displayed) {
|
||||
char text[512];
|
||||
sprintf(text, "Warning!\n\nThe currently active mod has generated %d warnings and/or errors during program startup. These could have been caused by anything from incorrectly formated table files to corrupt models.\n\nWhile FreeSpace Open will attempt to compensate for these issues, it cannot guarantee a trouble-free gameplay experience.\n\nPlease contact the authors of the mod for assistance.", Global_warning_count + Global_error_count);
|
||||
sprintf(text, XSTR ("Warning!\n\nThe currently active mod has generated %d warnings and/or errors during program startup. These could have been caused by anything from incorrectly formated table files to corrupt models.\n\nWhile FreeSpace Open will attempt to compensate for these issues, it cannot guarantee a trouble-free gameplay experience.\n\nPlease contact the authors of the mod for assistance.", 1640), Global_warning_count + Global_error_count);
|
||||
popup(PF_TITLE_BIG | PF_TITLE_RED | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, text);
|
||||
Startup_warning_dialog_displayed = true;
|
||||
}
|
||||
|
||||
@@ -569,7 +569,7 @@ void techroom_ships_render(float frametime)
|
||||
render_info.set_replacement_textures(Techroom_ship_modelnum, sip->replacement_textures);
|
||||
}
|
||||
|
||||
if(Cmdline_shadow_quality)
|
||||
if(Shadow_quality != ShadowQuality::Disabled)
|
||||
{
|
||||
gr_reset_clip();
|
||||
|
||||
|
||||
@@ -97,9 +97,8 @@ bool mission_is_ignored(const char *filename)
|
||||
// Mission_load takes no parameters.
|
||||
// It sets the following global variables
|
||||
// Game_current_mission_filename
|
||||
|
||||
// returns -1 if failed, 0 if successful
|
||||
int mission_load(const char* filename_ext)
|
||||
// returns true successful, false if failed
|
||||
bool mission_load(const char* filename_ext)
|
||||
{
|
||||
TRACE_SCOPE(tracing::LoadMissionLoad);
|
||||
|
||||
@@ -120,7 +119,7 @@ int mission_load(const char* filename_ext)
|
||||
|
||||
if (mission_is_ignored(filename)) {
|
||||
mprintf(("MISSION LOAD: Tried to load an ignored mission! Aborting..."));
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
strcat_s(filename, FS_MISSION_FILE_EXT);
|
||||
@@ -131,8 +130,8 @@ int mission_load(const char* filename_ext)
|
||||
// to choose the type of ship that he is to fly
|
||||
// return value of 0 indicates success, other is failure.
|
||||
|
||||
if ( parse_main(filename) )
|
||||
return -1;
|
||||
if ( !parse_main(filename) )
|
||||
return false;
|
||||
|
||||
if (Select_default_ship) {
|
||||
int ret;
|
||||
@@ -143,7 +142,8 @@ int mission_load(const char* filename_ext)
|
||||
ml_update_recent_missions(filename_ext); // update recently played missions list (save the csg later)
|
||||
|
||||
init_hud();
|
||||
return 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//====================================
|
||||
|
||||
@@ -27,7 +27,7 @@ extern SCP_vector<SCP_string> Ignored_missions;
|
||||
// It sets the following global variables:
|
||||
// Game_current_mission_filename
|
||||
|
||||
int mission_load(const char* filename_ext);
|
||||
bool mission_load(const char* filename_ext);
|
||||
|
||||
bool mission_is_ignored(const char *filename);
|
||||
|
||||
|
||||
@@ -1500,8 +1500,8 @@ void message_queue_process()
|
||||
return;
|
||||
|
||||
// Goober5000 - argh, don't conflate special sources with ships!
|
||||
// NOTA BENE: don't check for != MESSAGE_SOURCE_COMMAND, because with the new command persona code, Command could be a ship
|
||||
if ( q->source != MESSAGE_SOURCE_SPECIAL ) {
|
||||
// NOTA BENE: don't check for != HUD_SOURCE_TERRAN_CMD, because with the new command persona code, Command could be a ship
|
||||
if ( q->source != HUD_SOURCE_IMPORTANT ) {
|
||||
Message_shipnum = ship_name_lookup( q->who_from );
|
||||
|
||||
// see if we need to check if sending ship is alive
|
||||
@@ -1859,7 +1859,7 @@ void message_send_unique_to_player( char *id, void *data, int m_source, int prio
|
||||
source = HUD_SOURCE_TERRAN_CMD;
|
||||
} else if ( m_source == MESSAGE_SOURCE_SPECIAL ) {
|
||||
who_from = (char *)data;
|
||||
source = HUD_SOURCE_TERRAN_CMD;
|
||||
source = HUD_SOURCE_IMPORTANT;
|
||||
} else if ( m_source == MESSAGE_SOURCE_WINGMAN ) {
|
||||
int m_persona, ship_index;
|
||||
|
||||
|
||||
+126
-128
@@ -374,7 +374,7 @@ extern fix game_get_overall_frametime(); // for texture animation
|
||||
|
||||
// local prototypes
|
||||
void parse_player_info2(mission *pm);
|
||||
void post_process_mission();
|
||||
bool post_process_mission();
|
||||
int allocate_subsys_status();
|
||||
void parse_common_object_data(p_object *objp);
|
||||
void parse_asteroid_fields(mission *pm);
|
||||
@@ -1859,7 +1859,6 @@ int parse_create_object(p_object *pobjp)
|
||||
}
|
||||
|
||||
void parse_bring_in_docked_wing(p_object *p_objp, int wingnum, int shipnum);
|
||||
void ship_set_warp_effects(object *objp);
|
||||
|
||||
/**
|
||||
* Given a stuffed p_object struct, create an object and fill in the necessary fields.
|
||||
@@ -1906,7 +1905,6 @@ int parse_create_object_sub(p_object *p_objp)
|
||||
|
||||
shipp->group = p_objp->group;
|
||||
shipp->team = p_objp->team;
|
||||
strcpy_s(shipp->ship_name, p_objp->name);
|
||||
shipp->display_name = p_objp->display_name;
|
||||
shipp->escort_priority = p_objp->escort_priority;
|
||||
shipp->use_special_explosion = p_objp->use_special_explosion;
|
||||
@@ -1968,37 +1966,25 @@ int parse_create_object_sub(p_object *p_objp)
|
||||
}
|
||||
}
|
||||
|
||||
if (!Fred_running)
|
||||
{
|
||||
ship_assign_sound(&Ships[shipnum]);
|
||||
}
|
||||
|
||||
aip = &(Ai_info[shipp->ai_index]);
|
||||
aip->behavior = p_objp->behavior;
|
||||
aip->mode = aip->behavior;
|
||||
|
||||
// make sure aim_safety has its submode defined
|
||||
if (aip->mode == AIM_SAFETY) {
|
||||
aip->submode = AISS_1;
|
||||
}
|
||||
|
||||
// alternate stuff
|
||||
shipp->alt_type_index = p_objp->alt_type_index;
|
||||
shipp->callsign_index = p_objp->callsign_index;
|
||||
|
||||
// AI stuff. Note a lot of the AI was already initialized in ship_create.
|
||||
aip = &(Ai_info[shipp->ai_index]);
|
||||
|
||||
aip->ai_class = p_objp->ai_class;
|
||||
shipp->weapons.ai_class = p_objp->ai_class; // Fred uses this instead of above.
|
||||
//Fixes a bug where the AI class attributes were not copied if the AI class was set in the mission.
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ai_class_bug])
|
||||
ship_set_new_ai_class(shipnum, p_objp->ai_class);
|
||||
|
||||
// must reset the number of ai goals when the object is created
|
||||
for (i = 0; i < MAX_AI_GOALS; i++)
|
||||
{
|
||||
aip->goals[i].ai_mode = AI_GOAL_NONE;
|
||||
aip->goals[i].signature = -1;
|
||||
aip->goals[i].priority = -1;
|
||||
aip->goals[i].flags.reset();
|
||||
aip->behavior = p_objp->behavior;
|
||||
aip->mode = aip->behavior;
|
||||
|
||||
// make sure aim_safety has its submode defined
|
||||
if (aip->mode == AIM_SAFETY) {
|
||||
aip->submode = AISS_1;
|
||||
}
|
||||
|
||||
shipp->cargo1 = p_objp->cargo1;
|
||||
@@ -2259,11 +2245,13 @@ int parse_create_object_sub(p_object *p_objp)
|
||||
}
|
||||
}
|
||||
|
||||
// skip the rest because the Pilot subsystem is special
|
||||
continue;
|
||||
}
|
||||
|
||||
ptr = GET_FIRST(&shipp->subsys_list);
|
||||
while (ptr != END_OF_LIST(&shipp->subsys_list))
|
||||
// find the subsystem in the ship list that corresponds to the parsed subsystem
|
||||
ptr = ship_get_subsys(shipp, sssp->name);
|
||||
if (ptr != nullptr)
|
||||
{
|
||||
// check the mission flag to possibly free all beam weapons - Goober5000, taken from SEXP.CPP
|
||||
if (The_mission.flags[Mission::Mission_Flags::Beam_free_all_by_default])
|
||||
@@ -2285,81 +2273,80 @@ int parse_create_object_sub(p_object *p_objp)
|
||||
}
|
||||
}
|
||||
|
||||
if (!subsystem_stricmp(ptr->system_info->subobj_name, sssp->name))
|
||||
if (Fred_running)
|
||||
{
|
||||
if (Fred_running)
|
||||
ptr->current_hits = sssp->percent;
|
||||
ptr->max_hits = 100.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr->max_hits = ptr->system_info->max_subsys_strength * (shipp->ship_max_hull_strength / sip->max_hull_strength);
|
||||
|
||||
float new_hits = ptr->max_hits * (100.0f - sssp->percent) / 100.f;
|
||||
if (!(ptr->flags[Ship::Subsystem_Flags::No_aggregate])) {
|
||||
shipp->subsys_info[ptr->system_info->type].aggregate_current_hits -= (ptr->max_hits - new_hits);
|
||||
}
|
||||
|
||||
if ((100.0f - sssp->percent) < 0.5)
|
||||
{
|
||||
ptr->current_hits = sssp->percent;
|
||||
ptr->max_hits = 100.0f;
|
||||
ptr->current_hits = 0.0f;
|
||||
ptr->submodel_info_1.blown_off = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr->max_hits = ptr->system_info->max_subsys_strength * (shipp->ship_max_hull_strength / sip->max_hull_strength);
|
||||
|
||||
float new_hits = ptr->max_hits * (100.0f - sssp->percent) / 100.f;
|
||||
if (!(ptr->flags[Ship::Subsystem_Flags::No_aggregate])) {
|
||||
shipp->subsys_info[ptr->system_info->type].aggregate_current_hits -= (ptr->max_hits - new_hits);
|
||||
}
|
||||
|
||||
if ((100.0f - sssp->percent) < 0.5)
|
||||
{
|
||||
ptr->current_hits = 0.0f;
|
||||
ptr->submodel_info_1.blown_off = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr->current_hits = new_hits;
|
||||
}
|
||||
ptr->current_hits = new_hits;
|
||||
}
|
||||
|
||||
if (sssp->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
|
||||
for (j=0; j<MAX_SHIP_PRIMARY_BANKS; j++)
|
||||
ptr->weapons.primary_bank_weapons[j] = sssp->primary_banks[j];
|
||||
|
||||
if (sssp->secondary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
|
||||
for (j=0; j<MAX_SHIP_SECONDARY_BANKS; j++)
|
||||
ptr->weapons.secondary_bank_weapons[j] = sssp->secondary_banks[j];
|
||||
|
||||
// Goober5000
|
||||
for (j = 0; j < ptr->weapons.num_primary_banks; j++)
|
||||
{
|
||||
if (Fred_running) {
|
||||
ptr->weapons.primary_bank_ammo[j] = sssp->primary_ammo[j];
|
||||
} else if (ptr->weapons.primary_bank_weapons[j] >= 0 && Weapon_info[ptr->weapons.primary_bank_weapons[j]].wi_flags[Weapon::Info_Flags::Ballistic]) {
|
||||
Assertion(Weapon_info[ptr->weapons.primary_bank_weapons[j]].cargo_size > 0.0f,
|
||||
"Primary weapon cargo size <= 0. Ship (%s) Subsystem (%s) Bank (%i) Weapon (%s)",
|
||||
shipp->ship_name, sssp->name, j, Weapon_info[ptr->weapons.primary_bank_weapons[j]].name);
|
||||
|
||||
int capacity = (int)std::lround(sssp->primary_ammo[j]/100.0f * ptr->weapons.primary_bank_capacity[j]);
|
||||
ptr->weapons.primary_bank_ammo[j] = (int)std::lround(capacity / Weapon_info[ptr->weapons.primary_bank_weapons[j]].cargo_size);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < ptr->weapons.num_secondary_banks; j++)
|
||||
{
|
||||
if (Fred_running) {
|
||||
ptr->weapons.secondary_bank_ammo[j] = sssp->secondary_ammo[j];
|
||||
} else if (ptr->weapons.secondary_bank_weapons[j] >= 0) {
|
||||
Assertion(Weapon_info[ptr->weapons.secondary_bank_weapons[j]].cargo_size > 0.0f,
|
||||
"Secondary weapon cargo size <= 0. Ship (%s) Subsystem (%s) Bank (%i) Weapon (%s)",
|
||||
shipp->ship_name, sssp->name, j, Weapon_info[ptr->weapons.secondary_bank_weapons[j]].name);
|
||||
|
||||
int capacity = (int)std::lround(sssp->secondary_ammo[j]/100.0f * ptr->weapons.secondary_bank_capacity[j]);
|
||||
ptr->weapons.secondary_bank_ammo[j] = (int)std::lround(capacity / Weapon_info[ptr->weapons.secondary_bank_weapons[j]].cargo_size);
|
||||
}
|
||||
}
|
||||
|
||||
ptr->subsys_cargo_name = sssp->subsys_cargo_name;
|
||||
|
||||
if (sssp->ai_class != SUBSYS_STATUS_NO_CHANGE)
|
||||
ptr->weapons.ai_class = sssp->ai_class;
|
||||
|
||||
ptr->turret_best_weapon = -1;
|
||||
ptr->turret_animation_position = MA_POS_NOT_SET; // model animation position is not set
|
||||
ptr->turret_animation_done_time = 0;
|
||||
}
|
||||
|
||||
ptr = GET_NEXT(ptr);
|
||||
if (sssp->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
|
||||
for (j=0; j<MAX_SHIP_PRIMARY_BANKS; j++)
|
||||
ptr->weapons.primary_bank_weapons[j] = sssp->primary_banks[j];
|
||||
|
||||
if (sssp->secondary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
|
||||
for (j=0; j<MAX_SHIP_SECONDARY_BANKS; j++)
|
||||
ptr->weapons.secondary_bank_weapons[j] = sssp->secondary_banks[j];
|
||||
|
||||
// Goober5000
|
||||
for (j = 0; j < ptr->weapons.num_primary_banks; j++)
|
||||
{
|
||||
if (Fred_running) {
|
||||
ptr->weapons.primary_bank_ammo[j] = sssp->primary_ammo[j];
|
||||
} else if (ptr->weapons.primary_bank_weapons[j] >= 0 && Weapon_info[ptr->weapons.primary_bank_weapons[j]].wi_flags[Weapon::Info_Flags::Ballistic]) {
|
||||
Assertion(Weapon_info[ptr->weapons.primary_bank_weapons[j]].cargo_size > 0.0f,
|
||||
"Primary weapon cargo size <= 0. Ship (%s) Subsystem (%s) Bank (%i) Weapon (%s)",
|
||||
shipp->ship_name, sssp->name, j, Weapon_info[ptr->weapons.primary_bank_weapons[j]].name);
|
||||
|
||||
int capacity = (int)std::lround(sssp->primary_ammo[j]/100.0f * ptr->weapons.primary_bank_capacity[j]);
|
||||
ptr->weapons.primary_bank_ammo[j] = (int)std::lround(capacity / Weapon_info[ptr->weapons.primary_bank_weapons[j]].cargo_size);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < ptr->weapons.num_secondary_banks; j++)
|
||||
{
|
||||
if (Fred_running) {
|
||||
ptr->weapons.secondary_bank_ammo[j] = sssp->secondary_ammo[j];
|
||||
} else if (ptr->weapons.secondary_bank_weapons[j] >= 0) {
|
||||
Assertion(Weapon_info[ptr->weapons.secondary_bank_weapons[j]].cargo_size > 0.0f,
|
||||
"Secondary weapon cargo size <= 0. Ship (%s) Subsystem (%s) Bank (%i) Weapon (%s)",
|
||||
shipp->ship_name, sssp->name, j, Weapon_info[ptr->weapons.secondary_bank_weapons[j]].name);
|
||||
|
||||
int capacity = (int)std::lround(sssp->secondary_ammo[j]/100.0f * ptr->weapons.secondary_bank_capacity[j]);
|
||||
ptr->weapons.secondary_bank_ammo[j] = (int)std::lround(capacity / Weapon_info[ptr->weapons.secondary_bank_weapons[j]].cargo_size);
|
||||
}
|
||||
}
|
||||
|
||||
ptr->subsys_cargo_name = sssp->subsys_cargo_name;
|
||||
|
||||
if (sssp->ai_class != SUBSYS_STATUS_NO_CHANGE)
|
||||
ptr->weapons.ai_class = sssp->ai_class;
|
||||
|
||||
ptr->turret_best_weapon = -1;
|
||||
ptr->turret_animation_position = MA_POS_NOT_SET; // model animation position is not set
|
||||
ptr->turret_animation_done_time = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(LOCATION, "Unable to find '%s' in ship subsys_list!", sssp->name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2475,11 +2462,9 @@ int parse_create_object_sub(p_object *p_objp)
|
||||
// If the ship is in a wing, this will be done in mission_set_wing_arrival_location() instead
|
||||
// If the ship is in a wing, but the wing is docked then addition of bool brought_in_docked_wing accounts for that status --wookieejedi
|
||||
if (Game_mode & GM_IN_MISSION && ((shipp->wingnum == -1) || (brought_in_docked_wing))) {
|
||||
if (anchor_objnum >= 0)
|
||||
Script_system.SetHookObjects(2, "Ship", &Objects[objnum], "Parent", &Objects[anchor_objnum]);
|
||||
else
|
||||
Script_system.SetHookObjects(2, "Ship", &Objects[objnum], "Parent", NULL);
|
||||
object *anchor_objp = (anchor_objnum >= 0) ? &Objects[anchor_objnum] : nullptr;
|
||||
|
||||
Script_system.SetHookObjects(2, "Ship", &Objects[objnum], "Parent", anchor_objp);
|
||||
Script_system.RunCondition(CHA_ONSHIPARRIVE, &Objects[objnum]);
|
||||
Script_system.RemHookVars(2, "Ship", "Parent");
|
||||
}
|
||||
@@ -5613,7 +5598,7 @@ void parse_variables()
|
||||
}
|
||||
}
|
||||
|
||||
int parse_mission(mission *pm, int flags)
|
||||
bool parse_mission(mission *pm, int flags)
|
||||
{
|
||||
int saved_warning_count = Global_warning_count;
|
||||
int saved_error_count = Global_error_count;
|
||||
@@ -5654,7 +5639,7 @@ int parse_mission(mission *pm, int flags)
|
||||
Current_file_checksum = netmisc_calc_checksum(pm,MISSION_CHECKSUM_SIZE);
|
||||
|
||||
if (flags & MPF_ONLY_MISSION_INFO)
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
parse_plot_info(pm);
|
||||
parse_variables();
|
||||
@@ -5681,7 +5666,7 @@ int parse_mission(mission *pm, int flags)
|
||||
// if running on standalone server, just print to the log
|
||||
if (Game_mode & GM_STANDALONE_SERVER) {
|
||||
mprintf(("Warning! Could not load %d ship classes!", Num_unknown_ship_classes));
|
||||
return -2;
|
||||
return false;
|
||||
}
|
||||
// don't do this in FRED; we will display a separate popup
|
||||
else if (!Fred_running) {
|
||||
@@ -5703,16 +5688,29 @@ int parse_mission(mission *pm, int flags)
|
||||
strcat_s(text, pm->name);
|
||||
}
|
||||
|
||||
strcat_s(text, "\", and the current mod is \"");
|
||||
strcat_s(text, "\", and the current mod root is \"");
|
||||
|
||||
if (Cmdline_mod == NULL || *Cmdline_mod == 0) {
|
||||
strcat_s(text, "<retail default> ");
|
||||
} else {
|
||||
for (char *mod_token = Cmdline_mod; *mod_token != '\0'; mod_token += strlen(mod_token) + 1) {
|
||||
strcat_s(text, mod_token);
|
||||
strcat_s(text, " ");
|
||||
// Try to find the first non-user, folder root and display that. Not ideal but better than nothing I guess...
|
||||
// We go through all the specified mod roots and find the first non-user folder. If that does not exist then
|
||||
// user folders will be considered next. If that also fails, a default value is used.
|
||||
SCP_string modRoot = "<Unknown>";
|
||||
for (const auto& consider_user : {false, true})
|
||||
{
|
||||
for (const auto& root : Cmdline_mod)
|
||||
{
|
||||
if (root.type != cfile::ModRootType::Folder)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (root.user_location && !consider_user) {
|
||||
continue;
|
||||
}
|
||||
|
||||
modRoot = root.path;
|
||||
}
|
||||
}
|
||||
strcat_s(text, modRoot.c_str());
|
||||
|
||||
strcpy(text + strlen(text) - 1, "\".)\n\n You can continue to load the mission, but it is quite likely that you will encounter a large number of mysterious errors. It is recommended that you either select a ");
|
||||
strcat(text, (Game_mode & GM_CAMPAIGN_MODE) ? "campaign" : "mission");
|
||||
@@ -5723,12 +5721,14 @@ int parse_mission(mission *pm, int flags)
|
||||
// now display the popup
|
||||
int popup_rval = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_NO, POPUP_YES, text);
|
||||
if (popup_rval == 0) {
|
||||
return -2;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post_process_mission();
|
||||
if (!post_process_mission()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((saved_warning_count - Global_warning_count) > 10 || (saved_error_count - Global_error_count) > 0) {
|
||||
char text[512];
|
||||
@@ -5739,10 +5739,10 @@ int parse_mission(mission *pm, int flags)
|
||||
log_printf(LOGFILE_EVENT_LOG, "Mission %s loaded.\n", pm->name);
|
||||
|
||||
// success
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void post_process_mission()
|
||||
bool post_process_mission()
|
||||
{
|
||||
int i;
|
||||
int indices[MAX_SHIPS], objnum;
|
||||
@@ -5855,6 +5855,7 @@ void post_process_mission()
|
||||
|
||||
// entering this if statement will result in program termination!!!!!
|
||||
// print out an error based on the return value from check_sexp_syntax()
|
||||
// G5K: now entering this statement simply aborts the mission load
|
||||
if ( result ) {
|
||||
SCP_string sexp_str;
|
||||
SCP_string error_msg;
|
||||
@@ -5862,14 +5863,10 @@ void post_process_mission()
|
||||
convert_sexp_to_string(sexp_str, i, SEXP_ERROR_CHECK_MODE);
|
||||
truncate_message_lines(sexp_str, 30);
|
||||
sprintf(error_msg, "%s.\n\nIn sexpression: %s\n(Error appears to be: %s)", sexp_error_message(result), sexp_str.c_str(), Sexp_nodes[bad_node].text);
|
||||
Warning(LOCATION, "%s", error_msg.c_str());
|
||||
|
||||
if (!Fred_running) {
|
||||
nprintf(("Error", "%s", error_msg.c_str()));
|
||||
Error(LOCATION, "%s", error_msg.c_str());
|
||||
} else {
|
||||
nprintf(("Warning", "%s", error_msg.c_str()));
|
||||
Warning(LOCATION, "%s", error_msg.c_str());
|
||||
}
|
||||
// syntax errors are unrecoverable, so abort
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5973,6 +5970,9 @@ void post_process_mission()
|
||||
mission_hotkey_reset_saved();
|
||||
}
|
||||
Last_file_checksum = Current_file_checksum;
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
int get_mission_info(const char *filename, mission *mission_p, bool basic)
|
||||
@@ -6038,12 +6038,13 @@ void parse_init(bool basic)
|
||||
init_sexp();
|
||||
}
|
||||
|
||||
// mai parse routine for parsing a mission. The default parameter flags tells us which information
|
||||
// main parse routine for parsing a mission. The default parameter flags tells us which information
|
||||
// to get when parsing the mission. 0 means get everything (default). Other flags just gets us basic
|
||||
// info such as game type, number of players etc.
|
||||
int parse_main(const char *mission_name, int flags)
|
||||
bool parse_main(const char *mission_name, int flags)
|
||||
{
|
||||
int rval, i;
|
||||
int i;
|
||||
bool rval;
|
||||
|
||||
// reset parse error stuff
|
||||
Num_unknown_ship_classes = 0;
|
||||
@@ -6072,7 +6073,7 @@ int parse_main(const char *mission_name, int flags)
|
||||
Current_file_length = -1;
|
||||
Current_file_checksum = 0;
|
||||
|
||||
rval = -1;
|
||||
rval = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6098,7 +6099,7 @@ int parse_main(const char *mission_name, int flags)
|
||||
catch (const parse::ParseException& e)
|
||||
{
|
||||
mprintf(("MISSIONS: Unable to parse '%s'! Error message = %s.\n", mission_name, e.what()));
|
||||
rval = 1;
|
||||
rval = false;
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
@@ -6179,12 +6180,9 @@ void mission_set_wing_arrival_location( wing *wingp, int num_to_set )
|
||||
if (Game_mode & GM_IN_MISSION) {
|
||||
for ( index = wingp->current_count - num_to_set; index < wingp->current_count; index ++ ) {
|
||||
object *objp = &Objects[Ships[wingp->ship_index[index]].objnum];
|
||||
object *anchor_objp = (anchor_objnum >= 0) ? &Objects[anchor_objnum] : nullptr;
|
||||
|
||||
if (anchor_objnum >= 0)
|
||||
Script_system.SetHookObjects(2, "Ship", objp, "Parent", &Objects[anchor_objnum]);
|
||||
else
|
||||
Script_system.SetHookObjects(2, "Ship", objp, "Parent", NULL);
|
||||
|
||||
Script_system.SetHookObjects(2, "Ship", objp, "Parent", anchor_objp);
|
||||
Script_system.RunCondition(CHA_ONSHIPARRIVE, objp);
|
||||
Script_system.RemHookVars(2, "Ship", "Parent");
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@ extern p_object *Arriving_support_ship;
|
||||
extern char Neb2_texture_name[MAX_FILENAME_LEN];
|
||||
|
||||
|
||||
int parse_main(const char *mission_name, int flags = 0);
|
||||
bool parse_main(const char *mission_name, int flags = 0);
|
||||
p_object *mission_parse_get_arrival_ship(ushort net_signature);
|
||||
p_object *mission_parse_get_arrival_ship(const char *name);
|
||||
p_object *mission_parse_get_parse_object(ushort net_signature);
|
||||
|
||||
@@ -1780,7 +1780,7 @@ void draw_model_rotating(model_render_params *render_info, int model_id, int x1,
|
||||
render_info->set_detail_level_lock(0);
|
||||
|
||||
gr_zbuffer_set(true);
|
||||
if(Cmdline_shadow_quality)
|
||||
if(Shadow_quality != ShadowQuality::Disabled)
|
||||
{
|
||||
gr_end_view_matrix();
|
||||
gr_end_proj_matrix();
|
||||
@@ -1894,7 +1894,7 @@ void draw_model_rotating(model_render_params *render_info, int model_id, int x1,
|
||||
|
||||
render_info->set_detail_level_lock(0);
|
||||
|
||||
if(Cmdline_shadow_quality)
|
||||
if(Shadow_quality != ShadowQuality::Disabled)
|
||||
{
|
||||
if ( flags & MR_IS_MISSILE ) {
|
||||
shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 20.0f, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 1000.0f);
|
||||
|
||||
@@ -909,7 +909,7 @@ void wl_render_overhead_view(float frametime)
|
||||
render_info.set_replacement_textures(wl_ship->model_num, sip->replacement_textures);
|
||||
}
|
||||
|
||||
if(Cmdline_shadow_quality)
|
||||
if(Shadow_quality != ShadowQuality::Disabled)
|
||||
{
|
||||
gr_reset_clip();
|
||||
shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -sip->closeup_pos.xyz.z + pm->rad,
|
||||
|
||||
@@ -47,6 +47,7 @@ bool Enable_scripts_in_fred; // By default FRED does not initialize the scriptin
|
||||
SCP_string Window_icon_path;
|
||||
bool Disable_built_in_translations;
|
||||
bool Weapon_shockwaves_respect_huge;
|
||||
bool Using_in_game_options;
|
||||
|
||||
void parse_mod_table(const char *filename)
|
||||
{
|
||||
@@ -414,6 +415,16 @@ void parse_mod_table(const char *filename)
|
||||
stuff_float(&Minimum_player_warpout_time);
|
||||
}
|
||||
|
||||
if (optional_string("$Enable in-game options:")) {
|
||||
stuff_boolean(&Using_in_game_options);
|
||||
|
||||
if (Using_in_game_options) {
|
||||
mprintf(("Game Settings Table: Using in-game options system.\n"));
|
||||
} else {
|
||||
mprintf(("Game Settings Table: Not using in-game options system.\n"));
|
||||
}
|
||||
}
|
||||
|
||||
required_string("#END");
|
||||
}
|
||||
catch (const parse::ParseException& e)
|
||||
@@ -473,4 +484,5 @@ void mod_table_reset() {
|
||||
Window_icon_path = "app_icon_sse";
|
||||
Disable_built_in_translations = false;
|
||||
Weapon_shockwaves_respect_huge = false;
|
||||
Using_in_game_options = false;
|
||||
}
|
||||
@@ -38,6 +38,7 @@ extern bool Enable_scripts_in_fred;
|
||||
extern SCP_string Window_icon_path;
|
||||
extern bool Disable_built_in_translations;
|
||||
extern bool Weapon_shockwaves_respect_huge;
|
||||
extern bool Using_in_game_options;
|
||||
|
||||
void mod_table_init();
|
||||
|
||||
|
||||
@@ -346,6 +346,7 @@ public:
|
||||
bool use_render_box_offset; // whether an offset has been defined; needed because one can't tell just by looking at render_box_offset
|
||||
int use_render_sphere; // 0==do nothing, 1==only render this object if you are inside the sphere, -1==only if you're outside
|
||||
bool use_render_sphere_offset;// whether an offset has been defined; needed because one can't tell just by looking at render_sphere_offset
|
||||
bool do_not_scale_detail_distances{false}; // if set should not scale boxes or spheres based on 'model detail' settings
|
||||
bool gun_rotation; // for animated weapon models
|
||||
bool no_collisions; // for $no_collisions property - kazan
|
||||
bool nocollide_this_only; //SUSHI: Like no_collisions, but not recursive. For the "replacement" collision model scheme.
|
||||
|
||||
@@ -1513,6 +1513,11 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
|
||||
} else {
|
||||
pm->submodel[n].render_box_max = pm->submodel[n].max;
|
||||
}
|
||||
|
||||
if ( (p = strstr(props, "$do_not_scale_distances")) != nullptr ) {
|
||||
p += 23;
|
||||
pm->submodel[n].do_not_scale_detail_distances = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (p = strstr(props, "$detail_sphere:")) != NULL ) {
|
||||
@@ -1541,6 +1546,11 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
|
||||
} else {
|
||||
pm->submodel[n].render_sphere_offset = vmd_zero_vector;
|
||||
}
|
||||
|
||||
if ( (p = strstr(props, "$do_not_scale_distances")) != nullptr ) {
|
||||
p += 23;
|
||||
pm->submodel[n].do_not_scale_detail_distances = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Added for new handling of turret orientation - KeldorKatarn
|
||||
|
||||
@@ -502,7 +502,7 @@ void model_draw_list::add_buffer_draw(model_material *render_material, indexed_v
|
||||
|
||||
draw_data.render_material.set_deferred_lighting(possibly_deferred ? Deferred_lighting : false);
|
||||
draw_data.render_material.set_high_dynamic_range(High_dynamic_range);
|
||||
draw_data.render_material.set_shadow_receiving(Cmdline_shadow_quality != 0);
|
||||
draw_data.render_material.set_shadow_receiving(Shadow_quality != ShadowQuality::Disabled);
|
||||
}
|
||||
|
||||
if (tmap_flags & TMAP_FLAG_BATCH_TRANSFORMS && buffer->flags & VB_FLAG_MODEL_ID) {
|
||||
@@ -1461,6 +1461,9 @@ bool model_render_check_detail_box(vec3d *view_pos, polymodel *pm, int submodel_
|
||||
bsp_info *model = &pm->submodel[submodel_num];
|
||||
|
||||
float box_scale = model_render_determine_box_scale();
|
||||
if (model->do_not_scale_detail_distances) {
|
||||
box_scale = 1.0f;
|
||||
}
|
||||
|
||||
if ( !( flags & MR_FULL_DETAIL ) && model->use_render_box ) {
|
||||
vec3d box_min, box_max, offset;
|
||||
@@ -2254,7 +2257,8 @@ void model_queue_render_thrusters(model_render_params *interp, polymodel *pm, in
|
||||
|
||||
batching_add_beam(thruster_info.secondary_glow_bitmap, &pnt, &norm2, wVal*thruster_info.secondary_glow_rad_factor*0.5f, d);
|
||||
|
||||
if (Scene_framebuffer_in_frame && thruster_info.draw_distortion && Cmdline_fb_thrusters) {
|
||||
if (Scene_framebuffer_in_frame && thruster_info.draw_distortion &&
|
||||
Gr_framebuffer_effects[FramebufferEffects::Thrusters]) {
|
||||
vm_vec_scale_add(&norm2, &pnt, &fvec, wVal * 2 * thruster_info.distortion_length_factor);
|
||||
int dist_bitmap;
|
||||
if (thruster_info.distortion_bitmap > 0) {
|
||||
|
||||
@@ -1370,8 +1370,8 @@ void process_accept_player_data( ubyte *data, header *hinfo )
|
||||
if ( stop == APD_END_DATA ) {
|
||||
// if joining a game automatically, set the connect address to NULl so we don't try and
|
||||
// do this next time we enter a game
|
||||
if (Cmdline_connect_addr != NULL) {
|
||||
Cmdline_connect_addr = NULL;
|
||||
if (!Cmdline_connect_addr.empty()) {
|
||||
Cmdline_connect_addr.clear();
|
||||
}
|
||||
|
||||
// send my stats to the server if I'm not in observer mode
|
||||
|
||||
+23
-26
@@ -945,8 +945,7 @@ void multi_join_game_init()
|
||||
// if starting a network game, then go to the create game screen
|
||||
if ( Cmdline_start_netgame ) {
|
||||
multi_join_create_game();
|
||||
} else if ( Cmdline_connect_addr != NULL ) {
|
||||
char *p;
|
||||
} else if ( !Cmdline_connect_addr.empty() ) {
|
||||
short port_num;
|
||||
int ip_addr;
|
||||
|
||||
@@ -956,13 +955,11 @@ void multi_join_game_init()
|
||||
|
||||
// create the address, looking out for port number at the end
|
||||
port_num = DEFAULT_GAME_PORT;
|
||||
p = strrchr(Cmdline_connect_addr, ':');
|
||||
if ( p ) {
|
||||
*p = '\0';
|
||||
p++;
|
||||
port_num = (short)atoi(p);
|
||||
const auto number_end = Cmdline_connect_addr.find(':');
|
||||
if ( number_end != SCP_string::npos ) {
|
||||
port_num = (short)atoi(Cmdline_connect_addr.substr(0, number_end).c_str());
|
||||
}
|
||||
ip_addr = inet_addr(Cmdline_connect_addr);
|
||||
ip_addr = inet_addr(Cmdline_connect_addr.c_str());
|
||||
memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
|
||||
Multi_autojoin_addr.port = port_num;
|
||||
|
||||
@@ -1006,7 +1003,7 @@ void multi_join_game_do_frame()
|
||||
}
|
||||
|
||||
// when joining a network game, wait for the server query to come back, and then join the game
|
||||
if ( Cmdline_connect_addr != NULL ) {
|
||||
if ( !Cmdline_connect_addr.empty() ) {
|
||||
int rval;
|
||||
|
||||
if ( !Multi_did_autojoin ) {
|
||||
@@ -1014,7 +1011,7 @@ void multi_join_game_do_frame()
|
||||
if ( rval == 0 ) {
|
||||
// cancel was hit. Send the user back to the main hall
|
||||
gameseq_post_event(GS_EVENT_MAIN_MENU);
|
||||
Cmdline_connect_addr = NULL; // reset this value.
|
||||
Cmdline_connect_addr.clear(); // reset this value.
|
||||
}
|
||||
|
||||
// when we get here, we have the data -- join the game.
|
||||
@@ -2270,7 +2267,7 @@ void multi_sg_select_rank_default();
|
||||
// function which takes a rank name and returns the index. Useful for commandline options
|
||||
// for above and below rank. We return the index of the rank in the Ranks[] array. If
|
||||
// the rank isn't found, we return -1
|
||||
int multi_start_game_rank_from_name( char *rank ) {
|
||||
int multi_start_game_rank_from_name( const char *rank ) {
|
||||
int i;
|
||||
|
||||
for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
|
||||
@@ -2355,8 +2352,8 @@ void multi_start_game_init()
|
||||
|
||||
// if starting a netgame, set the name of the game and any other options that are appropriate
|
||||
if ( Cmdline_start_netgame ) {
|
||||
if ( Cmdline_game_name != NULL ) {
|
||||
strcpy_s( Multi_sg_netgame->name, Cmdline_game_name );
|
||||
if ( !Cmdline_game_name.empty() ) {
|
||||
strcpy_s( Multi_sg_netgame->name, Cmdline_game_name.c_str() );
|
||||
Multi_sg_game_name.set_text(Multi_sg_netgame->name);
|
||||
}
|
||||
|
||||
@@ -2366,21 +2363,21 @@ void multi_start_game_init()
|
||||
Multi_sg_netgame->mode = NG_MODE_CLOSED;
|
||||
} else if ( Cmdline_restricted_game ) {
|
||||
Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
|
||||
} else if ( Cmdline_game_password != NULL ) {
|
||||
} else if ( !Cmdline_game_password.empty() ) {
|
||||
Multi_sg_netgame->mode = NG_MODE_PASSWORD;
|
||||
strcpy_s(Multi_sg_netgame->passwd, Cmdline_game_password);
|
||||
strcpy_s(Multi_sg_netgame->passwd, Cmdline_game_password.c_str());
|
||||
Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
|
||||
}
|
||||
|
||||
// deal with rank above and rank below
|
||||
if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
|
||||
if ( (!Cmdline_rank_above.empty()) || (!Cmdline_rank_below.empty()) ) {
|
||||
int rank;
|
||||
char *rank_str;
|
||||
const char *rank_str;
|
||||
|
||||
if ( Cmdline_rank_above != NULL ) {
|
||||
rank_str = Cmdline_rank_above;
|
||||
if ( !Cmdline_rank_above.empty() ) {
|
||||
rank_str = Cmdline_rank_above.c_str();
|
||||
} else {
|
||||
rank_str = Cmdline_rank_below;
|
||||
rank_str = Cmdline_rank_below.c_str();
|
||||
}
|
||||
|
||||
// try and get the rank index from the name -- if found, then set the rank base
|
||||
@@ -2391,7 +2388,7 @@ void multi_start_game_init()
|
||||
Multi_sg_netgame->rank_base = Multi_sg_rank_select;
|
||||
|
||||
// now an arbitrary decision
|
||||
if ( Cmdline_rank_above != NULL ) {
|
||||
if ( !Cmdline_rank_above.empty() ) {
|
||||
Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
|
||||
} else {
|
||||
Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
|
||||
@@ -3647,7 +3644,7 @@ void multi_create_game_do()
|
||||
//DTP CHECK ALMISSION FLAG HERE AND SKIP THE BITMAP LOADING PROGRESS
|
||||
//SINCE WE ALREADY HAVE A MISSION SELECTED IF THIS MISSION IS A VALID MULTIPLAYER MISSION
|
||||
//IF NOT A VALID MULTIPLAYER MISSION CONTINUE LOADING, MAYBE CALL POPUP.
|
||||
if ((Cmdline_almission) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)) { //
|
||||
if ((!Cmdline_almission.empty()) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)) { //
|
||||
multi_create_list_do(); //uhm here because off, hehe, my mind is failing right now
|
||||
|
||||
// DTP Var section for the is mission multi player Check.
|
||||
@@ -3655,7 +3652,7 @@ void multi_create_game_do()
|
||||
char mission_name[NAME_LENGTH+1];
|
||||
int flags;
|
||||
char *filename;
|
||||
filename = cf_add_ext( Cmdline_almission, FS_MISSION_FILE_EXT ); //DTP ADD EXTENSION needed next line
|
||||
filename = cf_add_ext( Cmdline_almission.c_str(), FS_MISSION_FILE_EXT ); //DTP ADD EXTENSION needed next line
|
||||
flags = mission_parse_is_multi(filename, mission_name); //DTP flags will set if mission is multi
|
||||
|
||||
if (flags) { //only continue if mission is multiplayer mission
|
||||
@@ -3663,7 +3660,7 @@ void multi_create_game_do()
|
||||
ng = &Netgame;
|
||||
|
||||
char almissionname[256]; // needed, for the strncpy below
|
||||
strncpy(almissionname, Cmdline_almission,MAX_FILENAME_LEN); //DTP; copying name from cmd_almission line
|
||||
strncpy(almissionname, Cmdline_almission.c_str(), MAX_FILENAME_LEN); //DTP; copying name from cmd_almission line
|
||||
|
||||
Netgame.options.respawn = 99; //override anything //for debugging, i often forget this.
|
||||
ng->respawn = Netgame.options.respawn;
|
||||
@@ -3676,13 +3673,13 @@ void multi_create_game_do()
|
||||
Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING; //DTP must be set before a call to gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC) is done as it is below.
|
||||
gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);//DTP STart game
|
||||
|
||||
Cmdline_almission = NULL; // we don't want to autoload anymore do we, we will be able to quit. halleluja. Startgame has already been disabled, so no need to turn of "Cmdline_start_netgame"
|
||||
Cmdline_almission.clear(); // we don't want to autoload anymore do we, we will be able to quit. halleluja. Startgame has already been disabled, so no need to turn of "Cmdline_start_netgame"
|
||||
return; // we don't need to check or set regarding ships/weapons anything as we are already progressing into mission so return
|
||||
}
|
||||
else {
|
||||
popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR(" Not a multi player-mission",9999)); //DTP startgame popup pilot error
|
||||
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
||||
Cmdline_almission = NULL; //DTP make sure this gets nullified.
|
||||
Cmdline_almission.clear(); //DTP make sure this gets nullified.
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3263,7 +3263,7 @@ void multi_spew_pxo_checksums(int max_files, const char *outfile)
|
||||
char filename[65] = { 0 };
|
||||
char gametype[32] = { 0 };
|
||||
size_t offset = 0;
|
||||
char *p = NULL;
|
||||
const char *p = nullptr;
|
||||
|
||||
// allocate filename space
|
||||
file_names = (char**)vm_malloc(sizeof(char*) * max_files);
|
||||
@@ -3285,7 +3285,7 @@ void multi_spew_pxo_checksums(int max_files, const char *outfile)
|
||||
if (out == NULL)
|
||||
goto Done;
|
||||
|
||||
p = Cmdline_spew_mission_crcs;
|
||||
p = Cmdline_spew_mission_crcs.c_str();
|
||||
|
||||
while (*p && (offset < sizeof(description))) {
|
||||
if (*p == '"') {
|
||||
|
||||
@@ -66,11 +66,14 @@ int collide_debris_ship( obj_pair * pair )
|
||||
hit = debris_check_collision(pdebris, pship, &hitpos, &debris_hit_info );
|
||||
if ( hit )
|
||||
{
|
||||
Script_system.SetHookObjects(4, "Ship", pship, "Debris", pdebris, "Self", pship, "Object", pdebris);
|
||||
Script_system.SetHookObjects(4, "Self", pship, "Object", pdebris, "Ship", pship, "Debris", pdebris);
|
||||
bool ship_override = Script_system.IsConditionOverride(CHA_COLLIDEDEBRIS, pship);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Debris");
|
||||
|
||||
Script_system.SetHookObjects(2, "Self",pdebris, "Object", pship);
|
||||
Script_system.SetHookObjects(4, "Self", pdebris, "Object", pship, "Ship", pship, "Debris", pdebris);
|
||||
bool debris_override = Script_system.IsConditionOverride(CHA_COLLIDESHIP, pdebris);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Debris");
|
||||
|
||||
if(!ship_override && !debris_override)
|
||||
{
|
||||
float ship_damage;
|
||||
@@ -108,7 +111,7 @@ int collide_debris_ship( obj_pair * pair )
|
||||
int quadrant_num, apply_ship_damage;
|
||||
|
||||
// apply damage to ship unless 1) debris is from ship
|
||||
apply_ship_damage = !(pship->signature == pdebris->parent_sig);
|
||||
apply_ship_damage = (pship->signature != pdebris->parent_sig);
|
||||
|
||||
if ( debris_hit_info.heavy == pship ) {
|
||||
quadrant_num = get_ship_quadrant_from_global(&hitpos, pship);
|
||||
@@ -133,15 +136,15 @@ int collide_debris_ship( obj_pair * pair )
|
||||
collide_ship_ship_do_sound(&hitpos, pship, pdebris, pship==Player_obj);
|
||||
}
|
||||
|
||||
Script_system.SetHookObjects(2, "Self",pship, "Object", pdebris);
|
||||
Script_system.SetHookObjects(4, "Self", pship, "Object", pdebris, "Ship", pship, "Debris", pdebris);
|
||||
if(!(debris_override && !ship_override))
|
||||
Script_system.RunCondition(CHA_COLLIDEDEBRIS, pship);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Debris");
|
||||
|
||||
Script_system.SetHookObjects(2, "Self",pdebris, "Object", pship);
|
||||
Script_system.SetHookObjects(4, "Self", pdebris, "Object", pship, "Ship", pship, "Debris", pdebris);
|
||||
if((debris_override && !ship_override) || (!debris_override && !ship_override))
|
||||
Script_system.RunCondition(CHA_COLLIDESHIP, pdebris);
|
||||
|
||||
Script_system.RemHookVars(4, "Ship", "Debris", "Self", "Object");
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Debris");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -221,11 +224,13 @@ int collide_asteroid_ship( obj_pair * pair )
|
||||
if ( hit )
|
||||
{
|
||||
//Scripting support (WMC)
|
||||
Script_system.SetHookObjects(4, "Ship", pship, "Asteroid", pasteroid, "Self",pship, "Object", pasteroid);
|
||||
Script_system.SetHookObjects(4, "Self", pship, "Object", pasteroid, "Ship", pship, "Asteroid", pasteroid);
|
||||
bool ship_override = Script_system.IsConditionOverride(CHA_COLLIDEASTEROID, pship);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Asteroid");
|
||||
|
||||
Script_system.SetHookObjects(2, "Self",pasteroid, "Object", pship);
|
||||
Script_system.SetHookObjects(4, "Self", pasteroid, "Object", pship, "Ship", pship, "Asteroid", pasteroid);
|
||||
bool asteroid_override = Script_system.IsConditionOverride(CHA_COLLIDESHIP, pasteroid);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Asteroid");
|
||||
|
||||
if(!ship_override && !asteroid_override)
|
||||
{
|
||||
@@ -294,15 +299,15 @@ int collide_asteroid_ship( obj_pair * pair )
|
||||
collide_ship_ship_do_sound(&hitpos, pship, pasteroid, pship==Player_obj);
|
||||
}
|
||||
|
||||
Script_system.SetHookObjects(2, "Self",pship, "Object", pasteroid);
|
||||
Script_system.SetHookObjects(4, "Self", pship, "Object", pasteroid, "Ship", pship, "Asteroid", pasteroid);
|
||||
if(!(asteroid_override && !ship_override))
|
||||
Script_system.RunCondition(CHA_COLLIDEASTEROID, pship);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Asteroid");
|
||||
|
||||
Script_system.SetHookObjects(2, "Self",pasteroid, "Object", pship);
|
||||
Script_system.SetHookObjects(4, "Self", pasteroid, "Object", pship, "Ship", pship, "Asteroid", pasteroid);
|
||||
if((asteroid_override && !ship_override) || (!asteroid_override && !ship_override))
|
||||
Script_system.RunCondition(CHA_COLLIDESHIP, pasteroid);
|
||||
|
||||
Script_system.RemHookVars(4, "Ship", "Asteroid", "Self", "Object");
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Asteroid");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
int collide_debris_weapon( obj_pair * pair )
|
||||
{
|
||||
vec3d hitpos, hitnormal;
|
||||
int hit;
|
||||
object *pdebris = pair->a;
|
||||
object *weapon_obj = pair->b;
|
||||
|
||||
@@ -35,18 +34,20 @@ int collide_debris_weapon( obj_pair * pair )
|
||||
Assert( weapon_obj->type == OBJ_WEAPON );
|
||||
|
||||
// first check the bounding spheres of the two objects.
|
||||
hit = fvi_segment_sphere(&hitpos, &weapon_obj->last_pos, &weapon_obj->pos, &pdebris->pos, pdebris->radius);
|
||||
int hit = fvi_segment_sphere(&hitpos, &weapon_obj->last_pos, &weapon_obj->pos, &pdebris->pos, pdebris->radius);
|
||||
if (hit) {
|
||||
hit = debris_check_collision(pdebris, weapon_obj, &hitpos, NULL, &hitnormal );
|
||||
hit = debris_check_collision(pdebris, weapon_obj, &hitpos, nullptr, &hitnormal );
|
||||
|
||||
if ( !hit )
|
||||
return 0;
|
||||
|
||||
Script_system.SetHookObjects(4, "Weapon", weapon_obj, "Debris", pdebris, "Self", weapon_obj, "Object", pdebris);
|
||||
Script_system.SetHookObjects(4, "Self", weapon_obj, "Object", pdebris, "Weapon", weapon_obj, "Debris", pdebris);
|
||||
bool weapon_override = Script_system.IsConditionOverride(CHA_COLLIDEDEBRIS, weapon_obj);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "Debris");
|
||||
|
||||
Script_system.SetHookObjects(2, "Self", pdebris, "Object", weapon_obj);
|
||||
Script_system.SetHookObjects(4, "Self", pdebris, "Object", weapon_obj, "Weapon", weapon_obj, "Debris", pdebris);
|
||||
bool debris_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, pdebris);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "Debris");
|
||||
|
||||
if(!weapon_override && !debris_override)
|
||||
{
|
||||
@@ -54,15 +55,16 @@ int collide_debris_weapon( obj_pair * pair )
|
||||
debris_hit( pdebris, weapon_obj, &hitpos, Weapon_info[Weapons[weapon_obj->instance].weapon_info_index].damage );
|
||||
}
|
||||
|
||||
Script_system.SetHookObjects(2, "Self", weapon_obj, "Object", pdebris);
|
||||
Script_system.SetHookObjects(4, "Self", weapon_obj, "Object", pdebris, "Weapon", weapon_obj, "Debris", pdebris);
|
||||
if(!(debris_override && !weapon_override))
|
||||
Script_system.RunCondition(CHA_COLLIDEDEBRIS, weapon_obj);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "Debris");
|
||||
|
||||
Script_system.SetHookObjects(2, "Self", pdebris, "Object", weapon_obj);
|
||||
Script_system.SetHookObjects(4, "Self", pdebris, "Object", weapon_obj, "Weapon", weapon_obj, "Debris", pdebris);
|
||||
if((debris_override && !weapon_override) || (!debris_override && !weapon_override))
|
||||
Script_system.RunCondition(CHA_COLLIDEWEAPON, pdebris, Weapons[weapon_obj->instance].weapon_info_index);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "Debris");
|
||||
|
||||
Script_system.RemHookVars(4, "Weapon", "Debris", "Self", "Object");
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
@@ -83,7 +85,6 @@ int collide_asteroid_weapon( obj_pair * pair )
|
||||
return 0;
|
||||
|
||||
vec3d hitpos, hitnormal;
|
||||
int hit;
|
||||
object *pasteroid = pair->a;
|
||||
object *weapon_obj = pair->b;
|
||||
|
||||
@@ -91,17 +92,19 @@ int collide_asteroid_weapon( obj_pair * pair )
|
||||
Assert( weapon_obj->type == OBJ_WEAPON );
|
||||
|
||||
// first check the bounding spheres of the two objects.
|
||||
hit = fvi_segment_sphere(&hitpos, &weapon_obj->last_pos, &weapon_obj->pos, &pasteroid->pos, pasteroid->radius);
|
||||
int hit = fvi_segment_sphere(&hitpos, &weapon_obj->last_pos, &weapon_obj->pos, &pasteroid->pos, pasteroid->radius);
|
||||
if (hit) {
|
||||
hit = asteroid_check_collision(pasteroid, weapon_obj, &hitpos, NULL, &hitnormal);
|
||||
hit = asteroid_check_collision(pasteroid, weapon_obj, &hitpos, nullptr, &hitnormal);
|
||||
if ( !hit )
|
||||
return 0;
|
||||
|
||||
Script_system.SetHookObjects(4, "Weapon", weapon_obj, "Asteroid", pasteroid, "Self", weapon_obj, "Object", pasteroid);
|
||||
|
||||
Script_system.SetHookObjects(4, "Self", weapon_obj, "Object", pasteroid, "Weapon", weapon_obj, "Asteroid", pasteroid);
|
||||
bool weapon_override = Script_system.IsConditionOverride(CHA_COLLIDEASTEROID, weapon_obj);
|
||||
Script_system.SetHookObjects(2, "Self",pasteroid, "Object", weapon_obj);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "Asteroid");
|
||||
|
||||
Script_system.SetHookObjects(4, "Self", pasteroid, "Object", weapon_obj, "Weapon", weapon_obj, "Asteroid", pasteroid);
|
||||
bool asteroid_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, pasteroid);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "Asteroid");
|
||||
|
||||
if(!weapon_override && !asteroid_override)
|
||||
{
|
||||
@@ -109,15 +112,16 @@ int collide_asteroid_weapon( obj_pair * pair )
|
||||
asteroid_hit( pasteroid, weapon_obj, &hitpos, Weapon_info[Weapons[weapon_obj->instance].weapon_info_index].damage );
|
||||
}
|
||||
|
||||
Script_system.SetHookObjects(2, "Self", weapon_obj, "Object", pasteroid);
|
||||
Script_system.SetHookObjects(4, "Self", weapon_obj, "Object", pasteroid, "Weapon", weapon_obj, "Asteroid", pasteroid);
|
||||
if(!(asteroid_override && !weapon_override))
|
||||
Script_system.RunCondition(CHA_COLLIDEASTEROID, weapon_obj);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "Asteroid");
|
||||
|
||||
Script_system.SetHookObjects(2, "Self", pasteroid, "Object", weapon_obj);
|
||||
Script_system.SetHookObjects(4, "Self", pasteroid, "Object", weapon_obj, "Weapon", weapon_obj, "Asteroid", pasteroid);
|
||||
if((asteroid_override && !weapon_override) || (!asteroid_override && !weapon_override))
|
||||
Script_system.RunCondition(CHA_COLLIDEWEAPON, pasteroid, Weapons[weapon_obj->instance].weapon_info_index);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "Asteroid");
|
||||
|
||||
Script_system.RemHookVars(4, "Weapon", "Asteroid", "Self", "Object");
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
|
||||
@@ -129,7 +129,7 @@ int ship_ship_check_collision(collision_info_struct *ship_ship_hit_info, vec3d *
|
||||
}
|
||||
|
||||
// Make ships that are warping in not get collision detection done
|
||||
if ( heavy_shipp->flags[Ship::Ship_Flags::Arriving_stage_1] ) {
|
||||
if ( heavy_shipp->is_arriving(ship::warpstage::STAGE1, false) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -381,8 +381,6 @@ int ship_ship_check_collision(collision_info_struct *ship_ship_hit_info, vec3d *
|
||||
|
||||
// SET PHYSICS PARAMETERS
|
||||
// already have (hitpos - heavy) and light_cm_pos
|
||||
// get heavy cm pos - already have light_cm_pos
|
||||
ship_ship_hit_info->heavy_collision_cm_pos = zero;
|
||||
|
||||
// get r_heavy and r_light
|
||||
ship_ship_hit_info->r_heavy = ship_ship_hit_info->hit_pos;
|
||||
@@ -1137,12 +1135,15 @@ int collide_ship_ship( obj_pair * pair )
|
||||
|
||||
if ( hit )
|
||||
{
|
||||
Script_system.SetHookObjects(4, "Ship", A, "ShipB", B, "Self", A, "Object", B);
|
||||
Script_system.SetHookObjects(4, "Self", A, "Object", B, "Ship", A, "ShipB", B);
|
||||
bool a_override = Script_system.IsConditionOverride(CHA_COLLIDESHIP, A);
|
||||
|
||||
//Yes this should be reversed.
|
||||
Script_system.SetHookObjects(4, "Ship", B, "ShipB", A, "Self", B, "Object", A);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "ShipB");
|
||||
|
||||
// Yes, this should be reversed.
|
||||
Script_system.SetHookObjects(4, "Self", B, "Object", A, "Ship", B, "ShipB", A);
|
||||
bool b_override = Script_system.IsConditionOverride(CHA_COLLIDESHIP, B);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "ShipB");
|
||||
|
||||
if(!a_override && !b_override)
|
||||
{
|
||||
float damage;
|
||||
@@ -1247,17 +1248,18 @@ int collide_ship_ship( obj_pair * pair )
|
||||
|
||||
if(!(b_override && !a_override))
|
||||
{
|
||||
Script_system.SetHookObjects(4, "Ship", A, "ShipB", B, "Self", A, "Object", B);
|
||||
Script_system.SetHookObjects(4, "Self", A, "Object", B, "Ship", A, "ShipB", B);
|
||||
Script_system.RunCondition(CHA_COLLIDESHIP, A);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "ShipB");
|
||||
}
|
||||
if((b_override && !a_override) || (!b_override && !a_override))
|
||||
{
|
||||
//Yes this should be reversed.
|
||||
Script_system.SetHookObjects(4, "Ship", B, "ShipB", A, "Self", B, "Object", A);
|
||||
// Yes, this should be reversed.
|
||||
Script_system.SetHookObjects(4, "Self", B, "Object", A, "Ship", B, "ShipB", A);
|
||||
Script_system.RunCondition(CHA_COLLIDESHIP, B);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "ShipB");
|
||||
}
|
||||
|
||||
Script_system.RemHookVars(4, "Ship", "ShipB", "Self", "Object");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1268,8 +1270,8 @@ int collide_ship_ship( obj_pair * pair )
|
||||
// if ship is warping in, in stage 1, its velocity is 0, so make ship try to collide next frame
|
||||
|
||||
// if ship is huge and warping in or out
|
||||
if (((Ships[A->instance].flags[Ship::Ship_Flags::Arriving_stage_1]) && (Ship_info[Ships[A->instance].ship_info_index].is_huge_ship()))
|
||||
|| ((Ships[B->instance].flags[Ship::Ship_Flags::Arriving_stage_1]) && (Ship_info[Ships[B->instance].ship_info_index].is_huge_ship())) ) {
|
||||
if (((Ships[A->instance].is_arriving(ship::warpstage::STAGE1, false)) && (Ship_info[Ships[A->instance].ship_info_index].is_huge_ship()))
|
||||
|| ((Ships[B->instance].is_arriving(ship::warpstage::STAGE1, false)) && (Ship_info[Ships[B->instance].ship_info_index].is_huge_ship())) ) {
|
||||
pair->next_check_time = timestamp(0); // check next time
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ static void ship_weapon_do_hit_stuff(object *pship_obj, object *weapon_obj, vec3
|
||||
|
||||
extern int Framecount;
|
||||
|
||||
static int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, float time_limit = 0.0f, int *next_hit = NULL)
|
||||
static int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, float time_limit = 0.0f, int *next_hit = nullptr)
|
||||
{
|
||||
mc_info mc, mc_shield, mc_hull;
|
||||
ship *shipp;
|
||||
@@ -137,14 +137,14 @@ static int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, f
|
||||
weapon *wp;
|
||||
weapon_info *wip;
|
||||
|
||||
Assert( ship_objp != NULL );
|
||||
Assert( ship_objp != nullptr );
|
||||
Assert( ship_objp->type == OBJ_SHIP );
|
||||
Assert( ship_objp->instance >= 0 );
|
||||
|
||||
shipp = &Ships[ship_objp->instance];
|
||||
sip = &Ship_info[shipp->ship_info_index];
|
||||
|
||||
Assert( weapon_objp != NULL );
|
||||
Assert( weapon_objp != nullptr );
|
||||
Assert( weapon_objp->type == OBJ_WEAPON );
|
||||
Assert( weapon_objp->instance >= 0 );
|
||||
|
||||
@@ -422,11 +422,13 @@ static int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, f
|
||||
wp->collisionInfo = new mc_info; // The weapon will free this memory later
|
||||
memcpy(wp->collisionInfo, &mc, sizeof(mc_info));
|
||||
|
||||
Script_system.SetHookObjects(4, "Ship", ship_objp, "Weapon", weapon_objp, "Self",ship_objp, "Object", weapon_objp);
|
||||
Script_system.SetHookObjects(4, "Self", ship_objp, "Object", weapon_objp, "Ship", ship_objp, "Weapon", weapon_objp);
|
||||
bool ship_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, ship_objp);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Weapon");
|
||||
|
||||
Script_system.SetHookObjects(2, "Self",weapon_objp, "Object", ship_objp);
|
||||
Script_system.SetHookObjects(4, "Self", weapon_objp, "Object", ship_objp, "Ship", ship_objp, "Weapon", weapon_objp);
|
||||
bool weapon_override = Script_system.IsConditionOverride(CHA_COLLIDESHIP, weapon_objp);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Weapon");
|
||||
|
||||
if(!ship_override && !weapon_override) {
|
||||
if (shield_collision && quadrant_num >= 0) {
|
||||
@@ -437,15 +439,15 @@ static int ship_weapon_check_collision(object *ship_objp, object *weapon_objp, f
|
||||
ship_weapon_do_hit_stuff(ship_objp, weapon_objp, &mc.hit_point_world, &mc.hit_point, quadrant_num, mc.hit_submodel, mc.hit_normal);
|
||||
}
|
||||
|
||||
Script_system.SetHookObjects(2, "Self",ship_objp, "Object", weapon_objp);
|
||||
Script_system.SetHookObjects(4, "Self", ship_objp, "Object", weapon_objp, "Ship", ship_objp, "Weapon", weapon_objp);
|
||||
if(!(weapon_override && !ship_override))
|
||||
Script_system.RunCondition(CHA_COLLIDEWEAPON, ship_objp, wp->weapon_info_index);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Weapon");
|
||||
|
||||
Script_system.SetHookObjects(2, "Self",weapon_objp, "Object", ship_objp);
|
||||
Script_system.SetHookObjects(4, "Self", weapon_objp, "Object", ship_objp, "Ship", ship_objp, "Weapon", weapon_objp);
|
||||
if((weapon_override && !ship_override) || (!weapon_override && !ship_override))
|
||||
Script_system.RunCondition(CHA_COLLIDESHIP, weapon_objp);
|
||||
|
||||
Script_system.RemHookVars(4, "Ship", "Weapon", "Self","Object");
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Ship", "Weapon");
|
||||
}
|
||||
else if ((Missiontime - wp->creation_time > F1_0/2) && (wip->is_homing()) && (wp->homing_object == ship_objp)) {
|
||||
if (dist < wip->shockwave.inner_rad) {
|
||||
|
||||
@@ -82,12 +82,14 @@ int collide_weapon_weapon( obj_pair * pair )
|
||||
// Rats, do collision detection.
|
||||
if (collide_subdivide(&A->last_pos, &A->pos, A_radius, &B->last_pos, &B->pos, B_radius))
|
||||
{
|
||||
Script_system.SetHookObjects(4, "Weapon", A, "WeaponB", B, "Self",A, "Object", B);
|
||||
Script_system.SetHookObjects(4, "Self", A, "Object", B, "Weapon", A, "WeaponB", B);
|
||||
bool a_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, A);
|
||||
|
||||
//Should be reversed
|
||||
Script_system.SetHookObjects(4, "Weapon", B, "WeaponB", A, "Self",B, "Object", A);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "WeaponB");
|
||||
|
||||
// Yes, this should be reversed.
|
||||
Script_system.SetHookObjects(4, "Self", B, "Object", A, "Weapon", B, "WeaponB", A);
|
||||
bool b_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, B);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "WeaponB");
|
||||
|
||||
if(!a_override && !b_override)
|
||||
{
|
||||
@@ -170,17 +172,18 @@ int collide_weapon_weapon( obj_pair * pair )
|
||||
|
||||
if(!(b_override && !a_override))
|
||||
{
|
||||
Script_system.SetHookObjects(4, "Weapon", A, "WeaponB", B, "Self",A, "Object", B);
|
||||
Script_system.SetHookObjects(4, "Self", A, "Object", B, "Weapon", A, "WeaponB", B);
|
||||
Script_system.RunCondition(CHA_COLLIDEWEAPON, A, wpA->weapon_info_index);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "WeaponB");
|
||||
}
|
||||
else
|
||||
{
|
||||
//Should be reversed
|
||||
Script_system.SetHookObjects(4, "Weapon", B, "WeaponB", A, "Self",B, "Object", A);
|
||||
// Yes, this should be reversed.
|
||||
Script_system.SetHookObjects(4, "Self", B, "Object", A, "Weapon", B, "WeaponB", A);
|
||||
Script_system.RunCondition(CHA_COLLIDEWEAPON, B, wpB->weapon_info_index);
|
||||
Script_system.RemHookVars(4, "Self", "Object", "Weapon", "WeaponB");
|
||||
}
|
||||
|
||||
Script_system.RemHookVars(4, "Weapon", "WeaponB", "Self", "Object");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+433
-865
File diff suppressed because it is too large.
Load diff
@@ -19,10 +19,9 @@ struct CFILE;
|
||||
struct mc_info;
|
||||
|
||||
// used for ship:ship and ship:debris
|
||||
typedef struct collision_info_struct {
|
||||
struct collision_info_struct {
|
||||
object *heavy;
|
||||
object *light;
|
||||
vec3d heavy_collision_cm_pos; // should be zero
|
||||
vec3d light_collision_cm_pos; // relative cm collision pos
|
||||
vec3d r_heavy; // relative to A
|
||||
vec3d r_light; // relative to B
|
||||
@@ -36,7 +35,7 @@ typedef struct collision_info_struct {
|
||||
int edge_hit; // if edge is hit, need to change collision normal
|
||||
int submodel_rot_hit; // if collision is against rotating submodel
|
||||
bool is_landing; //SUSHI: Maybe treat current collision as a landing
|
||||
} collision_info_struct;
|
||||
};
|
||||
|
||||
//Collision physics constants
|
||||
#define COLLISION_FRICTION_FACTOR 0.0f //Default value if not set in ships.tbl
|
||||
@@ -51,13 +50,12 @@ typedef struct collision_info_struct {
|
||||
//===============================================================================
|
||||
|
||||
// Keeps track of pairs of objects for collision detection
|
||||
typedef struct obj_pair {
|
||||
struct obj_pair {
|
||||
object *a;
|
||||
object *b;
|
||||
int (*check_collision)( obj_pair * pair );
|
||||
int next_check_time; // a timestamp that when elapsed means to check for a collision
|
||||
struct obj_pair *next;
|
||||
} obj_pair;
|
||||
};
|
||||
|
||||
|
||||
#define COLLISION_OF(a,b) (((a)<<8)|(b))
|
||||
@@ -66,17 +64,10 @@ typedef struct obj_pair {
|
||||
#define SUBMODEL_ROT_HIT 1
|
||||
void set_hit_struct_info(collision_info_struct *hit, mc_info *mc, int submodel_rot_hit);
|
||||
|
||||
void obj_add_pair( object *A, object *B, int check_time = -1, int add_to_end = 0 );
|
||||
|
||||
void obj_add_collider(int obj_index);
|
||||
void obj_remove_collider(int obj_index);
|
||||
void obj_reset_colliders();
|
||||
|
||||
void obj_sort_and_collide();
|
||||
void obj_quicksort_colliders(SCP_vector<int> *list, int left, int right, int axis);
|
||||
void obj_find_overlap_colliders(SCP_vector<int> *overlap_list_out, SCP_vector<int> *list, int axis, bool collide);
|
||||
float obj_get_collider_endpoint(int obj_num, int axis, bool min);
|
||||
void obj_collide_pair(object *A, object *B);
|
||||
|
||||
// retimes all collision pairs to be checked (in 25ms by default)
|
||||
void obj_collide_retime_cached_pairs(int checkdly=25);
|
||||
@@ -134,7 +125,7 @@ int pp_collide(vec3d *curpos, vec3d *goalpos, object *goalobjp, float radius);
|
||||
int collide_predict_large_ship(object *objp, float distance);
|
||||
|
||||
// function to remove old weapons when no more weapon slots available.
|
||||
int collide_remove_weapons(void);
|
||||
int collide_remove_weapons();
|
||||
|
||||
void collide_ship_ship_do_sound(vec3d *world_hit_pos, object *A, object *B, int player_involved);
|
||||
void collide_ship_ship_sounds_init();
|
||||
|
||||
+1
-82
@@ -1498,8 +1498,8 @@ void obj_move_all(float frametime)
|
||||
|
||||
Script_system.SetHookObjects(2, "User", objp, "Target", target);
|
||||
Script_system.RunCondition(CHA_ONWPEQUIPPED, objp);
|
||||
Script_system.RemHookVars(2, "User", "Target");
|
||||
}
|
||||
Script_system.RemHookVars(2, "User", "Target");
|
||||
}
|
||||
|
||||
// Now that we've moved all the objects, move all the models that use intrinsic rotations. We do that here because we already handled the
|
||||
@@ -1853,92 +1853,11 @@ int obj_team(object *objp)
|
||||
return team;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an element to the CheckObjects[] array, and update the
|
||||
* object pairs. This is called from obj_create(), and the restore
|
||||
* save-game code.
|
||||
*/
|
||||
void obj_add_pairs(int objnum)
|
||||
{
|
||||
object *objp;
|
||||
|
||||
Assert(objnum != -1);
|
||||
objp = &Objects[objnum];
|
||||
|
||||
// don't do anything if its already in the object pair list
|
||||
if(!(objp->flags[Object::Object_Flags::Not_in_coll])){
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef OBJECT_CHECK
|
||||
CheckObjects[objnum].type = objp->type;
|
||||
CheckObjects[objnum].signature = objp->signature;
|
||||
CheckObjects[objnum].flags = objp->flags;
|
||||
CheckObjects[objnum].flags.remove(Object::Object_Flags::Not_in_coll);
|
||||
CheckObjects[objnum].parent_sig = objp->parent_sig;
|
||||
CheckObjects[objnum].parent_type = objp->parent_type;
|
||||
#endif
|
||||
|
||||
// Find all the objects that can collide with this and add
|
||||
// it to the collision pair list.
|
||||
object * A;
|
||||
for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
|
||||
obj_add_pair( objp, A );
|
||||
}
|
||||
|
||||
objp->flags.remove(Object::Object_Flags::Not_in_coll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any occurances of object 'a' from
|
||||
* the pairs list.
|
||||
*/
|
||||
extern int Num_pairs;
|
||||
extern obj_pair pair_used_list;
|
||||
extern obj_pair pair_free_list;
|
||||
void obj_remove_pairs( object * a )
|
||||
{
|
||||
obj_pair *parent, *tmp;
|
||||
|
||||
a->flags.set(Object::Object_Flags::Not_in_coll);
|
||||
#ifdef OBJECT_CHECK
|
||||
CheckObjects[OBJ_INDEX(a)].flags.set(Object::Object_Flags::Not_in_coll);
|
||||
#endif
|
||||
|
||||
if ( a->num_pairs < 1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Num_pairs-=a->num_pairs;
|
||||
|
||||
parent = &pair_used_list;
|
||||
tmp = parent->next;
|
||||
|
||||
while( tmp != NULL ) {
|
||||
if ( (tmp->a==a) || (tmp->b==a) ) {
|
||||
// Hmmm... a potenial compiler optimization problem here... either tmp->a or tmp->b
|
||||
// is equal to 'a' and we modify 'num_pairs' in one of these and then use the value
|
||||
// stored in 'a' later one... will the optimizer find that? Hmmm...
|
||||
tmp->a->num_pairs--;
|
||||
Assert( tmp->a->num_pairs > -1 );
|
||||
tmp->b->num_pairs--;
|
||||
Assert( tmp->b->num_pairs > -1 );
|
||||
parent->next = tmp->next;
|
||||
tmp->a = tmp->b = NULL;
|
||||
tmp->next = pair_free_list.next;
|
||||
pair_free_list.next = tmp;
|
||||
tmp = parent->next;
|
||||
|
||||
if ( a->num_pairs==0 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
parent = tmp;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all collisions
|
||||
|
||||
@@ -254,12 +254,6 @@ void obj_merge_created_list(void);
|
||||
// recalculate object pairs for an object
|
||||
#define OBJ_RECALC_PAIRS(obj_to_reset) do { obj_set_flags(obj_to_reset, obj_to_reset->flags - Object::Object_Flags::Collides); obj_set_flags(obj_to_reset, obj_to_reset->flags + Object::Object_Flags::Collides); } while(0);
|
||||
|
||||
// Removes any occurances of object 'a' from the pairs list.
|
||||
void obj_remove_pairs( object * a );
|
||||
|
||||
// add an object to the pairs list
|
||||
void obj_add_pairs(int objnum);
|
||||
|
||||
// Returns true if objects A and B are expected to collide in next duration seconds.
|
||||
// For purposes of this check, the first object moves from current location to predicted
|
||||
// location. The second object is assumed to be where it will be at time duration, NOT
|
||||
|
||||
@@ -708,6 +708,31 @@ void dock_find_max_speed_helper(object *objp, dock_function_info *infop)
|
||||
infop->maintained_variables.objp_value = objp;
|
||||
}
|
||||
}
|
||||
|
||||
void object_set_arriving_stage1_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ )
|
||||
{
|
||||
if (! Ships[objp->instance].flags[Ship::Ship_Flags::Dock_leader])
|
||||
Ships[objp->instance].flags.set(Ship::Ship_Flags::Arriving_stage_1_dock_follower);
|
||||
}
|
||||
|
||||
void object_remove_arriving_stage1_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ )
|
||||
{
|
||||
if (! Ships[objp->instance].flags[Ship::Ship_Flags::Dock_leader])
|
||||
Ships[objp->instance].flags.remove(Ship::Ship_Flags::Arriving_stage_1_dock_follower);
|
||||
}
|
||||
|
||||
void object_set_arriving_stage2_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ )
|
||||
{
|
||||
if (! Ships[objp->instance].flags[Ship::Ship_Flags::Dock_leader])
|
||||
Ships[objp->instance].flags.set(Ship::Ship_Flags::Arriving_stage_2_dock_follower);
|
||||
}
|
||||
|
||||
void object_remove_arriving_stage2_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ )
|
||||
{
|
||||
if (! Ships[objp->instance].flags[Ship::Ship_Flags::Dock_leader])
|
||||
Ships[objp->instance].flags.remove(Ship::Ship_Flags::Arriving_stage_2_dock_follower);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// end of über code block ----------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -141,4 +141,10 @@ void dock_undock_all(object *objp);
|
||||
// free the entire dock list without undocking anything; should only be used on object cleanup
|
||||
void dock_free_dock_list(object *objp);
|
||||
|
||||
// flag set/remove helpers required in multiple places
|
||||
void object_set_arriving_stage1_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ );
|
||||
void object_remove_arriving_stage1_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ );
|
||||
void object_set_arriving_stage2_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ );
|
||||
void object_remove_arriving_stage2_ndl_flag_helper(object *objp, dock_function_info * /*infop*/ );
|
||||
|
||||
#endif // _OBJECT_DOCK_H
|
||||
@@ -391,18 +391,23 @@ void maybe_play_flyby_snd(float closest_dist, object *closest_objp, object *list
|
||||
return;
|
||||
}
|
||||
|
||||
// pick a random species-based sound
|
||||
// check to see if a flyby sound is specified in ship class
|
||||
// if not, then pick a random species-based sound
|
||||
|
||||
ship_info *sip = &Ship_info[Ships[closest_objp->instance].ship_info_index];
|
||||
ship_info* sip = &Ship_info[Ships[closest_objp->instance].ship_info_index];
|
||||
game_snd *snd;
|
||||
|
||||
if (sip->flags[Ship::Info_Flags::Bomber])
|
||||
snd = &Species_info[sip->species].snd_flyby_bomber;
|
||||
else
|
||||
snd = &Species_info[sip->species].snd_flyby_fighter;
|
||||
if (sip->flyby_snd.isValid()) {
|
||||
snd = gamesnd_get_game_sound(sip->flyby_snd);
|
||||
} else {
|
||||
if (sip->flags[Ship::Info_Flags::Bomber])
|
||||
snd = &Species_info[sip->species].snd_flyby_bomber;
|
||||
else
|
||||
snd = &Species_info[sip->species].snd_flyby_fighter;
|
||||
}
|
||||
|
||||
if (snd->sound_entries.empty())
|
||||
return; //This species does not define any relevant flyby sounds
|
||||
return; // This class or species does not define any relevant flyby sounds
|
||||
|
||||
// play da sound
|
||||
snd_play_3d(snd, &closest_objp->pos, &View_position);
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
//
|
||||
//
|
||||
|
||||
#include "Option.h"
|
||||
#include "OptionsManager.h"
|
||||
#include "parse/parselo.h"
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace options {
|
||||
|
||||
namespace internal {
|
||||
|
||||
json_t* string_serializer(const SCP_string& value) { return json_pack("s", value.c_str()); }
|
||||
SCP_string string_deserializer(const json_t* value) {
|
||||
const char* s;
|
||||
|
||||
json_error_t err;
|
||||
if (json_unpack_ex((json_t*)value, &err, 0, "s", &s) != 0) {
|
||||
throw json_exception(err);
|
||||
}
|
||||
|
||||
return SCP_string(s);
|
||||
}
|
||||
template<>
|
||||
void set_defaults<SCP_string>(Option<SCP_string>& opt) {
|
||||
opt.setDeserializer(string_deserializer);
|
||||
opt.setSerializer(string_serializer);
|
||||
}
|
||||
|
||||
float float_deserializer(const json_t* value)
|
||||
{
|
||||
double f;
|
||||
|
||||
json_error_t err;
|
||||
if (json_unpack_ex((json_t*)value, &err, 0, "f", &f) != 0) {
|
||||
throw json_exception(err);
|
||||
}
|
||||
|
||||
return (float)f;
|
||||
}
|
||||
json_t* float_serializer(float value) { return json_pack("f", value); }
|
||||
SCP_string float_display(float value)
|
||||
{
|
||||
SCP_string out;
|
||||
sprintf(out, "%.1f", value);
|
||||
return out;
|
||||
}
|
||||
template<>
|
||||
void set_defaults<float>(Option<float>& opt) {
|
||||
opt.setDeserializer(float_deserializer);
|
||||
opt.setSerializer(float_serializer);
|
||||
opt.setDisplayFunc(float_display);
|
||||
|
||||
opt.setType(OptionType::Range);
|
||||
}
|
||||
|
||||
bool boolean_deserializer(const json_t* value)
|
||||
{
|
||||
int b;
|
||||
|
||||
json_error_t err;
|
||||
if (json_unpack_ex((json_t*)value, &err, 0, "b", &b) != 0) {
|
||||
throw json_exception(err);
|
||||
}
|
||||
|
||||
return b != 0;
|
||||
}
|
||||
json_t* boolean_serializer(bool value) { return json_pack("b", value ? 1 : 0); }
|
||||
SCP_string boolean_display(bool value) { return value ? "On" : "Off"; }
|
||||
template<>
|
||||
void set_defaults<bool>(Option<bool>& opt) {
|
||||
opt.setDeserializer(boolean_deserializer);
|
||||
opt.setSerializer(boolean_serializer);
|
||||
opt.setDisplayFunc(boolean_display);
|
||||
opt.setValueEnumerator(VectorEnumerator<bool>({false, true}));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
ValueDescription::ValueDescription(SCP_string _display, SCP_string _serialized)
|
||||
: display(std::move(_display)), serialized(std::move(_serialized))
|
||||
{
|
||||
}
|
||||
|
||||
OptionBase::~OptionBase() = default;
|
||||
OptionBase::OptionBase(SCP_string config_key, SCP_string title, SCP_string description)
|
||||
: _config_key(std::move(config_key)), _title(std::move(title)), _description(std::move(description))
|
||||
{
|
||||
_parent = OptionsManager::instance();
|
||||
}
|
||||
std::unique_ptr<json_t> OptionBase::getConfigValue() const { return _parent->getValueFromConfig(_config_key); }
|
||||
|
||||
ExpertLevel OptionBase::getExpertLevel() const { return _expert_level; }
|
||||
void OptionBase::setExpertLevel(ExpertLevel expert_level) { _expert_level = expert_level; }
|
||||
|
||||
void OptionBase::setPreset(PresetKind preset, const SCP_string& value) { _preset_values.emplace(preset, value); }
|
||||
|
||||
const SCP_string& OptionBase::getCategory() const { return _category; }
|
||||
void OptionBase::setCategory(const SCP_string& category) { _category = category; }
|
||||
|
||||
const SCP_string& OptionBase::getConfigKey() const {
|
||||
return _config_key;
|
||||
}
|
||||
const SCP_string& OptionBase::getTitle() const {
|
||||
return _title;
|
||||
}
|
||||
const SCP_string& OptionBase::getDescription() const {
|
||||
return _description;
|
||||
}
|
||||
int OptionBase::getImportance() const {
|
||||
return _importance;
|
||||
}
|
||||
void OptionBase::setImportance(int importance) {
|
||||
_importance = importance;
|
||||
}
|
||||
bool operator<(const OptionBase& lhs, const OptionBase& rhs) {
|
||||
if (lhs._category < rhs._category)
|
||||
return true;
|
||||
if (rhs._category < lhs._category)
|
||||
return false;
|
||||
return lhs._importance > rhs._importance; // Importance is sorted from highest to lowest
|
||||
}
|
||||
bool operator>(const OptionBase& lhs, const OptionBase& rhs) {
|
||||
return rhs < lhs;
|
||||
}
|
||||
bool operator<=(const OptionBase& lhs, const OptionBase& rhs) {
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
bool operator>=(const OptionBase& lhs, const OptionBase& rhs) {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
} // namespace options
|
||||
@@ -0,0 +1,501 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/flagset.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "libs/jansson.h"
|
||||
#include "options/OptionsManager.h"
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace options {
|
||||
|
||||
enum class OptionType { Range, Selection };
|
||||
|
||||
enum class ExpertLevel { Beginner, Advanced, Expert };
|
||||
|
||||
enum class PresetKind { Basic, Low, Medium, High, Ultra };
|
||||
|
||||
struct ValueDescription {
|
||||
const SCP_string display;
|
||||
const SCP_string serialized;
|
||||
|
||||
ValueDescription(SCP_string _display, SCP_string _serialized);
|
||||
};
|
||||
|
||||
class OptionBase {
|
||||
protected:
|
||||
OptionsManager* _parent = nullptr;
|
||||
ExpertLevel _expert_level = ExpertLevel::Expert;
|
||||
|
||||
SCP_string _config_key;
|
||||
|
||||
SCP_string _category = "Other";
|
||||
|
||||
SCP_string _title;
|
||||
SCP_string _description;
|
||||
|
||||
int _importance = 0;
|
||||
|
||||
SCP_unordered_map<PresetKind, SCP_string> _preset_values;
|
||||
|
||||
std::unique_ptr<json_t> getConfigValue() const;
|
||||
|
||||
OptionBase(SCP_string config_key, SCP_string title, SCP_string description);
|
||||
|
||||
public:
|
||||
virtual ~OptionBase() = 0;
|
||||
|
||||
ExpertLevel getExpertLevel() const;
|
||||
void setExpertLevel(ExpertLevel expert_level);
|
||||
|
||||
const SCP_string& getCategory() const;
|
||||
void setCategory(const SCP_string& category);
|
||||
|
||||
int getImportance() const;
|
||||
void setImportance(int importance);
|
||||
|
||||
const SCP_string& getConfigKey() const;
|
||||
const SCP_string& getTitle() const;
|
||||
const SCP_string& getDescription() const;
|
||||
|
||||
void setPreset(PresetKind preset, const SCP_string& value);
|
||||
|
||||
virtual bool valueChanged(const json_t* val) const = 0;
|
||||
|
||||
virtual ValueDescription getCurrentValueDescription() const = 0;
|
||||
|
||||
virtual void setValueDescription(const ValueDescription& desc) const = 0;
|
||||
|
||||
virtual OptionType getType() const = 0;
|
||||
|
||||
virtual SCP_vector<ValueDescription> getValidValues() const = 0;
|
||||
|
||||
virtual ValueDescription getValueFromRange(float interpolant) const = 0;
|
||||
|
||||
virtual float getInterpolantFromValue(const ValueDescription& val) const = 0;
|
||||
|
||||
virtual void loadInitial() const = 0;
|
||||
|
||||
friend bool operator<(const OptionBase& lhs, const OptionBase& rhs);
|
||||
friend bool operator>(const OptionBase& lhs, const OptionBase& rhs);
|
||||
friend bool operator<=(const OptionBase& lhs, const OptionBase& rhs);
|
||||
friend bool operator>=(const OptionBase& lhs, const OptionBase& rhs);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using ValueEnumerator = std::function<SCP_vector<T>()>;
|
||||
template <typename T>
|
||||
using ValueDeserializer = std::function<T(const json_t*)>;
|
||||
template <typename T>
|
||||
using ValueSerializer = std::function<json_t*(const T&)>;
|
||||
template <typename T>
|
||||
using ValueDisplay = std::function<SCP_string(const T&)>;
|
||||
template <typename T>
|
||||
using DefaultValueFunctor = std::function<T()>;
|
||||
template <typename T>
|
||||
using ValueChangeListener = std::function<bool(const T&, bool initial)>;
|
||||
template <typename T>
|
||||
using ValueInterpolator = std::function<T(float)>;
|
||||
template <typename T>
|
||||
using ValueDeinterpolator = std::function<float(const T&)>;
|
||||
|
||||
template <typename T>
|
||||
class ValueFunctor {
|
||||
T _value;
|
||||
|
||||
public:
|
||||
ValueFunctor() = default;
|
||||
explicit ValueFunctor(T value) : _value(value) {}
|
||||
|
||||
T operator()() { return _value; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Option : public OptionBase {
|
||||
protected:
|
||||
ValueEnumerator<T> _valueEnumerator;
|
||||
ValueDeserializer<T> _deserializer;
|
||||
ValueSerializer<T> _serializer;
|
||||
ValueDisplay<T> _displayFunc;
|
||||
DefaultValueFunctor<T> _defaultValueFunc = ValueFunctor<T>();
|
||||
ValueChangeListener<T> _changeListener;
|
||||
ValueInterpolator<T> _interpolator;
|
||||
ValueDeinterpolator<T> _deinterpolator;
|
||||
|
||||
OptionType _type = OptionType::Selection; // By default the generic type is always a selection
|
||||
|
||||
ValueDescription toDescription(const T& val) const
|
||||
{
|
||||
auto json = _serializer(val);
|
||||
auto json_str = json_dump_string_new(json, JSON_COMPACT | JSON_ENSURE_ASCII | JSON_ENCODE_ANY);
|
||||
if (_displayFunc) {
|
||||
return ValueDescription(_displayFunc(val), json_str);
|
||||
} else {
|
||||
return ValueDescription(json_str, json_str);
|
||||
}
|
||||
}
|
||||
std::unique_ptr<json_t> fromDescription(const ValueDescription& desc) const
|
||||
{
|
||||
json_error_t err;
|
||||
std::unique_ptr<json_t> el(json_loads(desc.serialized.c_str(), JSON_DECODE_ANY, &err));
|
||||
if (el == nullptr) {
|
||||
// Shouldn't really happen...
|
||||
throw json_exception(err);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
public:
|
||||
Option(const SCP_string& config_key, const SCP_string& title, const SCP_string& description)
|
||||
: OptionBase(config_key, title, description)
|
||||
{
|
||||
}
|
||||
~Option() override = default;
|
||||
|
||||
OptionType getType() const override { return _type; }
|
||||
void setType(OptionType type) { _type = type; }
|
||||
|
||||
SCP_vector<ValueDescription> getValidValues() const override
|
||||
{
|
||||
Assertion(_valueEnumerator, "Invalid value enumerator detected!");
|
||||
Assertion(_type == OptionType::Selection, "getValidValues may only be called on selection option!");
|
||||
|
||||
auto values = _valueEnumerator();
|
||||
SCP_vector<ValueDescription> descriptions;
|
||||
descriptions.reserve(values.size());
|
||||
|
||||
for (const auto& val : values) {
|
||||
descriptions.emplace_back(toDescription(val));
|
||||
}
|
||||
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
ValueDescription getValueFromRange(float interpolant) const override
|
||||
{
|
||||
Assertion(_interpolator, "Need a valid interpolator!");
|
||||
Assertion(_type == OptionType::Range, "getValidValues may only be called on range option!");
|
||||
try {
|
||||
return toDescription(_interpolator(interpolant));
|
||||
} catch (const std::exception&) {
|
||||
return toDescription(_defaultValueFunc());
|
||||
}
|
||||
}
|
||||
float getInterpolantFromValue(const ValueDescription& val) const override
|
||||
{
|
||||
Assertion(_deinterpolator, "Need a valid deinterpolator!");
|
||||
Assertion(_type == OptionType::Range, "getInterpolantFromValue may only be called on range option!");
|
||||
try {
|
||||
auto el = fromDescription(val);
|
||||
return _deinterpolator(_deserializer(el.get()));
|
||||
} catch (const std::exception&) {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
T getValue() const
|
||||
{
|
||||
try {
|
||||
return _deserializer(getConfigValue().get());
|
||||
} catch (const std::exception&) {
|
||||
// deserializers are allowed to throw on error
|
||||
return _defaultValueFunc();
|
||||
}
|
||||
}
|
||||
ValueDescription getCurrentValueDescription() const override
|
||||
{
|
||||
auto val = getValue();
|
||||
return toDescription(val);
|
||||
}
|
||||
void setValueDescription(const ValueDescription& desc) const override
|
||||
{
|
||||
std::unique_ptr<json_t> el;
|
||||
try {
|
||||
el = fromDescription(desc);
|
||||
} catch (const std::exception&) {
|
||||
// Functions are allowed to throw
|
||||
}
|
||||
if (el) {
|
||||
_parent->setConfigValue(_config_key, std::move(el));
|
||||
}
|
||||
}
|
||||
bool valueChanged(const json_t* val) const override
|
||||
{
|
||||
try {
|
||||
if (_changeListener) {
|
||||
return _changeListener(_deserializer(val), false);
|
||||
}
|
||||
return false; // Value not changed, requires restart
|
||||
} catch (const std::exception&) {
|
||||
// Functions are allowed to throw
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void loadInitial() const override
|
||||
{
|
||||
if (!_changeListener) {
|
||||
// This requires a change listener
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_changeListener(getValue(), true);
|
||||
} catch (const std::exception&) {
|
||||
}
|
||||
}
|
||||
|
||||
const DefaultValueFunctor<T>& getDefaultValueFunc() const { return _defaultValueFunc; }
|
||||
void setDefaultValueFunc(const DefaultValueFunctor<T>& defaultValueFunc) { _defaultValueFunc = defaultValueFunc; }
|
||||
|
||||
const ValueDeserializer<T>& getDeserializer() const { return _deserializer; }
|
||||
void setDeserializer(const ValueDeserializer<T>& converter) { _deserializer = converter; }
|
||||
|
||||
const ValueSerializer<T>& getSerializer() const { return _serializer; }
|
||||
void setSerializer(const ValueSerializer<T>& serializer) { _serializer = serializer; }
|
||||
|
||||
const ValueDisplay<T>& getDisplayFunc() const { return _displayFunc; }
|
||||
void setDisplayFunc(const ValueDisplay<T>& displayFunc) { _displayFunc = displayFunc; }
|
||||
|
||||
const ValueEnumerator<T>& getValueEnumerator() const { return _valueEnumerator; }
|
||||
void setValueEnumerator(const ValueEnumerator<T>& valueEnumerator)
|
||||
{
|
||||
_valueEnumerator = valueEnumerator;
|
||||
_type = OptionType::Selection;
|
||||
}
|
||||
|
||||
const ValueChangeListener<T>& getChangeListener() const { return _changeListener; }
|
||||
void setChangeListener(const ValueChangeListener<T>& changeListener) { _changeListener = changeListener; }
|
||||
|
||||
const ValueInterpolator<T>& getInterpolator() const { return _interpolator; }
|
||||
const ValueDeinterpolator<T>& getDeinterpolator() const { return _deinterpolator; }
|
||||
void setInterpolator(const ValueInterpolator<T>& interpolator, const ValueDeinterpolator<T>& deinterpolator)
|
||||
{
|
||||
_interpolator = interpolator;
|
||||
_deinterpolator = deinterpolator;
|
||||
_type = OptionType::Range;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class VectorEnumerator {
|
||||
SCP_vector<T> _values;
|
||||
|
||||
public:
|
||||
explicit VectorEnumerator(SCP_vector<T> values) : _values(std::move(values)) {}
|
||||
|
||||
SCP_vector<T> operator()() { return _values; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class MapValueDisplay {
|
||||
SCP_unordered_map<T, SCP_string> _mapping;
|
||||
|
||||
public:
|
||||
explicit MapValueDisplay(SCP_unordered_map<T, SCP_string> mapping)
|
||||
: _mapping(std::move(mapping))
|
||||
{
|
||||
}
|
||||
|
||||
SCP_string operator()(const T& value) const
|
||||
{
|
||||
auto iter = _mapping.find(value);
|
||||
if (iter == _mapping.end()) {
|
||||
throw std::runtime_error("Display called with invalid value!");
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
// This would be so much easier with Concepts...
|
||||
template <typename T>
|
||||
typename std::enable_if<!std::is_enum<T>::value, void>::type set_defaults(Option<T>& /*opt*/)
|
||||
{
|
||||
// No defaults for the generic case since we don't know anything about this
|
||||
}
|
||||
|
||||
template <>
|
||||
void set_defaults<SCP_string>(Option<SCP_string>& opt);
|
||||
|
||||
template <>
|
||||
void set_defaults<float>(Option<float>& opt);
|
||||
|
||||
template <>
|
||||
void set_defaults<bool>(Option<bool>& opt);
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_enum<T>::value, void>::type set_defaults(Option<T>& opt)
|
||||
{
|
||||
opt.setSerializer([](T val) { return json_pack("I", static_cast<json_int_t>(val)); });
|
||||
opt.setDeserializer([](const json_t* value) {
|
||||
json_int_t i;
|
||||
|
||||
json_error_t err;
|
||||
if (json_unpack_ex((json_t*)value, &err, 0, "I", &i) != 0) {
|
||||
throw json_exception(err);
|
||||
}
|
||||
|
||||
return static_cast<T>(i);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
void set_defaults(Option<flagset<T, N>>& opt)
|
||||
{
|
||||
opt.setSerializer([](const flagset<T, N>& val) { return json_pack("I", static_cast<json_int_t>(val.to_u64())); });
|
||||
opt.setDeserializer([](const json_t* value) {
|
||||
json_int_t i;
|
||||
|
||||
json_error_t err;
|
||||
if (json_unpack_ex((json_t*)value, &err, 0, "I", &i) != 0) {
|
||||
throw json_exception(err);
|
||||
}
|
||||
|
||||
flagset<T, N> out;
|
||||
out.from_u64((std::uint64_t)i);
|
||||
return out;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename T>
|
||||
class OptionBuilder {
|
||||
Option<T> _instance;
|
||||
|
||||
SCP_unordered_map<PresetKind, T> _preset_values;
|
||||
|
||||
public:
|
||||
OptionBuilder(const SCP_string& config_key, const SCP_string& title, const SCP_string& description)
|
||||
: _instance(config_key, title, description)
|
||||
{
|
||||
internal::set_defaults(_instance);
|
||||
}
|
||||
|
||||
OptionBuilder(const OptionBuilder&) = delete;
|
||||
OptionBuilder& operator=(const OptionBuilder&) = delete;
|
||||
|
||||
OptionBuilder(OptionBuilder&&) noexcept = delete;
|
||||
OptionBuilder& operator=(OptionBuilder&&) noexcept = delete;
|
||||
|
||||
OptionBuilder& category(const SCP_string& category)
|
||||
{
|
||||
_instance.setCategory(category);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& level(ExpertLevel level)
|
||||
{
|
||||
_instance.setExpertLevel(level);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& default_val(const T& val)
|
||||
{
|
||||
_instance.setDefaultValueFunc(ValueFunctor<T>(val));
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& default_func(const DefaultValueFunctor<T>& func)
|
||||
{
|
||||
_instance.setDefaultValueFunc(func);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& deserializer(const ValueDeserializer<T>& converter)
|
||||
{
|
||||
_instance.setDeserializer(converter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& serializer(const ValueSerializer<T>& serializer)
|
||||
{
|
||||
_instance.setSerializer(serializer);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& enumerator(const ValueEnumerator<T>& enumerator)
|
||||
{
|
||||
_instance.setValueEnumerator(enumerator);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& values(const SCP_vector<std::pair<T, SCP_string>>& value_display_pairs)
|
||||
{
|
||||
SCP_vector<T> values;
|
||||
SCP_unordered_map<T, SCP_string> display_mapping;
|
||||
|
||||
for (auto& p : value_display_pairs) {
|
||||
values.push_back(p.first);
|
||||
display_mapping.emplace(p.first, p.second);
|
||||
}
|
||||
|
||||
_instance.setValueEnumerator(VectorEnumerator<T>(values));
|
||||
_instance.setDisplayFunc(MapValueDisplay<T>(display_mapping));
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& display(const ValueDisplay<T>& display)
|
||||
{
|
||||
_instance.setDisplayFunc(display);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& change_listener(const ValueChangeListener<T>& listener)
|
||||
{
|
||||
_instance.setChangeListener(listener);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& bind_to(T* dest)
|
||||
{
|
||||
return change_listener([dest](const T& val, bool) {
|
||||
*dest = val;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
OptionBuilder& bind_to_once(T* dest)
|
||||
{
|
||||
return change_listener([dest](const T& val, bool initial) {
|
||||
if (initial) {
|
||||
*dest = val;
|
||||
}
|
||||
return initial;
|
||||
});
|
||||
}
|
||||
|
||||
OptionBuilder& range(T min, T max)
|
||||
{
|
||||
Assertion(min <= max, "Invalid number range!");
|
||||
_instance.setInterpolator([min, max](T f) { return min + (max - min) * f; },
|
||||
[min, max](T f) { return (f - min) / (max - min); });
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& preset(PresetKind kind, const T& value)
|
||||
{
|
||||
_preset_values.emplace(kind, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionBuilder& importance(int value)
|
||||
{
|
||||
_instance.setImportance(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Option<T>* finish()
|
||||
{
|
||||
for (auto& val : _preset_values) {
|
||||
_instance.setPreset(val.first, json_dump_string_new(_instance.getSerializer()(val.second),
|
||||
JSON_COMPACT | JSON_ENSURE_ASCII | JSON_ENCODE_ANY));
|
||||
}
|
||||
std::unique_ptr<Option<T>> opt_ptr(new Option<T>(_instance));
|
||||
auto ptr = opt_ptr.get(); // We need to get the pointer now since we loose the type information otherwise
|
||||
OptionsManager::instance()->addOption(std::unique_ptr<OptionBase>(opt_ptr.release()));
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace options
|
||||
@@ -0,0 +1,150 @@
|
||||
//
|
||||
//
|
||||
|
||||
#include "OptionsManager.h"
|
||||
#include "Option.h"
|
||||
#include "mod_table/mod_table.h"
|
||||
#include "osapi/osregistry.h"
|
||||
|
||||
namespace {
|
||||
std::pair<SCP_string, SCP_string> parse_key(const SCP_string& key)
|
||||
{
|
||||
auto point_pos = key.find('.');
|
||||
if (point_pos == SCP_string::npos) {
|
||||
// Invalid key
|
||||
return std::pair<SCP_string, SCP_string>();
|
||||
}
|
||||
|
||||
auto section = key.substr(0, point_pos);
|
||||
auto section_key = key.substr(point_pos + 1);
|
||||
|
||||
return std::make_pair(section, section_key);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace options {
|
||||
|
||||
OptionsManager::OptionsManager() = default;
|
||||
OptionsManager::~OptionsManager() = default;
|
||||
|
||||
OptionsManager* options::OptionsManager::instance()
|
||||
{
|
||||
static OptionsManager instance;
|
||||
return &instance;
|
||||
}
|
||||
std::unique_ptr<json_t> OptionsManager::getValueFromConfig(const SCP_string& key) const
|
||||
{
|
||||
auto override_iter = _config_overrides.find(key);
|
||||
if (override_iter != _config_overrides.end()) {
|
||||
// We return a reference to an existing object so we need to increment the reference count
|
||||
json_incref(override_iter->second.get());
|
||||
return std::unique_ptr<json_t>(override_iter->second.get());
|
||||
}
|
||||
|
||||
auto changed_iter = _changed_values.find(key);
|
||||
if (changed_iter != _changed_values.end()) {
|
||||
json_incref(changed_iter->second.get());
|
||||
return std::unique_ptr<json_t>(changed_iter->second.get());
|
||||
}
|
||||
|
||||
auto parts = parse_key(key);
|
||||
if (parts.first.empty()) {
|
||||
// Invalid key
|
||||
throw std::runtime_error("Invalid key");
|
||||
}
|
||||
|
||||
auto value = os_config_read_string(parts.first.c_str(), parts.second.c_str());
|
||||
|
||||
if (value == nullptr) {
|
||||
// TODO: This is not really an error but I would like to avoid return nullptr here...
|
||||
throw std::runtime_error("No value available");
|
||||
}
|
||||
|
||||
json_error_t err;
|
||||
auto el = json_loads(value, JSON_DECODE_ANY, &err);
|
||||
if (el == nullptr) {
|
||||
throw json_exception(err);
|
||||
}
|
||||
|
||||
return std::unique_ptr<json_t>(el);
|
||||
}
|
||||
void OptionsManager::setConfigValue(const SCP_string& key,std::unique_ptr<json_t>&& value)
|
||||
{
|
||||
_changed_values[key] = std::move(value);
|
||||
}
|
||||
void OptionsManager::setOverride(const SCP_string& key, const SCP_string& json)
|
||||
{
|
||||
json_error_t err;
|
||||
auto el = json_loads(json.c_str(), JSON_DECODE_ANY, &err);
|
||||
if (el == nullptr) {
|
||||
return;
|
||||
}
|
||||
_config_overrides.emplace(key, std::unique_ptr<json_t>(el));
|
||||
}
|
||||
const OptionBase* OptionsManager::addOption(std::unique_ptr<const OptionBase>&& option)
|
||||
{
|
||||
_options.emplace_back(std::move(option));
|
||||
_optionsSorted = false; // Order got invalidated by adding a new option
|
||||
|
||||
auto ptr = _options.back().get();
|
||||
_optionsMapping.emplace(ptr->getConfigKey(), ptr);
|
||||
return ptr;
|
||||
}
|
||||
void OptionsManager::removeOption(const OptionBase* option)
|
||||
{
|
||||
_optionsMapping.erase(option->getConfigKey());
|
||||
_options.erase(
|
||||
std::remove_if(_options.begin(), _options.end(),
|
||||
[option](const std::unique_ptr<const OptionBase>& ptr) { return ptr.get() == option; }));
|
||||
}
|
||||
const SCP_vector<std::unique_ptr<const options::OptionBase>>& OptionsManager::getOptions()
|
||||
{
|
||||
if (!_optionsSorted) {
|
||||
// Keep options sorted by only sorting them when necessary
|
||||
|
||||
std::sort(_options.begin(), _options.end(),
|
||||
[](const std::unique_ptr<const OptionBase>& left, const std::unique_ptr<const OptionBase>& right) {
|
||||
return *left < *right;
|
||||
});
|
||||
|
||||
_optionsSorted = true;
|
||||
}
|
||||
return _options;
|
||||
}
|
||||
SCP_vector<const options::OptionBase*> OptionsManager::persistChanges()
|
||||
{
|
||||
for (auto& entry : _changed_values) {
|
||||
auto parts = parse_key(entry.first);
|
||||
if (parts.first.empty()) {
|
||||
// Invalid key
|
||||
continue;
|
||||
}
|
||||
|
||||
auto val = json_dump_string(entry.second.get(), JSON_COMPACT | JSON_ENSURE_ASCII | JSON_ENCODE_ANY);
|
||||
|
||||
os_config_write_string(parts.first.c_str(), parts.second.c_str(), val.c_str());
|
||||
}
|
||||
SCP_vector<const options::OptionBase*> unchanged;
|
||||
|
||||
// Only notify options once all values have been written to config
|
||||
for (auto& entry : _changed_values) {
|
||||
auto iter = _optionsMapping.find(entry.first);
|
||||
if (iter != _optionsMapping.end()) {
|
||||
if (!iter->second->valueChanged(entry.second.get())) {
|
||||
unchanged.push_back(iter->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
_changed_values.clear();
|
||||
|
||||
return unchanged;
|
||||
}
|
||||
void OptionsManager::discardChanges() { _changed_values.clear(); }
|
||||
void OptionsManager::loadInitialValues()
|
||||
{
|
||||
for (auto& opt : _options) {
|
||||
opt->loadInitial();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace options
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "libs/jansson.h"
|
||||
#include <memory>
|
||||
|
||||
namespace options {
|
||||
|
||||
// Forward declaration
|
||||
class OptionBase;
|
||||
|
||||
class OptionsManager {
|
||||
OptionsManager();
|
||||
|
||||
SCP_unordered_map<SCP_string, std::unique_ptr<json_t>> _config_overrides;
|
||||
|
||||
SCP_unordered_map<SCP_string, std::unique_ptr<json_t>> _changed_values;
|
||||
|
||||
SCP_unordered_map<SCP_string, const OptionBase*> _optionsMapping;
|
||||
|
||||
SCP_vector<std::unique_ptr<const OptionBase>> _options;
|
||||
bool _optionsSorted = false;
|
||||
|
||||
public:
|
||||
static OptionsManager* instance();
|
||||
|
||||
~OptionsManager();
|
||||
|
||||
std::unique_ptr<json_t> getValueFromConfig(const SCP_string& key) const;
|
||||
|
||||
void setConfigValue(const SCP_string& key, std::unique_ptr<json_t>&& value);
|
||||
|
||||
void setOverride(const SCP_string& key, const SCP_string& json);
|
||||
|
||||
const OptionBase* addOption(std::unique_ptr<const OptionBase>&& option);
|
||||
|
||||
void removeOption(const OptionBase* option);
|
||||
|
||||
const SCP_vector<std::unique_ptr<const options::OptionBase>>& getOptions();
|
||||
|
||||
/**
|
||||
* @brief Persists changes to the disk
|
||||
*
|
||||
* This writes all the outstanding changes to the config file and returns a vector of all the options that indicated
|
||||
* that they could not change the value. If the return value is empty then all changes were applied but if it is not
|
||||
* empty then a restart is required for all the options to take effect.
|
||||
*
|
||||
* @return A vector of options that did not support changing the value
|
||||
*/
|
||||
SCP_vector<const options::OptionBase*> persistChanges();
|
||||
|
||||
void discardChanges();
|
||||
|
||||
void loadInitialValues();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "gamesequence/gamesequence.h"
|
||||
#include "freespace.h"
|
||||
#include "gamesequence/gamesequence.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "parse/parselo.h"
|
||||
|
||||
#ifdef SCP_UNIX
|
||||
@@ -300,6 +300,8 @@ void os_set_title( const char * title )
|
||||
// call at program end
|
||||
void os_cleanup()
|
||||
{
|
||||
os_deinit_registry_stuff();
|
||||
|
||||
#ifndef NDEBUG
|
||||
outwnd_close();
|
||||
#endif
|
||||
|
||||
+4
-4
@@ -171,7 +171,7 @@ namespace os
|
||||
/**
|
||||
* @brief Sets the swap interval
|
||||
*/
|
||||
virtual void setSwapInterval(int status) = 0;
|
||||
virtual bool setSwapInterval(int status) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -213,9 +213,9 @@ namespace os
|
||||
* @ingroup os_graphics_api
|
||||
*/
|
||||
enum class ViewportState {
|
||||
Windowed,
|
||||
Borderless,
|
||||
Fullscreen
|
||||
Windowed = 0,
|
||||
Borderless = 1,
|
||||
Fullscreen = 2
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+37
-25
@@ -11,6 +11,8 @@
|
||||
#include "osapi/osregistry.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "cmdline/cmdline.h"
|
||||
#include "osregistry.h"
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
@@ -828,6 +830,8 @@ static void profile_save(Profile *profile, const char *file)
|
||||
|
||||
// os registry functions -------------------------------------------------------------
|
||||
|
||||
static Profile* Osreg_profile = nullptr;
|
||||
|
||||
// initialize the registry. setup default keys to use
|
||||
void os_init_registry_stuff(const char *company, const char *app)
|
||||
{
|
||||
@@ -845,15 +849,36 @@ void os_init_registry_stuff(const char *company, const char *app)
|
||||
strcpy_s(szAppName, Osreg_app_name);
|
||||
}
|
||||
|
||||
Osreg_profile = profile_read(Osreg_config_file_name);
|
||||
|
||||
Os_reg_inited = 1;
|
||||
}
|
||||
void os_deinit_registry_stuff()
|
||||
{
|
||||
if (Osreg_profile != nullptr) {
|
||||
profile_free(Osreg_profile);
|
||||
Osreg_profile = nullptr;
|
||||
}
|
||||
}
|
||||
bool os_config_has_value(const char* section, const char* name) {
|
||||
#ifdef WIN32
|
||||
if (Osreg_profile == nullptr) {
|
||||
// No config file, fall back to registy
|
||||
return registry_read_string(section, name, nullptr) != nullptr;
|
||||
}
|
||||
#endif
|
||||
if (section == nullptr)
|
||||
section = DEFAULT_SECTION;
|
||||
|
||||
char *ptr = profile_get_value(Osreg_profile, section, name);
|
||||
|
||||
return ptr != nullptr;
|
||||
}
|
||||
|
||||
const char *os_config_read_string(const char *section, const char *name, const char *default_value)
|
||||
{
|
||||
Profile *p = profile_read(Osreg_config_file_name);
|
||||
|
||||
#ifdef WIN32
|
||||
if (p == nullptr) {
|
||||
if (Osreg_profile == nullptr) {
|
||||
// No config file, fall back to registy
|
||||
return registry_read_string(section, name, default_value);
|
||||
}
|
||||
@@ -864,24 +889,20 @@ const char *os_config_read_string(const char *section, const char *name, const c
|
||||
if (section == NULL)
|
||||
section = DEFAULT_SECTION;
|
||||
|
||||
char *ptr = profile_get_value(p, section, name);
|
||||
char *ptr = profile_get_value(Osreg_profile, section, name);
|
||||
|
||||
if (ptr != NULL) {
|
||||
strncpy(tmp_string_data, ptr, 1023);
|
||||
default_value = tmp_string_data;
|
||||
}
|
||||
|
||||
profile_free(p);
|
||||
|
||||
return default_value;
|
||||
}
|
||||
|
||||
unsigned int os_config_read_uint(const char *section, const char *name, unsigned int default_value)
|
||||
{
|
||||
Profile *p = profile_read(Osreg_config_file_name);
|
||||
|
||||
#ifdef WIN32
|
||||
if (p == nullptr) {
|
||||
if (Osreg_profile == nullptr) {
|
||||
// No config file, fall back to registy
|
||||
return registry_read_uint(section, name, default_value);
|
||||
}
|
||||
@@ -890,25 +911,21 @@ unsigned int os_config_read_uint(const char *section, const char *name, unsigned
|
||||
if (section == NULL)
|
||||
section = DEFAULT_SECTION;
|
||||
|
||||
char *ptr = profile_get_value(p, section, name);
|
||||
char *ptr = profile_get_value(Osreg_profile, section, name);
|
||||
|
||||
if (ptr != NULL) {
|
||||
default_value = atoi(ptr);
|
||||
}
|
||||
|
||||
profile_free(p);
|
||||
|
||||
return default_value;
|
||||
}
|
||||
|
||||
void os_config_write_string(const char *section, const char *name, const char *value)
|
||||
{
|
||||
Profile *p = profile_read(Osreg_config_file_name);
|
||||
|
||||
#ifdef WIN32
|
||||
// When there is no config file then it shouldn't be created because that would "hide" all previous settings
|
||||
// Instead fall back to writing the settings to the config file
|
||||
if (p == nullptr) {
|
||||
if (Osreg_profile == nullptr) {
|
||||
registry_write_string(section, name, value);
|
||||
return;
|
||||
}
|
||||
@@ -917,20 +934,16 @@ void os_config_write_string(const char *section, const char *name, const char *v
|
||||
if (section == NULL)
|
||||
section = DEFAULT_SECTION;
|
||||
|
||||
p = profile_update(p, section, name, value);
|
||||
profile_save(p, Osreg_config_file_name);
|
||||
profile_free(p);
|
||||
Osreg_profile = profile_update(Osreg_profile, section, name, value);
|
||||
profile_save(Osreg_profile, Osreg_config_file_name);
|
||||
}
|
||||
|
||||
void os_config_write_uint(const char *section, const char *name, unsigned int value)
|
||||
{
|
||||
|
||||
Profile *p = profile_read(Osreg_config_file_name);
|
||||
|
||||
#ifdef WIN32
|
||||
// When there is no config file then it shouldn't be created because that would "hide" all previous settings
|
||||
// Instead fall back to writing the settings to the config file
|
||||
if (p == nullptr) {
|
||||
if (Osreg_profile == nullptr) {
|
||||
registry_write_uint(section, name, value);
|
||||
return;
|
||||
}
|
||||
@@ -943,8 +956,7 @@ void os_config_write_uint(const char *section, const char *name, unsigned int va
|
||||
|
||||
snprintf(buf, 20, "%u", value);
|
||||
|
||||
p = profile_update(p, section, name, buf);
|
||||
profile_save(p, Osreg_config_file_name);
|
||||
profile_free(p);
|
||||
Osreg_profile = profile_update(Osreg_profile, section, name, buf);
|
||||
profile_save(Osreg_profile, Osreg_config_file_name);
|
||||
}
|
||||
|
||||
@@ -36,12 +36,16 @@ time_t os_registry_get_last_modification_time();
|
||||
// initialize the registry. setup default keys to use
|
||||
void os_init_registry_stuff( const char *company, const char *app);
|
||||
|
||||
void os_deinit_registry_stuff();
|
||||
|
||||
// Writes a string to the registry
|
||||
void os_config_write_string( const char *section, const char *name, const char *value );
|
||||
|
||||
// Writes an unsigned int to the INI file.
|
||||
void os_config_write_uint( const char *section, const char *name, unsigned int value );
|
||||
|
||||
bool os_config_has_value(const char *section, const char *name);
|
||||
|
||||
// Reads a string from the INI file. If default is passed,
|
||||
// and the string isn't found, returns ptr to default otherwise
|
||||
// returns NULL; Copy the return value somewhere before
|
||||
|
||||
+797
-796
File diff suppressed because it is too large.
Load diff
+16
-7
@@ -235,6 +235,8 @@ class waypoint_list;
|
||||
#define OP_UNSET_BIT (0x0011 | OP_CATEGORY_ARITHMETIC) // Goober5000
|
||||
#define OP_IS_BIT_SET (0x0012 | OP_CATEGORY_ARITHMETIC) // Goober5000
|
||||
#define OP_SIGNUM (0x0013 | OP_CATEGORY_ARITHMETIC) // Goober5000
|
||||
#define OP_IS_NAN (0x0014 | OP_CATEGORY_ARITHMETIC) // Goober5000
|
||||
#define OP_NAN_TO_NUMBER (0x0015 | OP_CATEGORY_ARITHMETIC) // Goober5000
|
||||
|
||||
|
||||
#define OP_TRUE (0x0000 | OP_CATEGORY_LOGICAL)
|
||||
@@ -390,6 +392,7 @@ class waypoint_list;
|
||||
#define OP_TURRET_GET_SECONDARY_AMMO (0x0050 | OP_CATEGORY_STATUS | OP_NONCAMPAIGN_FLAG) // DahBlount, part of the turret ammo code
|
||||
#define OP_IS_DOCKED (0x0051 | OP_CATEGORY_STATUS | OP_NONCAMPAIGN_FLAG) // Goober5000
|
||||
#define OP_IS_IN_TURRET_FOV (0x0052 | OP_CATEGORY_STATUS | OP_NONCAMPAIGN_FLAG) // Goober5000
|
||||
#define OP_GET_HOTKEY (0x0053 | OP_CATEGORY_STATUS | OP_NONCAMPAIGN_FLAG) // wookieejedi
|
||||
|
||||
// conditional sexpressions
|
||||
#define OP_WHEN (0x0000 | OP_CATEGORY_CONDITIONAL)
|
||||
@@ -408,8 +411,10 @@ class waypoint_list;
|
||||
#define OP_INVALIDATE_ALL_ARGUMENTS (0x000d | OP_CATEGORY_CONDITIONAL) // Karajorma
|
||||
#define OP_VALIDATE_ALL_ARGUMENTS (0x000e | OP_CATEGORY_CONDITIONAL) // Karajorma
|
||||
#define OP_FOR_COUNTER (0x000f | OP_CATEGORY_CONDITIONAL) // Goober5000
|
||||
|
||||
#define OP_IF_THEN_ELSE (0x0010 | OP_CATEGORY_CONDITIONAL) // Goober5000
|
||||
#define OP_NUM_VALID_ARGUMENTS (0x0011 | OP_CATEGORY_CONDITIONAL) // Karajorma
|
||||
#define OP_FUNCTIONAL_IF_THEN_ELSE (0x0012 | OP_CATEGORY_CONDITIONAL) // Goober5000
|
||||
|
||||
|
||||
// sexpressions with side-effects
|
||||
@@ -740,6 +745,7 @@ class waypoint_list;
|
||||
|
||||
#define OP_MODIFY_VARIABLE_XSTR (0X0030 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // m!m
|
||||
#define OP_RESET_POST_EFFECTS (0x0031 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // Goober5000
|
||||
#define OP_ADD_REMOVE_HOTKEY (0x0032 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // wookieejedi
|
||||
|
||||
// defined for AI goals
|
||||
#define OP_AI_CHASE (0x0000 | OP_CATEGORY_AI | OP_NONCAMPAIGN_FLAG)
|
||||
@@ -945,14 +951,17 @@ char *CTEXT(int n);
|
||||
|
||||
#define SEXP_TRUE 1
|
||||
#define SEXP_FALSE 0
|
||||
|
||||
// Goober5000: changed these to unlikely values, because now we have sexps using negative numbers
|
||||
#define SEXP_KNOWN_FALSE -32767 //-1
|
||||
#define SEXP_KNOWN_TRUE -32766 //-2
|
||||
#define SEXP_UNKNOWN -32765 //-3
|
||||
#define SEXP_NAN -32764 //-4 // not a number -- used when ships/wing part of boolean and haven't arrived yet
|
||||
#define SEXP_NAN_FOREVER -32763 //-5 // not a number and will never change -- used to falsify boolean sexpressions
|
||||
#define SEXP_CANT_EVAL -32762 //-6 // can't evaluate yet for whatever reason (acts like false)
|
||||
#define SEXP_NUM_EVAL -32761 //-7 // already completed an arithmetic operation and result is stored
|
||||
#define SEXP_KNOWN_FALSE (INT_MIN+10)
|
||||
#define SEXP_KNOWN_TRUE (INT_MIN+11)
|
||||
#define SEXP_UNKNOWN (INT_MIN+12)
|
||||
#define SEXP_NAN (INT_MIN+13) // not a number -- used when ships/wing part of boolean and haven't arrived yet
|
||||
#define SEXP_NAN_FOREVER (INT_MIN+14) // not a number and will never change -- used to falsify boolean sexpressions
|
||||
#define SEXP_CANT_EVAL (INT_MIN+15) // can't evaluate yet for whatever reason (acts like false)
|
||||
#define SEXP_NUM_EVAL (INT_MIN+16) // already completed an arithmetic operation and result is stored
|
||||
// in case we want to test for any of the above
|
||||
#define SEXP_UNLIKELY_RETURN_VALUE_BOUND (INT_MIN+17)
|
||||
|
||||
// defines for check_sexp_syntax
|
||||
#define SEXP_CHECK_NONOP_ARGS -1 // non-operator has arguments
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
*/
|
||||
|
||||
#include "cfile/cfilesystem.h"
|
||||
#include "pilotfile/pilotfile_convert.h"
|
||||
#include "cutscene/cutscenes.h"
|
||||
#include "menuui/techmenu.h"
|
||||
#include "pilotfile/pilotfile_convert.h"
|
||||
#include "ship/ship.h"
|
||||
#include "stats/medals.h"
|
||||
#include "weapon/weapon.h"
|
||||
|
||||
@@ -30,7 +30,7 @@ pilotfile_convert::~pilotfile_convert()
|
||||
}
|
||||
}
|
||||
|
||||
void pilotfile_convert::startSection(Section::id section_id)
|
||||
void pilotfile_convert::startSection(Section section_id)
|
||||
{
|
||||
Assert( cfp );
|
||||
|
||||
|
||||
Loaded 100 of 167 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user