mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1588644fec | ||
|
|
0abd592038 | ||
|
|
909e799bdd | ||
|
|
5a9f181aff | ||
|
|
b0ba22a43d | ||
|
|
c89faebe4e | ||
|
|
059e149590 |
No files matched your search
@@ -6,5 +6,3 @@ code/executor/ @asarium
|
||||
code/scpui/ @asarium
|
||||
|
||||
code/scripting/ @asarium
|
||||
|
||||
code/controlconfig/ @z64555
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
configuration: [FastDebug, Release]
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-e29f882
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-a8ca321
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
@@ -29,8 +29,6 @@ jobs:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
setup-python: 'false'
|
||||
aqtversion: ==1.1.3
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
name: Linux
|
||||
needs: create_release
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-e29f882
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-a8ca321
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
@@ -53,8 +53,6 @@ jobs:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
setup-python: 'false'
|
||||
aqtversion: ==1.1.3
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
configuration: [FastDebug, Release]
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-e29f882
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-a8ca321
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
@@ -29,8 +29,6 @@ jobs:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
setup-python: 'false'
|
||||
aqtversion: ==1.1.3
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
|
||||
@@ -8,6 +8,7 @@ env:
|
||||
jobs:
|
||||
build_linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
compiler: [gcc-5, gcc-10, clang-9]
|
||||
@@ -22,7 +23,7 @@ jobs:
|
||||
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-e29f882
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-e977d36
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
@@ -37,7 +38,6 @@ jobs:
|
||||
dir: ${{ github.workspace }}/..
|
||||
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
setup-python: 'false'
|
||||
aqtversion: ==1.1.3
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
|
||||
@@ -3,8 +3,7 @@ name: Publish Standalone docker image
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'nightly_*'
|
||||
- 'release_*'
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build_images:
|
||||
@@ -13,9 +12,13 @@ jobs:
|
||||
configuration: [FastDebug, Release]
|
||||
include:
|
||||
- configuration: FastDebug
|
||||
tag_suffix: -debug
|
||||
latest_tag: latest_debug
|
||||
nightly_tag: nightly_debug
|
||||
release_tag: release_debug
|
||||
- configuration: Release
|
||||
tag_suffix: ""
|
||||
latest_tag: latest
|
||||
nightly_tag: nightly
|
||||
release_tag: release
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -23,30 +26,23 @@ jobs:
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Docker login
|
||||
run: echo ${{ secrets.DOCKER_ACCESSTOKEN }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
- name: Docker build
|
||||
run: docker build --build-arg build_type=${{ matrix.configuration }} -f docker/standalone/Dockerfile -t ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }} .
|
||||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: crazy-max/ghaction-docker-meta@v1
|
||||
with:
|
||||
images: ghcr.io/scp-fs2open/standalone_server
|
||||
tag-latest: false
|
||||
- name: Push latest tag
|
||||
run: |
|
||||
docker push ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }}
|
||||
|
||||
- name: Login to GitHub Docker Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.CR_PAT }}
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/standalone/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.docker_meta.outputs.tags }}${{ matrix.tag_suffix }}
|
||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||
build-args: |
|
||||
build_type=${{ matrix.configuration }}
|
||||
- name: Push nightly tag
|
||||
if: contains(github.ref, 'refs/tags/nightly_')
|
||||
run: |
|
||||
docker tag ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }} ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.nightly_tag }}
|
||||
docker push ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.nightly_tag }}
|
||||
|
||||
- name: Push release tag
|
||||
if: contains(github.ref, 'refs/tags/release_')
|
||||
run: |
|
||||
docker tag ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.latest_tag }} ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.release_tag }}
|
||||
docker push ${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.release_tag }}
|
||||
+1
-1
@@ -6,7 +6,7 @@ if (EXISTS "${PROJECT_SOURCE_DIR}/version_override.cmake")
|
||||
include("${PROJECT_SOURCE_DIR}/version_override.cmake")
|
||||
endif()
|
||||
|
||||
set_if_not_defined(FSO_VERSION_MAJOR 21)
|
||||
set_if_not_defined(FSO_VERSION_MAJOR 20)
|
||||
set_if_not_defined(FSO_VERSION_MINOR 1)
|
||||
set_if_not_defined(FSO_VERSION_BUILD 0)
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ TARGET_LINK_LIBRARIES(code PUBLIC openal)
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${LUA_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${PNG_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${JPEG_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC lz4)
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC sdl2)
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ ActionDefinition::ActionDefinition(SCP_string name, SCP_vector<ActionParameter>
|
||||
: m_name(std::move(name)), m_parameters(std::move(parameters))
|
||||
{
|
||||
}
|
||||
ActionDefinition::~ActionDefinition() = default;
|
||||
|
||||
const SCP_string& ActionDefinition::getName() const
|
||||
{
|
||||
|
||||
@@ -15,7 +15,6 @@ struct ActionParameter {
|
||||
class ActionDefinition {
|
||||
public:
|
||||
ActionDefinition(SCP_string name, SCP_vector<ActionParameter> parameters);
|
||||
virtual ~ActionDefinition();
|
||||
|
||||
const SCP_string& getName() const;
|
||||
const SCP_vector<actions::ActionParameter>& getParameters() const;
|
||||
|
||||
@@ -47,7 +47,24 @@ void parseActionParameters(const SCP_vector<ActionParameter>& params,
|
||||
|
||||
const ActionDefinitionManager& ActionDefinitionManager::instance()
|
||||
{
|
||||
static const ActionDefinitionManager manager;
|
||||
static const ActionDefinitionManager manager = []() {
|
||||
ActionDefinitionManager newManager;
|
||||
|
||||
newManager.addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::MoveToSubmodel>("Move To Subobject")));
|
||||
newManager.addDefinition(std::unique_ptr<ActionDefinition>(
|
||||
new BuiltinActionDefinition<types::ParticleEffectAction>("Start Particle Effect")));
|
||||
newManager.addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::PlaySoundAction>("Play 3D Sound")));
|
||||
newManager.addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::SetDirectionAction>("Set Direction")));
|
||||
newManager.addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::SetPositionAction>("Set Position")));
|
||||
newManager.addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::WaitAction>("Wait")));
|
||||
|
||||
return newManager;
|
||||
}();
|
||||
return manager;
|
||||
}
|
||||
void ActionDefinitionManager::addDefinition(std::unique_ptr<ActionDefinition> def)
|
||||
@@ -127,19 +144,5 @@ std::unique_ptr<Action> ActionDefinitionManager::parseAction(const flagset<Progr
|
||||
|
||||
return std::unique_ptr<Action>();
|
||||
}
|
||||
ActionDefinitionManager::ActionDefinitionManager()
|
||||
{
|
||||
addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::MoveToSubmodel>("Move To Subobject")));
|
||||
addDefinition(std::unique_ptr<ActionDefinition>(
|
||||
new BuiltinActionDefinition<types::ParticleEffectAction>("Start Particle Effect")));
|
||||
addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::PlaySoundAction>("Play 3D Sound")));
|
||||
addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::SetDirectionAction>("Set Direction")));
|
||||
addDefinition(
|
||||
std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::SetPositionAction>("Set Position")));
|
||||
addDefinition(std::unique_ptr<ActionDefinition>(new BuiltinActionDefinition<types::WaitAction>("Wait")));
|
||||
}
|
||||
|
||||
} // namespace actions
|
||||
@@ -19,7 +19,7 @@ class ActionDefinitionManager {
|
||||
static const ActionDefinitionManager& instance();
|
||||
|
||||
private:
|
||||
ActionDefinitionManager();
|
||||
ActionDefinitionManager() = default;
|
||||
|
||||
void addDefinition(std::unique_ptr<ActionDefinition> def);
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
#include "BuiltinActionDefinition.h"
|
||||
@@ -15,7 +15,6 @@ class BuiltinActionDefinition : public ActionDefinition {
|
||||
: ActionDefinition(std::move(name), {{"parameter", expression::ValueTypeTraits<ActionValueType>::type}})
|
||||
{
|
||||
}
|
||||
~BuiltinActionDefinition() override = default;
|
||||
|
||||
flagset<ProgramContextFlags> getRequiredContext() const override
|
||||
{
|
||||
|
||||
@@ -123,9 +123,9 @@ Program& Program::operator=(const Program& other)
|
||||
Program& Program::operator=(Program&&) noexcept = default;
|
||||
Program::Program(Program&&) noexcept = default;
|
||||
|
||||
bool ProgramSet::isEmpty() const
|
||||
const SCP_vector<actions::Program>& ProgramSet::getPrograms() const
|
||||
{
|
||||
return _programs.empty();
|
||||
return _programs;
|
||||
}
|
||||
|
||||
ProgramSet::ProgramSet() = default;
|
||||
|
||||
@@ -84,7 +84,7 @@ class ProgramSet {
|
||||
ProgramSet();
|
||||
explicit ProgramSet(const flagset<ProgramContextFlags>& context_flags);
|
||||
|
||||
bool isEmpty() const;
|
||||
const SCP_vector<actions::Program>& getPrograms() const;
|
||||
|
||||
/**
|
||||
* @brief Starts the execution of all programs within this set with the specified parameters
|
||||
|
||||
@@ -14,12 +14,6 @@ class AbstractExpression;
|
||||
template <typename T>
|
||||
class TypedActionExpression;
|
||||
|
||||
/**
|
||||
* @brief An expression that can be executed to compute a value at runtime
|
||||
*
|
||||
* This is the basis of having dynamic values in actions by allowing mods to specify complex expressions that make use
|
||||
* of variables and functions for computing their values at runtime.
|
||||
*/
|
||||
class ActionExpression {
|
||||
public:
|
||||
ActionExpression() = default;
|
||||
@@ -42,13 +36,6 @@ class ActionExpression {
|
||||
std::shared_ptr<nodes::AbstractExpression> m_expression;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An action expression with a specific type specified at compile time
|
||||
*
|
||||
* This is most useful for built-in actions that have the required type fixed at compile time.
|
||||
*
|
||||
* @tparam T The type of the expression value.
|
||||
*/
|
||||
template <typename T>
|
||||
class TypedActionExpression {
|
||||
public:
|
||||
|
||||
@@ -22,7 +22,19 @@ Value value_minus(const SCP_vector<Value>& parameters)
|
||||
|
||||
const FunctionManager& FunctionManager::instance()
|
||||
{
|
||||
static const FunctionManager manager;
|
||||
static const FunctionManager manager = []() {
|
||||
FunctionManager newManager;
|
||||
|
||||
newManager.addOperator("+", {ValueType::Integer, ValueType::Integer}, ValueType::Integer, value_plus<int>);
|
||||
newManager.addOperator("+", {ValueType::Float, ValueType::Float}, ValueType::Float, value_plus<float>);
|
||||
newManager.addOperator("+", {ValueType::Vector, ValueType::Vector}, ValueType::Vector, value_plus<vec3d>);
|
||||
|
||||
newManager.addOperator("-", {ValueType::Integer, ValueType::Integer}, ValueType::Integer, value_minus<int>);
|
||||
newManager.addOperator("-", {ValueType::Float, ValueType::Float}, ValueType::Float, value_minus<float>);
|
||||
newManager.addOperator("-", {ValueType::Vector, ValueType::Vector}, ValueType::Vector, value_minus<vec3d>);
|
||||
|
||||
return newManager;
|
||||
}();
|
||||
return manager;
|
||||
}
|
||||
void FunctionManager::addOperator(const SCP_string& name,
|
||||
@@ -75,16 +87,7 @@ const FunctionDefinition* FunctionManager::findOperator(const SCP_string& name,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FunctionManager::FunctionManager()
|
||||
{
|
||||
addOperator("+", {ValueType::Integer, ValueType::Integer}, ValueType::Integer, value_plus<int>);
|
||||
addOperator("+", {ValueType::Float, ValueType::Float}, ValueType::Float, value_plus<float>);
|
||||
addOperator("+", {ValueType::Vector, ValueType::Vector}, ValueType::Vector, value_plus<vec3d>);
|
||||
|
||||
addOperator("-", {ValueType::Integer, ValueType::Integer}, ValueType::Integer, value_minus<int>);
|
||||
addOperator("-", {ValueType::Float, ValueType::Float}, ValueType::Float, value_minus<float>);
|
||||
addOperator("-", {ValueType::Vector, ValueType::Vector}, ValueType::Vector, value_minus<vec3d>);
|
||||
}
|
||||
FunctionManager::FunctionManager() = default;
|
||||
|
||||
} // namespace expression
|
||||
} // namespace actions
|
||||
+1
-1
@@ -561,7 +561,7 @@ const char *ai_get_goal_target_name(const char *name, int *index);
|
||||
void ai_clear_goal_target_names();
|
||||
|
||||
extern void init_ai_system(void);
|
||||
extern void ai_attack_object(object *attacker, object *attacked, int ship_info_index = -1);
|
||||
extern void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp, int ship_info_index = -1);
|
||||
extern void ai_evade_object(object *evader, object *evaded);
|
||||
extern void ai_ignore_object(object *ignorer, object *ignored, int ignore_new);
|
||||
extern void ai_ignore_wing(object *ignorer, int wingnum);
|
||||
|
||||
+1
-5
@@ -130,13 +130,9 @@ namespace AI {
|
||||
Use_only_single_fov_for_turrets,
|
||||
No_turning_directional_bias,
|
||||
Use_axial_turnrate_differences,
|
||||
all_nonshielded_ships_can_manage_ets,
|
||||
fightercraft_nonshielded_ships_can_manage_ets,
|
||||
nonshielded_ships_can_manage_ets,
|
||||
Better_collision_avoidance,
|
||||
Require_exact_los,
|
||||
Improved_missile_avoidance,
|
||||
Friendlies_use_countermeasure_firechance,
|
||||
Improved_subsystem_attack_pathing,
|
||||
|
||||
NUM_VALUES
|
||||
};
|
||||
|
||||
+2
-17
@@ -520,30 +520,15 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
profile->second_order_lead_predict_factor = 0;
|
||||
}
|
||||
}
|
||||
if (optional_string("$ships with no shields can manage ETS:"))
|
||||
{
|
||||
mprintf(("Warning: \"$ships with no shields can manage ETS\" flag is deprecated in favor of \"$any ship with no shields can manage ETS\"\n"));
|
||||
bool temp;
|
||||
stuff_boolean(&temp);
|
||||
profile->flags.set(AI::Profile_Flags::all_nonshielded_ships_can_manage_ets, temp);
|
||||
}
|
||||
|
||||
set_flag(profile, "$no directional bias for missile and ship turning:", AI::Profile_Flags::No_turning_directional_bias);
|
||||
|
||||
set_flag(profile, "$respect ship axial turnrate differences:", AI::Profile_Flags::Use_axial_turnrate_differences);
|
||||
|
||||
set_flag(profile, "$any ship with no shields can manage ETS:", AI::Profile_Flags::all_nonshielded_ships_can_manage_ets);
|
||||
|
||||
set_flag(profile, "$fighters/bombers with no shields can manage ETS:", AI::Profile_Flags::fightercraft_nonshielded_ships_can_manage_ets);
|
||||
set_flag(profile, "$ships with no shields can manage ETS:", AI::Profile_Flags::nonshielded_ships_can_manage_ets);
|
||||
|
||||
set_flag(profile, "$better combat collision avoidance for fightercraft:", AI::Profile_Flags::Better_collision_avoidance);
|
||||
|
||||
set_flag(profile, "$improved missile avoidance for fightercraft:", AI::Profile_Flags::Improved_missile_avoidance);
|
||||
|
||||
set_flag(profile, "$friendly ships use AI profile countermeasure chance:", AI::Profile_Flags::Friendlies_use_countermeasure_firechance);
|
||||
|
||||
set_flag(profile, "$improved subsystem attack pathing:", AI::Profile_Flags::Improved_subsystem_attack_pathing);
|
||||
|
||||
// if we've been through once already and are at the same place, force a move
|
||||
if (saved_Mp && (saved_Mp == Mp))
|
||||
{
|
||||
@@ -691,7 +676,7 @@ void ai_profile_t::reset()
|
||||
flags.set(AI::Profile_Flags::Fix_ai_path_order_bug);
|
||||
flags.set(AI::Profile_Flags::Aspect_invulnerability_fix);
|
||||
flags.set(AI::Profile_Flags::Use_actual_primary_range);
|
||||
flags.set(AI::Profile_Flags::fightercraft_nonshielded_ships_can_manage_ets);
|
||||
flags.set(AI::Profile_Flags::nonshielded_ships_can_manage_ets);
|
||||
}
|
||||
// this flag has been enabled ever since 3.7.2
|
||||
if (mod_supports_version(3, 7, 2)) {
|
||||
|
||||
+8
-136
@@ -21,7 +21,6 @@
|
||||
#include "ship/afterburner.h"
|
||||
#include "ship/ship.h"
|
||||
#include "weapon/weapon.h"
|
||||
#include "object/objcollide.h"
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@@ -530,112 +529,6 @@ int ai_big_maybe_follow_subsys_path(int do_dot_check)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ai_new_maybe_reposition_attack_subsys() {
|
||||
|
||||
ai_info* aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
|
||||
|
||||
if (aip->target_objnum < 0 || aip->targeted_subsys == nullptr)
|
||||
return false;
|
||||
|
||||
object* target_objp = &Objects[aip->target_objnum];
|
||||
|
||||
// dont bother with this if you're still far away
|
||||
if (vm_vec_dist(&Pl_objp->pos, &target_objp->pos) > target_objp->radius * 2.0f + 2000.0f)
|
||||
return false;
|
||||
|
||||
vec3d geye, gsubpos;
|
||||
eye* ep;
|
||||
polymodel* pm = model_get(Ship_info[Ships[Pl_objp->instance].ship_info_index].model_num);
|
||||
|
||||
// get world pos of eye (stored in geye)
|
||||
ep = &(pm->view_positions[Ships[Pl_objp->instance].current_viewpoint]);
|
||||
model_find_world_point(&geye, &ep->pnt, pm->id, 0, &Pl_objp->orient, &Pl_objp->pos);
|
||||
|
||||
// get world pos of subsystem
|
||||
vm_vec_unrotate(&gsubpos, &aip->targeted_subsys->system_info->pnt, &En_objp->orient);
|
||||
vm_vec_add2(&gsubpos, &En_objp->pos);
|
||||
|
||||
// you're in sight! shoot it!
|
||||
if (ship_subsystem_in_sight(En_objp, aip->targeted_subsys, &geye, &gsubpos, 0))
|
||||
return false;
|
||||
|
||||
// not in sight, gotta get there
|
||||
vec3d tgt2pl = Pl_objp->pos - target_objp->pos;
|
||||
vec3d tgt2subsys = gsubpos - target_objp->pos;
|
||||
float pl_dist = vm_vec_mag(&tgt2pl);
|
||||
float subsys_dist = vm_vec_mag(&tgt2subsys) * 1.3f; // add a bit to this, we don't want to go directly at the subsys, we want to go above it
|
||||
|
||||
float angle2subsys = vm_vec_delta_ang(&tgt2pl, &tgt2subsys, nullptr);
|
||||
float angle2goal = angle2subsys;
|
||||
if (angle2goal > 1.2f) // let's not try to go more than about 70 degrees around the ship
|
||||
angle2goal = 1.2f;
|
||||
|
||||
// we'll start by travelling along the sphere at our current distance around the ship
|
||||
// remember that goal_pos will be relative to our target_ship (so we can scale in and out easily)
|
||||
vec3d goal_pos, cross;
|
||||
vm_vec_cross(&cross, &tgt2pl, &tgt2subsys);
|
||||
vm_vec_normalize(&cross);
|
||||
vm_vec_negate(&cross);
|
||||
vm_rot_point_around_line(&goal_pos, &tgt2pl, angle2goal, &vmd_zero_vector, &cross);
|
||||
|
||||
// then scale in (or out) towards our subsys
|
||||
vec3d world_goal_pos;
|
||||
float new_radius = ((subsys_dist - pl_dist) * (angle2goal / angle2subsys)) + pl_dist;
|
||||
vm_vec_copy_scale(&world_goal_pos, &goal_pos, new_radius / pl_dist);
|
||||
world_goal_pos += target_objp->pos;
|
||||
|
||||
vec3d* good_pos = nullptr;
|
||||
if (pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f)) {
|
||||
// ok best case won't work
|
||||
if (pl_dist > subsys_dist) {
|
||||
//we're farther away than the subsys, let's try at our current distance
|
||||
world_goal_pos = goal_pos + target_objp->pos;
|
||||
if (!pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f))
|
||||
good_pos = &world_goal_pos;
|
||||
}
|
||||
|
||||
// ok, let's start moving it away and see if that's better
|
||||
for (float i = 1.0f; i < 4.0f; i += 1.0f) {
|
||||
if (good_pos)
|
||||
break;
|
||||
|
||||
vm_vec_copy_scale(&world_goal_pos, &goal_pos, (new_radius / pl_dist) * powf(1.3f,i));
|
||||
world_goal_pos += target_objp->pos;
|
||||
if (!pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f))
|
||||
good_pos = &world_goal_pos;
|
||||
}
|
||||
|
||||
// welp this sucks, just run away i guess
|
||||
if (!good_pos) {
|
||||
vm_vec_copy_normalize(&world_goal_pos, &tgt2pl);
|
||||
world_goal_pos *= target_objp->radius + pl_dist;
|
||||
world_goal_pos += target_objp->pos;
|
||||
good_pos = &world_goal_pos;
|
||||
}
|
||||
}
|
||||
else
|
||||
good_pos = &world_goal_pos;
|
||||
|
||||
ai_turn_towards_vector(good_pos, Pl_objp, nullptr, nullptr, 0.0f, 0);
|
||||
float subsys_distance = vm_vec_dist(&Pl_objp->pos, &gsubpos);
|
||||
|
||||
// full speed until you're close, then slow down a bit
|
||||
accelerate_ship(aip, subsys_distance > 200.0f ? 1.0f : subsys_distance / 200.0f);
|
||||
|
||||
if (!(Ships[Pl_objp->instance].flags[Ship::Ship_Flags::Afterburner_locked])) {
|
||||
if (subsys_distance > 300.0f) {
|
||||
if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
|
||||
afterburners_start(Pl_objp);
|
||||
aip->afterburner_stop_time = Missiontime + 2 * F1_0;
|
||||
}
|
||||
}
|
||||
else
|
||||
afterburners_stop(Pl_objp);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// This function is only called from ai_big_chase_attack() when a ship is flying very slowly and
|
||||
// attacking a big ship. The ship should scan for enemy fighter/bombers... if any are close, then
|
||||
// return 1, otherwise return 0;
|
||||
@@ -744,10 +637,7 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
|
||||
}
|
||||
|
||||
// see if Pl_objp needs to reposition to get a good shot at subsystem which is being attacked
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing]) {
|
||||
if (ai_new_maybe_reposition_attack_subsys())
|
||||
return;
|
||||
} else if ( ai_big_maybe_follow_subsys_path() ) {
|
||||
if ( ai_big_maybe_follow_subsys_path() ) {
|
||||
if ((Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && !(aip->ai_flags[AI::AI_Flags::Kamikaze])) {
|
||||
afterburners_stop(Pl_objp);
|
||||
}
|
||||
@@ -867,14 +757,7 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
|
||||
swp = &Ships[Pl_objp->instance].weapons;
|
||||
|
||||
float target_radius = En_objp->radius;
|
||||
|
||||
// normal subsystem attacking uses whether or not its on a path and skips this function entirely to stop "over-eager" firing
|
||||
// since the improved method doesnt use paths, refine the target radius to stop over-eager shooting
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing] && aip->targeted_subsys != nullptr)
|
||||
target_radius = aip->targeted_subsys->system_info->radius;
|
||||
|
||||
if (dot_to_enemy > 0.95f - 0.5f * target_radius/MAX(1.0f, target_radius + dist_to_enemy)) {
|
||||
if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/MAX(1.0f, En_objp->radius + dist_to_enemy)) {
|
||||
aip->time_enemy_in_range += flFrametime;
|
||||
|
||||
// Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
|
||||
@@ -948,7 +831,6 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
} else {
|
||||
swp->burst_counter[current_bank_adjusted] = 0;
|
||||
}
|
||||
swp->burst_seed[current_bank_adjusted] = rand32();
|
||||
}
|
||||
} else {
|
||||
if (swip->burst_shots > swp->burst_counter[current_bank_adjusted]) {
|
||||
@@ -961,7 +843,6 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
} else {
|
||||
swp->burst_counter[current_bank_adjusted] = 0;
|
||||
}
|
||||
swp->burst_seed[current_bank_adjusted] = rand32();
|
||||
}
|
||||
}
|
||||
swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
|
||||
@@ -1158,10 +1039,7 @@ void ai_big_chase()
|
||||
float dist_normal_to_enemy;
|
||||
|
||||
if (vm_vec_mag_squared(&aip->big_attack_surface_normal) > 0.9) {
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing])
|
||||
dist_normal_to_enemy = fl_abs((dist_to_enemy / vm_vec_dot(&vec_to_enemy, &aip->big_attack_surface_normal)));
|
||||
else // I'm fairly sure this retail code VVV meant to use / instead of *
|
||||
dist_normal_to_enemy = fl_abs((dist_to_enemy * vm_vec_dot(&vec_to_enemy, &aip->big_attack_surface_normal)));
|
||||
dist_normal_to_enemy = fl_abs((dist_to_enemy * vm_vec_dot(&vec_to_enemy, &aip->big_attack_surface_normal)));
|
||||
} else {
|
||||
// don;t have normal so use a conservative value here
|
||||
dist_normal_to_enemy = 0.3f * dist_to_enemy;
|
||||
@@ -1438,17 +1316,13 @@ void ai_big_strafe_attack()
|
||||
ai_info *aip;
|
||||
vec3d target_pos;
|
||||
vec3d rand_vec;
|
||||
float accel, t;
|
||||
float target_dist = 0.0f;
|
||||
float target_dot = 0.0f;
|
||||
float target_dist, target_dot, accel, t;
|
||||
|
||||
aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
|
||||
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing]) {
|
||||
if (ai_new_maybe_reposition_attack_subsys())
|
||||
return;
|
||||
} else if (ai_big_maybe_follow_subsys_path(0))
|
||||
if ( ai_big_maybe_follow_subsys_path(0) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
|
||||
if ( ai_big_strafe_maybe_retreat(&target_pos) )
|
||||
@@ -1565,11 +1439,9 @@ void ai_big_strafe_glide_attack()
|
||||
flight_dot_to_enemy = vm_vec_dot(&target_objp->pos, &norm_vel_vec); //Angle between flight path and target ship
|
||||
|
||||
//If we're following a path, then we shouldn't be doing glide strafe
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Improved_subsystem_attack_pathing]) {
|
||||
if (ai_new_maybe_reposition_attack_subsys())
|
||||
return;
|
||||
} else if (ai_big_maybe_follow_subsys_path(0))
|
||||
if ( ai_big_maybe_follow_subsys_path(0) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Gets a point on the target ship to attack, as well as distance and the angle between the nose of the attacker and that point.
|
||||
ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
|
||||
|
||||
+90
-149
@@ -238,7 +238,7 @@ const char *Skill_level_names(int level, int translate)
|
||||
str = XSTR("Insane", 473);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Unhandled difficulty level of '%d' was passed to Skill_level_names().", level);
|
||||
Int3();
|
||||
}
|
||||
} else {
|
||||
switch( level ) {
|
||||
@@ -258,7 +258,7 @@ const char *Skill_level_names(int level, int translate)
|
||||
str = NOX("Insane");
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("An invalid difficulty level of '%d' was passed to Skill_level_names().", level);
|
||||
Int3();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -944,8 +944,7 @@ float get_skill_stealth_dist_scaler()
|
||||
return 1.3f;
|
||||
|
||||
default:
|
||||
UNREACHABLE("An invalid difficulty level of %d was passed to get_skill_stealth_dist_scaler().", Game_skill_level);
|
||||
|
||||
Int3();
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
@@ -973,7 +972,7 @@ float get_skill_stealth_dot_scaler()
|
||||
return 0.7f;
|
||||
|
||||
default:
|
||||
UNREACHABLE("An invalid difficulty level of %d was passed to get_skill_stealth_dot_scaler().", Game_skill_level);
|
||||
Int3();
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
@@ -1343,10 +1342,8 @@ void ai_turn_towards_vector(vec3d* dest, object* objp, vec3d* slide_vec, vec3d*
|
||||
}
|
||||
|
||||
if (Framerate_independent_turning) {
|
||||
if (IS_MAT_NULL(&pip->ai_desired_orient)) {
|
||||
pip->ai_desired_orient = out_orient;
|
||||
pip->desired_rotvel = vel_out;
|
||||
} // if ai_desired_orient is NOT null, that means a SEXP is trying to move the ship right now, and the AI should defer to it
|
||||
pip->ai_desired_orient = out_orient;
|
||||
pip->desired_rotvel = vel_out;
|
||||
}
|
||||
else {
|
||||
objp->orient = out_orient;
|
||||
@@ -1954,28 +1951,35 @@ float get_wing_lowest_av_ab_speed(object *objp)
|
||||
*/
|
||||
int is_ignore_object_sub(int *ignore_objnum, int *ignore_signature, int objnum)
|
||||
{
|
||||
// Should never happen. I thought I removed this behavior! -- MK, 5/17/98
|
||||
Assertion(((*ignore_objnum <= UNUSED_OBJNUM) || (*ignore_objnum >= 0)), "Unexpected value for ignore_objnum %d. This is a coder error, please report.", *ignore_objnum);
|
||||
// whether this is an invalid OBJNUM or the official UNUSED_OBJNUM, we should just return here.
|
||||
// As a note, if it was set to UNUSED_OBJNUM, it would be trying to ignore a wing and not an object. (at least according to previously written comments)
|
||||
if (*ignore_objnum < 0) {
|
||||
// Not ignoring anything.
|
||||
if (*ignore_objnum == UNUSED_OBJNUM)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// This means it's ignoring an object, not a wing.
|
||||
else if (*ignore_objnum >= 0)
|
||||
{
|
||||
// see if this object became invalid
|
||||
if (Objects[*ignore_objnum].signature != *ignore_signature)
|
||||
{
|
||||
// reset
|
||||
*ignore_objnum = UNUSED_OBJNUM;
|
||||
}
|
||||
// objects and signatures match
|
||||
else if (*ignore_objnum == objnum)
|
||||
{
|
||||
// found it
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// see if this object became invalid
|
||||
if (Objects[*ignore_objnum].signature != *ignore_signature)
|
||||
// Ignoring a wing.
|
||||
else
|
||||
{
|
||||
// reset
|
||||
*ignore_objnum = UNUSED_OBJNUM;
|
||||
Int3(); // Should never happen. I thought I removed this behavior! -- MK, 5/17/98
|
||||
return 0;
|
||||
}
|
||||
// objects and signatures match
|
||||
else if (*ignore_objnum == objnum)
|
||||
{
|
||||
// found it
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
@@ -2342,7 +2346,7 @@ int find_enemy(int objnum, float range, int max_attackers, int ship_info_index)
|
||||
* If issued an order to a ship that's awaiting repair, abort that process.
|
||||
* However, do not abort process for an object that is currently being repaired -- let it finish.
|
||||
*/
|
||||
void ai_set_goal_abort_support_call(object *objp, ai_info *aip)
|
||||
void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
|
||||
{
|
||||
if (aip->ai_flags[AI::AI_Flags::Awaiting_repair]) {
|
||||
object *repair_obj;
|
||||
@@ -2370,36 +2374,32 @@ void force_avoid_player_check(object *objp, ai_info *aip)
|
||||
* If attacked == NULL, then attack any enemy object.
|
||||
* Attack point *rel_pos on object. This is for supporting attacking subsystems.
|
||||
*/
|
||||
void ai_attack_object(object* attacker, object* attacked, int ship_info_index)
|
||||
void ai_attack_object(object *attacker, object *attacked, ship_subsys *ssp, int ship_info_index)
|
||||
{
|
||||
int temp;
|
||||
ai_info* aip;
|
||||
ai_info *aip;
|
||||
|
||||
Assert(attacker != nullptr);
|
||||
Assert(attacker != NULL);
|
||||
Assert(attacker->instance != -1);
|
||||
Assert(Ships[attacker->instance].ai_index != -1);
|
||||
// Bogus! Who tried to get me to attack myself!
|
||||
if (attacker == attacked) {
|
||||
Warning(LOCATION, "%s was told to attack itself! This may be an error in the mission file. If that checks out, please report to a coder!", Ships[attacker->instance].ship_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (attacker == nullptr || attacker->instance == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
aip = &Ai_info[Ships[attacker->instance].ai_index];
|
||||
force_avoid_player_check(attacker, aip);
|
||||
|
||||
aip->ok_to_target_timestamp = timestamp(0); // Guarantee we can target.
|
||||
|
||||
if (attacker == attacked) {
|
||||
Int3(); // Bogus! Who tried to get me to attack myself! Trace out and fix!
|
||||
return;
|
||||
}
|
||||
|
||||
// Only set to chase if a fighter or bomber, otherwise just return.
|
||||
if (!(Ship_info[Ships[attacker->instance].ship_info_index].is_small_ship()) && (attacked != NULL)) {
|
||||
nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
|
||||
}
|
||||
|
||||
// This is how "engage enemy" gets processed
|
||||
if (attacked == nullptr) {
|
||||
if (attacked == NULL) {
|
||||
aip->choose_enemy_timestamp = timestamp(0);
|
||||
// nebula safe
|
||||
set_target_objnum(aip, find_enemy(OBJ_INDEX(attacker), 99999.9f, 4, ship_info_index));
|
||||
@@ -2411,10 +2411,7 @@ void ai_attack_object(object* attacker, object* attacked, int ship_info_index)
|
||||
set_target_objnum(aip, OBJ_INDEX(attacked));
|
||||
}
|
||||
|
||||
// don't abort a support call if you're disabled!
|
||||
if (ship_get_subsystem_strength(&Ships[attacker->instance], SUBSYSTEM_ENGINE) > 0.0f)
|
||||
ai_set_goal_abort_support_call(attacker, aip);
|
||||
|
||||
ai_set_goal_maybe_abort_dock(attacker, aip);
|
||||
aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME); // No dynamic targeting for 7 seconds.
|
||||
|
||||
// Goober5000
|
||||
@@ -2432,9 +2429,13 @@ void ai_attack_object(object* attacker, object* attacked, int ship_info_index)
|
||||
aip->submode = SM_ATTACK; // AL 12-15-97: need to set submode? I got an assert() where submode was bogus
|
||||
aip->submode_start_time = Missiontime; // for AIM_CHASE... it may have been not set correctly here
|
||||
|
||||
set_targeted_subsys(aip, nullptr , -1);
|
||||
if (aip->target_objnum != -1) {
|
||||
Objects[aip->target_objnum].flags.remove(Object::Object_Flags::Protected); // If ship had been protected, unprotect it.
|
||||
if (ssp == NULL) {
|
||||
set_targeted_subsys(aip, NULL, -1);
|
||||
if (aip->target_objnum != -1) {
|
||||
Objects[aip->target_objnum].flags.remove(Object::Object_Flags::Protected); // If ship had been protected, unprotect it.
|
||||
}
|
||||
} else {
|
||||
Int3(); // Not supported yet!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2470,7 +2471,7 @@ void ai_attack_wing(object *attacker, int wingnum)
|
||||
|
||||
set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
|
||||
|
||||
ai_set_goal_abort_support_call(attacker, aip);
|
||||
ai_set_goal_maybe_abort_dock(attacker, aip);
|
||||
aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME); // No dynamic targeting for 7 seconds.
|
||||
}
|
||||
}
|
||||
@@ -3440,7 +3441,7 @@ void ai_form_on_wing(object *objp, object *goal_objp)
|
||||
|
||||
ai_formation_object_recalculate_slotnums(aip->goal_objnum);
|
||||
|
||||
ai_set_goal_abort_support_call(objp, aip);
|
||||
ai_set_goal_maybe_abort_dock(objp, aip);
|
||||
aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4); // Super extra long time until can target another ship.
|
||||
|
||||
}
|
||||
@@ -4603,14 +4604,13 @@ void ai_fly_to_target_position(vec3d* target_pos, bool* pl_done_p=NULL, bool* pl
|
||||
}
|
||||
}
|
||||
|
||||
float dist_to_cover_this_frame = (Pl_objp->phys_info.speed * flFrametime);
|
||||
if ( (dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || dist_to_cover_this_frame > 0.1f ) {
|
||||
if ( (dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) ) {
|
||||
vec3d nearest_point;
|
||||
float r;
|
||||
|
||||
r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, target_pos);
|
||||
|
||||
if ( (dist_to_goal < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius) + dist_to_cover_this_frame))
|
||||
if ( (dist_to_goal < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius) + vm_vec_dist_quick(&Pl_objp->pos, &Pl_objp->last_pos)))
|
||||
|| (((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, target_pos) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius)))))
|
||||
{
|
||||
int treat_as_ship;
|
||||
@@ -5075,38 +5075,17 @@ void evade_weapon()
|
||||
aip->danger_weapon_objnum = -1;
|
||||
return;
|
||||
} else if (dot_from_enemy > 0.7f) {
|
||||
bool should_afterburn = dist < 200.0f;
|
||||
int aburn_time = F1_0 / 2;
|
||||
|
||||
// within 200m bad, within 1.5 seconds good
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Improved_missile_avoidance]) {
|
||||
should_afterburn = dist < weapon_objp->phys_info.speed * 1.5f;
|
||||
aburn_time = F1_0 + F1_0 / 2;
|
||||
}
|
||||
|
||||
if (should_afterburn) {
|
||||
if (dist < 200.0f) {
|
||||
if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
|
||||
if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
|
||||
afterburners_start(Pl_objp);
|
||||
aip->afterburner_stop_time = Missiontime + aburn_time;
|
||||
aip->afterburner_stop_time = Missiontime + F1_0/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fancy (read: effective) dodging
|
||||
// Try to go perpindicular to the incoming missile, and pick the quickest direction to get there
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Improved_missile_avoidance]) {
|
||||
vec3d avoid_point;
|
||||
vm_project_point_onto_plane(&avoid_point, &vec_from_enemy, &Pl_objp->orient.vec.fvec, &vmd_zero_vector);
|
||||
vm_vec_normalize(&avoid_point);
|
||||
if (vm_vec_dot(&Pl_objp->orient.vec.fvec, &vec_from_enemy) > 0.f)
|
||||
vm_vec_negate(&avoid_point);
|
||||
vm_vec_scale_add(&avoid_point, &Pl_objp->pos, &avoid_point, 200.0f);
|
||||
|
||||
turn_towards_point(Pl_objp, &avoid_point, nullptr, 0.0f);
|
||||
}
|
||||
// If we're sort of pointing towards it...
|
||||
else if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
|
||||
if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
|
||||
float rdot;
|
||||
float udot;
|
||||
|
||||
@@ -5469,30 +5448,26 @@ int ai_select_primary_weapon(object *objp, object *other_objp, Weapon::Info_Flag
|
||||
}
|
||||
}
|
||||
|
||||
bool other_is_ship = (other_objp->type == OBJ_SHIP);
|
||||
auto other_is_ship = (other_objp->type == OBJ_SHIP);
|
||||
float enemy_remaining_shield = get_shield_pct(other_objp);
|
||||
|
||||
if ( other_is_ship )
|
||||
if ( other_is_ship && (Ship_info[Ships[other_objp->instance].ship_info_index].is_big_or_huge() ) )
|
||||
{
|
||||
ship_info* other_sip = &Ship_info[Ships[other_objp->instance].ship_info_index];
|
||||
|
||||
if (other_sip->class_type >= 0 && Ship_types[other_sip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only]) {
|
||||
//Check if we have a capital+ weapon on board
|
||||
for (int i = 0; i < swp->num_primary_banks; i++)
|
||||
//Check if we have a capital+ weapon on board
|
||||
for (int i = 0; i < swp->num_primary_banks; i++)
|
||||
{
|
||||
if (swp->primary_bank_weapons[i] >= 0) // Make sure there is a weapon in the bank
|
||||
{
|
||||
if (swp->primary_bank_weapons[i] >= 0) // Make sure there is a weapon in the bank
|
||||
auto wip = &Weapon_info[swp->primary_bank_weapons[i]];
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Capital_plus])
|
||||
{
|
||||
auto wip = &Weapon_info[swp->primary_bank_weapons[i]];
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Capital_plus])
|
||||
// handle shielded big/huge ships (non FS-verse)
|
||||
// only pick capital+ if the target has no shields; or the weapon is an ok shield-breaker anyway
|
||||
if (enemy_remaining_shield <= 0.05f || (wip->shield_factor * 1.33f > wip->armor_factor))
|
||||
{
|
||||
// handle shielded big/huge ships (non FS-verse)
|
||||
// only pick capital+ if the target has no shields; or the weapon is an ok shield-breaker anyway
|
||||
if (enemy_remaining_shield <= 0.05f || (wip->shield_factor * 1.33f > wip->armor_factor))
|
||||
{
|
||||
swp->current_primary_bank = i;
|
||||
nprintf(("AI", "%i: Ship %s selecting weapon %s (capital+) vs target %s\n", Framecount, Ships[objp->instance].ship_name, wip->name, Ships[other_objp->instance].ship_name));
|
||||
return i;
|
||||
}
|
||||
swp->current_primary_bank = i;
|
||||
nprintf(("AI", "%i: Ship %s selecting weapon %s (capital+) vs target %s\n", Framecount, Ships[objp->instance].ship_name, wip->name, Ships[other_objp->instance].ship_name));
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7702,7 +7677,7 @@ int ai_set_attack_subsystem(object *objp, int subnum)
|
||||
|
||||
// -- Done at caller in ai_process_mission_orders -- attacked_objp->flags .add(Object::Object_Flags::Protected);
|
||||
|
||||
ai_set_goal_abort_support_call(objp, aip);
|
||||
ai_set_goal_maybe_abort_dock(objp, aip);
|
||||
aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
|
||||
|
||||
return 1;
|
||||
@@ -7770,7 +7745,7 @@ void ai_set_guard_wing(object *objp, int wingnum)
|
||||
aip = &Ai_info[shipp->ai_index];
|
||||
force_avoid_player_check(objp, aip);
|
||||
|
||||
ai_set_goal_abort_support_call(objp, aip);
|
||||
ai_set_goal_maybe_abort_dock(objp, aip);
|
||||
aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
|
||||
|
||||
// This function is called whenever a guarded ship is destroyed, so this code
|
||||
@@ -7840,7 +7815,7 @@ void ai_set_guard_object(object *objp, object *other_objp)
|
||||
|
||||
ai_set_guard_vec(objp, &Objects[other_objnum]);
|
||||
|
||||
ai_set_goal_abort_support_call(objp, aip);
|
||||
ai_set_goal_maybe_abort_dock(objp, aip);
|
||||
aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
|
||||
}
|
||||
}
|
||||
@@ -7865,7 +7840,7 @@ void update_aspect_lock_information(ai_info *aip, vec3d *vec_to_enemy, float dis
|
||||
object *aiobjp = &Objects[shipp->objnum];
|
||||
|
||||
// AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
|
||||
if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank >= swp->num_secondary_banks) ) {
|
||||
if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -7971,7 +7946,7 @@ int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
|
||||
void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
|
||||
{
|
||||
float subsystem_strength = 0.0f;
|
||||
bool is_big_ship;
|
||||
int is_big_ship;
|
||||
flagset<Weapon::Info_Flags> wif_priority1, wif_priority2;
|
||||
ship_weapon *swp;
|
||||
ship_info *esip;
|
||||
@@ -8006,9 +7981,9 @@ void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
|
||||
}
|
||||
|
||||
if ( esip ) {
|
||||
is_big_ship = esip->class_type >= 0 && Ship_types[esip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only];
|
||||
is_big_ship = esip->is_big_or_huge();
|
||||
} else {
|
||||
is_big_ship = false;
|
||||
is_big_ship=0;
|
||||
}
|
||||
|
||||
if (is_big_ship)
|
||||
@@ -9090,7 +9065,7 @@ void ai_chase()
|
||||
}
|
||||
}
|
||||
|
||||
if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank]) && weapon_target_satisfies_lock_restrictions(swip, En_objp)) {
|
||||
if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank]) && weapon_can_lock_on_ship(swip, En_objp->instance)) {
|
||||
if (current_bank >= 0) {
|
||||
float firing_range;
|
||||
|
||||
@@ -9152,7 +9127,6 @@ void ai_chase()
|
||||
} else {
|
||||
swp->burst_counter[current_bank_adjusted] = 0;
|
||||
}
|
||||
swp->burst_seed[current_bank_adjusted] = rand32();
|
||||
}
|
||||
} else {
|
||||
if (swip->burst_shots > swp->burst_counter[current_bank_adjusted]) {
|
||||
@@ -9165,7 +9139,6 @@ void ai_chase()
|
||||
} else {
|
||||
swp->burst_counter[current_bank_adjusted] = 0;
|
||||
}
|
||||
swp->burst_seed[current_bank_adjusted] = rand32();
|
||||
}
|
||||
}
|
||||
swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
|
||||
@@ -11552,7 +11525,7 @@ void process_subobjects(int objnum)
|
||||
// a ship may get repaired... and it should still try to depart. Since docking bay departures
|
||||
// are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
|
||||
if (aip->mode != AIM_BAY_DEPART) {
|
||||
ai_attack_object(objp, nullptr); // Regardless of current mode, enter attack mode.
|
||||
ai_attack_object(objp, NULL, NULL); // Regardless of current mode, enter attack mode.
|
||||
aip->submode = SM_ATTACK_FOREVER; // Never leave attack submode, don't avoid, evade, etc.
|
||||
aip->submode_start_time = Missiontime;
|
||||
}
|
||||
@@ -12399,28 +12372,19 @@ void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
|
||||
|
||||
weapon_objp = &Objects[aip->nearest_locked_object];
|
||||
|
||||
dist = vm_vec_dist(&objp->pos, &weapon_objp->pos);
|
||||
|
||||
bool in_countermeasure_range = dist < weapon_objp->phys_info.speed * 2.0f;
|
||||
weapon_info *cmeasure = &Weapon_info[shipp->current_cmeasure];
|
||||
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Improved_missile_avoidance])
|
||||
in_countermeasure_range = dist < cmeasure->cm_effective_rad;
|
||||
|
||||
if ( in_countermeasure_range ) {
|
||||
if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
|
||||
|
||||
aip->nearest_locked_distance = dist;
|
||||
// Verify that this object is really homing on us.
|
||||
|
||||
float fire_chance;
|
||||
|
||||
// For ships on player's team, check if modder wants default constant chance to fire or value from specific AI class profile.
|
||||
// For enemies, use value from specific AI class profile (usually increasing chance with higher skill level).
|
||||
if ( (shipp->team == Player_ship->team) && !(aip->ai_profile_flags[AI::Profile_Flags::Friendlies_use_countermeasure_firechance]) ) {
|
||||
// For ships on player's team, have constant, average chance to fire.
|
||||
// For enemies, increasing chance with higher skill level.
|
||||
if (shipp->team == Player_ship->team)
|
||||
fire_chance = The_mission.ai_profile->cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
|
||||
} else {
|
||||
else
|
||||
fire_chance = aip->ai_cmeasure_fire_chance;
|
||||
}
|
||||
|
||||
// Decrease chance to fire at lower ai class (SUSHI: Only if autoscale is on)
|
||||
if (aip->ai_class_autoscale)
|
||||
@@ -12428,23 +12392,14 @@ void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
|
||||
|
||||
float r = frand();
|
||||
if (fire_chance < r) {
|
||||
int fail_delay;
|
||||
// check if modder wants to use custom fail delay, or default
|
||||
if (cmeasure->cmeasure_failure_delay_multiplier_ai >= 0) {
|
||||
// use firewait as delay
|
||||
fail_delay = cmeasure->cmeasure_firewait * cmeasure->cmeasure_failure_delay_multiplier_ai;
|
||||
} else {
|
||||
// use default to wait firwait + additional delay to decrease chance of firing very soon.
|
||||
fail_delay = cmeasure->cmeasure_firewait + (int) (fire_chance*2000);
|
||||
}
|
||||
shipp->cmeasure_fire_stamp = timestamp(fail_delay);
|
||||
shipp->cmeasure_fire_stamp = timestamp(CMEASURE_WAIT + (int) (fire_chance*2000)); // Wait 1/2 second (CMEASURE_WAIT) + additional delay to decrease chance of firing very soon.
|
||||
return;
|
||||
}
|
||||
|
||||
if (weapon_objp->type == OBJ_WEAPON) {
|
||||
if (weapon_objp->instance >= 0) {
|
||||
ship_launch_countermeasure(objp);
|
||||
shipp->cmeasure_fire_stamp = timestamp(cmeasure->cmeasure_firewait * cmeasure->cmeasure_sucess_delay_multiplier_ai);
|
||||
shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -12619,21 +12574,9 @@ void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
|
||||
}
|
||||
|
||||
if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].is_homing())) {
|
||||
|
||||
vec3d v2m;
|
||||
float dist = vm_vec_normalized_dir(&v2m, &objp->pos, &missile_objp->pos);
|
||||
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Improved_missile_avoidance]
|
||||
&& vm_vec_dot(&v2m, &missile_objp->orient.vec.fvec) < 0.5f ) {
|
||||
// don't bother if the missile isn't actually coming towards us
|
||||
aip->nearest_locked_object = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// evade missiles 4 seconds away normally, 2 seconds away with the flag (evading too early is a thing!)
|
||||
float evade_dist_scalar = The_mission.ai_profile->flags[AI::Profile_Flags::Improved_missile_avoidance] ? 2.0f : 4.0f;
|
||||
float evade_distance = evade_dist_scalar * vm_vec_mag_quick(&missile_objp->phys_info.vel);
|
||||
if (dist < evade_distance) {
|
||||
float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
|
||||
float dist2 = 4.0f * vm_vec_mag_quick(&missile_objp->phys_info.vel);
|
||||
if (dist < dist2) {
|
||||
switch (aip->mode) {
|
||||
// If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
|
||||
case AIM_STRAFE:
|
||||
@@ -12647,11 +12590,9 @@ void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
|
||||
case AIM_CHASE:
|
||||
// Don't always go into evade weapon mode. Usually, a countermeasure gets launched.
|
||||
// If low on countermeasures, more likely to try to evade. If 8+, never evade due to low cmeasures.
|
||||
// Asteroth - If Improved_missile_avoidance, always evade!! Don't assume anything else will save you
|
||||
if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) ||
|
||||
(objp->phys_info.speed < 40.0f) ||
|
||||
(frand() < 1.0f - (float) shipp->cmeasure_count/8.0f ||
|
||||
The_mission.ai_profile->flags[AI::Profile_Flags::Improved_missile_avoidance])) {
|
||||
(frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
|
||||
if (aip->submode != SM_ATTACK_FOREVER) { // SM_ATTACK_FOREVER means engines blown.
|
||||
aip->submode = SM_EVADE_WEAPON;
|
||||
aip->submode_start_time = Missiontime;
|
||||
|
||||
+5
-5
@@ -2147,14 +2147,14 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
|
||||
|
||||
// Mike -- debug code!
|
||||
// If a ship has a subobject on it, attack that instead of the main ship!
|
||||
ai_attack_object( objp, other_obj);
|
||||
ai_attack_object( objp, other_obj, NULL);
|
||||
break;
|
||||
|
||||
case AI_GOAL_CHASE_WEAPON:
|
||||
Assert( Weapons[current_goal->target_instance].objnum >= 0 );
|
||||
other_obj = &Objects[Weapons[current_goal->target_instance].objnum];
|
||||
Assert( other_obj->signature == current_goal->target_signature );
|
||||
ai_attack_object( objp, other_obj);
|
||||
ai_attack_object( objp, other_obj, NULL );
|
||||
break;
|
||||
|
||||
case AI_GOAL_GUARD:
|
||||
@@ -2279,7 +2279,7 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
|
||||
shipnum = ship_name_lookup( current_goal->target_name );
|
||||
Assert( shipnum >= 0 );
|
||||
other_obj = &Objects[Ships[shipnum].objnum];
|
||||
ai_attack_object( objp, other_obj);
|
||||
ai_attack_object( objp, other_obj, NULL);
|
||||
ai_set_attack_subsystem( objp, current_goal->ai_submode ); // submode stored the subsystem type
|
||||
if (current_goal->ai_mode != AI_GOAL_DESTROY_SUBSYSTEM) {
|
||||
if (aip->target_objnum != -1) {
|
||||
@@ -2301,14 +2301,14 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
|
||||
break;
|
||||
|
||||
case AI_GOAL_CHASE_ANY:
|
||||
ai_attack_object( objp, nullptr);
|
||||
ai_attack_object( objp, NULL, NULL );
|
||||
break;
|
||||
|
||||
// chase-ship-class is chase-any but restricted to a subset of ships
|
||||
case AI_GOAL_CHASE_SHIP_CLASS:
|
||||
shipnum = ship_info_lookup( current_goal->target_name );
|
||||
Assertion( shipnum >= 0, "The target of AI_GOAL_CHASE_SHIP_CLASS must refer to a valid ship class!" );
|
||||
ai_attack_object( objp, nullptr, shipnum );
|
||||
ai_attack_object( objp, NULL, NULL, shipnum );
|
||||
break;
|
||||
|
||||
case AI_GOAL_WARP: {
|
||||
|
||||
+175
-125
@@ -87,18 +87,6 @@ typedef struct eval_enemy_obj_struct {
|
||||
int nearest_objnum;
|
||||
} eval_enemy_obj_struct;
|
||||
|
||||
// the current world orientation of the turret matrix, corresponding to its fvec and uvec defined in the model
|
||||
// is NOT affected by the turret's current aiming
|
||||
void turret_instance_find_world_orient(matrix* out_mat, int model_instance_num, int submodel_num, const matrix* objorient)
|
||||
{
|
||||
auto pmi = model_get_instance(model_instance_num);
|
||||
auto pm = model_get(pmi->model_num);
|
||||
vec3d fvec, uvec;
|
||||
model_instance_find_world_dir(&fvec, &pm->submodel[submodel_num].frame_of_reference.vec.fvec, pm, pmi, pm->submodel[submodel_num].parent, objorient);
|
||||
model_instance_find_world_dir(&uvec, &pm->submodel[submodel_num].frame_of_reference.vec.uvec, pm, pmi, pm->submodel[submodel_num].parent, objorient);
|
||||
vm_vector_2_matrix_norm(out_mat, &fvec, &uvec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is object in turret field of view?
|
||||
*
|
||||
@@ -110,51 +98,117 @@ void turret_instance_find_world_orient(matrix* out_mat, int model_instance_num,
|
||||
*
|
||||
* @return 1 if objp is in fov of the specified turret. Otherwise return 0.
|
||||
*/
|
||||
bool object_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, float dist)
|
||||
int object_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, float dist)
|
||||
{
|
||||
vec3d v2e;
|
||||
float size_mod;
|
||||
bool in_fov = false;
|
||||
bool in_fov;
|
||||
|
||||
if (ss->flags[Ship::Subsystem_Flags::FOV_edge_check]) {
|
||||
int model_num;
|
||||
switch (objp->type) {
|
||||
case OBJ_SHIP:
|
||||
model_num = Ship_info[Ships[objp->instance].ship_info_index].model_num;
|
||||
break;
|
||||
case OBJ_ASTEROID:
|
||||
model_num = Asteroid_info[Asteroids[objp->instance].asteroid_type].model_num[Asteroids[objp->instance].asteroid_subtype];
|
||||
break;
|
||||
default:
|
||||
vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
|
||||
size_mod = objp->radius / (dist + objp->radius);
|
||||
vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
|
||||
size_mod = objp->radius / (dist + objp->radius);
|
||||
|
||||
in_fov = turret_fov_test(ss, tvec, &v2e, size_mod);
|
||||
in_fov = turret_fov_test(ss, tvec, &v2e, size_mod);
|
||||
|
||||
return in_fov;
|
||||
}
|
||||
|
||||
auto pm = model_get(model_num);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
vec3d bbox_point;
|
||||
vm_vec_unrotate(&bbox_point, &pm->bounding_box[i], &objp->orient);
|
||||
bbox_point += objp->pos;
|
||||
|
||||
vm_vec_normalized_dir(&v2e, &bbox_point, tpos);
|
||||
in_fov = turret_fov_test(ss, tvec, &v2e, -0.2f);
|
||||
|
||||
if (in_fov)
|
||||
return true;
|
||||
}
|
||||
|
||||
} else {
|
||||
vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
|
||||
size_mod = objp->radius / (dist + objp->radius);
|
||||
|
||||
in_fov = turret_fov_test(ss, tvec, &v2e, size_mod);
|
||||
if ( in_fov ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return in_fov;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_object_radius_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, vec3d *v2e, vec3d *predicted_pos, float distance)
|
||||
{
|
||||
float target_dist = distance;
|
||||
if (distance == 0.0f)
|
||||
target_dist = vm_vec_dist(predicted_pos,tpos);
|
||||
|
||||
if (object_in_turret_fov(objp, ss, tvec, tpos, target_dist + objp->radius)) {
|
||||
// so the targeted spot in not in fov but the enemy + radius is
|
||||
// lets align the darn gun and try shooting there
|
||||
vec3d temp_vec;
|
||||
float multiplier = 0;
|
||||
model_subsystem *tp = ss->system_info;
|
||||
|
||||
// project v2e_from_turret to turret normal
|
||||
// substract resultant vector from the temp_vec (v2e_from_turret)
|
||||
// adjust z component as necessary
|
||||
// calculate multiplier for the resultant vector
|
||||
// use multiplier and the z component and compose a new vector
|
||||
float dot = vm_vec_dot(v2e, tvec);
|
||||
|
||||
vm_vec_scale_add(&temp_vec, v2e, tvec, -dot);
|
||||
|
||||
if (IS_VEC_NULL_SQ_SAFE(&temp_vec)) {
|
||||
// return false, target is perfectly aligned over or below the turret
|
||||
// safe bet is to allow turret to reacquire better target
|
||||
return false;
|
||||
}
|
||||
|
||||
// normalize the vec, it needs to be done regardless
|
||||
vm_vec_normalize(&temp_vec);
|
||||
bool fix_elevation = false;
|
||||
bool fix_base_rot = false;
|
||||
|
||||
if (dot < tp->turret_fov) {
|
||||
dot = tp->turret_fov;
|
||||
fix_elevation = true;
|
||||
}
|
||||
if (dot > tp->turret_max_fov) {
|
||||
dot = tp->turret_max_fov;
|
||||
fix_elevation = true;
|
||||
}
|
||||
|
||||
if (tp->flags[Model::Subsystem_Flags::Turret_alt_math]) {
|
||||
vec3d temp_vec2;
|
||||
vm_vec_rotate(&temp_vec2, &temp_vec, &ss->world_to_turret_matrix);
|
||||
|
||||
// now in turrets frame of reference
|
||||
// check if math is actually possible
|
||||
if (!((temp_vec2.xyz.x == 0) && (temp_vec2.xyz.y == 0))) {
|
||||
float temp_z = temp_vec2.xyz.z;
|
||||
temp_vec2.xyz.z = 0.0f;
|
||||
// make sure null vecs wont happen
|
||||
if (!IS_VEC_NULL_SQ_SAFE(&temp_vec2)) {
|
||||
vm_vec_normalize(&temp_vec2);
|
||||
// only do this if it actually is required
|
||||
if (-temp_vec2.xyz.y < tp->turret_y_fov) {
|
||||
float check_pos = 1;
|
||||
|
||||
fix_base_rot = true;
|
||||
temp_vec2.xyz.y = -tp->turret_y_fov;
|
||||
if (temp_vec2.xyz.x < 0)
|
||||
check_pos = -1;
|
||||
temp_vec2.xyz.x = check_pos * sqrtf(1 - (temp_vec2.xyz.y*temp_vec2.xyz.y));
|
||||
|
||||
// restore z component
|
||||
float scaler = sqrtf(1 - (temp_z*temp_z));
|
||||
vm_vec_scale(&temp_vec2,scaler);
|
||||
temp_vec2.xyz.z = temp_z;
|
||||
// back to world frame
|
||||
vm_vec_unrotate(&temp_vec, &temp_vec2, &ss->world_to_turret_matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fix_elevation || fix_base_rot) {
|
||||
if (fix_elevation) {
|
||||
multiplier = sqrtf(1 - (dot*dot));
|
||||
// keep the temp_vec scaled with the tweaked vector
|
||||
vm_vec_scale(&temp_vec, multiplier);
|
||||
}
|
||||
vm_vec_scale_add(v2e, &temp_vec, tvec, dot);
|
||||
// and we are done with v2e...
|
||||
vm_vec_scale_add(predicted_pos, tpos, v2e, target_dist);
|
||||
// and we are done with predicted position
|
||||
return true;
|
||||
} else {
|
||||
mprintf(("Warning: Function 'is_object_radius_in_turret_fov' was called\nwithout need to fix turret alignments\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// outside of the expanded radii, unable to align, return false
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -501,7 +555,6 @@ void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
|
||||
|
||||
if ( objp->type == OBJ_SHIP ) {
|
||||
shipp = &Ships[objp->instance];
|
||||
ship_info* sip = &Ship_info[shipp->ship_info_index];
|
||||
|
||||
// check on enemy team
|
||||
if ( !iff_matches_mask(shipp->team, eeo->enemy_team_mask) ) {
|
||||
@@ -541,18 +594,16 @@ void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
|
||||
}
|
||||
}
|
||||
|
||||
// don't shoot at big ships with huge weapons unless they have the flag
|
||||
// don't shoot at small ships if we shouldn't
|
||||
if (eeo->eeo_flags & EEOF_BIG_ONLY) {
|
||||
if (sip->class_type == -1 || !(Ship_types[sip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only])) {
|
||||
if (!(Ship_info[shipp->ship_info_index].is_big_or_huge())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// ^ Note the difference between these checks
|
||||
// V "small only" EXCLUDES only big ships, "huge" INCLUDES only big ships
|
||||
|
||||
// don't shoot at ships ignored by small weapons if this is a small weapon
|
||||
// don't shoot at big ships if we shouldn't
|
||||
if (eeo->eeo_flags & EEOF_SMALL_ONLY) {
|
||||
if (sip->class_type >= 0 && (Ship_types[sip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only])) {
|
||||
if ((Ship_info[shipp->ship_info_index].is_big_or_huge())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1128,18 +1179,23 @@ int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vec3d *tpos, vec3d
|
||||
|
||||
if (!skip) {
|
||||
if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
|
||||
ship_info* esip = &Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index];
|
||||
// check for huge weapon and huge ship
|
||||
if ( !big_only_flag || (esip->class_type >= 0 && Ship_types[esip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only]) ) {
|
||||
if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].is_big_or_huge()) ) {
|
||||
// check for tagged only and tagged ship
|
||||
if ( tagged_only_flag && ship_is_tagged(&Objects[aip->target_objnum]) ) {
|
||||
// select new target if aip->target_objnum is out of field of view
|
||||
vec3d v2e;
|
||||
float dist;
|
||||
bool in_fov;
|
||||
vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
|
||||
dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
|
||||
|
||||
in_fov = turret_fov_test(turret_subsys, tvec, &v2e);
|
||||
|
||||
if (turret_subsys->flags[Ship::Subsystem_Flags::FOV_edge_check]) {
|
||||
if (in_fov == false)
|
||||
if (object_in_turret_fov(&Objects[aip->target_objnum], turret_subsys, tvec, tpos, dist + Objects[aip->target_objnum].radius))
|
||||
in_fov = true;
|
||||
}
|
||||
// MODIFY FOR ATTACKING BIG SHIP
|
||||
// dot += (0.5f * Objects[aip->target_objnum].radius / dist);
|
||||
if (in_fov) {
|
||||
@@ -1174,22 +1230,20 @@ int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vec3d *tpos, vec3d
|
||||
* of the turret.
|
||||
*
|
||||
* @param objp Pointer to object
|
||||
* @param tp Turret model system on that object
|
||||
* @param tp Turrent model system on that object
|
||||
* @param gpos [Output] Global absolute position of gun firing point
|
||||
* @param gvec [Output] Global vector
|
||||
*
|
||||
* @note The gun normal is the unrotated gun normal, (the center of the FOV cone), not
|
||||
* the actual gun normal given using the current turret heading. But it _is_ rotated into the model's orientation
|
||||
* in global space.
|
||||
* @note2 Because of this, both single-part and multi-part turrets are treated the same way; no need to find the multi-part's gun submodel.
|
||||
*/
|
||||
void ship_get_global_turret_info(const object *objp, const model_subsystem *tp, vec3d *gpos, vec3d *gvec)
|
||||
void ship_get_global_turret_info(object *objp, model_subsystem *tp, vec3d *gpos, vec3d *gvec)
|
||||
{
|
||||
auto model_instance_num = Ships[objp->instance].model_instance_num;
|
||||
if (gpos)
|
||||
model_instance_find_world_point(gpos, &vmd_zero_vector, model_instance_num, tp->subobj_num, &objp->orient, &objp->pos);
|
||||
if (gvec)
|
||||
model_instance_find_world_dir(gvec, &tp->turret_norm, model_instance_num, tp->subobj_num, &objp->orient, true);
|
||||
// vm_vec_unrotate(gpos, &tp->turret_avg_firing_point, &objp->orient);
|
||||
vm_vec_unrotate(gpos, &tp->pnt, &objp->orient);
|
||||
vm_vec_add2(gpos, &objp->pos);
|
||||
vm_vec_unrotate(gvec, &tp->turret_norm, &objp->orient);
|
||||
}
|
||||
|
||||
void turret_ai_update_aim(ai_info *aip, object *En_Objp, ship_subsys *ss);
|
||||
@@ -1249,8 +1303,7 @@ void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vec3d *gpos
|
||||
vm_vec_add2(&enemy_point, &ssp->last_aim_enemy_pos);
|
||||
} else {
|
||||
if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].is_big_or_huge())) {
|
||||
// the turret norm here is from the perspective of the base submodel, not the gun submodel
|
||||
model_instance_find_world_dir(&turret_norm, &tp->turret_norm, pm, pmi, pm->submodel[tp->subobj_num].parent, &objp->orient);
|
||||
vm_vec_unrotate(&turret_norm, &tp->turret_norm, &objp->orient);
|
||||
ai_big_pick_attack_point_turret(lep, ssp, &tmp_pos, &turret_norm, &enemy_point, MIN(wip->max_speed * wip->lifetime, wip->weapon_range), tp->turret_fov);
|
||||
}
|
||||
else {
|
||||
@@ -1381,13 +1434,18 @@ int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep,
|
||||
|
||||
in_fov = turret_fov_test(ss, gvec, &v2e);
|
||||
|
||||
if (ss->flags[Ship::Subsystem_Flags::FOV_edge_check]) {
|
||||
if (in_fov == false)
|
||||
in_fov = is_object_radius_in_turret_fov(&Objects[ss->turret_enemy_objnum], ss, gvec, &gun_pos, &v2e, predicted_enemy_pos, 0.0f);
|
||||
}
|
||||
|
||||
if (in_fov) {
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos);
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, tp, predicted_enemy_pos);
|
||||
} else if ((tp->flags[Model::Subsystem_Flags::Turret_reset_idle]) &&(timestamp_elapsed(ss->rotation_timestamp))) {
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos, true);
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, tp, predicted_enemy_pos, true);
|
||||
}
|
||||
} else if ((ss->system_info->flags[Model::Subsystem_Flags::Turret_reset_idle]) && (timestamp_elapsed(ss->rotation_timestamp))) {
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos, true);
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, ss->system_info, predicted_enemy_pos, true);
|
||||
}
|
||||
|
||||
// by default "ret_val" should be set to 1 for multi-part turrets, and 0 for single-part turrets
|
||||
@@ -1419,7 +1477,7 @@ float aifft_compute_turret_dot(object *objp, object *enemy_objp, vec3d *abs_gunp
|
||||
if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
|
||||
vec3d turret_norm;
|
||||
|
||||
model_instance_find_world_dir(&turret_norm, &turret_subsysp->system_info->turret_norm, Ships[objp->instance].model_instance_num, turret_subsysp->system_info->subobj_num, &objp->orient, true);
|
||||
vm_vec_unrotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
|
||||
float dot_return = vm_vec_dot(&turret_norm, &vector_out);
|
||||
|
||||
if (Ai_info[Ships[objp->instance].ai_index].ai_profile_flags[AI::Profile_Flags::Smart_subsystem_targeting_for_turrets]) {
|
||||
@@ -1489,7 +1547,8 @@ ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *en
|
||||
|
||||
// Compute absolute gun position.
|
||||
vec3d abs_gun_pos;
|
||||
ship_get_global_turret_info(objp, ssp->system_info, &abs_gun_pos, nullptr);
|
||||
vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
|
||||
vm_vec_add2(&abs_gun_pos, &objp->pos);
|
||||
|
||||
// Only pick a turret to attack on large ships.
|
||||
if (!esip->is_big_or_huge())
|
||||
@@ -1615,10 +1674,8 @@ void turret_set_next_fire_timestamp(int weapon_num, weapon_info *wip, ship_subsy
|
||||
}
|
||||
if ((wip->burst_shots > 0) && (wip->burst_flags[Weapon::Burst_Flags::Random_length])) {
|
||||
turret->weapons.burst_counter[weapon_num] = (myrand() % wip->burst_shots);
|
||||
turret->weapons.burst_seed[weapon_num] = rand32();
|
||||
} else {
|
||||
turret->weapons.burst_counter[weapon_num] = 0;
|
||||
turret->weapons.burst_seed[weapon_num] = rand32();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1712,7 +1769,7 @@ void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
|
||||
/**
|
||||
* Fire a weapon from a turret
|
||||
*/
|
||||
bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum, vec3d *turret_pos, vec3d *firing_vec, vec3d *predicted_pos = nullptr, float flak_range_override = 100.0f)
|
||||
bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum, vec3d *turret_pos, vec3d *turret_fvec, vec3d *predicted_pos = NULL, float flak_range_override = 100.0f)
|
||||
{
|
||||
matrix turret_orient;
|
||||
int weapon_objnum;
|
||||
@@ -1777,16 +1834,10 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
#endif
|
||||
|
||||
if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, wip, -1, turret)) {
|
||||
ship_weapon* swp = &turret->weapons;
|
||||
turret->turret_last_fire_direction = *firing_vec;
|
||||
|
||||
if (turret->turret_inaccuracy > 0.0f)
|
||||
vm_vec_random_cone(firing_vec, firing_vec, turret->turret_inaccuracy);
|
||||
|
||||
vm_vector_2_matrix(&turret_orient, firing_vec, nullptr, nullptr);
|
||||
vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
|
||||
turret->turret_last_fire_direction = *turret_fvec;
|
||||
|
||||
// set next fire timestamp for the turret
|
||||
int old_burst_seed = swp->burst_seed[weapon_num];
|
||||
if (last_shot_in_salvo)
|
||||
turret_set_next_fire_timestamp(weapon_num, wip, turret, parent_aip);
|
||||
|
||||
@@ -1810,7 +1861,6 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
else
|
||||
fire_info.target_subsys = NULL;
|
||||
fire_info.turret = turret;
|
||||
fire_info.burst_seed = old_burst_seed;
|
||||
fire_info.fire_method = BFM_TURRET_FIRED;
|
||||
|
||||
// fire a beam weapon
|
||||
@@ -1832,6 +1882,7 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
}
|
||||
// don't fire swam, but set up swarm info instead
|
||||
else if ((wip->wi_flags[Weapon::Info_Flags::Swarm]) || (wip->wi_flags[Weapon::Info_Flags::Corkscrew])) {
|
||||
ship_weapon *swp = &turret->weapons;
|
||||
if (swp->current_secondary_bank < 0) {
|
||||
swp->current_secondary_bank = 0;
|
||||
}
|
||||
@@ -1857,6 +1908,8 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
else {
|
||||
for (int i = 0; i < wip->shots; i++) {
|
||||
if (turret->system_info->flags[Model::Subsystem_Flags::Turret_use_ammo]) {
|
||||
ship_weapon *swp;
|
||||
swp = &turret->weapons;
|
||||
int bank_to_fire, num_slots = turret->system_info->turret_num_firing_points;
|
||||
if (wip->subtype == WP_LASER) {
|
||||
int points;
|
||||
@@ -1936,7 +1989,7 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
}
|
||||
// zookeeper - Firepoints should cycle normally between shots,
|
||||
// so we need to get the position info separately for each shot
|
||||
ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, turret_pos, firing_vec, 1, nullptr);
|
||||
ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, turret_pos, turret_fvec, 1, NULL);
|
||||
|
||||
weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, -1, 1, 0,0.0f, turret);
|
||||
weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);
|
||||
@@ -1959,7 +2012,9 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
|
||||
|
||||
// if the gun is a flak gun
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Flak]) {
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Flak]) {
|
||||
// show a muzzle flash
|
||||
flak_muzzle_flash(turret_pos, turret_fvec, &Objects[parent_ship->objnum].phys_info, turret_weapon_class);
|
||||
|
||||
if(predicted_pos != NULL)
|
||||
{
|
||||
@@ -1974,10 +2029,9 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
flak_set_range(objp, flak_range_override);
|
||||
}
|
||||
}
|
||||
|
||||
// do mflash if the weapon has it
|
||||
if (wip->muzzle_flash >= 0) {
|
||||
mflash_create(turret_pos, firing_vec, &Objects[parent_ship->objnum].phys_info, wip->muzzle_flash);
|
||||
// otherwise just do mflash if the weapon has it
|
||||
else if (wip->muzzle_flash >= 0) {
|
||||
mflash_create(turret_pos, turret_fvec, &Objects[parent_ship->objnum].phys_info, wip->muzzle_flash);
|
||||
}
|
||||
|
||||
// in multiplayer (and the master), then send a turret fired packet.
|
||||
@@ -2181,8 +2235,6 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
{
|
||||
ss->turret_enemy_objnum = -1;
|
||||
lep = nullptr;
|
||||
ss->flags.remove(Ship::Subsystem_Flags::Forced_target);
|
||||
ss->flags.remove(Ship::Subsystem_Flags::Forced_subsys_target);
|
||||
}
|
||||
|
||||
Assert((shipp->objnum >= 0) && (shipp->objnum < MAX_OBJECTS));
|
||||
@@ -2190,10 +2242,25 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
objp = &Objects[shipp->objnum];
|
||||
Assert(objp->type == OBJ_SHIP);
|
||||
|
||||
// Wanderer - make sure turrets already have all the data
|
||||
if ( !(tp->flags[Model::Subsystem_Flags::Turret_matrix]) )
|
||||
{
|
||||
if (!(tp->turret_gun_sobj == tp->subobj_num))
|
||||
{
|
||||
auto pmi = model_get_instance(shipp->model_instance_num);
|
||||
auto pm = model_get(pmi->model_num);
|
||||
model_make_turret_matrix(pm, pmi, tp);
|
||||
}
|
||||
}
|
||||
|
||||
// Use the turret info for all guns, not one gun in particular.
|
||||
vec3d gvec, gpos;
|
||||
ship_get_global_turret_info(objp, tp, &gpos, &gvec);
|
||||
|
||||
if (tp->flags[Model::Subsystem_Flags::Turret_alt_math]) {
|
||||
vm_matrix_x_matrix( &ss->world_to_turret_matrix, &objp->orient, &tp->turret_matrix );
|
||||
}
|
||||
|
||||
// Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
|
||||
int use_angles = aifft_rotate_turret(objp, shipp, ss, lep, &predicted_enemy_pos, &gvec);
|
||||
|
||||
@@ -2308,15 +2375,13 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
continue;
|
||||
}
|
||||
|
||||
ship_info* esip = lep->type != OBJ_SHIP ? nullptr : &Ship_info[Ships[lep->instance].ship_info_index];
|
||||
|
||||
// If targeted a small ship and have a huge weapon, don't fire. But this shouldn't happen, as a small ship should not get selected.
|
||||
if ( wip->wi_flags[Weapon::Info_Flags::Huge] ) {
|
||||
if ( lep->type != OBJ_SHIP ) {
|
||||
tentative_return = true;
|
||||
continue;
|
||||
}
|
||||
if ( esip->class_type >= 0 && !(Ship_types[esip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only])) {
|
||||
if ( !(Ship_info[Ships[lep->instance].ship_info_index].is_big_or_huge()) ) {
|
||||
tentative_return = true;
|
||||
continue;
|
||||
}
|
||||
@@ -2324,8 +2389,7 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
|
||||
// Similar check for small weapons
|
||||
if ( wip->wi_flags[Weapon::Info_Flags::Small_only] ) {
|
||||
if ( (lep->type == OBJ_SHIP) && esip->class_type >= 0 &&
|
||||
(Ship_types[esip->class_type].flags[Ship::Type_Info_Flags::Targeted_by_huge_Ignored_by_small_only])) {
|
||||
if ( (lep->type == OBJ_SHIP) && Ship_info[Ships[lep->instance].ship_info_index].is_big_or_huge() ) {
|
||||
tentative_return = true;
|
||||
continue;
|
||||
}
|
||||
@@ -2389,10 +2453,8 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
|
||||
//none of our guns can hit the enemy, so find a new enemy
|
||||
if (num_valid == 0) {
|
||||
if (!ss->flags[Ship::Subsystem_Flags::Forced_target]) {
|
||||
ss->turret_enemy_objnum = -1;
|
||||
ss->turret_time_enemy_in_range = 0.0f;
|
||||
}
|
||||
ss->turret_enemy_objnum = -1;
|
||||
ss->turret_time_enemy_in_range = 0.0f;
|
||||
|
||||
// in the original code, we returned where the "tentative return" variable was set,
|
||||
// so return for real now, since we don't have a reason to remain in the function
|
||||
@@ -2400,16 +2462,8 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
return;
|
||||
}
|
||||
|
||||
// if we've got Forced_subsys_target targeted_subsys *really* shouldnt be null but just in case...
|
||||
if (ss->flags[Ship::Subsystem_Flags::Forced_subsys_target] && ss->targeted_subsys != nullptr) {
|
||||
if (ss->targeted_subsys->current_hits <= 0.0f) {
|
||||
ss->flags.remove(Ship::Subsystem_Flags::Forced_subsys_target);
|
||||
ss->flags.remove(Ship::Subsystem_Flags::Forced_target);
|
||||
}
|
||||
}
|
||||
|
||||
// Maybe pick a new enemy, unless targeting has been taken over by scripting
|
||||
if ( turret_should_pick_new_target(ss) && !ss->scripting_target_override && !(ss->flags[Ship::Subsystem_Flags::Forced_target])) {
|
||||
if ( turret_should_pick_new_target(ss) && !ss->scripting_target_override ) {
|
||||
Num_find_turret_enemy++;
|
||||
int objnum = find_turret_enemy(ss, parent_objnum, &gpos, &gvec, ss->turret_enemy_objnum);
|
||||
|
||||
@@ -2682,7 +2736,7 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
{
|
||||
// If nothing is OK to fire (lost track of the target?)
|
||||
// reset the target (so we don't continue to track what we can't hit)
|
||||
if (tp->flags[Model::Subsystem_Flags::Turret_only_target_if_can_fire] && !(ss->flags[Ship::Subsystem_Flags::Forced_target]))
|
||||
if (tp->flags[Model::Subsystem_Flags::Turret_only_target_if_can_fire])
|
||||
{
|
||||
ss->turret_enemy_objnum = -1; // Reset enemy objnum, find a new one next frame.
|
||||
ss->turret_time_enemy_in_range = 0.0f;
|
||||
@@ -2717,7 +2771,7 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!(ss->flags[Ship::Subsystem_Flags::Forced_target]))
|
||||
else
|
||||
{
|
||||
// Lost him!
|
||||
ss->turret_enemy_objnum = -1; // Reset enemy objnum, find a new one next frame.
|
||||
@@ -2740,20 +2794,16 @@ bool turret_adv_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mo
|
||||
model_subsystem *tp = ss->system_info;
|
||||
float dot = vm_vec_dot(v2e, gvec);
|
||||
if (((dot + size_mod) >= tp->turret_fov) && ((dot - size_mod) <= tp->turret_max_fov)) {
|
||||
object* objp = &Objects[ss->parent_objnum];
|
||||
matrix turret_matrix;
|
||||
turret_instance_find_world_orient(&turret_matrix, Ships[objp->instance].model_instance_num, tp->subobj_num, &objp->orient);
|
||||
|
||||
vec3d of_dst;
|
||||
vm_vec_rotate(&of_dst, v2e, &turret_matrix);
|
||||
if ((of_dst.xyz.x == 0) && (of_dst.xyz.z == 0))
|
||||
return true;
|
||||
else {
|
||||
of_dst.xyz.y = 0;
|
||||
vm_vec_rotate( &of_dst, v2e, &ss->world_to_turret_matrix );
|
||||
if ((of_dst.xyz.x == 0) && (of_dst.xyz.y == 0)) {
|
||||
return true;
|
||||
} else {
|
||||
of_dst.xyz.z = 0;
|
||||
if (!IS_VEC_NULL_SQ_SAFE(&of_dst)) {
|
||||
vm_vec_normalize(&of_dst);
|
||||
// now we have 2d vector with lenght of 1 that points at the targets direction after being rotated to turrets FOR
|
||||
if ((of_dst.xyz.z + size_mod) >= tp->turret_base_fov)
|
||||
if ((-of_dst.xyz.y + size_mod) >= tp->turret_y_fov)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2764,7 +2814,7 @@ bool turret_adv_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mo
|
||||
bool turret_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mod)
|
||||
{
|
||||
bool in_fov = false;
|
||||
if (ss->system_info->flags[Model::Subsystem_Flags::Turret_restricted_fov])
|
||||
if (ss->system_info->flags[Model::Subsystem_Flags::Turret_alt_math])
|
||||
in_fov = turret_adv_fov_test(ss, gvec, v2e, size_mod);
|
||||
else
|
||||
in_fov = turret_std_fov_test(ss, gvec, v2e, size_mod);
|
||||
|
||||
+129
-115
@@ -38,6 +38,7 @@
|
||||
#include "ship/ship.h"
|
||||
#include "ship/shipfx.h"
|
||||
#include "ship/shiphit.h"
|
||||
#include "species_defs/species_defs.h"
|
||||
#include "stats/scoring.h"
|
||||
#include "weapon/weapon.h"
|
||||
|
||||
@@ -49,9 +50,9 @@ asteroid_obj Asteroid_objs[MAX_ASTEROID_OBJS]; // array used to store asteroid o
|
||||
asteroid_obj Asteroid_obj_list; // head of linked list of asteroid_obj structs
|
||||
|
||||
// used for randomly generating debris type when there are multiple sizes.
|
||||
const float SMALL_DEBRIS_WEIGHT = 8.0f;
|
||||
const float MEDIUM_DEBRIS_WEIGHT = 4.0f;
|
||||
const float LARGE_DEBRIS_WEIGHT = 1.0f;
|
||||
#define SMALL_DEBRIS_WEIGHT 8
|
||||
#define MEDIUM_DEBRIS_WEIGHT 4
|
||||
#define LARGE_DEBRIS_WEIGHT 1
|
||||
|
||||
int Asteroids_enabled = 1;
|
||||
int Num_asteroids = 0;
|
||||
@@ -256,7 +257,7 @@ object *asteroid_create(asteroid_field *asfieldp, int asteroid_type, int asteroi
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if((asteroid_type < 0) || (asteroid_type >= (int)Asteroid_info.size())) {
|
||||
if((asteroid_type < 0) || (asteroid_type >= (((int)Species_info.size() + 1) * NUM_DEBRIS_SIZES))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -487,6 +488,31 @@ static void asteroid_load(int asteroid_info_index, int asteroid_subtype)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a weight that depends on asteroid size.
|
||||
*
|
||||
* The weight is then used to determine the frequencty of different sizes of ship debris
|
||||
*/
|
||||
static int get_debris_weight(int ship_debris_index)
|
||||
{
|
||||
int size = ship_debris_index % NUM_DEBRIS_SIZES;
|
||||
switch (size)
|
||||
{
|
||||
case ASTEROID_TYPE_SMALL:
|
||||
return SMALL_DEBRIS_WEIGHT;
|
||||
|
||||
case ASTEROID_TYPE_MEDIUM:
|
||||
return MEDIUM_DEBRIS_WEIGHT;
|
||||
|
||||
case ASTEROID_TYPE_LARGE:
|
||||
return LARGE_DEBRIS_WEIGHT;
|
||||
|
||||
default:
|
||||
Int3();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all the asteroids for the mission
|
||||
*/
|
||||
@@ -499,11 +525,11 @@ void asteroid_create_all()
|
||||
// choose next type from table ship_debris_odds_table by rand()%max_weighted_range,
|
||||
// the threshold *below* which the debris type is selected.
|
||||
struct {
|
||||
float random_threshold;
|
||||
int random_threshold;
|
||||
int debris_type;
|
||||
} ship_debris_odds_table[MAX_ACTIVE_DEBRIS_TYPES];
|
||||
|
||||
float max_weighted_range = 0.0f;
|
||||
int max_weighted_range = 0;
|
||||
|
||||
if (!Asteroids_enabled)
|
||||
return;
|
||||
@@ -526,7 +552,7 @@ void asteroid_create_all()
|
||||
|
||||
// Calculate the odds table
|
||||
for (idx=0; idx<num_debris_types; idx++) {
|
||||
float debris_weight = Asteroid_info[Asteroid_field.field_debris_type[idx]].spawn_weight;
|
||||
int debris_weight = get_debris_weight(Asteroid_field.field_debris_type[idx]);
|
||||
ship_debris_odds_table[idx].random_threshold = max_weighted_range + debris_weight;
|
||||
ship_debris_odds_table[idx].debris_type = Asteroid_field.field_debris_type[idx];
|
||||
max_weighted_range += debris_weight;
|
||||
@@ -573,7 +599,7 @@ void asteroid_create_all()
|
||||
} else {
|
||||
Assert(num_debris_types > 0);
|
||||
|
||||
float rand_choice = frand() * max_weighted_range;
|
||||
int rand_choice = rand() % max_weighted_range;
|
||||
|
||||
for (idx=0; idx<MAX_ACTIVE_DEBRIS_TYPES; idx++) {
|
||||
// for ship debris, choose type according to odds table
|
||||
@@ -1019,13 +1045,13 @@ int asteroid_check_collision(object *pasteroid, object *other_obj, vec3d *hitpos
|
||||
smi->collision_checked = false;
|
||||
|
||||
// set angles for last frame (need to set to prev to get p0)
|
||||
matrix copy_matrix = smi->canonical_orient;
|
||||
angles copy_angles = smi->angs;
|
||||
|
||||
// find the start and end positions of the sphere in submodel RF
|
||||
smi->canonical_orient = smi->canonical_prev_orient;
|
||||
smi->angs = smi->prev_angs;
|
||||
world_find_model_instance_point(&p0, &light_obj->last_pos, pm, pmi, submodel, &heavy_obj->last_orient, &heavy_obj->last_pos);
|
||||
|
||||
smi->canonical_orient = copy_matrix;
|
||||
smi->angs = copy_angles;
|
||||
world_find_model_instance_point(&p1, &light_obj->pos, pm, pmi, submodel, &heavy_obj->orient, &heavy_obj->pos);
|
||||
|
||||
mc.p0 = &p0;
|
||||
@@ -1043,7 +1069,7 @@ int asteroid_check_collision(object *pasteroid, object *other_obj, vec3d *hitpos
|
||||
|
||||
// set up asteroid_hit_info for rotating submodel
|
||||
if (asteroid_hit_info->edge_hit == 0) {
|
||||
model_instance_find_world_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
|
||||
model_instance_find_obj_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
|
||||
}
|
||||
|
||||
// find position in submodel RF of light object at collison
|
||||
@@ -1075,7 +1101,7 @@ int asteroid_check_collision(object *pasteroid, object *other_obj, vec3d *hitpos
|
||||
|
||||
// get collision normal if not edge hit
|
||||
if (asteroid_hit_info->edge_hit == 0) {
|
||||
model_instance_find_world_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
|
||||
model_instance_find_obj_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
|
||||
}
|
||||
|
||||
// find position in submodel RF of light object at collison
|
||||
@@ -1762,7 +1788,7 @@ static void asteroid_parse_section(asteroid_info *asip)
|
||||
}
|
||||
|
||||
required_string("$Detail distance:");
|
||||
asip->num_detail_levels = (int)stuff_int_list(asip->detail_distance, MAX_ASTEROID_DETAIL_LEVELS, RAW_INTEGER_TYPE);
|
||||
asip->num_detail_levels = stuff_int_list(asip->detail_distance, MAX_ASTEROID_DETAIL_LEVELS, RAW_INTEGER_TYPE);
|
||||
|
||||
required_string("$Max Speed:");
|
||||
stuff_float(&asip->max_speed);
|
||||
@@ -1776,7 +1802,7 @@ static void asteroid_parse_section(asteroid_info *asip)
|
||||
|
||||
if(optional_string("$Explosion Animations:")){
|
||||
int temp[MAX_FIREBALL_TYPES];
|
||||
auto parsed_ints = stuff_int_list(temp, MAX_FIREBALL_TYPES, RAW_INTEGER_TYPE);
|
||||
int parsed_ints = stuff_int_list(temp, MAX_FIREBALL_TYPES, RAW_INTEGER_TYPE);
|
||||
asip->explosion_bitmap_anims.clear();
|
||||
asip->explosion_bitmap_anims.insert(asip->explosion_bitmap_anims.begin(), temp, temp+parsed_ints);
|
||||
}
|
||||
@@ -1823,81 +1849,20 @@ static void asteroid_parse_section(asteroid_info *asip)
|
||||
} else
|
||||
Warning(LOCATION, "Invalid asteroid reference %i used for $Split in asteroids table, ignoring.", split_type);
|
||||
}
|
||||
|
||||
if (optional_string("$Spawn Weight:")) {
|
||||
stuff_float(&asip->spawn_weight);
|
||||
if (asip->spawn_weight <= 0.0f) {
|
||||
Warning(LOCATION, "Spawn weight for asteroid '%s' must be greater than 0", asip->name);
|
||||
asip->spawn_weight = 1.0f;
|
||||
}
|
||||
} else {
|
||||
switch (Asteroid_info.size() % NUM_DEBRIS_SIZES)
|
||||
{
|
||||
case ASTEROID_TYPE_SMALL:
|
||||
asip->spawn_weight = SMALL_DEBRIS_WEIGHT;
|
||||
break;
|
||||
case ASTEROID_TYPE_MEDIUM:
|
||||
asip->spawn_weight = MEDIUM_DEBRIS_WEIGHT;
|
||||
break;
|
||||
case ASTEROID_TYPE_LARGE:
|
||||
asip->spawn_weight = LARGE_DEBRIS_WEIGHT;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Here be dragons");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// changes the name to "[species] Debris" if it had a name like "[species] debris #"
|
||||
void maybe_change_asteroid_name(asteroid_info* asip) {
|
||||
|
||||
SCP_string name = asip->name;
|
||||
size_t split = std::string::npos;
|
||||
|
||||
for (species_info species : Species_info) {
|
||||
if (name.compare(0, strlen(species.species_name), species.species_name) == 0) {
|
||||
split = strlen(species.species_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (split == std::string::npos)
|
||||
return;
|
||||
|
||||
SCP_string remaining_name = name.substr(split + 1, name.length());
|
||||
|
||||
split = remaining_name.find(' ');
|
||||
|
||||
if (split == std::string::npos)
|
||||
return;
|
||||
|
||||
SCP_string debris = remaining_name.substr(0, split);
|
||||
SCP_string num = remaining_name.substr(split + 1, remaining_name.length());
|
||||
|
||||
if (stricmp(debris.c_str(), "Debris") != 0)
|
||||
return;
|
||||
|
||||
if (num.empty() || std::find_if(num.begin(), num.end(), [](char c) {
|
||||
return !std::isdigit(c, SCP_default_locale);
|
||||
}) != num.end())
|
||||
return;
|
||||
|
||||
// make sure this asteroid would correspond the 'species section' of the old style retail asteroids
|
||||
if (Asteroid_info.size() < NUM_DEBRIS_SIZES + Species_info.size() * NUM_DEBRIS_SIZES && Asteroid_info.size() >= NUM_DEBRIS_SIZES) {
|
||||
int idx = (int)(Asteroid_info.size()) / NUM_DEBRIS_SIZES - 1;
|
||||
strcpy_s(asip->name, Species_info[idx].species_name);
|
||||
strcat(asip->name, " ");
|
||||
strcat(asip->name, XSTR("debris", 348));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Read in data from asteroid.tbl into Asteroid_info[] array.
|
||||
|
||||
After this function is complete, the Asteroid_info[] array
|
||||
will have at least 3 generic asteroids plus whatever else
|
||||
special ones were added in the table. The generic asteroids are
|
||||
a set of small, medium, and large asteroids in that exact order.
|
||||
After this function is completes, the Asteroid_info[] array will
|
||||
contain exactly three asteroids per species plus exactly three
|
||||
generic asteroids. These three asteroids are a set
|
||||
of small, medium, and large asteroids in that exact order.
|
||||
|
||||
If asteroid.tbl contains too many or too few asteroids as per
|
||||
the rules above this function will abort the engine with an
|
||||
error message stating the number expected and it will write
|
||||
to the debug.log how everything was parsed.
|
||||
|
||||
Note that by saying "asteroid" this code is talking about
|
||||
the asteroids and the debris that make up asteroid fields
|
||||
@@ -1909,6 +1874,12 @@ that is being protected.
|
||||
static void asteroid_parse_tbl()
|
||||
{
|
||||
char impact_ani_file[MAX_FILENAME_LEN];
|
||||
|
||||
// How did we get here without having any species defined?
|
||||
Assertion(!Species_info.empty(),
|
||||
"Cannot parse asteroids/debris if there "
|
||||
"are no species for them to belong to."
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1918,6 +1889,8 @@ static void asteroid_parse_tbl()
|
||||
required_string("#Asteroid Types");
|
||||
|
||||
size_t tally = 0;
|
||||
size_t max_asteroids =
|
||||
NUM_DEBRIS_SIZES + Species_info.size() * NUM_DEBRIS_SIZES;
|
||||
|
||||
#ifndef NDEBUG
|
||||
SCP_vector<SCP_string> parsed_asteroids;
|
||||
@@ -1930,41 +1903,82 @@ static void asteroid_parse_tbl()
|
||||
|
||||
asteroid_parse_section(&new_asteroid);
|
||||
|
||||
maybe_change_asteroid_name(&new_asteroid);
|
||||
|
||||
#ifndef NDEBUG
|
||||
SCP_string msg;
|
||||
msg.append("Parsing asteroid: '");
|
||||
msg.append(new_asteroid.name);
|
||||
msg.append("' as a '");
|
||||
msg.append((tally >= NUM_DEBRIS_SIZES) ? "generic" : "special");
|
||||
msg.append("'");
|
||||
switch (tally % NUM_DEBRIS_SIZES) {
|
||||
case ASTEROID_TYPE_SMALL:
|
||||
msg.append(" small\n");
|
||||
break;
|
||||
case ASTEROID_TYPE_MEDIUM:
|
||||
msg.append(" medium\n");
|
||||
break;
|
||||
case ASTEROID_TYPE_LARGE:
|
||||
msg.append(" large\n");
|
||||
break;
|
||||
default:
|
||||
Error(LOCATION, "Get a coder! Math has broken!\n"
|
||||
"Important numbers:\n"
|
||||
"\ttally: " SIZE_T_ARG "\n"
|
||||
"\tNUM_DEBRIS_SIZES: %d\n",
|
||||
tally, NUM_DEBRIS_SIZES
|
||||
);
|
||||
msg.append(" unknown\n");
|
||||
}
|
||||
parsed_asteroids.push_back(msg);
|
||||
size_t species = tally / NUM_DEBRIS_SIZES;
|
||||
if (tally >= max_asteroids)
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
// Bump the warning count in release so they get something
|
||||
// even if the message never gets displayed.
|
||||
Warning(LOCATION, "Ignoring extra asteroid/debris");
|
||||
#else
|
||||
SCP_string msg("Ignoring extra asteroid/debris '");
|
||||
msg.append(new_asteroid.name);
|
||||
msg.append("'\n");
|
||||
Warning(LOCATION, "%s", msg.c_str());
|
||||
parsed_asteroids.push_back(msg);
|
||||
#endif
|
||||
Asteroid_info.push_back(new_asteroid);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
SCP_string msg;
|
||||
msg.append("Parsing asteroid: '");
|
||||
msg.append(new_asteroid.name);
|
||||
msg.append("' as a '");
|
||||
msg.append((species == 0) ? "generic" : Species_info[species - 1].species_name);
|
||||
msg.append("'");
|
||||
switch (tally % NUM_DEBRIS_SIZES) {
|
||||
case ASTEROID_TYPE_SMALL:
|
||||
msg.append(" small\n");
|
||||
break;
|
||||
case ASTEROID_TYPE_MEDIUM:
|
||||
msg.append(" medium\n");
|
||||
break;
|
||||
case ASTEROID_TYPE_LARGE:
|
||||
msg.append(" large\n");
|
||||
break;
|
||||
default:
|
||||
Error(LOCATION, "Get a coder! Math has broken!\n"
|
||||
"Important numbers:\n"
|
||||
"\ttally: " SIZE_T_ARG "\n"
|
||||
"\tNUM_DEBRIS_SIZES: %d\n",
|
||||
tally, NUM_DEBRIS_SIZES
|
||||
);
|
||||
msg.append(" unknown\n");
|
||||
}
|
||||
parsed_asteroids.push_back(msg);
|
||||
#endif
|
||||
Asteroid_info.push_back(new_asteroid);
|
||||
}
|
||||
tally++;
|
||||
}
|
||||
required_string("#End");
|
||||
|
||||
if (tally != max_asteroids)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
mprintf(("Asteroid.tbl as parsed:\n"));
|
||||
for (SCP_vector<SCP_string>::iterator iter = parsed_asteroids.begin();
|
||||
iter != parsed_asteroids.end(); ++iter)
|
||||
{
|
||||
mprintf(("%s\n", iter->c_str()));
|
||||
}
|
||||
#endif
|
||||
Error(LOCATION,
|
||||
"Found " SIZE_T_ARG " asteroids/debris when " SIZE_T_ARG " expected\n\n"
|
||||
"<Number expected> = <Number of species> * %d + %d generic asteroids\n"
|
||||
SIZE_T_ARG " = " SIZE_T_ARG "*%d + %d\n\n"
|
||||
#ifdef NDEBUG
|
||||
"Run a debug build to see a list of all parsed asteroids\n",
|
||||
#else
|
||||
"See the debug.log for a listing of all parsed asteroids\n",
|
||||
#endif
|
||||
tally, max_asteroids,
|
||||
NUM_DEBRIS_SIZES, NUM_DEBRIS_SIZES,
|
||||
max_asteroids, Species_info.size(), NUM_DEBRIS_SIZES, NUM_DEBRIS_SIZES
|
||||
);
|
||||
}
|
||||
|
||||
Asteroid_impact_explosion_ani = -1;
|
||||
required_string("$Impact Explosion:");
|
||||
stuff_string(impact_ani_file, F_NAME, MAX_FILENAME_LEN);
|
||||
|
||||
@@ -73,8 +73,6 @@ public:
|
||||
int model_num[NUM_DEBRIS_POFS];
|
||||
SCP_vector<int> explosion_bitmap_anims;
|
||||
float fireball_radius_multiplier; // the model radius is multiplied by this to determine the fireball size
|
||||
SCP_string display_name; // only used for hud targeting display and for 'ship' asteroids
|
||||
float spawn_weight; // ship asteroids only, relative proportion to spawn compared to other types in its asteroid field
|
||||
|
||||
asteroid_info( )
|
||||
: num_detail_levels( 0 ), max_speed( 0 ), damage_type_idx( 0 ),
|
||||
|
||||
@@ -287,7 +287,7 @@ static int _cfile_chdir(const char *new_dir, const char *cur_dir __UNUSED)
|
||||
const char *colon = strchr(new_dir, ':');
|
||||
|
||||
if (colon) {
|
||||
if (!cfile_chdrive(SCP_tolower(*(colon - 1)) - 'a' + 1, 1))
|
||||
if (!cfile_chdrive(tolower(*(colon - 1)) - 'a' + 1, 1))
|
||||
return 1;
|
||||
|
||||
path = colon + 1;
|
||||
@@ -305,7 +305,7 @@ static int _cfile_chdir(const char *new_dir, const char *cur_dir __UNUSED)
|
||||
status = _chdir(path);
|
||||
if (status != 0) {
|
||||
#ifdef _WIN32
|
||||
cfile_chdrive(SCP_tolower(cur_dir[0]) - 'a' + 1, 1);
|
||||
cfile_chdrive(tolower(cur_dir[0]) - 'a' + 1, 1);
|
||||
#endif /* _WIN32 */
|
||||
return 2;
|
||||
}
|
||||
@@ -866,7 +866,6 @@ static int cfget_cfile_block()
|
||||
cfile->data = nullptr;
|
||||
cfile->fp = nullptr;
|
||||
cfile->type = CFILE_BLOCK_USED;
|
||||
cf_clear_compression_info(cfile);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -922,7 +921,7 @@ int cfclose( CFILE * cfile )
|
||||
} else {
|
||||
// VP do nothing
|
||||
}
|
||||
cf_clear_compression_info(cfile);
|
||||
|
||||
cfile->type = CFILE_BLOCK_UNUSED;
|
||||
return result;
|
||||
}
|
||||
@@ -974,7 +973,7 @@ static CFILE *cf_open_fill_cfblock(const char* source, int line, const char* ori
|
||||
if(pos == -1L)
|
||||
pos = 0;
|
||||
cf_init_lowlevel_read_code(cfp,0,filelength(fileno(fp)), 0 );
|
||||
cf_check_compression(cfp);
|
||||
|
||||
return cfp;
|
||||
}
|
||||
}
|
||||
@@ -1009,7 +1008,7 @@ static CFILE *cf_open_packed_cfblock(const char* source, int line, const char* o
|
||||
cfp->line_num = line;
|
||||
|
||||
cf_init_lowlevel_read_code(cfp,offset, size, 0 );
|
||||
cf_check_compression(cfp);
|
||||
|
||||
return cfp;
|
||||
}
|
||||
|
||||
@@ -1053,7 +1052,6 @@ static CFILE *cf_open_mapped_fill_cfblock(const char* source, int line, const ch
|
||||
cfp->line_num = line;
|
||||
|
||||
cf_init_lowlevel_read_code(cfp, 0, 0, 0 );
|
||||
|
||||
#if defined _WIN32
|
||||
cfp->hMapFile = CreateFileMapping(cfp->hInFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (cfp->hMapFile == NULL) {
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "cfile/cfile.h"
|
||||
#include "cfile/cfilearchive.h"
|
||||
#include "cfile/cfilecompression.h"
|
||||
#include "luaconf.h"
|
||||
|
||||
#include <sstream>
|
||||
@@ -34,6 +33,7 @@
|
||||
#define CHECK_POSITION
|
||||
|
||||
// Called once to setup the low-level reading code.
|
||||
|
||||
void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size, size_t pos )
|
||||
{
|
||||
Assert(cfile != nullptr);
|
||||
@@ -54,33 +54,6 @@ void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size,
|
||||
}
|
||||
}
|
||||
|
||||
//This function checks the file header to see if the file is compressed, if so it fills the correct compression info.
|
||||
void cf_check_compression(CFILE* cfile)
|
||||
{
|
||||
if (cfile->size <= 16)
|
||||
return;
|
||||
int header=cfread_int(cfile);
|
||||
cfseek(cfile, 0, SEEK_SET);
|
||||
if (comp_check_header(header) == COMP_HEADER_MATCH)
|
||||
comp_create_ci(cfile, header);
|
||||
}
|
||||
|
||||
//This function is called to cleanup compression info when the cfile handle is reused.
|
||||
void cf_clear_compression_info(CFILE* cfile)
|
||||
{
|
||||
if (cfile->compression_info.header != 0)
|
||||
{
|
||||
free(cfile->compression_info.offsets);
|
||||
free(cfile->compression_info.decoder_buffer);
|
||||
cfile->compression_info.offsets = nullptr;
|
||||
cfile->compression_info.decoder_buffer = nullptr;
|
||||
cfile->compression_info.header = 0;
|
||||
cfile->compression_info.block_size = 0;
|
||||
cfile->compression_info.last_decoded_block_pos = 0;
|
||||
cfile->compression_info.last_decoded_block_bytes = 0;
|
||||
cfile->compression_info.num_offsets = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// cfeof() Tests for end-of-file on a stream
|
||||
@@ -88,11 +61,11 @@ void cf_clear_compression_info(CFILE* cfile)
|
||||
// returns a nonzero value after the first read operation that attempts to read
|
||||
// past the end of the file. It returns 0 if the current position is not end of file.
|
||||
// There is no error return.
|
||||
|
||||
int cfeof(CFILE *cfile)
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
if (cfile->compression_info.header != 0)
|
||||
return comp_feof(cfile);
|
||||
|
||||
int result = 0;
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
@@ -119,8 +92,6 @@ int cfeof(CFILE *cfile)
|
||||
int cftell( CFILE * cfile )
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
if (cfile->compression_info.header != 0)
|
||||
return (int)comp_ftell(cfile);
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
if (cfile->fp) {
|
||||
@@ -146,9 +117,6 @@ int cfseek( CFILE *cfile, int offset, int where )
|
||||
|
||||
Assert(cfile != NULL);
|
||||
|
||||
if (cfile->compression_info.header != 0)
|
||||
return comp_fseek(cfile, offset, where);
|
||||
|
||||
// TODO: seek to offset in memory mapped file
|
||||
Assert( !cfile->mem_mapped );
|
||||
|
||||
@@ -234,31 +202,23 @@ int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
// This is a file from memory
|
||||
bytes_read = size;
|
||||
memcpy(buf, reinterpret_cast<const char*>(cfile->data) + cfile->raw_position, size);
|
||||
} else if (cfile->compression_info.header != 0) {
|
||||
bytes_read = comp_fread(cfile, reinterpret_cast<char*>(buf),size);
|
||||
if (bytes_read != size)
|
||||
{
|
||||
mprintf(("\nFile: %s decompression error. Result was: %d expected: %d\n", cfile->original_filename.c_str(), (int)bytes_read, (int)size));
|
||||
Assertion(bytes_read == size, "File decompression error!");
|
||||
}
|
||||
} else {
|
||||
bytes_read = fread(buf, 1, size, cfile->fp);
|
||||
bytes_read = fread( buf, 1, size, cfile->fp );
|
||||
}
|
||||
|
||||
if ( bytes_read > 0 ) {
|
||||
cfile->raw_position += bytes_read;
|
||||
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
|
||||
}
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
//Raw position is not the fp position with compressed files
|
||||
if (cfile->fp && cfile->compression_info.header == 0) {
|
||||
if (cfile->fp) {
|
||||
auto tmp_offset = ftell(cfile->fp) - cfile->lib_offset;
|
||||
Assert(tmp_offset == cfile->raw_position);
|
||||
Assert(tmp_offset==cfile->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (int)(bytes_read / elsize);
|
||||
|
||||
}
|
||||
|
||||
int cfread_lua_number(double *buf, CFILE *cfile)
|
||||
@@ -306,3 +266,4 @@ int cfread_lua_number(double *buf, CFILE *cfile)
|
||||
return items_read;
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _CFILEARCHIVE_H
|
||||
#define _CFILEARCHIVE_H
|
||||
|
||||
@@ -22,17 +23,6 @@
|
||||
#define CFILE_BLOCK_UNUSED 0
|
||||
#define CFILE_BLOCK_USED 1
|
||||
|
||||
struct COMPRESSION_INFO {
|
||||
int header = 0;
|
||||
size_t compressed_size = 0;
|
||||
int block_size = 0;
|
||||
int num_offsets = 0;
|
||||
int* offsets = nullptr;
|
||||
char* decoder_buffer = nullptr;
|
||||
int last_decoded_block_pos = 0;
|
||||
int last_decoded_block_bytes = 0;
|
||||
};
|
||||
|
||||
struct CFILE {
|
||||
int type = CFILE_BLOCK_UNUSED; // CFILE_BLOCK_UNUSED, CFILE_BLOCK_USED
|
||||
int dir_type; // directory location
|
||||
@@ -54,19 +44,12 @@ struct CFILE {
|
||||
SCP_string original_filename;
|
||||
const char* source_file;
|
||||
int line_num;
|
||||
COMPRESSION_INFO compression_info;
|
||||
};
|
||||
|
||||
#define MAX_CFILE_BLOCKS 64
|
||||
extern std::array<CFILE, MAX_CFILE_BLOCKS> Cfile_block_list;
|
||||
|
||||
// Called once to setup the low-level reading code.
|
||||
void cf_init_lowlevel_read_code(CFILE* cfile, size_t lib_offset, size_t size, size_t pos);
|
||||
|
||||
// This checks if the file is compressed or not, and creates the proper compression info if so.
|
||||
void cf_check_compression(CFILE* cfile);
|
||||
|
||||
// Used to clear compression info data and free dynamic memory used by compression
|
||||
void cf_clear_compression_info(CFILE* cfile);
|
||||
void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size, size_t pos );
|
||||
|
||||
#endif
|
||||
@@ -1,206 +0,0 @@
|
||||
#define _CFILE_INTERNAL
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
#endif
|
||||
|
||||
#include "lz4.h"
|
||||
#include "cfilecompression.h"
|
||||
#include "cfilearchive.h"
|
||||
|
||||
/*INTERNAL FUNCTIONS*/
|
||||
/*LZ41*/
|
||||
void lz41_load_offsets(CFILE* cf);
|
||||
size_t lz41_stream_random_access(CFILE* cf, char* bytes_out, size_t offset, size_t length);
|
||||
void lz41_create_ci(CFILE* cf, int header);
|
||||
/*MISC*/
|
||||
int fso_fseek(CFILE* cfile, int offset, int where);
|
||||
/*END OF INTERNAL FUNCTIONS*/
|
||||
|
||||
int comp_check_header(int header)
|
||||
{
|
||||
if (LZ41_FILE_HEADER == header)
|
||||
return COMP_HEADER_MATCH;
|
||||
|
||||
return COMP_HEADER_MISMATCH;
|
||||
}
|
||||
|
||||
void comp_create_ci(CFILE* cf, int header)
|
||||
{
|
||||
//mprintf(("(CI)Compressed File Opened: %s \n", cf->original_filename.c_str()));
|
||||
|
||||
if (LZ41_FILE_HEADER == header)
|
||||
lz41_create_ci(cf, header);
|
||||
|
||||
//mprintf(("(CI)Uncompressed FileSize: %d \n", cf->size));
|
||||
//mprintf(("(CI)Compressed FileSize: %d \n", cf->compression_info.compressed_size));
|
||||
//mprintf(("(CI)Block Size: %d \n", cf->compression_info.block_size));
|
||||
}
|
||||
|
||||
size_t comp_fread(CFILE* cf, char* buffer, size_t length)
|
||||
{
|
||||
/* Check the request to be at least 1 byte */
|
||||
Assertion(length > 0, "Invalid length requested.");
|
||||
|
||||
/* Check that we are not requesting to read beyond end of file */
|
||||
Assertion(cf->raw_position + length <= cf->size, "Invalid length requested.");
|
||||
|
||||
if (LZ41_FILE_HEADER == cf->compression_info.header)
|
||||
return lz41_stream_random_access(cf, buffer, cf->raw_position, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t comp_ftell(CFILE* cf)
|
||||
{
|
||||
return cf->raw_position;
|
||||
}
|
||||
|
||||
int comp_feof(CFILE* cf)
|
||||
{
|
||||
if (cf->raw_position >= cf->size)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int comp_fseek(CFILE* cf, int offset, int where)
|
||||
{
|
||||
size_t goal_position;
|
||||
switch (where) {
|
||||
case SEEK_SET: goal_position = offset; break;
|
||||
case SEEK_CUR: goal_position = cf->raw_position + offset; break;
|
||||
case SEEK_END: goal_position = cf->size + offset; break;
|
||||
default: return 1;
|
||||
}
|
||||
|
||||
// Make sure we don't seek beyond the end of the file
|
||||
CAP(goal_position,(size_t)0, cf->size);
|
||||
|
||||
cf->raw_position = goal_position;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Special fseek for compressed files handled by FSO, only SEEK_SET and SEEK_END is supported.
|
||||
int fso_fseek(CFILE* cfile, int offset, int where)
|
||||
{
|
||||
size_t goal_position;
|
||||
|
||||
switch (where) {
|
||||
case SEEK_SET: goal_position = offset + cfile->lib_offset; break;
|
||||
case SEEK_END: goal_position = cfile->compression_info.compressed_size + offset + cfile->lib_offset; break;
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
// Make sure we don't seek beyond the end of the file
|
||||
CAP(goal_position, cfile->lib_offset, cfile->lib_offset + cfile->compression_info.compressed_size);
|
||||
|
||||
int result = 0;
|
||||
if (cfile->fp)
|
||||
{
|
||||
result = fseek(cfile->fp, (long)goal_position, SEEK_SET);
|
||||
Assertion(goal_position >= cfile->lib_offset, "Invalid offset values detected while seeking! Goal was " SIZE_T_ARG ", lib_offset is " SIZE_T_ARG ".", goal_position, cfile->lib_offset);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void lz41_create_ci(CFILE* cf, int header)
|
||||
{
|
||||
cf->compression_info.header = header;
|
||||
cf->compression_info.compressed_size = cf->size;
|
||||
fso_fseek(cf, (int)sizeof(int)*-3, SEEK_END);
|
||||
auto fNumoffsets = fread(&cf->compression_info.num_offsets, sizeof(int), 1, cf->fp);
|
||||
auto fSize = fread(&cf->size, sizeof(int), 1, cf->fp);
|
||||
auto fBsize = fread(&cf->compression_info.block_size, sizeof(int), 1, cf->fp);
|
||||
|
||||
Assertion(cf->compression_info.num_offsets > 0, "Invalid number of offsets, compressed file is possibly in the wrong format or corrupted.");
|
||||
#if !defined(NDEBUG)
|
||||
Assertion(cf->size > 4, "Invalid filesize, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(cf->compression_info.block_size > 16, "Invalid block size, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(fNumoffsets == 1, "Error while reading the number of offsets, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(fSize == 1, "Error while reading original filesize, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(fBsize == 1, "Error while reading block size, compressed file is possibly in the wrong format or corrupted.");
|
||||
#endif
|
||||
|
||||
cf->compression_info.decoder_buffer = (char*)malloc(LZ4_compressBound(cf->compression_info.block_size));
|
||||
cf->compression_info.last_decoded_block_pos = 0;
|
||||
cf->compression_info.last_decoded_block_bytes = 0;
|
||||
lz41_load_offsets(cf);
|
||||
}
|
||||
|
||||
void lz41_load_offsets(CFILE* cf)
|
||||
{
|
||||
cf->compression_info.offsets = (int*)malloc(cf->compression_info.num_offsets * sizeof(int));
|
||||
int block;
|
||||
int* offsets_ptr = cf->compression_info.offsets;
|
||||
|
||||
/* Seek to the first offset position, remember to consider the trailing ints */
|
||||
fso_fseek(cf, ( ( sizeof(int) * cf->compression_info.num_offsets ) * -1 ) - (sizeof(int)*3 ), SEEK_END);
|
||||
for (block = 0; block < cf->compression_info.num_offsets; ++block)
|
||||
{
|
||||
auto bytes_read = fread(offsets_ptr++, sizeof(int), 1, cf->fp);
|
||||
Assertion(bytes_read == 1, "Error reading offset list.");
|
||||
}
|
||||
}
|
||||
|
||||
size_t lz41_stream_random_access(CFILE* cf, char* bytes_out, size_t offset, size_t length)
|
||||
{
|
||||
LZ4_streamDecode_t lz4_stream_decode_body;
|
||||
LZ4_streamDecode_t* lz4_stream_decode = &lz4_stream_decode_body;
|
||||
/* The blocks (current_block to end_block) contain the data we want */
|
||||
size_t current_block = offset / cf->compression_info.block_size;
|
||||
size_t end_block = ((offset + length - 1) / cf->compression_info.block_size) + 1;
|
||||
size_t written_bytes = 0;
|
||||
|
||||
if (cf->compression_info.num_offsets <= (int)end_block)
|
||||
return (size_t)LZ41_OFFSETS_MISMATCH;
|
||||
|
||||
/* Seek to the first block to read, if it matches the cached block, search the next one instead */
|
||||
if (cf->compression_info.last_decoded_block_pos == cf->compression_info.offsets[current_block] && current_block + 1 <= end_block)
|
||||
fso_fseek(cf, cf->compression_info.offsets[current_block+1], SEEK_SET);
|
||||
else
|
||||
fso_fseek(cf, cf->compression_info.offsets[current_block], SEEK_SET);
|
||||
|
||||
offset = offset % cf->compression_info.block_size;
|
||||
char* cmp_buf = (char*)malloc(LZ4_compressBound(cf->compression_info.block_size));
|
||||
|
||||
/* Start decoding */
|
||||
for (; current_block < end_block; ++current_block)
|
||||
{
|
||||
/* Only read and decode if the requested block is not the cached one */
|
||||
if (cf->compression_info.last_decoded_block_pos != cf->compression_info.offsets[current_block])
|
||||
{
|
||||
cf->compression_info.last_decoded_block_pos = cf->compression_info.offsets[current_block];
|
||||
|
||||
/* The difference in offsets is the size of the block */
|
||||
int cmp_bytes = cf->compression_info.offsets[current_block + 1] - cf->compression_info.offsets[current_block];
|
||||
auto bytes_read = fread(cmp_buf, cmp_bytes, 1, cf->fp);
|
||||
Assertion(bytes_read == 1, "Error reading from compressed file.");
|
||||
|
||||
cf->compression_info.last_decoded_block_bytes = LZ4_decompress_safe_continue(lz4_stream_decode, cmp_buf, cf->compression_info.decoder_buffer, cmp_bytes, cf->compression_info.block_size);
|
||||
if (cf->compression_info.last_decoded_block_bytes <= 0)
|
||||
{
|
||||
free(cmp_buf);
|
||||
return (size_t)LZ41_DECOMPRESSION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write out the part of the data we care about from buffer */
|
||||
size_t block_length = (length < (cf->compression_info.last_decoded_block_bytes - offset) ? length : (cf->compression_info.last_decoded_block_bytes - offset));
|
||||
memcpy(bytes_out + written_bytes, cf->compression_info.decoder_buffer + offset, (size_t)block_length);
|
||||
written_bytes += block_length;
|
||||
offset = 0;
|
||||
length -= block_length;
|
||||
}
|
||||
|
||||
free(cmp_buf);
|
||||
return written_bytes;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
ShivanSpS - Compressed files support for FSO. Many thanks to ngld, taylor and everyone elsewho helped me in getting this done.
|
||||
|
||||
-The file header is a version, this is used to tell FSO how to decompress that file, always use 4 chars to mantain alignment.
|
||||
-The header ID can be used to add diferent revisions to LZ41 decompression system or to add other compression format supports whiout breaking compatibility.
|
||||
-The system uses a offset list to record the position of every block in file, this list, along with the number of offsets, original filesize,
|
||||
and block size, must be written by the compressor app.
|
||||
-A decoder cache is used to store the last decoded block, this ensures each block is read and decoded only once in sequential reads. This cache
|
||||
is a little bit bigger than the block size, a higher block size means less overhead added to the file, but it also means a little more ram will
|
||||
be used during decompression.
|
||||
-All this dynamic memory is assigned at cfopen() and it is cleared on cfclose().
|
||||
|
||||
................................char[4]..........(n ints)...(int)..........(int)..........(int)
|
||||
-COMPRESSED FILE DATA STUCTURE: HEADER|N BLOCKS|N OFFSETS|NUM_OFFSETS|ORIGINAL_FILESIZE|BLOCK_SIZE
|
||||
*/
|
||||
|
||||
#ifndef _CFILECOMPRESSION_H
|
||||
#define _CFILECOMPRESSION_H
|
||||
struct CFILE;
|
||||
|
||||
/*LZ41*/
|
||||
#define LZ41_FILE_HEADER 0x31345A4C // "14ZL" -> "LZ41", inverted
|
||||
#define LZ41_DECOMPRESSION_ERROR -1
|
||||
#define LZ41_MAX_BLOCKS_OVERFLOW -2
|
||||
#define LZ41_HEADER_MISMATCH -3
|
||||
#define LZ41_OFFSETS_MISMATCH -4
|
||||
/******/
|
||||
|
||||
#define COMP_HEADER_MATCH 1
|
||||
#define COMP_HEADER_MISMATCH 0
|
||||
|
||||
/*
|
||||
Returns COMP_HEADER_MATCH if header is a valid compressed file header,
|
||||
returns COMP_HEADER_MISMATCH if it dosent.
|
||||
*/
|
||||
int comp_check_header(int header);
|
||||
|
||||
/*
|
||||
This is called to generate the correct compression_info data
|
||||
after the file has been indentified as a compressed file.
|
||||
This must be done before calling any other function.
|
||||
*/
|
||||
void comp_create_ci(CFILE* cf, int header);
|
||||
|
||||
/*
|
||||
Read X bytes from the uncompressed file starting from X offset.
|
||||
Returns the amount of bytes read, and 0 or lower to indicate errors.
|
||||
*/
|
||||
size_t comp_fread(CFILE* cf, char* buffer, size_t length);
|
||||
|
||||
/*
|
||||
Returns the current uncompressed file position.
|
||||
*/
|
||||
size_t comp_ftell(CFILE* cf);
|
||||
|
||||
/*
|
||||
Returns 1 of the uncompressed file has been completely read, otherwise it returns a 0.
|
||||
*/
|
||||
int comp_feof(CFILE* cf);
|
||||
|
||||
/*
|
||||
Used to move the uncompressed file current position.
|
||||
*/
|
||||
int comp_fseek(CFILE* cf, int offset, int where);
|
||||
|
||||
#endif
|
||||
@@ -246,7 +246,7 @@ Flag exe_params[] =
|
||||
{ "-noninteractive", "Disables interactive dialogs", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noninteractive", },
|
||||
{ "-no_unfocused_pause","Don't pause if the window isn't focused", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_unfocused_pause", },
|
||||
{ "-benchmark_mode", "Puts the game into benchmark mode", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-benchmark_mode", },
|
||||
{ "-profile_frame_time","Profile frame time", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_frame_time", },
|
||||
{ "-profile_frame_time","Profile frame time", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_frame_time", },
|
||||
{ "-profile_write_file", "Write profiling information to file", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_write_file", },
|
||||
{ "-json_profiling", "Generate JSON profiling output", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-json_profiling", },
|
||||
{ "-debug_window", "Enable the debug window", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-debug_window", },
|
||||
@@ -274,6 +274,7 @@ cmdline_parm gameclosed_arg("-closed", NULL, AT_NONE); // Cmdline_closed_game
|
||||
cmdline_parm gamerestricted_arg("-restricted", NULL, AT_NONE); // Cmdline_restricted_game
|
||||
cmdline_parm port_arg("-port", "Multiplayer network port", AT_INT);
|
||||
cmdline_parm multilog_arg("-multilog", NULL, AT_NONE); // Cmdline_multi_log
|
||||
cmdline_parm client_dodamage("-clientdamage", NULL, AT_NONE); // Cmdline_client_dodamage
|
||||
cmdline_parm pof_spew("-pofspew", NULL, AT_NONE); // Cmdline_spew_pof_info
|
||||
cmdline_parm weapon_spew("-weaponspew", nullptr, AT_STRING); // Cmdline_spew_weapon_stats
|
||||
cmdline_parm mouse_coords("-coords", NULL, AT_NONE); // Cmdline_mouse_coords
|
||||
@@ -286,6 +287,7 @@ char *Cmdline_game_password = NULL;
|
||||
char *Cmdline_rank_above = NULL;
|
||||
char *Cmdline_rank_below = NULL;
|
||||
int Cmdline_cd_check = 1;
|
||||
int Cmdline_client_dodamage = 0;
|
||||
int Cmdline_closed_game = 0;
|
||||
int Cmdline_freespace_no_music = 0;
|
||||
int Cmdline_freespace_no_sound = 0;
|
||||
@@ -479,7 +481,6 @@ cmdline_parm reparse_mainhall_arg("-reparse_mainhall", NULL, AT_NONE); //Cmdline
|
||||
cmdline_parm frame_profile_write_file("-profile_write_file", NULL, AT_NONE); // Cmdline_profile_write_file
|
||||
cmdline_parm no_unfocused_pause_arg("-no_unfocused_pause", NULL, AT_NONE); //Cmdline_no_unfocus_pause
|
||||
cmdline_parm benchmark_mode_arg("-benchmark_mode", NULL, AT_NONE); //Cmdline_benchmark_mode
|
||||
cmdline_parm pilot_arg("-pilot", nullptr, AT_STRING); //Cmdline_pilot
|
||||
cmdline_parm noninteractive_arg("-noninteractive", NULL, AT_NONE); //Cmdline_noninteractive
|
||||
cmdline_parm json_profiling("-json_profiling", NULL, AT_NONE); //Cmdline_json_profiling
|
||||
cmdline_parm show_video_info("-show_video_info", NULL, AT_NONE); //Cmdline_show_video_info
|
||||
@@ -511,7 +512,6 @@ int Cmdline_reparse_mainhall = 0;
|
||||
bool Cmdline_profile_write_file = false;
|
||||
bool Cmdline_no_unfocus_pause = false;
|
||||
bool Cmdline_benchmark_mode = false;
|
||||
const char *Cmdline_pilot = nullptr;
|
||||
bool Cmdline_noninteractive = false;
|
||||
bool Cmdline_json_profiling = false;
|
||||
bool Cmdline_frame_profile = false;
|
||||
@@ -812,7 +812,7 @@ void os_validate_parms(int argc, char *argv[])
|
||||
if (!stricmp(token, "-help") || !stricmp(token, "--help") || !stricmp(token, "-h") || !stricmp(token, "-?")) {
|
||||
printf("FreeSpace 2 Open, version %s\n", FS_VERSION_FULL);
|
||||
printf("Website: http://scp.indiegames.us\n");
|
||||
printf("Github (bug reporting): https://github.com/scp-fs2open/fs2open.github.com/issues\n\n");
|
||||
printf("Mantis (bug reporting): http://scp.indiegames.us/mantis/\n\n");
|
||||
printf("Usage: fs2_open [options]\n");
|
||||
|
||||
// not the prettiest thing but the job gets done
|
||||
@@ -1663,6 +1663,12 @@ bool SetCmdlineParams()
|
||||
Cmdline_multi_log = 1;
|
||||
}
|
||||
|
||||
|
||||
// maybe use old-school client damage
|
||||
if(client_dodamage.found()){
|
||||
Cmdline_client_dodamage = 1;
|
||||
}
|
||||
|
||||
// spew pof info
|
||||
if(pof_spew.found()){
|
||||
Cmdline_spew_pof_info = 1;
|
||||
@@ -2125,11 +2131,6 @@ bool SetCmdlineParams()
|
||||
Cmdline_benchmark_mode = true;
|
||||
}
|
||||
|
||||
if (pilot_arg.found())
|
||||
{
|
||||
Cmdline_pilot = pilot_arg.str();
|
||||
}
|
||||
|
||||
if (noninteractive_arg.found())
|
||||
{
|
||||
Cmdline_noninteractive = true;
|
||||
|
||||
@@ -30,6 +30,7 @@ extern char *Cmdline_game_password;
|
||||
extern char *Cmdline_rank_above;
|
||||
extern char *Cmdline_rank_below;
|
||||
extern int Cmdline_cd_check;
|
||||
extern int Cmdline_client_dodamage;
|
||||
extern int Cmdline_closed_game;
|
||||
extern int Cmdline_freespace_no_music;
|
||||
extern int Cmdline_freespace_no_sound;
|
||||
@@ -143,7 +144,6 @@ extern int Cmdline_reparse_mainhall;
|
||||
extern bool Cmdline_profile_write_file;
|
||||
extern bool Cmdline_no_unfocus_pause;
|
||||
extern bool Cmdline_benchmark_mode;
|
||||
extern const char *Cmdline_pilot;
|
||||
extern bool Cmdline_noninteractive;
|
||||
extern bool Cmdline_json_profiling;
|
||||
extern bool Cmdline_frame_profile;
|
||||
|
||||
+787
-1000
File diff suppressed because it is too large.
Load diff
@@ -28,27 +28,12 @@ enum Joy_axis_index {
|
||||
JOY_RZ_AXIS
|
||||
};
|
||||
|
||||
/*!
|
||||
* Controller index enumeration
|
||||
* @details For use with hardcoded bindings and scripting API to allow human-readable translation
|
||||
* @note These enums are hardcoded so that an int value of 0 would be JOY0
|
||||
*/
|
||||
enum CID : short {
|
||||
CID_NONE = -3, // No device bound
|
||||
CID_KEYBOARD = -2, // button belongs to keyboard
|
||||
CID_MOUSE = -1, // to mouse
|
||||
CID_JOY0 = 0, // to Joy0
|
||||
CID_JOY1 = 1, // to Joy1 (Throttle?)
|
||||
CID_JOY2 = 2, // to Joy2 (Pedals?)
|
||||
CID_JOY3 = 3 // to Joy3 (Head tracker?)
|
||||
// Any additional joystick is an int >3
|
||||
};
|
||||
|
||||
/*!
|
||||
* These are used to index a corresponding (analog) action, namely controlling the orientation angles and throttle.
|
||||
*/
|
||||
enum Joy_axis_action_index {
|
||||
JOY_HEADING_AXIS =0, //
|
||||
JOY_HEADING_AXIS =0,
|
||||
JOY_PITCH_AXIS,
|
||||
JOY_BANK_AXIS,
|
||||
JOY_ABS_THROTTLE_AXIS,
|
||||
@@ -60,16 +45,7 @@ enum Joy_axis_action_index {
|
||||
NUM_JOY_AXIS_ACTIONS //!< The total number of actions an axis may map to
|
||||
};
|
||||
|
||||
/*!
|
||||
* Joystick action modes
|
||||
* note: should this be in joy.h?
|
||||
*/
|
||||
enum Joy_axis_action_mode {
|
||||
JAAM_ABS, //!< Absolute mode. Axis position = output value
|
||||
JAAM_REL, //!< Relative mode. Axis postion away from its center adds or subtracts an output value register.
|
||||
JAAM_BTN_NEG, //!< Button mode, negative side. Axis position in the negative side will trigger a button action
|
||||
JAAM_BTN_POS, //!< Button mode, positive side. Axis position in the positive side will trigger a button action
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* Control Configuration Types. Namely differ in how the control is activated
|
||||
@@ -79,12 +55,27 @@ enum CC_type {
|
||||
CC_TYPE_CONTINUOUS //!< A continous control that is activated as long as the key or button is held down
|
||||
};
|
||||
|
||||
/*!
|
||||
* Control configuration item type.
|
||||
*/
|
||||
typedef struct config_item {
|
||||
short key_default; //!< default key bound to action
|
||||
short joy_default; //!< default joystick button bound to action
|
||||
char tab; //!< what tab (category) it belongs in
|
||||
int indexXSTR; //!< what string index we should use to translate this with an XSTR
|
||||
const char *text; //!< describes the action in the config screen
|
||||
char type; //!< manner control should be checked in
|
||||
short key_id; //!< actual key bound to action
|
||||
short joy_id; //!< joystick button bound to action
|
||||
int used; //!< has control been used yet in mission? If so, this is the timestamp
|
||||
bool disabled; //!< whether this action should be available at all
|
||||
bool continuous_ongoing;//!< whether this action is a continuous one and is currently ongoing
|
||||
} config_item;
|
||||
|
||||
/*!
|
||||
* All available actions
|
||||
* This is the value of the id field in config_item
|
||||
* The first group of items are ship targeting.
|
||||
*
|
||||
* Note: Do not adjust the order or numeric value
|
||||
*/
|
||||
enum IoActionId {
|
||||
TARGET_NEXT =0, //!< target next
|
||||
@@ -261,7 +252,7 @@ enum IoActionId {
|
||||
DOWN_SLIDE_THRUST =111, //!< DOWN_SLIDE_THRUST
|
||||
HUD_TARGETBOX_TOGGLE_WIREFRAME =112, //!< HUD_TARGETBOX_TOGGLE_WIREFRAME
|
||||
VIEW_TOPDOWN =113, //!< VIEW_TOPDOWN
|
||||
VIEW_TRACK_TARGET =114, //!< VIEW_TRACK_TARGET (Switfty) Toggle for VM_Track
|
||||
VIEW_TRACK_TARGET =114, //!< VIEW_TRACK_TARGET
|
||||
|
||||
//!< @n
|
||||
//!< AutoPilot - Kazan
|
||||
@@ -294,200 +285,10 @@ enum IoActionId {
|
||||
CCFG_MAX //!< The total number of defined control actions (or last define + 1)
|
||||
};
|
||||
|
||||
class CCB;
|
||||
|
||||
/*!
|
||||
* A singular button binding
|
||||
*/
|
||||
class CC_bind {
|
||||
public:
|
||||
CID cid = CID_NONE; //!< Which controller this belongs to
|
||||
short btn = -1; //!< Which button to index; If cid == CID_KEYBOARD, this is a key hash.
|
||||
|
||||
public:
|
||||
CC_bind() = default;
|
||||
CC_bind(const CC_bind &A) = default;
|
||||
|
||||
CC_bind(CID _cid, short _btn) : cid(_cid), btn(_btn) {};
|
||||
|
||||
CC_bind& operator=(const CC_bind &A);
|
||||
|
||||
/*!
|
||||
* Checks if this CC_bind is equal to the given CC_bind
|
||||
*/
|
||||
bool operator==(const CC_bind &B) const;
|
||||
|
||||
/*!
|
||||
* Checks if this CC_bind is equal to either in the pair
|
||||
*/
|
||||
bool operator==(const CCB &pair) const;
|
||||
|
||||
/*!
|
||||
* Checks if this CC_bind is not equal to the given CC_bind
|
||||
*/
|
||||
bool operator!=(const CC_bind &B) const;
|
||||
|
||||
/*!
|
||||
* Checks if this CC_bind is not equal to either in the pair
|
||||
*/
|
||||
bool operator!=(const CCB &pair) const;
|
||||
|
||||
/*!
|
||||
* Clears the binding
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/*!
|
||||
* True if not bound
|
||||
*/
|
||||
bool empty() const;
|
||||
|
||||
/*!
|
||||
* Returns a human-readable string of this binding
|
||||
*/
|
||||
SCP_string textify() const;
|
||||
};
|
||||
|
||||
/*!
|
||||
* A pair of bindings.
|
||||
* @note Please don't set the bindings directly, use ::take() instead.
|
||||
*/
|
||||
class CCB {
|
||||
public:
|
||||
CC_bind first; // The primary binding
|
||||
CC_bind second; // The secondary binding
|
||||
|
||||
public:
|
||||
CCB() = default;
|
||||
CCB(const CCB& A) = default;
|
||||
|
||||
/*!
|
||||
* Returns true if nothing is bound
|
||||
*/
|
||||
bool empty() const;
|
||||
|
||||
/*!
|
||||
* Takes a given binding
|
||||
*
|
||||
* @param[in] A The bind to take
|
||||
* @param[in] order 0 is primary, 1 is secondary, -1 is overwrite existing bind (if any)
|
||||
*
|
||||
* @note Should there be a binding to the same controller as the given binding, it is cleared
|
||||
*/
|
||||
void take(CC_bind A, int order);
|
||||
|
||||
/*!
|
||||
* Clears both bindings
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/*!
|
||||
* Gets the value of btn if either CC_bind has the given CID
|
||||
*
|
||||
* @returns the btn of the bound controller, or
|
||||
* @returns -1 if that controller is not bound
|
||||
*/
|
||||
short get_btn(CID) const;
|
||||
|
||||
/*!
|
||||
* Assigns the contents of the given CCB to this CCB
|
||||
*/
|
||||
CCB& operator=(const CCB&);
|
||||
|
||||
/*!
|
||||
* Returns True if this CCB's first isn't empty and the given CCB has a binding equal to it
|
||||
*/
|
||||
bool has_first(const CCB&) const;
|
||||
|
||||
/*!
|
||||
* Returns True if this CCB's second isn't empty and the given CCB has a binding equal to it
|
||||
*/
|
||||
bool has_second(const CCB&) const;
|
||||
};
|
||||
|
||||
/*!
|
||||
* A preset, a collection of bindings for use in Control_config with an associated name
|
||||
*/
|
||||
class CC_preset {
|
||||
public:
|
||||
SCP_vector<CCB> bindings;
|
||||
SCP_string name;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Control configuration item type.
|
||||
* @detail Contains binding info, documentation, behavior, etc. for a single control
|
||||
*/
|
||||
class CCI : public CCB {
|
||||
public:
|
||||
// Inherited from CCB
|
||||
// CC_bind first;
|
||||
// CC_bind second;
|
||||
|
||||
// Items Set in menu
|
||||
char tab; //!< what tab (category) it belongs in
|
||||
int indexXSTR; //!< what string index we should use to translate this with an XSTR 0 = None, 1= Use item index + CONTROL_CONFIG_XSTR, 2 <= use CCI::indexXSTR directly
|
||||
SCP_string text; //!< describes the action in the config screen
|
||||
|
||||
CC_type type; //!< manner control should be checked in
|
||||
|
||||
// Items used during gameplay
|
||||
int used; //!< has control been used yet in mission? If so, this is the timestamp
|
||||
bool disabled = true; //!< whether this action should be available at all
|
||||
bool continuous_ongoing; //!< whether this action is a continuous one and is currently ongoing
|
||||
|
||||
public:
|
||||
CCI() = default;
|
||||
CCI(const CCI& A) = default;
|
||||
|
||||
/*!
|
||||
* Assigns the contents of the given CCI to this CCI
|
||||
*/
|
||||
CCI& operator=(const CCI&);
|
||||
|
||||
/*!
|
||||
* @brief Takes the bindings of the given CCB, but leaves all other members alone
|
||||
*/
|
||||
CCI& operator=(const CCB&);
|
||||
};
|
||||
|
||||
/*!
|
||||
* Builder predicate to populate a ControlConfig vector with hardcoded default bindings.
|
||||
*/
|
||||
class CCI_builder {
|
||||
public:
|
||||
/*!
|
||||
* Initilizes the given ControlConfig vector
|
||||
*/
|
||||
CCI_builder(SCP_vector<CCI>& _ControlConfig);
|
||||
|
||||
/*!
|
||||
* Start a chain of factory methods. If there are any pre-init work to be done, that's done here.
|
||||
*/
|
||||
CCI_builder& start();
|
||||
|
||||
/*!
|
||||
* End a chain of factory methods. If there any post-init work to be done, that's done here.
|
||||
*/
|
||||
void end();
|
||||
|
||||
/*!
|
||||
* Assigns the hardcoded binding to the given action
|
||||
* @note This differs from the original hardcode from :V: in the hopes of it being more intuitive to future IoAction additions
|
||||
*/
|
||||
CCI_builder& operator()(IoActionId action_id, short key_default, short joy_default, char tab, int indexXSTR, const char *text, CC_type type, bool disabled = false);
|
||||
|
||||
private:
|
||||
CCI_builder(); // Only one builder per Control Config, so a default constructor is useless
|
||||
SCP_vector<CCI>& ControlConfig;
|
||||
};
|
||||
|
||||
|
||||
extern int Failed_key_index;
|
||||
|
||||
extern int Axis_map_to[]; // Array to map an axis action to a joy axis. size() = NUM_JOY_AXIS_ACTIONS
|
||||
extern int Axis_map_to_defaults[];
|
||||
extern int Invert_axis[]; // Array to hold inversion bools for a joy axis. size() = JOY_NUM_AXES
|
||||
extern int Axis_map_to[];
|
||||
extern int Invert_axis[];
|
||||
extern int Invert_axis_defaults[];
|
||||
|
||||
extern int Joy_dead_zone_size;
|
||||
@@ -495,76 +296,23 @@ extern int Joy_sensitivity;
|
||||
|
||||
extern int Control_config_overlay_id;
|
||||
|
||||
extern SCP_vector<CCI> Control_config; //!< Stores the keyboard configuration
|
||||
extern SCP_vector<CC_preset> Control_config_presets; // tabled control presets; pointers to config_item arrays
|
||||
extern config_item Control_config[]; //!< Stores the keyboard configuration
|
||||
extern SCP_vector<config_item*> Control_config_presets; // tabled control presets; pointers to config_item arrays
|
||||
extern SCP_vector<SCP_string> Control_config_preset_names; // names for Control_config_presets (identical order of items)
|
||||
extern const char **Scan_code_text;
|
||||
extern const char **Joy_button_text;
|
||||
|
||||
/*!
|
||||
* @brief Checks if either binding in the CCB has the given cid
|
||||
*
|
||||
* @returns 0 if its the first element, or
|
||||
* @returns 1 if its the second element, or
|
||||
* @returns -1 if neither
|
||||
*/
|
||||
int is_cid_either(CID, const CCB);
|
||||
void control_config_common_init(); //!< initialize common control config stuff - call at game startup after localization has been initialized
|
||||
void control_config_common_close(); //!< close common control config stuff - call at game shutdown
|
||||
|
||||
/*!
|
||||
* @brief initialize common control config stuff - call at game startup after localization has been initialized
|
||||
*/
|
||||
void control_config_common_init();
|
||||
|
||||
/*!
|
||||
* @brief close common control config stuff - call at game shutdown
|
||||
*/
|
||||
void control_config_common_close();
|
||||
|
||||
/*!
|
||||
* @brief init config menu
|
||||
*/
|
||||
void control_config_init();
|
||||
|
||||
/*!
|
||||
* @brief do a frame of the config menu
|
||||
*/
|
||||
void control_config_do_frame(float frametime);
|
||||
|
||||
/*!
|
||||
* @brief close config menu
|
||||
*/
|
||||
void control_config_close();
|
||||
|
||||
/*!
|
||||
* @brief Cancel configuration of controls, revert any changes, return to previous menu/game state
|
||||
*/
|
||||
void control_config_cancel_exit();
|
||||
|
||||
/*!
|
||||
* @brief Resets all controls to values within the currently selected preset
|
||||
*/
|
||||
void control_config_reset_defaults();
|
||||
|
||||
/*!
|
||||
* Returns the IoActionId of a control bound to the given key
|
||||
*
|
||||
* @param[in] key The key combo to look for
|
||||
* @param[in] find_override If true, return the IoActionId of a control that has this key as its default
|
||||
* @details If find_override is set to true, then this returns the index of the action
|
||||
*/
|
||||
void control_config_reset_defaults(int presetnum=-1);
|
||||
int translate_key_to_index(const char *key, bool find_override=true);
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Given the system default key 'key', return the current key that is bound to that function.
|
||||
*
|
||||
* @param[in] key The default key combo (as string) to a certain control
|
||||
*
|
||||
* @returns The key combo (as string) currently bound to the control
|
||||
*
|
||||
* @details Both the 'key' and the return value are descriptive strings that can be displayed
|
||||
* directly to the user. If 'key' isn't a real key, is not normally bound to anything,
|
||||
* or there is no key currently bound to the function, NULL is returned.
|
||||
*/
|
||||
char *translate_key(char *key);
|
||||
|
||||
/**
|
||||
@@ -586,70 +334,14 @@ const char *textify_scancode(int code);
|
||||
*/
|
||||
const char *textify_scancode_universal(int code);
|
||||
|
||||
/*!
|
||||
* @brief Checks how long a control has been active
|
||||
*
|
||||
* @param[in] id The IoActionId of the control to check
|
||||
*
|
||||
* @returns Time, in milliseconds, that the control has been active.
|
||||
*
|
||||
* @note This function is only to be used on CC_CONTINOUS type controls, or it will trigger an assertion
|
||||
*/
|
||||
float check_control_timef(int id);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for check_control_used. Allows the game to ignore the key if told to do so by the ignore-key SEXP.
|
||||
*
|
||||
* @brief id The IoActionId of the control to check
|
||||
* @brief key The key combo to check against the control.
|
||||
*
|
||||
* @returns 0 If the control wasn't used, or
|
||||
* @returns 1 If the control was used
|
||||
*/
|
||||
int check_control(int id, int key = -1);
|
||||
|
||||
/**
|
||||
* @brief Gets the scaled reading for all control axes.
|
||||
*
|
||||
* @param[out] h heading
|
||||
* @param[out] p pitch
|
||||
* @param[out] b bank
|
||||
* @param[out] ta Throttle - Absolute
|
||||
* @param[out] tr Throttle - Relative
|
||||
*
|
||||
* @note None of the input pointers may be nullptr
|
||||
*/
|
||||
void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr);
|
||||
|
||||
/**
|
||||
* @brief Markes the given control (by IoActionId) as used
|
||||
*
|
||||
* @details Updates the ::used timestamp, triggers a script hook, and marks ::continous_ongoing as true
|
||||
*/
|
||||
void control_used(int id);
|
||||
|
||||
/**
|
||||
* @brief Clears the bindings of all controls
|
||||
*/
|
||||
void control_config_clear();
|
||||
|
||||
/**
|
||||
* @brief debug function used to indicate number of controls checked
|
||||
*/
|
||||
void control_check_indicate();
|
||||
|
||||
/**
|
||||
* @brief Clears the used timestamp of all controls
|
||||
*/
|
||||
void control_config_clear_used_status();
|
||||
|
||||
/**
|
||||
* @brief Applies sensitivity multiplier and deadzone histerisis to the raw axis value
|
||||
*
|
||||
* @param[in] raw The raw axis value to transform
|
||||
*
|
||||
* @return The transformed value
|
||||
*/
|
||||
int joy_get_scaled_reading(int raw);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -286,7 +286,7 @@ std::unique_ptr<DecoderStatus> initializeStatus(std::unique_ptr<InputStream>& st
|
||||
|
||||
if (status->subtitleStream == nullptr) {
|
||||
// We don't have an external subtitle stream or loading from that file has failed
|
||||
auto& current_language = Lcl_languages[lcl_get_current_lang_index()];
|
||||
auto& current_language = Lcl_languages[Lcl_current_lang == LCL_UNTRANSLATED ? LCL_DEFAULT : Lcl_current_lang];
|
||||
for (uint32_t i = 0; i < ctx->nb_streams; ++i) {
|
||||
auto test_stream = ctx->streams[i];
|
||||
|
||||
@@ -428,7 +428,7 @@ std::unique_ptr<InputStream> openInputStream(const SCP_string& name) {
|
||||
|
||||
std::unique_ptr<InputStream> openSubtitleStream(const SCP_string& name)
|
||||
{
|
||||
auto& current_language = Lcl_languages[lcl_get_current_lang_index()];
|
||||
auto& current_language = Lcl_languages[Lcl_current_lang == LCL_UNTRANSLATED ? LCL_DEFAULT : Lcl_current_lang];
|
||||
|
||||
for (auto ext : CHECKED_SUBT_EXTENSIONS) {
|
||||
SCP_string fileName;
|
||||
@@ -453,7 +453,7 @@ bool FFMPEGDecoder::initialize(const SCP_string& fileName, const PlaybackPropert
|
||||
{
|
||||
SCP_string movieName = fileName;
|
||||
// First make the file name lower case
|
||||
SCP_tolower(movieName);
|
||||
std::transform(movieName.begin(), movieName.end(), movieName.begin(), [](char c) { return (char)::tolower(c); });
|
||||
|
||||
// Then remove the extension
|
||||
size_t dotPos = movieName.find('.');
|
||||
|
||||
+39
-73
@@ -424,6 +424,17 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
sip = &Ship_info[shipp->ship_info_index];
|
||||
vaporize = (shipp->flags[Ship::Ship_Flags::Vaporize]);
|
||||
|
||||
if ( !hull_flag ) {
|
||||
// Make vaporize debris seen from farther away
|
||||
float dist = vm_vec_dist_quick( pos, &Eye_position );
|
||||
if (vaporize) {
|
||||
dist /= 2.0f;
|
||||
}
|
||||
if ( dist > 200.0f ) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// try to maintain our soft limit
|
||||
if (hull_flag && (Num_hull_pieces >= SOFT_LIMIT_DEBRIS_PIECES)) {
|
||||
// cause oldest hull debris chunk to blow up
|
||||
@@ -446,41 +457,6 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
|
||||
db = &Debris[n];
|
||||
|
||||
if (!hull_flag) {
|
||||
if (model_num >= 0) {
|
||||
db->model_num = model_num;
|
||||
db->submodel_num = (myrand() >> 4) % sip->generic_debris_num_submodels;
|
||||
}
|
||||
else if (vaporize) {
|
||||
db->model_num = Debris_vaporize_model;
|
||||
db->submodel_num = (myrand() >> 4) % Debris_num_submodels;
|
||||
}
|
||||
else {
|
||||
db->model_num = Debris_model;
|
||||
db->submodel_num = (myrand() >> 4) % Debris_num_submodels;
|
||||
}
|
||||
}
|
||||
else {
|
||||
db->model_num = model_num;
|
||||
db->submodel_num = submodel_num;
|
||||
}
|
||||
|
||||
db->model_instance_num = model_create_instance(false, db->model_num);
|
||||
|
||||
float radius = submodel_get_radius(db->model_num, db->submodel_num);
|
||||
|
||||
// if its generic, maybe cull it if its too small and far
|
||||
if (!hull_flag) {
|
||||
float dist = vm_vec_dist_quick(pos, &Eye_position);
|
||||
// Make vaporize debris seen from farther away
|
||||
if (vaporize) {
|
||||
dist /= 2.0f;
|
||||
}
|
||||
if (dist > radius * 200.0f) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//WMC - We must survive until now, at least.
|
||||
db->must_survive_until = timestamp();
|
||||
|
||||
@@ -493,15 +469,13 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
// Create Debris piece n!
|
||||
if ( hull_flag ) {
|
||||
if (rand() < RAND_MAX/6) // Make some pieces blow up shortly after explosion.
|
||||
db->lifeleft = 2.0f * (frand()) + 0.5f;
|
||||
db->lifeleft = 2.0f * (myrand() * RAND_MAX_1f) + 0.5f;
|
||||
else {
|
||||
db->flags.set(Debris_Flags::DoNotExpire);
|
||||
db->lifeleft = -1.0f; // large hull pieces stay around forever
|
||||
}
|
||||
} else {
|
||||
// small non-hull pieces should stick around longer the larger they are
|
||||
// sqrtf should make sure its never too crazy long
|
||||
db->lifeleft = (frand() * 2.0f + 0.1f) * sqrtf(radius);
|
||||
} else {
|
||||
db->lifeleft = (myrand() * RAND_MAX_1f) * 2.0f + 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,9 +533,25 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
db->arc_frequency = 0;
|
||||
}
|
||||
|
||||
if ( model_num < 0 ) {
|
||||
if (vaporize) {
|
||||
db->model_num = Debris_vaporize_model;
|
||||
} else {
|
||||
db->model_num = Debris_model;
|
||||
}
|
||||
db->submodel_num = (myrand()>>4) % Debris_num_submodels;
|
||||
} else {
|
||||
db->model_num = model_num;
|
||||
db->submodel_num = submodel_num;
|
||||
}
|
||||
|
||||
db->model_instance_num = model_create_instance(false, db->model_num);
|
||||
|
||||
float radius = submodel_get_radius( db->model_num, db->submodel_num );
|
||||
|
||||
db->next_fireball = timestamp_rand(500,2000); //start one 1/2 - 2 secs later
|
||||
|
||||
if ( pos == nullptr )
|
||||
if ( pos == NULL )
|
||||
pos = &source_obj->pos;
|
||||
|
||||
flagset<Object::Object_Flags> default_flags;
|
||||
@@ -569,18 +559,10 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
default_flags.set(Object::Object_Flags::Physics);
|
||||
default_flags.set(Object::Object_Flags::Collides, hull_flag != 0);
|
||||
|
||||
matrix orient = source_obj->orient;
|
||||
if (!db->is_hull) {
|
||||
// non-hull debris has no relation to its parent orientation
|
||||
vec3d rand;
|
||||
vm_vec_rand_vec(&rand);
|
||||
vm_vector_2_matrix(&orient, &rand);
|
||||
}
|
||||
|
||||
objnum = obj_create( OBJ_DEBRIS, parent_objnum, n, &orient, pos, radius, default_flags);
|
||||
objnum = obj_create( OBJ_DEBRIS, parent_objnum, n, &source_obj->orient, pos, radius, default_flags);
|
||||
if ( objnum == -1 ) {
|
||||
mprintf(("Couldn't create debris object -- out of object slots\n"));
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db->objnum = objnum;
|
||||
@@ -917,13 +899,13 @@ int debris_check_collision(object *pdebris, object *other_obj, vec3d *hitpos, co
|
||||
smi->collision_checked = false;
|
||||
|
||||
// set angles for last frame (need to set to prev to get p0)
|
||||
matrix copy_matrix = smi->canonical_orient;
|
||||
angles copy_angles = smi->angs;
|
||||
|
||||
// find the start and end positions of the sphere in submodel RF
|
||||
smi->canonical_orient = smi->canonical_prev_orient;
|
||||
smi->angs = smi->prev_angs;
|
||||
world_find_model_instance_point(&p0, &light_obj->last_pos, pm, pmi, submodel, &heavy_obj->last_orient, &heavy_obj->last_pos);
|
||||
|
||||
smi->canonical_orient = copy_matrix;
|
||||
smi->angs = copy_angles;
|
||||
world_find_model_instance_point(&p1, &light_obj->pos, pm, pmi, submodel, &heavy_obj->orient, &heavy_obj->pos);
|
||||
|
||||
mc.p0 = &p0;
|
||||
@@ -942,7 +924,7 @@ int debris_check_collision(object *pdebris, object *other_obj, vec3d *hitpos, co
|
||||
|
||||
// set up debris_hit_info for rotating submodel
|
||||
if (debris_hit_info->edge_hit == 0) {
|
||||
model_instance_find_world_dir(&debris_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
|
||||
model_instance_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
|
||||
}
|
||||
|
||||
// find position in submodel RF of light object at collison
|
||||
@@ -974,7 +956,7 @@ int debris_check_collision(object *pdebris, object *other_obj, vec3d *hitpos, co
|
||||
|
||||
// get collision normal if not edge hit
|
||||
if (debris_hit_info->edge_hit == 0) {
|
||||
model_instance_find_world_dir(&debris_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
|
||||
model_instance_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, pm, pmi, mc.hit_submodel, &heavy_obj->orient);
|
||||
}
|
||||
|
||||
// find position in submodel RF of light object at collison
|
||||
@@ -1145,7 +1127,7 @@ void debris_render(object * obj, model_draw_list *scene)
|
||||
MONITOR_INC(NumHullDebrisRend, 1);
|
||||
} else {
|
||||
MONITOR_INC(NumSmallDebrisRend, 1);
|
||||
// render_info.set_flags(MR_NO_LIGHTING);
|
||||
render_info.set_flags(MR_NO_LIGHTING);
|
||||
}
|
||||
|
||||
submodel_render_queue( &render_info, scene, pm, pmi, db->submodel_num, &obj->orient, &obj->pos );
|
||||
@@ -1164,19 +1146,3 @@ bool debris_is_vaporized(debris *db)
|
||||
{
|
||||
return db->model_num == Debris_vaporize_model;
|
||||
}
|
||||
|
||||
void create_generic_debris(object* ship_objp, vec3d* pos, float min_num_debris, float max_num_debris, float speed_mult, bool use_ship_debris) {
|
||||
Assertion(ship_objp->type == OBJ_SHIP, "create_generic_debris called for a non-ship, only ships can spew debris!");
|
||||
if (ship_objp->type != OBJ_SHIP)
|
||||
return;
|
||||
|
||||
float num_debris = frand_range(min_num_debris, max_num_debris);
|
||||
|
||||
num_debris *= (Detail.num_small_debris + 0.5f) / 4.5f;
|
||||
|
||||
vec3d create_pos = *pos;
|
||||
for (int i = 0; i < num_debris; i++) {
|
||||
int model_num = use_ship_debris ? Ship_info[Ships[ship_objp->instance].ship_info_index].generic_debris_model_num : -1;
|
||||
debris_create(ship_objp, model_num, -1, &create_pos, pos, 0, speed_mult);
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ typedef struct debris {
|
||||
int model_instance_num; // What model instance this uses - needed for arcs
|
||||
int submodel_num; // What submodel this uses
|
||||
int next_fireball; // When to start a fireball
|
||||
int is_hull; // indicates whether this is a collideable, destructable piece of debris from the model, or just a generic debris fragment
|
||||
int is_hull; // indicates a large hull chunk of debris
|
||||
int species; // What species this is from. -1 if don't care.
|
||||
int fire_timeout; // timestamp that holds time for fireballs to stop appearing
|
||||
int sound_delay; // timestamp to signal when sound should start
|
||||
@@ -82,8 +82,4 @@ void debris_remove_from_hull_list(debris *db);
|
||||
bool debris_is_generic(debris *db);
|
||||
bool debris_is_vaporized(debris *db);
|
||||
|
||||
// creates a burst of generic debris at hitpos from ship_objp, with a random number between min and max
|
||||
// use_ship_debris is for whether the ship's generic debris should be used, or simply debris01.pof
|
||||
void create_generic_debris(object* ship_objp, vec3d* hitpos, float min_num_debris, float max_num_debris, float speed_mult, bool use_ship_debris);
|
||||
|
||||
#endif // _DEBRIS_H
|
||||
@@ -65,23 +65,15 @@ void GetLightInfo(vec3 position, out vec3 lightDir, out float attenuation)
|
||||
if (lightType == LT_TUBE) { // Tube light
|
||||
float beamLength = length(beamVec);
|
||||
vec3 beamDir = beamVec / beamLength;
|
||||
//The actual 'lighting element' is shorter than the light volume cylinder
|
||||
//To compensate the light is moved forward along the beam one radius and the length shortened
|
||||
//this allows room for clean falloff of the light past the ends of beams.
|
||||
vec3 adjustedLightPos = lightPosition - (beamDir * lightRadius);
|
||||
beamLength = beamLength - (lightRadius * 2.0);
|
||||
//adjustments having been made, lightdir needs recalculating
|
||||
lightDir = adjustedLightPos - position.xyz;
|
||||
// Get nearest point on line
|
||||
float neardist = clamp(dot(lightDir, beamDir), 0.0, beamLength);
|
||||
// Move back from the endpoint of the beam along the beam by the distance we calculated
|
||||
vec3 nearest = adjustedLightPos - beamDir * neardist;
|
||||
vec3 nearest = lightPosition - beamDir * neardist;
|
||||
lightDir = nearest - position.xyz;
|
||||
dist = length(lightDir);
|
||||
if(dist > lightRadius) {
|
||||
discard;
|
||||
}
|
||||
attenuation = 1.0 - clamp(dist / lightRadius, 0.0, 1.0);
|
||||
} else if (lightType == LT_CONE) {
|
||||
float coneDot = dot(normalize(-lightDir), coneDir);
|
||||
if(dualCone) {
|
||||
|
||||
@@ -12,7 +12,7 @@ layout (std140) uniform genericData {
|
||||
vec3 fog_color;
|
||||
float fog_start;
|
||||
|
||||
float fog_density;
|
||||
float fog_scale;
|
||||
float zNear;
|
||||
float zFar;
|
||||
};
|
||||
@@ -23,15 +23,17 @@ void main()
|
||||
|
||||
// If the scene is in fog then we change the color we operate on based on the depth of the pixel
|
||||
float depth_val = texture(depth_tex, fragTexCoord.xy).x;
|
||||
// Transform depth value from [0, 1] to [-1, 1]
|
||||
float depth_normalized = 2 * depth_val - 1;
|
||||
// Transform depth value from [-1, 1] to [0, 1]
|
||||
float depth_normalized = fma(depth_val, 0.5, 0.5);
|
||||
// Now we compute the depth value in projection space using the clipping plane information
|
||||
float view_depth = 2.0 * zNear * zFar / (zFar + zNear - depth_normalized * (zFar - zNear));
|
||||
|
||||
if (isinf(view_depth)) {
|
||||
fragOut0.rgb = color_in.rgb;
|
||||
} else {
|
||||
float fog_dist = clamp(1 - pow(fog_density, view_depth - fog_start) , 0.0, 1.0);
|
||||
// This is the same formula that is also used by the main shader
|
||||
float fog_dist = clamp((view_depth - fog_start) * 0.75 * fog_scale, 0.0, 1.0);
|
||||
|
||||
vec3 finalFogColor = srgb_to_linear(fog_color);
|
||||
|
||||
fragOut0.rgb = mix(color_in.rgb, finalFogColor, fog_dist);
|
||||
|
||||
@@ -221,7 +221,6 @@ $AI:
|
||||
+Can Form Wing: YES
|
||||
+Passive docks: ( "support" )
|
||||
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
|
||||
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
|
||||
$Vaporize Percent Chance: 0.0
|
||||
$Name: Freighter
|
||||
$Counts for Alone: YES
|
||||
@@ -251,7 +250,6 @@ $AI:
|
||||
+Active docks: ( "cargo" )
|
||||
+Passive docks: ( "support" )
|
||||
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
|
||||
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
|
||||
$Vaporize Percent Chance: 0.0
|
||||
$Name: AWACS
|
||||
$Counts for Alone: YES
|
||||
@@ -279,7 +277,6 @@ $AI:
|
||||
+Can Form Wing: YES
|
||||
+Passive docks: ( "support" )
|
||||
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
|
||||
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
|
||||
$Vaporize Percent Chance: 0.0
|
||||
$Name: Gas Miner
|
||||
$Counts for Alone: YES
|
||||
@@ -307,7 +304,6 @@ $AI:
|
||||
+Can Form Wing: YES
|
||||
+Passive docks: ( "support" )
|
||||
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
|
||||
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
|
||||
$Vaporize Percent Chance: 0.0
|
||||
$Name: Cruiser
|
||||
$Counts for Alone: YES
|
||||
@@ -335,7 +331,6 @@ $AI:
|
||||
+Can Form Wing: YES
|
||||
+Passive docks: ( "support" )
|
||||
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
|
||||
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
|
||||
$Vaporize Percent Chance: 0.0
|
||||
$Name: Corvette
|
||||
$Counts for Alone: YES
|
||||
@@ -363,7 +358,6 @@ $AI:
|
||||
+Can Form Wing: YES
|
||||
+Passive docks: ( "support" )
|
||||
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
|
||||
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
|
||||
$Vaporize Percent Chance: 0.0
|
||||
$Name: Capital
|
||||
$Counts for Alone: YES
|
||||
@@ -392,7 +386,6 @@ $AI:
|
||||
+Can Form Wing: YES
|
||||
+Passive docks: ( "support" )
|
||||
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
|
||||
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
|
||||
$Vaporize Percent Chance: 0.0
|
||||
$Name: Super Cap
|
||||
$Counts for Alone: YES
|
||||
@@ -419,7 +412,6 @@ $AI:
|
||||
+Can Form Wing: YES
|
||||
+Passive docks: ( "support" )
|
||||
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
|
||||
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
|
||||
$Vaporize Percent Chance: 0.0
|
||||
$Name: Drydock
|
||||
$Counts for Alone: YES
|
||||
@@ -444,7 +436,6 @@ $AI:
|
||||
+Turrets attack this: YES
|
||||
+Passive docks: ( "support" )
|
||||
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
|
||||
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
|
||||
$Vaporize Percent Chance: 0.0
|
||||
$Name: Knossos Device
|
||||
$Counts for Alone: YES
|
||||
@@ -467,6 +458,5 @@ $AI:
|
||||
+Turrets attack this: YES
|
||||
+Passive docks: ( "support" )
|
||||
+Ignored on cripple by: ( "navbuoy" "sentry gun" "escape pod" "cargo" "support" "stealth" "fighter" "bomber" "fighter/bomber" "transport" "freighter" "awacs" "gas miner" "cruiser" "corvette" "capital" "super cap" "drydock" "knossos device" )
|
||||
+Targeted by 'Huge' weapons and Ignored by 'small only' weapons: YES
|
||||
$Vaporize Percent Chance: 0.0
|
||||
#End
|
||||
+102
-100
@@ -14,7 +14,6 @@
|
||||
#include "graphics/tmapper.h"
|
||||
#include "localization/localize.h"
|
||||
#include "model/model.h"
|
||||
#include "nebula/neb.h"
|
||||
#include "object/object.h"
|
||||
#include "options/Option.h"
|
||||
#include "parse/parselo.h"
|
||||
@@ -32,15 +31,13 @@ int Knossos_warp_ani_used;
|
||||
|
||||
#define MAX_WARP_LOD 0
|
||||
|
||||
constexpr int INTITIAL_FIREBALL_CONTAINTER_SIZE = 256;
|
||||
|
||||
SCP_vector<fireball> Fireballs;
|
||||
SCP_vector<int> Unused_fireball_indices;
|
||||
fireball Fireballs[MAX_FIREBALLS];
|
||||
|
||||
fireball_info Fireball_info[MAX_FIREBALL_TYPES];
|
||||
|
||||
int fireball_used[MAX_FIREBALL_TYPES];
|
||||
|
||||
int Num_fireballs = 0;
|
||||
int Num_fireball_types = 0;
|
||||
|
||||
bool fireballs_inited = false;
|
||||
@@ -287,13 +284,11 @@ static void parse_fireball_tbl(const char *table_filename)
|
||||
}
|
||||
}
|
||||
|
||||
bool first_time;
|
||||
// now select our entry accordingly...
|
||||
// are we using a previous entry?
|
||||
if (existing_idx >= 0)
|
||||
{
|
||||
fi = &Fireball_info[existing_idx];
|
||||
first_time = false;
|
||||
}
|
||||
// we are creating a new entry, so set some defaults
|
||||
else
|
||||
@@ -317,7 +312,6 @@ static void parse_fireball_tbl(const char *table_filename)
|
||||
fireball_set_default_warp_attributes(Num_fireball_types);
|
||||
|
||||
Num_fireball_types++;
|
||||
first_time = true;
|
||||
}
|
||||
|
||||
// copy over what we already parsed
|
||||
@@ -325,7 +319,6 @@ static void parse_fireball_tbl(const char *table_filename)
|
||||
strcpy_s(fi->unique_id, unique_id);
|
||||
strcpy_s(fi->lod[0].filename, fireball_filename);
|
||||
|
||||
|
||||
// Do we have a LOD num?
|
||||
if (optional_string("$LOD:"))
|
||||
{
|
||||
@@ -333,9 +326,6 @@ static void parse_fireball_tbl(const char *table_filename)
|
||||
|
||||
if (fi->lod_count > MAX_FIREBALL_LOD)
|
||||
fi->lod_count = MAX_FIREBALL_LOD;
|
||||
} else if (first_time) {
|
||||
//assume a LOD of at least 1
|
||||
fi->lod_count = 1;
|
||||
}
|
||||
|
||||
// check for particular lighting color
|
||||
@@ -445,6 +435,8 @@ void fireball_load_data()
|
||||
// This will get called at the start of each level.
|
||||
void fireball_init()
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( !fireballs_inited ) {
|
||||
// Do all the processing that happens only once
|
||||
fireball_parse_tbl();
|
||||
@@ -454,10 +446,10 @@ void fireball_init()
|
||||
}
|
||||
|
||||
// Reset everything between levels
|
||||
Fireballs.clear();
|
||||
Fireballs.reserve(INTITIAL_FIREBALL_CONTAINTER_SIZE);
|
||||
Unused_fireball_indices.clear();
|
||||
Unused_fireball_indices.reserve(INTITIAL_FIREBALL_CONTAINTER_SIZE);
|
||||
Num_fireballs = 0;
|
||||
for (i=0; i<MAX_FIREBALLS; i++ ) {
|
||||
Fireballs[i].objnum = -1;
|
||||
}
|
||||
|
||||
// Goober5000 - reset Knossos warp flag
|
||||
Knossos_warp_ani_used = 0;
|
||||
@@ -475,15 +467,13 @@ void fireball_delete( object * obj )
|
||||
fireball *fb;
|
||||
|
||||
num = obj->instance;
|
||||
// Make sure the new system works fine.
|
||||
Assert(obj->instance > -1);
|
||||
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
|
||||
fb = &Fireballs[num];
|
||||
|
||||
Assert( fb->objnum == OBJ_INDEX(obj));
|
||||
|
||||
Fireballs[num].objnum = -1;
|
||||
Unused_fireball_indices.push_back(num);
|
||||
Num_fireballs--;
|
||||
Assert( Num_fireballs >= 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -491,9 +481,13 @@ void fireball_delete( object * obj )
|
||||
*/
|
||||
void fireball_delete_all()
|
||||
{
|
||||
for (auto& current_fireball : Fireballs) {
|
||||
if ( current_fireball.objnum != -1 ) {
|
||||
obj_delete(current_fireball.objnum);
|
||||
fireball *fb;
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < MAX_FIREBALLS; i++ ) {
|
||||
fb = &Fireballs[i];
|
||||
if ( fb->objnum != -1 ) {
|
||||
obj_delete(fb->objnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,8 +499,6 @@ void fireball_set_framenum(int num)
|
||||
fireball_info *fd;
|
||||
fireball_lod *fl;
|
||||
|
||||
Assert(static_cast<int>(Fireballs.size()) > num);
|
||||
|
||||
fb = &Fireballs[num];
|
||||
fd = &Fireball_info[Fireballs[num].fireball_info_index];
|
||||
|
||||
@@ -545,9 +537,6 @@ int fireball_is_perishable(object * obj)
|
||||
|
||||
num = obj->instance;
|
||||
objnum = OBJ_INDEX(obj);
|
||||
// Make sure the new system works fine.
|
||||
Assert(obj->instance > -1);
|
||||
Assert((int)Fireballs.size() > obj->instance);
|
||||
Assert( Fireballs[num].objnum == objnum );
|
||||
|
||||
fb = &Fireballs[num];
|
||||
@@ -564,6 +553,43 @@ int fireball_is_perishable(object * obj)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* There are too many fireballs, so delete the oldest small one
|
||||
* to free up a slot.
|
||||
*
|
||||
* @return The fireball slot freed.
|
||||
*/
|
||||
int fireball_free_one()
|
||||
{
|
||||
fireball *fb;
|
||||
int i;
|
||||
|
||||
int oldest_objnum = -1, oldest_slotnum = -1;
|
||||
float lifeleft, oldest_lifeleft = 0.0f;
|
||||
|
||||
for ( i = 0; i < MAX_FIREBALLS; i++ ) {
|
||||
fb = &Fireballs[i];
|
||||
|
||||
// only remove the ones that aren't warp effects
|
||||
if ( (fb->objnum >= 0) && fireball_is_perishable(&Objects[fb->objnum]) ) {
|
||||
|
||||
lifeleft = fb->total_time - fb->time_elapsed;
|
||||
if ( (oldest_objnum < 0) || (lifeleft < oldest_lifeleft) ) {
|
||||
oldest_slotnum = i;
|
||||
oldest_lifeleft = lifeleft;
|
||||
oldest_objnum = fb->objnum;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( oldest_objnum > -1 ) {
|
||||
obj_delete(oldest_objnum);
|
||||
}
|
||||
return oldest_slotnum;
|
||||
}
|
||||
|
||||
int fireball_is_warp(object * obj)
|
||||
{
|
||||
int num, objnum;
|
||||
@@ -571,9 +597,6 @@ int fireball_is_warp(object * obj)
|
||||
|
||||
num = obj->instance;
|
||||
objnum = OBJ_INDEX(obj);
|
||||
// Make sure the new system works fine.
|
||||
Assert(obj->instance > -1);
|
||||
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
|
||||
Assert( Fireballs[num].objnum == objnum );
|
||||
|
||||
fb = &Fireballs[num];
|
||||
@@ -618,9 +641,6 @@ void fireball_process_post(object * obj, float frame_time)
|
||||
|
||||
num = obj->instance;
|
||||
objnum = OBJ_INDEX(obj);
|
||||
// Make sure the new system works fine.
|
||||
Assert(obj->instance > -1);
|
||||
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
|
||||
Assert( Fireballs[num].objnum == objnum );
|
||||
|
||||
fb = &Fireballs[num];
|
||||
@@ -645,10 +665,6 @@ float fireball_lifeleft( object *obj )
|
||||
|
||||
num = obj->instance;
|
||||
objnum = OBJ_INDEX(obj);
|
||||
// Make sure the new system works fine.
|
||||
Assert(obj->instance > -1);
|
||||
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
|
||||
|
||||
Assert( Fireballs[num].objnum == objnum );
|
||||
|
||||
fb = &Fireballs[num];
|
||||
@@ -664,10 +680,6 @@ float fireball_lifeleft_percent( object *obj )
|
||||
int num, objnum;
|
||||
fireball *fb;
|
||||
|
||||
// Make sure the new system works fine.
|
||||
Assert(obj->instance > -1);
|
||||
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
|
||||
|
||||
num = obj->instance;
|
||||
objnum = OBJ_INDEX(obj);
|
||||
Assert( Fireballs[num].objnum == objnum );
|
||||
@@ -769,8 +781,10 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
|
||||
{
|
||||
int n, objnum, fb_lod;
|
||||
object *obj;
|
||||
fireball *fb;
|
||||
fireball_info *fd;
|
||||
fireball_lod *fl;
|
||||
|
||||
Assert( fireball_type > -1 );
|
||||
Assert( fireball_type < Num_fireball_types );
|
||||
|
||||
@@ -786,21 +800,23 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
|
||||
}
|
||||
}
|
||||
|
||||
if (Num_objects >= MAX_OBJECTS) {
|
||||
return -1;
|
||||
if ( (Num_fireballs >= MAX_FIREBALLS) || (Num_objects >= MAX_OBJECTS) ) {
|
||||
|
||||
// out of slots, so free one up.
|
||||
n = fireball_free_one();
|
||||
if ( n < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
for ( n = 0; n < MAX_FIREBALLS; n++ ) {
|
||||
if ( Fireballs[n].objnum < 0 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert( n != MAX_FIREBALLS );
|
||||
}
|
||||
|
||||
|
||||
if (!Unused_fireball_indices.empty()) {
|
||||
n = Unused_fireball_indices.back();
|
||||
Unused_fireball_indices.pop_back();
|
||||
}
|
||||
else {
|
||||
n = static_cast<int>(Fireballs.size());
|
||||
Fireballs.emplace_back();
|
||||
}
|
||||
|
||||
fireball* new_fireball = &Fireballs[n];
|
||||
fb = &Fireballs[n];
|
||||
|
||||
// get an lod to use
|
||||
fb_lod = fireball_get_lod(pos, fd, size);
|
||||
@@ -817,13 +833,13 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
|
||||
}
|
||||
fl = &fd->lod[fb_lod];
|
||||
|
||||
new_fireball->lod = (char)fb_lod;
|
||||
fb->lod = (char)fb_lod;
|
||||
|
||||
new_fireball->flags = extra_flags;
|
||||
new_fireball->warp_open_sound_index = warp_open_sound;
|
||||
new_fireball->warp_close_sound_index = warp_close_sound;
|
||||
new_fireball->warp_open_duration = (warp_open_duration < 0.0f) ? WARPHOLE_GROW_TIME : warp_open_duration;
|
||||
new_fireball->warp_close_duration = (warp_close_duration < 0.0f) ? WARPHOLE_GROW_TIME : warp_close_duration;
|
||||
fb->flags = extra_flags;
|
||||
fb->warp_open_sound_index = warp_open_sound;
|
||||
fb->warp_close_sound_index = warp_close_sound;
|
||||
fb->warp_open_duration = (warp_open_duration < 0.0f) ? WARPHOLE_GROW_TIME : warp_open_duration;
|
||||
fb->warp_close_duration = (warp_close_duration < 0.0f) ? WARPHOLE_GROW_TIME : warp_close_duration;
|
||||
|
||||
matrix orient;
|
||||
if(orient_override != NULL){
|
||||
@@ -840,53 +856,58 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
|
||||
default_flags.set(Object::Object_Flags::Renders);
|
||||
objnum = obj_create(OBJ_FIREBALL, parent_obj, n, &orient, pos, size, default_flags);
|
||||
|
||||
if (objnum < 0) {
|
||||
Int3(); // Get John, we ran out of objects for fireballs
|
||||
return objnum;
|
||||
}
|
||||
|
||||
obj = &Objects[objnum];
|
||||
|
||||
new_fireball->fireball_info_index = fireball_type;
|
||||
new_fireball->fireball_render_type = render_type;
|
||||
new_fireball->time_elapsed = 0.0f;
|
||||
new_fireball->objnum = objnum;
|
||||
new_fireball->current_bitmap = -1;
|
||||
fb->fireball_info_index = fireball_type;
|
||||
fb->fireball_render_type = render_type;
|
||||
fb->time_elapsed = 0.0f;
|
||||
fb->objnum = objnum;
|
||||
fb->current_bitmap = -1;
|
||||
|
||||
switch( new_fireball->fireball_render_type ) {
|
||||
switch( fb->fireball_render_type ) {
|
||||
|
||||
case FIREBALL_MEDIUM_EXPLOSION:
|
||||
new_fireball->orient = (myrand()>>8) & 7; // 0 - 7
|
||||
fb->orient = (myrand()>>8) & 7; // 0 - 7
|
||||
break;
|
||||
|
||||
case FIREBALL_LARGE_EXPLOSION:
|
||||
new_fireball->orient = (myrand()>>8) % 360; // 0 - 359
|
||||
fb->orient = (myrand()>>8) % 360; // 0 - 359
|
||||
break;
|
||||
|
||||
case FIREBALL_WARP_EFFECT:
|
||||
// Play sound effect for warp hole opening up
|
||||
fireball_play_warphole_open_sound(ship_class, new_fireball);
|
||||
fireball_play_warphole_open_sound(ship_class, fb);
|
||||
|
||||
// warp in type
|
||||
if (reverse) {
|
||||
new_fireball->orient = 1;
|
||||
fb->orient = 1;
|
||||
// if warp out, then reverse the orientation
|
||||
vm_vec_scale( &obj->orient.vec.fvec, -1.0f ); // Reverse the forward vector
|
||||
vm_vec_scale( &obj->orient.vec.rvec, -1.0f ); // Reverse the right vector
|
||||
} else {
|
||||
new_fireball->orient = 0;
|
||||
fb->orient = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE("Bad type set in fireball_create");
|
||||
Int3();
|
||||
break;
|
||||
}
|
||||
|
||||
if ( new_fireball->fireball_render_type == FIREBALL_WARP_EFFECT ) {
|
||||
if ( fb->fireball_render_type == FIREBALL_WARP_EFFECT ) {
|
||||
Assert( warp_lifetime >= 4.0f ); // Warp lifetime must be at least 4 seconds!
|
||||
if ( warp_lifetime < 4.0f )
|
||||
warp_lifetime = 4.0f;
|
||||
new_fireball->total_time = warp_lifetime; // in seconds
|
||||
fb->total_time = warp_lifetime; // in seconds
|
||||
} else {
|
||||
new_fireball->total_time = i2fl(fl->num_frames) / fl->fps; // in seconds
|
||||
fb->total_time = i2fl(fl->num_frames) / fl->fps; // in seconds
|
||||
}
|
||||
|
||||
|
||||
fireball_set_framenum(n);
|
||||
|
||||
if ( velocity ) {
|
||||
@@ -902,6 +923,7 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
|
||||
vm_vec_zero(&obj->phys_info.max_rotvel);
|
||||
}
|
||||
|
||||
Num_fireballs++;
|
||||
return objnum;
|
||||
}
|
||||
|
||||
@@ -1036,33 +1058,24 @@ void fireball_render(object* obj, model_draw_list *scene)
|
||||
|
||||
MONITOR_INC( NumFireballsRend, 1 );
|
||||
|
||||
// Make sure the new system works fine.
|
||||
Assert(obj->instance > -1);
|
||||
Assert(static_cast<int>(Fireballs.size()) > obj->instance);
|
||||
|
||||
num = obj->instance;
|
||||
fb = &Fireballs[num];
|
||||
|
||||
if ( fb->current_bitmap < 0 )
|
||||
if ( Fireballs[num].current_bitmap < 0 )
|
||||
return;
|
||||
|
||||
float alpha = 1.0f;
|
||||
|
||||
if (The_mission.flags[Mission::Mission_Flags::Fullneb] && Neb_affects_fireballs)
|
||||
alpha *= neb2_get_fog_visibility(&obj->pos, NEB_FOG_VISIBILITY_MULT_FIREBALL(obj->radius));
|
||||
|
||||
g3_transfer_vertex(&p, &obj->pos);
|
||||
|
||||
switch ( fb->fireball_render_type ) {
|
||||
|
||||
case FIREBALL_MEDIUM_EXPLOSION: {
|
||||
batching_add_volume_bitmap(fb->current_bitmap, &p, fb->orient, obj->radius, alpha);
|
||||
batching_add_volume_bitmap(Fireballs[num].current_bitmap, &p, fb->orient, obj->radius);
|
||||
}
|
||||
break;
|
||||
|
||||
case FIREBALL_LARGE_EXPLOSION: {
|
||||
// Make the big explosions rotate with the viewer.
|
||||
batching_add_volume_bitmap_rotated(fb->current_bitmap, &p, (i2fl(fb->orient)*PI) / 180.0f, obj->radius, alpha);
|
||||
batching_add_volume_bitmap_rotated(Fireballs[num].current_bitmap, &p, (i2fl(fb->orient)*PI) / 180.0f, obj->radius);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1080,14 +1093,3 @@ void fireball_render(object* obj, model_draw_list *scene)
|
||||
Int3();
|
||||
}
|
||||
}
|
||||
|
||||
// Because fireballs are only added and removed in two places, and Unused_fireball_indices is always updated in those places to contain unused indices,
|
||||
// this very simple code will give you the correct count of currently existing fireballs in use.
|
||||
int fireball_get_count()
|
||||
{
|
||||
int count = static_cast<int>(Fireballs.size()) - static_cast<int>(Unused_fireball_indices.size());
|
||||
|
||||
Assert (count >= 0);
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -95,7 +95,8 @@ typedef struct fireball {
|
||||
} fireball;
|
||||
// end move
|
||||
|
||||
extern SCP_vector<fireball> Fireballs;
|
||||
extern fireball Fireballs[MAX_FIREBALLS];
|
||||
extern int Num_fireballs;
|
||||
|
||||
extern bool fireballs_inited;
|
||||
|
||||
@@ -145,7 +146,4 @@ extern int Knossos_warp_ani_used;
|
||||
|
||||
extern bool Fireball_use_3d_warp;
|
||||
|
||||
// Cyborg - get a count of how many valid fireballs are in the mission.
|
||||
int fireball_get_count();
|
||||
|
||||
#endif /* _FIREBALLS_H */
|
||||
@@ -91,7 +91,7 @@ void warpin_queue_render(model_draw_list *scene, object *obj, matrix *orient, ve
|
||||
|
||||
g3_transfer_vertex( &verts[4], &vecs[4] );
|
||||
|
||||
float alpha = (The_mission.flags[Mission::Mission_Flags::Fullneb]) ? neb2_get_fog_visibility(obj) : 1.0f;
|
||||
float alpha = (The_mission.flags[Mission::Mission_Flags::Fullneb]) ? (1.0f - neb2_get_fog_intensity(obj)) : 1.0f;
|
||||
|
||||
//batch_add_bitmap(warp_glow_bitmap, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT, &verts[4], 0, r, alpha);
|
||||
batching_add_bitmap(warp_glow_bitmap, &verts[4], 0, r, alpha);
|
||||
|
||||
@@ -112,27 +112,35 @@ static int Current_help_page;
|
||||
|
||||
void gameplay_help_blit_control_line(int x, int y, int id)
|
||||
{
|
||||
int has_key=0, has_joy=0;
|
||||
char buf[256];
|
||||
config_item *ci;
|
||||
|
||||
auto ci = &Control_config[id];
|
||||
ci = &Control_config[id];
|
||||
|
||||
buf[0] = 0;
|
||||
|
||||
strcpy_s(buf, ci->first.textify().c_str());
|
||||
|
||||
if (!ci->second.empty()) {
|
||||
strcat_s(buf, XSTR( ", ", 129));
|
||||
strcat_s(buf, ci->second.textify().c_str());
|
||||
if ( ci->key_id >= 0 ) {
|
||||
strcpy_s(buf, textify_scancode(ci->key_id));
|
||||
has_key=1;
|
||||
}
|
||||
|
||||
if (ci->empty()) {
|
||||
if ( ci->joy_id >= 0 ) {
|
||||
if ( has_key ) {
|
||||
strcat_s(buf, XSTR( ", ", 129));
|
||||
}
|
||||
strcat_s(buf, Joy_button_text[ci->joy_id]);
|
||||
has_joy=1;
|
||||
}
|
||||
|
||||
if ( !has_key && !has_joy ) {
|
||||
strcpy_s(buf, XSTR( "no binding", 130));
|
||||
}
|
||||
|
||||
gr_string(x,y,buf,GR_RESIZE_MENU);
|
||||
|
||||
// gr_string(x+KEY_DESCRIPTION_OFFSET,y,ci->text,GR_RESIZE_MENU);
|
||||
gr_string(x+KEY_DESCRIPTION_OFFSET, y, XSTR(ci->text.c_str(), CONTROL_CONFIG_XSTR + id), GR_RESIZE_MENU);
|
||||
gr_string(x+KEY_DESCRIPTION_OFFSET, y, XSTR(ci->text, CONTROL_CONFIG_XSTR + id), GR_RESIZE_MENU);
|
||||
}
|
||||
|
||||
void gameplay_help_blit_control_line_raw(int x, int y, const char *control_text, const char *control_description)
|
||||
|
||||
@@ -304,10 +304,6 @@ gamesnd_id gamesnd_get_by_name(const char* name)
|
||||
{
|
||||
Assert( Snds.size() <= INT_MAX );
|
||||
|
||||
// empty name is not valid!
|
||||
if (name == nullptr || name[0] == '\0')
|
||||
return gamesnd_id(-1);
|
||||
|
||||
int index = gamesnd_lookup_name(name, Snds);
|
||||
|
||||
if (index < 0)
|
||||
@@ -679,9 +675,11 @@ void parse_gamesnd_old(game_snd* gs)
|
||||
{
|
||||
int temp_min, temp_max;
|
||||
|
||||
if (stuff_int_optional(&temp_min) == 2)
|
||||
ignore_gray_space();
|
||||
if (stuff_int_optional(&temp_min, true) == 2)
|
||||
{
|
||||
if (stuff_int_optional(&temp_max) == 2)
|
||||
ignore_gray_space();
|
||||
if (stuff_int_optional(&temp_max, true) == 2)
|
||||
{
|
||||
mprintf(("Dutifully converting retail sound %s, '%s' to a 3D sound...\n", gs->name.c_str(), entry.filename));
|
||||
is_3d = 1;
|
||||
@@ -695,7 +693,8 @@ void parse_gamesnd_old(game_snd* gs)
|
||||
}
|
||||
|
||||
// check for extra values per Mantis #2408
|
||||
if (stuff_int_optional(&temp) == 2)
|
||||
ignore_gray_space();
|
||||
if (stuff_int_optional(&temp, true) == 2)
|
||||
{
|
||||
Warning(LOCATION, "Unexpected extra value %d found for sound '%s' (filename '%s')! Check the format of the sounds.tbl (or .tbm) entry.", temp, gs->name.c_str(), entry.filename);
|
||||
}
|
||||
|
||||
@@ -511,19 +511,3 @@ void insertion_sort(void *array_base, size_t array_size, size_t element_size, in
|
||||
// free the allocated space
|
||||
free(current);
|
||||
}
|
||||
|
||||
// Stuff that can't be included in vmallocator.h
|
||||
|
||||
std::locale SCP_default_locale("");
|
||||
|
||||
void SCP_tolower(char *str)
|
||||
{
|
||||
for (; *str != '\0'; ++str)
|
||||
*str = SCP_tolower(*str);
|
||||
}
|
||||
|
||||
void SCP_toupper(char *str)
|
||||
{
|
||||
for (; *str != '\0'; ++str)
|
||||
*str = SCP_toupper(*str);
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
* z64555's Undo system, created for the FreeSpace Source Code project
|
||||
*/
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/undosys.h"
|
||||
#include "globalincs/vmallocator.h"
|
||||
|
||||
// Pure virtual deconstructors must be defined.
|
||||
Undo_item_base::~Undo_item_base() = default;
|
||||
|
||||
Undo_system::Undo_system()
|
||||
: max_undos(10) {};
|
||||
|
||||
Undo_system::Undo_system(size_t _undos)
|
||||
: max_undos(_undos) {};
|
||||
|
||||
void Undo_system::clamp_stacks() {
|
||||
while (undo_stack.size() > max_undos) {
|
||||
// If we're past the stack limit, de-construct and remove until we're in the limit
|
||||
// If max_undos is held constant (as it should be), the loop isn't necassary.
|
||||
// But, it also isn't that much different from an if{} when assembled, so there's no harm in using it
|
||||
delete undo_stack.front();
|
||||
undo_stack.pop_front();
|
||||
}
|
||||
|
||||
// This while() shouldn't ever be triggered, but this is a safety to prevent any weird edge-cases.
|
||||
while (redo_stack.size() > max_undos) {
|
||||
delete redo_stack.front();
|
||||
undo_stack.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void Undo_system::clear() {
|
||||
clear_redo();
|
||||
|
||||
for (auto &element : undo_stack) {
|
||||
delete element;
|
||||
}
|
||||
undo_stack.clear();
|
||||
}
|
||||
|
||||
void Undo_system::clear_redo() {
|
||||
for (auto &element : redo_stack) {
|
||||
delete element;
|
||||
}
|
||||
redo_stack.clear();
|
||||
}
|
||||
|
||||
size_t Undo_system::save_stack(Undo_stack& stack) {
|
||||
if (stack.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
clear_redo();
|
||||
|
||||
// Copy the stack onto the heap, and push it into our undo_stack as a single item
|
||||
Undo_item_base *new_item = new Undo_stack(stack);
|
||||
|
||||
Assert(new_item != nullptr);
|
||||
undo_stack.push_back(new_item);
|
||||
|
||||
// Input stack is told to untrack the items, so that there's only one deletion handler for them
|
||||
stack.untrack();
|
||||
|
||||
clamp_stacks();
|
||||
|
||||
return undo_stack.size();
|
||||
}
|
||||
|
||||
std::pair<const void*, const void*> Undo_system::redo() {
|
||||
std::pair<const void*, const void*> retval;
|
||||
|
||||
if (redo_stack.empty()) {
|
||||
// Nothing to redo
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
|
||||
auto &item = redo_stack.back();
|
||||
retval = item->restore();
|
||||
undo_stack.push_back(item);
|
||||
redo_stack.pop_back();
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
std::pair<const void*, const void*> Undo_system::undo() {
|
||||
std::pair<const void*, const void*> retval;
|
||||
|
||||
if (undo_stack.empty()) {
|
||||
// Nothing to undo
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
|
||||
auto &item = undo_stack.back();
|
||||
retval = item->restore();
|
||||
redo_stack.push_back(item);
|
||||
undo_stack.pop_back();
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
bool Undo_system::empty() {
|
||||
return undo_stack.empty();
|
||||
}
|
||||
|
||||
bool Undo_system::empty_redo() {
|
||||
return redo_stack.empty();
|
||||
}
|
||||
size_t Undo_system::size() {
|
||||
return undo_stack.size();
|
||||
}
|
||||
|
||||
size_t Undo_system::size_redo() {
|
||||
return redo_stack.size();
|
||||
}
|
||||
|
||||
|
||||
Undo_stack::Undo_stack()
|
||||
: reverse(false) {
|
||||
}
|
||||
|
||||
Undo_stack::Undo_stack(size_t size)
|
||||
: reverse(false) {
|
||||
stack.reserve(size);
|
||||
}
|
||||
|
||||
Undo_stack::~Undo_stack() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void Undo_stack::reserve(size_t size) {
|
||||
stack.reserve(size);
|
||||
}
|
||||
|
||||
std::pair<const void*, const void*> Undo_stack::restore() {
|
||||
std::pair<const void*, const void*> retval;
|
||||
|
||||
if (reverse) {
|
||||
for (auto it = stack.rbegin(); it != stack.rend(); ++it) {
|
||||
retval = (*it)->restore();
|
||||
}
|
||||
} else {
|
||||
for (auto &element : stack) {
|
||||
retval = element->restore();
|
||||
}
|
||||
}
|
||||
|
||||
reverse = !reverse;
|
||||
return retval;
|
||||
}
|
||||
|
||||
size_t Undo_stack::size() {
|
||||
return stack.size();
|
||||
}
|
||||
|
||||
void Undo_stack::untrack() {
|
||||
stack.clear();
|
||||
}
|
||||
|
||||
void Undo_stack::clear() {
|
||||
for (auto &element : stack) {
|
||||
delete element;
|
||||
}
|
||||
stack.clear();
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
/*
|
||||
* z64555's Undo system, created for the FreeSpace Source Code project
|
||||
*/
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/vmallocator.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
/**********************************************************
|
||||
Usage:
|
||||
First, you need an instance of the Undo_system within your module:
|
||||
{
|
||||
Undo_styem Undo_controls;
|
||||
}
|
||||
|
||||
From now on, you can save and restore items. (Just be sure to save them before you change them!)
|
||||
{
|
||||
Undo_controls.save(Control_config[z]); // 1. Save the item!!
|
||||
Control_config[z].bind(cid(CID_KEYBOARD, KEY_UP)); // 2. Make changes to the item!!
|
||||
Undo_controls.undo(); // 3. Undo's the changes you did in step 2!
|
||||
Undo_controls.redo(); // 4. Redo's the changes you did in step 2!
|
||||
}
|
||||
The undo system can save any type of data, you don't have to have an instance of a system per data type.
|
||||
!!NOTE!! C style arrays and strings should be wrapped in a std::array when saving. Simply passing the array/string's head won't save the entire array.
|
||||
{
|
||||
Undo_controls.save(std::array<int, JOY_AXIS_ACTIONS>(Axis_map_to));
|
||||
}
|
||||
|
||||
In several cases, the client code would like to know exactly what was changed. The undo system provides a mechanism for doing this:
|
||||
{
|
||||
Undo_controls.save(Control_config[z], &Control_config[0]); // Save the item, and a reference to its container. Here, we use the location of the first item in Control_config as our reference
|
||||
Undo_controls.save(Axis_map_to[z], Axis_map_to); // Saves an axis mapping, here, we use the array's head pointer as our reference (since a int[] is the same as a int*)
|
||||
|
||||
std::pair<const void*, const void*> ref = Undo_controls.undo(); // ::undo() returns a std::pair<const void*, const void*>, the first member referencing the item that changed, and the second referencing the item's container (if we provided it)
|
||||
|
||||
// Once we've stored the return value from ::undo, we can found out what the item is by comparing the second member with containers that we've been saving
|
||||
if (ref.second == &Control_config[0]) {
|
||||
cout << "I'm a button!";
|
||||
Tab = static_cast<Config_item>(ref.first).tab; // Sets the selected tab within the Controls Config menu. We do a static cast here so we can access the Config_item::tab member
|
||||
} else if (ref.second == Axis_map_to) {
|
||||
cout << "I'm an axis!";
|
||||
Tab = SHIP_TAB; // Sets the selected tab within the Controls config menu. Can't do a cast on this one, since it's a simple C array. But we already know where the axes are kept (on the ship tab)
|
||||
} else {
|
||||
cout << "I don't know what I am!!! D:"; // Usually an error condition. Depending on the client code this can be fatal or just a minor setback
|
||||
}
|
||||
}
|
||||
|
||||
Lastly, you can make stacks of undo operations, so that a single op in the system can undo/redo multiple item changes.
|
||||
{
|
||||
Undo_stack stack; // We'll want to save multiple changes as a single operation. So we have to make a stack.
|
||||
|
||||
for (int i = 0; i < JOY_AXIS_ACTIONS; ++i) {
|
||||
stack.save(Axis_map_to[i], Axis_map_to); // This example saves a C style array. Does the same thing without a wrapper, but wasteful
|
||||
|
||||
Undo_controls.save_stack(stack) // Save the stack as a single item!
|
||||
}
|
||||
|
||||
std::pair<const void*, const void*> ref = Undo_controls.undo();
|
||||
// Undo rolls through the items as they were saved, should your data operate on the same location, this will apply the changes in the correct sequence
|
||||
ref.second == Axis_map_to;
|
||||
ref.first == &Axis_map_to[0]; // ::first references the _last_ item that the undo stack changed
|
||||
|
||||
//Also, performing an undo reverses the stack. So when you do a system undo, the changes are applied in reverse
|
||||
ref = Undo_controls.redo();
|
||||
ref.second == Axis_map_to;
|
||||
ref.first == &Axis_map_to[JOY_AXIS_ACTIONS - 1]; // ::first references the _last_ item that the undo stack changed
|
||||
|
||||
***********************************************************/
|
||||
|
||||
/*!
|
||||
* @brief Base class of Undo_item. Use this when making an undo stack!
|
||||
* @sa Undo_item
|
||||
*/
|
||||
class Undo_item_base
|
||||
{
|
||||
public:
|
||||
virtual ~Undo_item_base() = 0;
|
||||
virtual std::pair<const void*, const void*> restore() = 0;
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Class which handles undo operations.
|
||||
* @details This class works with a single data item, to make a undo or redo stack you'd use something like a
|
||||
* std::vector or a std::deque of this class. Undo and Redo stacks would be separate. Before restoring data from
|
||||
* either, you'd save a copy of the item in the opposite stack.
|
||||
*
|
||||
* For example, if you're about to restore from the undo stack, you'd first save a copy of the current data to the
|
||||
* redo stack, then do the undo. The Undo_item in the undo stack can then be popped off the stack.
|
||||
*
|
||||
* @note If you want to save changes to an entire C-style array (such as 'int array[]'), try wrapping it in a std::array first.
|
||||
*
|
||||
* @sa Undo_item_base
|
||||
*/
|
||||
template<typename T> class Undo_item : public Undo_item_base
|
||||
{
|
||||
public:
|
||||
Undo_item()
|
||||
: dest(nullptr), data(nullptr) {};
|
||||
|
||||
Undo_item(T& _data) {
|
||||
save(_data);
|
||||
};
|
||||
|
||||
Undo_item(T& _data, T* _cont) {
|
||||
save(_data, _cont);
|
||||
};
|
||||
|
||||
~Undo_item() override {
|
||||
if (data != nullptr) {
|
||||
delete data;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Restores the saved data
|
||||
*
|
||||
* @returns A pair of const void* referencing the affected item and its container (if it was provided)
|
||||
* @details swaps the data with the destination's, effectively making an undo item into a redo item, and vice versa
|
||||
*/
|
||||
std::pair<const void*, const void*> restore() override {
|
||||
Assert((dest != nullptr) && (data != nullptr));
|
||||
|
||||
std::swap(*static_cast<T*>(dest),*static_cast<T*>(data));
|
||||
|
||||
return std::pair<const void*, const void*>(dest, container);
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Saves data and its destination as an undo item
|
||||
*
|
||||
* @param[in] _data The data to save
|
||||
* @param[in] _cont The container this data is originally found in
|
||||
* @details The explicit definition is here to ensure type safety
|
||||
*/
|
||||
void save(T& _data, T* _cont = nullptr) {
|
||||
|
||||
dest = &_data;
|
||||
data = new T(_data);
|
||||
container = _cont;
|
||||
};
|
||||
|
||||
private:
|
||||
T* dest; //!< Destination of the data
|
||||
T* data; //!< Reference to a copy of data on the heap
|
||||
T* container; //!< Optional reference to this item's container
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Class which handles multiple undo operations as a single op within the Undo_system
|
||||
* @note Caution: Upon deconstruction the tracked Undo_items are deleted.
|
||||
*/
|
||||
class Undo_stack : public Undo_item_base
|
||||
{
|
||||
public:
|
||||
Undo_stack();
|
||||
|
||||
Undo_stack(size_t size);
|
||||
|
||||
~Undo_stack() override;
|
||||
|
||||
/*!
|
||||
* @brief Restores all items within the undo stack
|
||||
*
|
||||
* @details Maintains an internal flag which will reverse direction on the next call, thereby re-doing
|
||||
*
|
||||
* @returns A pair of references to the last item restored
|
||||
*/
|
||||
std::pair<const void*, const void*> restore() override;
|
||||
|
||||
/*!
|
||||
* @brief Saves the item onto the undo stack
|
||||
*
|
||||
* @param[in] item The item to save
|
||||
* @param[in] container (Optional) The container wherein this item is located
|
||||
* @note Call this _before_ you do your operation on the item
|
||||
*/
|
||||
template<typename T>
|
||||
size_t save(T& item, T* container = nullptr) {
|
||||
// Create a new instance of Undo_tem, with the correct type reference
|
||||
Undo_item_base *new_item = new Undo_item<T>(item, container);
|
||||
|
||||
stack.push_back(new_item);
|
||||
|
||||
return stack.size();
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Calls ::reserve() on the internal vector
|
||||
*/
|
||||
void reserve(size_t size);
|
||||
|
||||
/*!
|
||||
* @brief Returns the size of the stack
|
||||
*/
|
||||
size_t size();
|
||||
|
||||
/*!
|
||||
* @brief Deletes all tracked items in the internal vector
|
||||
*/
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
friend class Undo_system;
|
||||
|
||||
/*!
|
||||
* @brief Calls ::clear() on the internal vector
|
||||
* @note Caution: This does not delete the tracked Undo_items
|
||||
*/
|
||||
void untrack();
|
||||
|
||||
private:
|
||||
bool reverse; // Direction to walk the stack. forward = false, reverse = True
|
||||
|
||||
std::vector<Undo_item_base*> stack;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Generic Undo/Redo system. Save whatever, restore whatever!
|
||||
*
|
||||
* @details Currently uses a pair of deques. Would ideally use a ring container so that the actual Undo_item instances don't go anywhere.
|
||||
*/
|
||||
class Undo_system
|
||||
{
|
||||
public:
|
||||
Undo_system();
|
||||
|
||||
Undo_system(size_t _undos);
|
||||
|
||||
/*!
|
||||
* @brief Deletes all undo and redo data
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/*!
|
||||
* @brief Saves the item onto the undo stack
|
||||
*
|
||||
* @param[in] item The item to save
|
||||
* @param[in] container (Optional) The container wherein this item is located
|
||||
* @note Call this _before_ you do your operation on the item
|
||||
*/
|
||||
template<typename T>
|
||||
size_t save(T& item, T* container = nullptr) {
|
||||
// De-construct all intances of Undo_item on the redo stack, then clear the stack
|
||||
clear_redo();
|
||||
|
||||
// Create a new instance of Undo_tem, with the correct type reference
|
||||
Undo_item_base *new_item = new Undo_item<T>(item, container);
|
||||
|
||||
undo_stack.push_back(new_item);
|
||||
|
||||
clamp_stacks();
|
||||
|
||||
return undo_stack.size();
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Saves a stack of undo-items as a single undo-item within the system
|
||||
*
|
||||
* @param[in,out] stack The undo stack to save to the undo system. Data in the stack is "unspecified" after this operation
|
||||
*
|
||||
* @details The undo system effectively moves the stack into its internal containers, claiming ownership of the
|
||||
* Undo_items and deleting them upon going out of scope. The input stack is told to untrack the Undo_items in
|
||||
* the process, so that there is only ever one reference to the Undo_item like a std::unique_ptr
|
||||
*/
|
||||
size_t save_stack(Undo_stack& stack);
|
||||
|
||||
/*!
|
||||
* @brief Undo's the last changed item and save the changes into the Redo stack
|
||||
*
|
||||
* @returns A pair of const void* which reference the affected item, and its container (if it was provided)
|
||||
*/
|
||||
std::pair<const void*, const void*> undo();
|
||||
|
||||
/*!
|
||||
* @brief Redo's the last changed item and save the changes into the undo
|
||||
*
|
||||
* @returns A pair of const void* which reference the affected item, and its container (if it was provided)
|
||||
*/
|
||||
std::pair<const void*, const void*> redo();
|
||||
|
||||
/*!
|
||||
* @brief Returns the size of the undo stack
|
||||
*/
|
||||
size_t size();
|
||||
|
||||
/*!
|
||||
* @brief Returns the size of the redo stack
|
||||
*/
|
||||
size_t size_redo();
|
||||
|
||||
/*!
|
||||
* @brief True if undo stack size = 0
|
||||
*/
|
||||
bool empty();
|
||||
|
||||
/*!
|
||||
* @brief True if redo stack size = 0
|
||||
*/
|
||||
bool empty_redo();
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* @brief Deletes all redo data
|
||||
*/
|
||||
void clear_redo();
|
||||
|
||||
/*!
|
||||
* @brief Clamp the stack sizes to be <= max_undos, deleting data as needed
|
||||
*/
|
||||
void clamp_stacks();
|
||||
|
||||
private:
|
||||
size_t max_undos; //!< Max number of Undo's available
|
||||
|
||||
/*!
|
||||
* @brief The undo and redo stacks. These are containers of pointers, because we can't simply store the base class.
|
||||
* If we did, then the data of the Undo_item instances would be sliced out.
|
||||
* @{
|
||||
*/
|
||||
SCP_deque<Undo_item_base*> undo_stack;
|
||||
SCP_deque<Undo_item_base*> redo_stack;
|
||||
//! @}
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef _VMALLOCATOR_H_INCLUDED_
|
||||
#define _VMALLOCATOR_H_INCLUDED_
|
||||
|
||||
/* SCP_vm_allocator - maintained by portej05 (i.e. please don't patch this one yourself!) */
|
||||
|
||||
#include <deque>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
@@ -25,31 +25,10 @@ bool SCP_vector_contains(SCP_vector<T>& vector, T item) {
|
||||
template< typename T >
|
||||
using SCP_list = std::list< T, std::allocator< T > >;
|
||||
|
||||
|
||||
extern std::locale SCP_default_locale;
|
||||
|
||||
template< class charT >
|
||||
charT SCP_toupper(charT ch) { return std::toupper(ch, SCP_default_locale); }
|
||||
|
||||
template< class charT >
|
||||
charT SCP_tolower(charT ch) { return std::tolower(ch, SCP_default_locale); }
|
||||
|
||||
typedef std::basic_string<char, std::char_traits<char>, std::allocator<char> > SCP_string;
|
||||
|
||||
typedef std::basic_stringstream<char, std::char_traits<char>, std::allocator<char> > SCP_stringstream;
|
||||
|
||||
inline void SCP_tolower(SCP_string &str) {
|
||||
std::transform(str.begin(), str.end(), str.begin(), [](char c) { return SCP_tolower(c); });
|
||||
}
|
||||
|
||||
inline void SCP_toupper(SCP_string &str) {
|
||||
std::transform(str.begin(), str.end(), str.begin(), [](char c) { return SCP_toupper(c); });
|
||||
}
|
||||
|
||||
extern void SCP_tolower(char *str);
|
||||
extern void SCP_toupper(char *str);
|
||||
|
||||
|
||||
template< typename T, typename U >
|
||||
using SCP_map = std::map<T, U, std::less<T>, std::allocator<std::pair<const T, U> > >;
|
||||
|
||||
@@ -96,59 +75,6 @@ template <typename T>
|
||||
using SCP_hash = std::hash<T>;
|
||||
#endif
|
||||
|
||||
struct SCP_string_lcase_hash {
|
||||
size_t operator()(const SCP_string& elem) const {
|
||||
SCP_string lcase_copy;
|
||||
std::transform(elem.begin(), elem.end(), std::back_inserter(lcase_copy),
|
||||
[](char c) {
|
||||
return SCP_tolower(c);
|
||||
});
|
||||
return SCP_hash<SCP_string>()(lcase_copy);
|
||||
}
|
||||
};
|
||||
|
||||
struct SCP_string_lcase_equal_to {
|
||||
bool operator()(const SCP_string& _Left, const SCP_string& _Right) const {
|
||||
if (_Left.size() != _Right.size()) {
|
||||
return false;
|
||||
}
|
||||
auto l_it = _Left.cbegin();
|
||||
auto r_it = _Right.cbegin();
|
||||
while (l_it != _Left.cend()) {
|
||||
if (SCP_tolower(*l_it) != SCP_tolower(*r_it)) {
|
||||
return false;
|
||||
}
|
||||
++l_it;
|
||||
++r_it;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct SCP_string_lcase_less_than {
|
||||
bool operator()(const SCP_string& _Left, const SCP_string& _Right) const {
|
||||
auto l_it = _Left.cbegin();
|
||||
auto r_it = _Right.cbegin();
|
||||
while (true) {
|
||||
if (l_it == _Left.cend()) {
|
||||
return (r_it != _Right.cend());
|
||||
} else if (r_it == _Right.cend()) {
|
||||
return false;
|
||||
}
|
||||
auto lch = SCP_tolower(*l_it);
|
||||
auto rch = SCP_tolower(*r_it);
|
||||
if (lch < rch) {
|
||||
return true;
|
||||
} else if (lch > rch) {
|
||||
return false;
|
||||
}
|
||||
++l_it;
|
||||
++r_it;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
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>>>;
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#include "scripting/scripting.h"
|
||||
#include "tracing/tracing.h"
|
||||
#include "utils/boost/hash_combine.h"
|
||||
#include "gamesequence/gamesequence.h"
|
||||
|
||||
#ifdef WITH_OPENGL
|
||||
#include "graphics/opengl/gropengl.h"
|
||||
@@ -2548,8 +2547,7 @@ void gr_flip(bool execute_scripting)
|
||||
executor::OnFrameExecutor->process();
|
||||
|
||||
// m!m avoid running CHA_ONFRAME when the "Quit mission" popup is shown. See mantis 2446 for reference
|
||||
// Cyborg - A similar bug will occur when a mission is restarted so check for that, too.
|
||||
if (execute_scripting && !popup_active() && GameState_Stack_Valid() && !((gameseq_get_state() == GS_STATE_GAME_PLAY) && !(Game_mode & GM_IN_MISSION))) {
|
||||
if (execute_scripting && !popup_active()) {
|
||||
TRACE_SCOPE(tracing::LuaOnFrame);
|
||||
|
||||
// WMC - Do conditional hooks. Yippee!
|
||||
|
||||
@@ -130,7 +130,13 @@ void gr_opengl_deferred_lighting_finish()
|
||||
using namespace graphics;
|
||||
|
||||
// We need to precompute how many elements we are going to need
|
||||
size_t num_data_elements = Lights.size();
|
||||
size_t num_data_elements = 0;
|
||||
for (auto& l : Lights) {
|
||||
++num_data_elements;
|
||||
if (l.type == Light_Type::Tube) {
|
||||
++num_data_elements;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a uniform buffer for our data
|
||||
auto buffer = gr_get_uniform_buffer(uniform_block_type::Lights, num_data_elements);
|
||||
@@ -223,16 +229,26 @@ void gr_opengl_deferred_lighting_finish()
|
||||
vec3d a;
|
||||
vm_vec_sub(&a, &l.vec, &l.vec2);
|
||||
auto length = vm_vec_mag(&a);
|
||||
//Tube light volumes must be extended past the length of their requested light vector
|
||||
//to allow smooth fall-off from all angles. Since the light volume starts at the mesh
|
||||
//origin we must extend it here. Later the position will be adjusted as well.
|
||||
length += light_data->lightRadius * 2.0f;
|
||||
|
||||
light_data->scale.xyz.x = l.radb * 1.53f;
|
||||
light_data->scale.xyz.y = l.radb * 1.53f;
|
||||
light_data->scale.xyz.z = length;
|
||||
|
||||
vm_vec_scale(&light_data->specLightColor, static_tube_factor);
|
||||
|
||||
// Tube lights consist of two different types of lights with almost the same properties
|
||||
light_data = uniformAligner.addTypedElement<deferred_light_data>();
|
||||
light_data->diffuseLightColor = diffuse;
|
||||
light_data->specLightColor = spec;
|
||||
|
||||
vm_vec_scale(&light_data->specLightColor, static_tube_factor);
|
||||
|
||||
light_data->lightRadius = l.radb * 1.5f;
|
||||
light_data->lightType = LT_POINT;
|
||||
|
||||
light_data->scale.xyz.x = l.radb * 1.53f;
|
||||
light_data->scale.xyz.y = l.radb * 1.53f;
|
||||
light_data->scale.xyz.z = l.radb * 1.53f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -268,17 +284,20 @@ void gr_opengl_deferred_lighting_finish()
|
||||
gr_bind_uniform_buffer(uniform_block_type::Lights, buffer.getAlignerElementOffset(element_index),
|
||||
sizeof(graphics::deferred_light_data), buffer.bufferHandle());
|
||||
|
||||
vec3d dir, newPos;
|
||||
vec3d a;
|
||||
matrix orient;
|
||||
vm_vec_sub(&dir, &l.vec, &l.vec2);
|
||||
vm_vector_2_matrix(&orient, &dir, nullptr, nullptr);
|
||||
//Tube light volumes must be extended past the length of their requested light vector
|
||||
//to allow smooth fall-off from all angles. Since the light volume starts at the mesh
|
||||
//origin we must extend it, which has been done above, and then move it backwards one radius.
|
||||
vm_vec_normalize(&dir);
|
||||
//1.5 multiplier matches scaling used earlier to scale light radius for all tubes
|
||||
vm_vec_scale_sub(&newPos, &l.vec2, &dir, l.radb * 1.5f);
|
||||
gr_opengl_draw_deferred_light_cylinder(&newPos, &orient);
|
||||
vm_vec_sub(&a, &l.vec, &l.vec2);
|
||||
vm_vector_2_matrix(&orient, &a, NULL, NULL);
|
||||
|
||||
gr_opengl_draw_deferred_light_cylinder(&l.vec2, &orient);
|
||||
++element_index;
|
||||
|
||||
// The next two draws use the same uniform block element
|
||||
gr_bind_uniform_buffer(uniform_block_type::Lights, buffer.getAlignerElementOffset(element_index),
|
||||
sizeof(graphics::deferred_light_data), buffer.bufferHandle());
|
||||
|
||||
gr_opengl_draw_deferred_light_sphere(&l.vec);
|
||||
gr_opengl_draw_deferred_light_sphere(&l.vec2);
|
||||
++element_index;
|
||||
break;
|
||||
default:
|
||||
@@ -301,8 +320,8 @@ void gr_opengl_deferred_lighting_finish()
|
||||
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_emissive_texture);
|
||||
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_depth_texture);
|
||||
|
||||
float fog_near, fog_far, fog_density;
|
||||
neb2_get_adjusted_fog_values(&fog_near, &fog_far, &fog_density);
|
||||
float fog_near, fog_far;
|
||||
neb2_get_adjusted_fog_values(&fog_near, &fog_far, nullptr);
|
||||
unsigned char r, g, b;
|
||||
neb2_get_fog_color(&r, &g, &b);
|
||||
|
||||
@@ -311,7 +330,7 @@ void gr_opengl_deferred_lighting_finish()
|
||||
|
||||
opengl_set_generic_uniform_data<graphics::generic_data::fog_data>([&](graphics::generic_data::fog_data* data) {
|
||||
data->fog_start = fog_near;
|
||||
data->fog_density = fog_density;
|
||||
data->fog_scale = 1.0f / (fog_far - fog_near);
|
||||
data->fog_color.xyz.x = r / 255.f;
|
||||
data->fog_color.xyz.y = g / 255.f;
|
||||
data->fog_color.xyz.z = b / 255.f;
|
||||
|
||||
@@ -284,7 +284,7 @@ struct fog_data {
|
||||
vec3d fog_color;
|
||||
float fog_start;
|
||||
|
||||
float fog_density;
|
||||
float fog_scale;
|
||||
float zNear;
|
||||
float zFar;
|
||||
|
||||
|
||||
@@ -643,7 +643,7 @@ void HudGaugeBrackets::renderBoundingBrackets(int x1, int y1, int x2, int y2, in
|
||||
break;
|
||||
|
||||
case OBJ_DEBRIS:
|
||||
tinfo_name = XSTR("debris", 348);
|
||||
tinfo_name = XSTR("Debris", 348);
|
||||
break;
|
||||
case OBJ_WEAPON:
|
||||
tinfo_name = Weapon_info[Weapons[t_objp->instance].weapon_info_index].get_display_name();
|
||||
@@ -653,10 +653,10 @@ void HudGaugeBrackets::renderBoundingBrackets(int x1, int y1, int x2, int y2, in
|
||||
case ASTEROID_TYPE_SMALL:
|
||||
case ASTEROID_TYPE_MEDIUM:
|
||||
case ASTEROID_TYPE_LARGE:
|
||||
tinfo_name = XSTR("asteroid", 431);
|
||||
tinfo_name = NOX("Asteroid");
|
||||
break;
|
||||
default:
|
||||
tinfo_name = XSTR("debris", 348);
|
||||
tinfo_name = XSTR("Debris", 348);
|
||||
}
|
||||
break;
|
||||
case OBJ_JUMP_NODE:
|
||||
|
||||
@@ -566,7 +566,7 @@ typedef struct hc_col {
|
||||
ubyte r, g, b;
|
||||
} hc_col;
|
||||
|
||||
hc_col HC_colors[HUD_COLOR_SIZE] =
|
||||
hc_col HC_colors[3] =
|
||||
{
|
||||
{0, 255, 0}, // Green - get RGB from Adam so it matches palette?-??.pcx
|
||||
{67, 123, 203}, // Blue - get RGB from Adam so it matches palette?-??.pcx
|
||||
@@ -1509,8 +1509,6 @@ void hud_config_close()
|
||||
if (HC_background_bitmap_mask != -1) {
|
||||
bm_release(HC_background_bitmap_mask);
|
||||
}
|
||||
|
||||
HUD_config_inited = 0;
|
||||
}
|
||||
|
||||
// hud_set_default_hud_config() will set the hud configuration to default values
|
||||
@@ -1559,7 +1557,7 @@ void hud_config_backup()
|
||||
|
||||
void hud_config_as_observer(ship *shipp,ai_info *aif)
|
||||
{
|
||||
// store the current hud
|
||||
// store the current hus
|
||||
hud_config_backup();
|
||||
|
||||
// bash these values so the HUD is not offset incorrectly
|
||||
@@ -1665,10 +1663,10 @@ void hud_config_alpha_slider_up()
|
||||
HC_color_sliders[HCS_GREEN].force_currentItem( HCS_CONV(HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()) + 1) );
|
||||
HC_color_sliders[HCS_BLUE].force_currentItem( HCS_CONV(HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) + 1) );
|
||||
|
||||
// apply -- Cyborg17 -- Unless you have nothing to apply it to!
|
||||
// apply
|
||||
if(HC_select_all){
|
||||
hud_config_stuff_colors( HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) );
|
||||
} else if (HC_gauge_selected >= 0) {
|
||||
} else {
|
||||
gr_init_alphacolor(&HUD_config.clr[HC_gauge_selected], HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()), 255);
|
||||
}
|
||||
}
|
||||
@@ -1689,10 +1687,10 @@ void hud_config_alpha_slider_down()
|
||||
HC_color_sliders[HCS_GREEN].force_currentItem( HCS_CONV(HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()) - 1) );
|
||||
HC_color_sliders[HCS_BLUE].force_currentItem( HCS_CONV(HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) - 1) );
|
||||
|
||||
// apply -- Cyborg17 -- Unless you have nothing to apply it to!
|
||||
// apply
|
||||
if(HC_select_all){
|
||||
hud_config_stuff_colors( HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) );
|
||||
} else if (HC_gauge_selected >= 0) {
|
||||
} else {
|
||||
gr_init_alphacolor(&HUD_config.clr[HC_gauge_selected], HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()), 255);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-15
@@ -21,7 +21,6 @@ struct ai_info;
|
||||
#define HUD_COLOR_GREEN 0
|
||||
#define HUD_COLOR_BLUE 1
|
||||
#define HUD_COLOR_AMBER 2
|
||||
#define HUD_COLOR_SIZE 3 // Number of default colors. Keep this up to date.
|
||||
|
||||
// specify the max distance that the radar should detect objects
|
||||
// Index in Radar_ranges[] array to get values
|
||||
@@ -46,22 +45,19 @@ extern int HUD_default_popup_mask2;
|
||||
extern int HUD_config_default_flags;
|
||||
extern int HUD_config_default_flags2;
|
||||
|
||||
/**
|
||||
* @brief Contains core HUD configuration data
|
||||
* @note Is not default init'd. Assumes new player, PLR, or CSG reads will correctly set data.
|
||||
*/
|
||||
typedef struct HUD_CONFIG_TYPE {
|
||||
int show_flags; //!< bitfield, whether to show gauge (0 ~ 31)
|
||||
int show_flags2; //!< bitfield, whether to show gauge (32 ~ 63)
|
||||
int popup_flags; //!< bitfield, whether gauge is popup (0 ~ 31)
|
||||
int popup_flags2; //!< bitfield, whether gauge is popup (32 ~ 63)
|
||||
int rp_flags; //!< one of RP_ #defines in hudconfig.h; Chiefly shows/hides non-ship objects
|
||||
int rp_dist; //!< one of RR_ #defines above; Is the maxium radar view distance setting
|
||||
int is_observer; //!< 1 or 0, observer mode or not, respectively
|
||||
int main_color; //!< the default HUD_COLOR selection for all gauges; each gauge may override this with a custom RGB
|
||||
ubyte num_msg_window_lines; //!< Number of message lines. (Deprecated by HudGaugeMessages::Max_lines)
|
||||
int show_flags; // whether to show gauge
|
||||
int show_flags2; // whether to show gauge
|
||||
int popup_flags; // whether gauge is popup
|
||||
int popup_flags2; // whether gauge is popup
|
||||
int rp_flags; // see RP_ flags above
|
||||
int rp_dist; // one of RR_ #defines above
|
||||
int is_observer; // 1 or 0, observer mode or not, respectively
|
||||
int main_color; // the main color
|
||||
ubyte num_msg_window_lines;
|
||||
|
||||
color clr[NUM_HUD_GAUGES]; //!< colors for all the gauges
|
||||
// colors for all the gauges
|
||||
color clr[NUM_HUD_GAUGES];
|
||||
} HUD_CONFIG_TYPE;
|
||||
|
||||
extern HUD_CONFIG_TYPE HUD_config;
|
||||
|
||||
+3
-7
@@ -212,14 +212,10 @@ void ai_manage_ets(object* obj)
|
||||
return;
|
||||
|
||||
// check if weapons or engines are not being used. If so, don't allow energy management.
|
||||
if (!ship_info_p->max_speed || !ship_info_p->max_weapon_reserve) {
|
||||
return;
|
||||
}
|
||||
|
||||
// also check if the ship has no shields and if the AI is allowed to manage weapons and engines --wookieejedi
|
||||
if ( !(ship_p->ship_max_shield_strength) && !( (aip->ai_profile_flags[AI::Profile_Flags::all_nonshielded_ships_can_manage_ets]) ||
|
||||
( (ship_info_p->is_fighter_bomber()) && (aip->ai_profile_flags[AI::Profile_Flags::fightercraft_nonshielded_ships_can_manage_ets])) ) ) {
|
||||
return;
|
||||
if ( !ship_info_p->max_speed || !ship_info_p->max_weapon_reserve ||
|
||||
(!ship_p->ship_max_shield_strength && !aip->ai_profile_flags[AI::Profile_Flags::nonshielded_ships_can_manage_ets]) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
float weapon_left_percent = ship_p->weapon_energy/ship_info_p->max_weapon_reserve;
|
||||
|
||||
@@ -924,7 +924,7 @@ void hud_lock_acquire_uncaged_target(lock_info *current_lock, weapon_info *wip)
|
||||
continue;
|
||||
}*/
|
||||
|
||||
if (!weapon_target_satisfies_lock_restrictions(wip, A)) {
|
||||
if (!weapon_can_lock_on_ship(wip, A->instance)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1042,7 +1042,7 @@ void hud_lock_determine_lock_target(lock_info *lock_slot, weapon_info *wip)
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !weapon_target_satisfies_lock_restrictions(wip, lock_slot->obj) ) {
|
||||
if ( !weapon_can_lock_on_ship(wip, lock_slot->obj->instance) ) {
|
||||
ship_clear_lock(lock_slot);
|
||||
return;
|
||||
}
|
||||
@@ -1110,7 +1110,7 @@ void hud_lock_determine_lock_target(lock_info *lock_slot, weapon_info *wip)
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !weapon_target_satisfies_lock_restrictions(wip, lock_slot->obj) ) {
|
||||
if ( !weapon_can_lock_on_ship(wip, lock_slot->obj->instance) ) {
|
||||
ship_clear_lock(lock_slot);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ void parse_hud_gauges_tbl(const char *filename)
|
||||
case 1:
|
||||
int shiparray[256];
|
||||
|
||||
n_ships = (int)stuff_int_list(shiparray, 256, SHIP_INFO_TYPE);
|
||||
n_ships = stuff_int_list(shiparray, 256, SHIP_INFO_TYPE);
|
||||
|
||||
if (optional_string("$Load Retail Configuration:")) {
|
||||
stuff_boolean(&retail_config);
|
||||
|
||||
@@ -236,7 +236,7 @@ void HudGaugeReticle::initBitmaps(char *fname)
|
||||
{
|
||||
crosshair.first_frame = bm_load_animation(fname, &crosshair.num_frames);
|
||||
if (crosshair.first_frame < 0) {
|
||||
Warning(LOCATION, "Cannot load hud ani: %s\n", fname);
|
||||
mprintf(("Cannot load hud ani: %s\n", fname));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,7 +495,7 @@ void HudGaugeThrottle::initBitmaps(char *fname)
|
||||
throttle_frames.first_frame = bm_load_animation(fname, &throttle_frames.num_frames);
|
||||
|
||||
if (throttle_frames.first_frame < 0) {
|
||||
Warning(LOCATION, "Cannot load hud ani: %s\n", fname);
|
||||
mprintf(("Cannot load hud ani: %s\n", fname));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,17 +748,17 @@ void HudGaugeThreatIndicator::initBitmaps(char *fname_arc, char *fname_laser, ch
|
||||
{
|
||||
threat_arc.first_frame = bm_load_animation(fname_arc, &threat_arc.num_frames);
|
||||
if (threat_arc.first_frame < 0) {
|
||||
Warning(LOCATION, "Cannot load hud ani: %s\n", fname_arc);
|
||||
mprintf(("Cannot load hud ani: %s\n", fname_arc));
|
||||
}
|
||||
|
||||
laser_warn.first_frame = bm_load_animation(fname_laser, &laser_warn.num_frames);
|
||||
if (laser_warn.first_frame < 0) {
|
||||
Warning(LOCATION, "Cannot load hud ani: %s\n", fname_laser);
|
||||
mprintf(("Cannot load hud ani: %s\n", fname_laser));
|
||||
}
|
||||
|
||||
lock_warn.first_frame = bm_load_animation(fname_lock, &lock_warn.num_frames);
|
||||
if (lock_warn.first_frame < 0) {
|
||||
Warning(LOCATION, "Cannot load hud ani: %s\n", fname_lock);
|
||||
mprintf(("Cannot load hud ani: %s\n", fname_lock));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5323,18 +5323,17 @@ SCP_string hud_get_ship_class(const ship *shipp)
|
||||
// try to get alt name
|
||||
if (Fred_running) {
|
||||
ship_class_text = Fred_alt_names[shipp-Ships];
|
||||
|
||||
if (ship_class_text.empty()) {
|
||||
ship_class_text = Ship_info[shipp->ship_info_index].get_display_name();
|
||||
}
|
||||
} else {
|
||||
if (shipp->alt_type_index >= 0) {
|
||||
ship_class_text = mission_parse_lookup_alt_index(shipp->alt_type_index);
|
||||
} else {
|
||||
ship_class_text = Ship_info[shipp->ship_info_index].get_display_name();
|
||||
}
|
||||
}
|
||||
|
||||
// maybe get ship class
|
||||
if (ship_class_text.empty()) {
|
||||
ship_class_text = Ship_info[shipp->ship_info_index].get_display_name();
|
||||
}
|
||||
|
||||
if (!Disable_built_in_translations) {
|
||||
// handle translation
|
||||
if (Lcl_gr) {
|
||||
|
||||
@@ -797,7 +797,7 @@ void HudGaugeTargetBox::renderTargetDebris(object *target_objp)
|
||||
printable_ship_class = Ship_info[debrisp->ship_info_index].get_display_name();
|
||||
|
||||
renderString(position[0] + Class_offsets[0], position[1] + Class_offsets[1], EG_TBOX_CLASS, printable_ship_class);
|
||||
renderString(position[0] + Name_offsets[0], position[1] + Name_offsets[1], EG_TBOX_NAME, XSTR("debris", 348));
|
||||
renderString(position[0] + Name_offsets[0], position[1] + Name_offsets[1], EG_TBOX_NAME, XSTR("Debris", 348));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1160,11 +1160,11 @@ void HudGaugeTargetBox::renderTargetAsteroid(object *target_objp)
|
||||
case ASTEROID_TYPE_SMALL:
|
||||
case ASTEROID_TYPE_MEDIUM:
|
||||
case ASTEROID_TYPE_LARGE:
|
||||
strcpy_s(hud_name, XSTR("asteroid", 431));
|
||||
strcpy_s(hud_name, NOX("asteroid"));
|
||||
break;
|
||||
|
||||
default:
|
||||
strcpy_s(hud_name, Asteroid_info[asteroidp->asteroid_type].name);
|
||||
sprintf(hud_name, NOX("%s debris"), Species_info[(asteroidp->asteroid_type / NUM_DEBRIS_SIZES) - 1].species_name);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1172,7 +1172,7 @@ void HudGaugeTargetBox::renderTargetAsteroid(object *target_objp)
|
||||
|
||||
|
||||
if ( time_to_impact >= 0.0f ) {
|
||||
renderPrintf(position[0] + Class_offsets[0], position[1] + Class_offsets[1], EG_TBOX_CLASS, XSTR("impact: %.1f sec", 1596), time_to_impact);
|
||||
renderPrintf(position[0] + Class_offsets[0], position[1] + Class_offsets[1], EG_TBOX_CLASS, NOX("impact: %.1f sec"), time_to_impact);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
|
||||
|
||||
#include <cctype> // for 'tolower'
|
||||
|
||||
|
||||
#include "globalincs/alphacolors.h"
|
||||
#include "globalincs/linklist.h"
|
||||
#include "hud/hudtargetbox.h"
|
||||
@@ -454,9 +457,9 @@ void HudGaugeWingmanStatus::renderDots(int wing_index, int screen_index, int num
|
||||
|
||||
// Goober5000 - get the lowercase abbreviation
|
||||
char abbrev[4];
|
||||
abbrev[0] = SCP_tolower(Squadron_wing_names[wing_index][0]);
|
||||
abbrev[1] = SCP_tolower(Squadron_wing_names[wing_index][1]);
|
||||
abbrev[2] = SCP_tolower(Squadron_wing_names[wing_index][2]);
|
||||
abbrev[0] = (char) tolower(Squadron_wing_names[wing_index][0]);
|
||||
abbrev[1] = (char) tolower(Squadron_wing_names[wing_index][1]);
|
||||
abbrev[2] = (char) tolower(Squadron_wing_names[wing_index][2]);
|
||||
abbrev[3] = '\0';
|
||||
|
||||
// Goober5000 - center it (round the offset rather than truncate it)
|
||||
|
||||
+11
-12
@@ -126,6 +126,7 @@ void iff_init()
|
||||
int num_attack_names[MAX_IFFS];
|
||||
int num_observed_colors[MAX_IFFS];
|
||||
int i, j, k;
|
||||
int string_idx;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -339,7 +340,7 @@ void iff_init()
|
||||
|
||||
// get the list of iffs attacked
|
||||
if (optional_string("$Attacks:"))
|
||||
num_attack_names[cur_iff] = (int)stuff_string_list(attack_names[cur_iff], MAX_IFFS);
|
||||
num_attack_names[cur_iff] = stuff_string_list(attack_names[cur_iff], MAX_IFFS);
|
||||
else
|
||||
num_attack_names[cur_iff] = 0;
|
||||
|
||||
@@ -383,25 +384,23 @@ void iff_init()
|
||||
iff->flags = 0;
|
||||
if (optional_string("$Flags:"))
|
||||
{
|
||||
SCP_vector<SCP_string> flag_strings;
|
||||
stuff_string_list(flag_strings);
|
||||
char flag_strings[MAX_IFF_FLAGS][NAME_LENGTH];
|
||||
|
||||
for (auto &item: flag_strings)
|
||||
int num_strings = stuff_string_list(flag_strings, MAX_IFF_FLAGS);
|
||||
for (string_idx = 0; string_idx < num_strings; string_idx++)
|
||||
{
|
||||
auto flag_string = item.c_str();
|
||||
|
||||
if (!stricmp(NOX("support allowed"), flag_string))
|
||||
if (!stricmp(NOX("support allowed"), flag_strings[string_idx]))
|
||||
iff->flags |= IFFF_SUPPORT_ALLOWED;
|
||||
else if (!stricmp(NOX("exempt from all teams at war"), flag_string))
|
||||
else if (!stricmp(NOX("exempt from all teams at war"), flag_strings[string_idx]))
|
||||
iff->flags |= IFFF_EXEMPT_FROM_ALL_TEAMS_AT_WAR;
|
||||
else if (!stricmp(NOX("orders hidden"), flag_string))
|
||||
else if (!stricmp(NOX("orders hidden"), flag_strings[string_idx]))
|
||||
iff->flags |= IFFF_ORDERS_HIDDEN;
|
||||
else if (!stricmp(NOX("orders shown"), flag_string))
|
||||
else if (!stricmp(NOX("orders shown"), flag_strings[string_idx]))
|
||||
iff->flags |= IFFF_ORDERS_SHOWN;
|
||||
else if (!stricmp(NOX("wing name hidden"), flag_string))
|
||||
else if (!stricmp(NOX("wing name hidden"), flag_strings[string_idx]))
|
||||
iff->flags |= IFFF_WING_NAME_HIDDEN;
|
||||
else
|
||||
Warning(LOCATION, "Bogus string in iff flags: %s\n", flag_string);
|
||||
Warning(LOCATION, "Bogus string in iff flags: %s\n", flag_strings[string_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -78,7 +78,8 @@ SCP_string getJoystickGUID(SDL_Joystick* stick)
|
||||
joystickGUID.resize(GUID_STR_SIZE - 1);
|
||||
|
||||
// Make sure the GUID is upper case
|
||||
SCP_toupper(joystickGUID);
|
||||
std::transform(begin(joystickGUID), end(joystickGUID), begin(joystickGUID),
|
||||
[](char c) { return (char)::toupper(c); });
|
||||
|
||||
return joystickGUID;
|
||||
}
|
||||
@@ -91,7 +92,7 @@ bool joystickMatchesGuid(Joystick* testStick, const SCP_string& guid, int id)
|
||||
}
|
||||
|
||||
SCP_string guidStr(guid);
|
||||
SCP_toupper(guidStr);
|
||||
std::transform(begin(guidStr), end(guidStr), begin(guidStr), [](char c) { return (char)::toupper(c); });
|
||||
|
||||
if (testStick->getGUID() != guidStr) {
|
||||
return false; // GUID doesn't match
|
||||
|
||||
@@ -692,6 +692,15 @@ void process_debug_keys(int k)
|
||||
case KEY_DEBUGGED + KEY_H:
|
||||
hud_target_toggle_hidden_from_sensors();
|
||||
break;
|
||||
|
||||
case KEY_DEBUGGED + KEY_F:
|
||||
extern int wacky_scheme;
|
||||
if(wacky_scheme == 3){
|
||||
wacky_scheme = 0;
|
||||
} else {
|
||||
wacky_scheme++;
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_DEBUGGED + KEY_ALTED + KEY_F:
|
||||
Framerate_delay += 10;
|
||||
|
||||
@@ -308,22 +308,11 @@ void LabRenderer::useBackground(const SCP_string& mission_name) {
|
||||
gr_set_ambient_light(ambient_light_level & 0xff, (ambient_light_level >> 8) & 0xff,
|
||||
(ambient_light_level >> 16) & 0xff);
|
||||
|
||||
strcpy_s(Neb2_texture_name, "");
|
||||
strcpy_s(Neb2_texture_name, "Eraseme3");
|
||||
Neb2_poof_flags = ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5));
|
||||
bool nebula = false;
|
||||
if (optional_string("+Neb2:")) {
|
||||
nebula = true;
|
||||
stuff_string(Neb2_texture_name, F_NAME, MAX_FILENAME_LEN);
|
||||
} else if (optional_string("+Neb2Color:")) {
|
||||
nebula = true;
|
||||
int neb_colors[3];
|
||||
stuff_int_list(neb_colors, 3, RAW_INTEGER_TYPE);
|
||||
Neb2_fog_color[0] = (ubyte)neb_colors[0];
|
||||
Neb2_fog_color[1] = (ubyte)neb_colors[1];
|
||||
Neb2_fog_color[2] = (ubyte)neb_colors[2];
|
||||
}
|
||||
|
||||
if (nebula){
|
||||
required_string("+Neb2Flags:");
|
||||
stuff_int(&Neb2_poof_flags);
|
||||
|
||||
|
||||
@@ -133,8 +133,6 @@ public:
|
||||
int setAmbientFactor(int factor) {
|
||||
ambientFactor = factor;
|
||||
Cmdline_ambient_factor = factor;
|
||||
gr_calculate_ambient_factor();
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
|
||||
#include "FFmpegContext.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
const size_t AVIO_BUFFER_SIZE = 8192;
|
||||
|
||||
@@ -53,70 +51,15 @@ int64_t cfileSeek(void* ptr, int64_t offset, int whence) {
|
||||
// cfseek returns the offset in the archive file (who thought that would be a good idea?)
|
||||
return cftell(cfile);
|
||||
}
|
||||
|
||||
int memfile_read(void* opaque, uint8_t* buf, int buf_size) {
|
||||
auto memsound = reinterpret_cast<libs::ffmpeg::MemFileCursor*>(opaque);
|
||||
auto vec_buf_size = static_cast<SCP_vector<uint8_t>::size_type>(buf_size);
|
||||
if ((int)vec_buf_size != buf_size) {
|
||||
// Overflow!!!
|
||||
return -1;
|
||||
}
|
||||
|
||||
vec_buf_size = std::min(vec_buf_size, memsound->snddata.size() - memsound->cursor_pos);
|
||||
|
||||
if (vec_buf_size > 0) {
|
||||
std::memcpy(buf, memsound->snddata.data() + memsound->cursor_pos, vec_buf_size);
|
||||
|
||||
memsound->cursor_pos += vec_buf_size;
|
||||
|
||||
return static_cast<int>(vec_buf_size);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t memfile_seek(void* opaque, int64_t offset, int whence) {
|
||||
auto memsound = reinterpret_cast<libs::ffmpeg::MemFileCursor*>(opaque);
|
||||
|
||||
if (whence == AVSEEK_SIZE) {
|
||||
return memsound->snddata.size();
|
||||
}
|
||||
int64_t cursor_pos = memsound->cursor_pos;
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
cursor_pos = offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
cursor_pos += offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
cursor_pos = memsound->snddata.size() + offset;
|
||||
break;
|
||||
}
|
||||
|
||||
memsound->cursor_pos = std::min(static_cast<SCP_vector<uint8_t>::size_type>(cursor_pos), memsound->snddata.size());
|
||||
|
||||
return static_cast<int64_t>(memsound->cursor_pos);
|
||||
}
|
||||
|
||||
}//namespace
|
||||
|
||||
namespace libs {
|
||||
namespace ffmpeg {
|
||||
|
||||
MemFileCursor::MemFileCursor(const uint8_t* data, size_t snd_len) : snddata(data, data + snd_len), cursor_pos(0) {
|
||||
}
|
||||
|
||||
MemFileCursor::MemFileCursor() : snddata() , cursor_pos(0) {}
|
||||
|
||||
FFmpegContext::FFmpegContext(CFILE* inFile) : m_ctx(nullptr), m_file(inFile), m_memsound() {
|
||||
FFmpegContext::FFmpegContext(CFILE* inFile) : m_ctx(nullptr), m_file(inFile) {
|
||||
Assertion(inFile != nullptr, "Invalid file pointer passed!");
|
||||
}
|
||||
|
||||
FFmpegContext::FFmpegContext(const uint8_t* snddata, size_t snd_len) : m_ctx(nullptr), m_file(nullptr), m_memsound(snddata, snd_len) {
|
||||
}
|
||||
|
||||
FFmpegContext::~FFmpegContext() {
|
||||
if (m_ctx) {
|
||||
if (m_ctx->pb) {
|
||||
@@ -135,58 +78,6 @@ FFmpegContext::~FFmpegContext() {
|
||||
cfclose(m_file);
|
||||
m_file = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FFmpegContext::prepare(void *opaque,
|
||||
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
|
||||
int64_t (*seek)(void *opaque, int64_t offset, int whence)) {
|
||||
m_ctx = avformat_alloc_context();
|
||||
|
||||
if (!m_ctx) {
|
||||
throw FFmpegException("Failed to allocate context!");
|
||||
}
|
||||
|
||||
auto avioBuffer = reinterpret_cast<uint8_t*>(av_malloc(AVIO_BUFFER_SIZE));
|
||||
|
||||
if (!avioBuffer) {
|
||||
throw FFmpegException("Failed to allocate IO buffer!");
|
||||
}
|
||||
|
||||
auto ioContext =
|
||||
avio_alloc_context(avioBuffer, AVIO_BUFFER_SIZE, 0, opaque, read_packet, nullptr, seek);
|
||||
|
||||
if (!ioContext) {
|
||||
throw FFmpegException("Failed to allocate IO context!");
|
||||
}
|
||||
|
||||
m_ctx->pb = ioContext;
|
||||
|
||||
auto probe_ret = av_probe_input_buffer2(m_ctx->pb, &m_ctx->iformat, nullptr, nullptr, 0, 0);
|
||||
if (probe_ret < 0) {
|
||||
char errorStr[1024];
|
||||
av_strerror(probe_ret, errorStr, 1024);
|
||||
|
||||
throw FFmpegException(SCP_string("Could not open movie file! Error: ") + errorStr);
|
||||
}
|
||||
|
||||
m_ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
|
||||
|
||||
auto ret = avformat_open_input(&m_ctx, nullptr, m_ctx->iformat, nullptr);
|
||||
if (ret < 0) {
|
||||
char errorStr[1024];
|
||||
av_strerror(ret, errorStr, 1024);
|
||||
|
||||
throw FFmpegException(SCP_string("Could not open movie file! Error: ") + errorStr);
|
||||
}
|
||||
|
||||
ret = avformat_find_stream_info(m_ctx, nullptr);
|
||||
if (ret < 0) {
|
||||
char errorStr[1024];
|
||||
av_strerror(ret, errorStr, 1024);
|
||||
|
||||
throw FFmpegException(SCP_string("Failed to get stream information! Error: ") + errorStr);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<FFmpegContext> FFmpegContext::createContext(CFILE* mediaFile) {
|
||||
@@ -194,16 +85,54 @@ std::unique_ptr<FFmpegContext> FFmpegContext::createContext(CFILE* mediaFile) {
|
||||
|
||||
std::unique_ptr<FFmpegContext> instance(new FFmpegContext(mediaFile));
|
||||
|
||||
instance->prepare(instance->m_file, cfileRead, cfileSeek);
|
||||
return instance;
|
||||
}
|
||||
instance->m_ctx = avformat_alloc_context();
|
||||
|
||||
if (!instance->m_ctx) {
|
||||
throw FFmpegException("Failed to allocate context!");
|
||||
}
|
||||
|
||||
std::unique_ptr<FFmpegContext> FFmpegContext::createContextMem(const uint8_t* snddata, size_t snd_len) {
|
||||
std::unique_ptr<FFmpegContext> instance(new FFmpegContext(snddata, snd_len));
|
||||
auto avioBuffer = reinterpret_cast<uint8_t*>(av_malloc(AVIO_BUFFER_SIZE));
|
||||
|
||||
instance->prepare(&(instance->m_memsound), memfile_read, memfile_seek);
|
||||
return instance;
|
||||
if (!avioBuffer) {
|
||||
throw FFmpegException("Failed to allocate IO buffer!");
|
||||
}
|
||||
|
||||
auto ioContext =
|
||||
avio_alloc_context(avioBuffer, AVIO_BUFFER_SIZE, 0, instance->m_file, cfileRead, nullptr, cfileSeek);
|
||||
|
||||
if (!ioContext) {
|
||||
throw FFmpegException("Failed to allocate IO context!");
|
||||
}
|
||||
|
||||
instance->m_ctx->pb = ioContext;
|
||||
|
||||
auto probe_ret = av_probe_input_buffer2(instance->m_ctx->pb, &instance->m_ctx->iformat, nullptr, nullptr, 0, 0);
|
||||
if (probe_ret < 0) {
|
||||
char errorStr[1024];
|
||||
av_strerror(probe_ret, errorStr, 1024);
|
||||
|
||||
throw FFmpegException(SCP_string("Could not open movie file! Error: ") + errorStr);
|
||||
}
|
||||
|
||||
instance->m_ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
|
||||
|
||||
auto ret = avformat_open_input(&instance->m_ctx, nullptr, instance->m_ctx->iformat, nullptr);
|
||||
if (ret < 0) {
|
||||
char errorStr[1024];
|
||||
av_strerror(ret, errorStr, 1024);
|
||||
|
||||
throw FFmpegException(SCP_string("Could not open movie file! Error: ") + errorStr);
|
||||
}
|
||||
|
||||
ret = avformat_find_stream_info(instance->m_ctx, nullptr);
|
||||
if (ret < 0) {
|
||||
char errorStr[1024];
|
||||
av_strerror(ret, errorStr, 1024);
|
||||
|
||||
throw FFmpegException(SCP_string("Failed to get stream information! Error: ") + errorStr);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::unique_ptr<FFmpegContext> FFmpegContext::createContext(const SCP_string& path, int dir_type) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/vmallocator.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "cfile/cfile.h"
|
||||
|
||||
@@ -12,42 +11,18 @@
|
||||
namespace libs {
|
||||
namespace ffmpeg {
|
||||
|
||||
/**
|
||||
* @brief Stores a vector of bytes and an access position to treat it as file
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
struct MemFileCursor {
|
||||
const SCP_vector<uint8_t> snddata;
|
||||
SCP_vector<uint8_t>::size_type cursor_pos;
|
||||
|
||||
MemFileCursor(const uint8_t* snddata, size_t snd_len);
|
||||
MemFileCursor();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Contains a FFmpeg context
|
||||
*
|
||||
* This should be used for making sure a FFmpeg context is always deallocated even if an exception is throws.
|
||||
* createContext can be used for opening a specific file for FFmpeg decoding
|
||||
* createContex can be used for opening a specific file for FFmpeg decoding
|
||||
*/
|
||||
|
||||
|
||||
class FFmpegContext {
|
||||
private:
|
||||
AVFormatContext* m_ctx;
|
||||
CFILE* m_file;
|
||||
|
||||
MemFileCursor m_memsound;
|
||||
|
||||
FFmpegContext(CFILE* file);
|
||||
FFmpegContext(const uint8_t* snddata, size_t snd_len);
|
||||
|
||||
void prepare(void *opaque,
|
||||
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
|
||||
int64_t (*seek)(void *opaque, int64_t offset, int whence));
|
||||
public:
|
||||
~FFmpegContext();
|
||||
|
||||
@@ -57,11 +32,9 @@ class FFmpegContext {
|
||||
inline AVFormatContext* ctx() { return m_ctx; }
|
||||
|
||||
static std::unique_ptr<FFmpegContext> createContext(CFILE* mediaFile);
|
||||
|
||||
static std::unique_ptr<FFmpegContext> createContextMem(const uint8_t* snddata, size_t snd_len);
|
||||
|
||||
static std::unique_ptr<FFmpegContext> createContext(const SCP_string& path, int dir_type);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
// general language/localization data ---------------------
|
||||
|
||||
// current language
|
||||
int Lcl_current_lang = LCL_RETAIL_HYBRID;
|
||||
int Lcl_current_lang = LCL_UNTRANSLATED;
|
||||
SCP_vector<lang_info> Lcl_languages;
|
||||
|
||||
// These are the original languages supported by FS2. The code expects these languages to be supported even if the tables don't
|
||||
@@ -100,17 +100,6 @@ void parse_stringstbl_quick(const char *filename);
|
||||
// LOCALIZE FUNCTIONS
|
||||
//
|
||||
|
||||
// get an index we can use to look into the array
|
||||
int lcl_get_current_lang_index()
|
||||
{
|
||||
Assertion(Lcl_current_lang >= 0, "Lcl_current_lang should never be negative!");
|
||||
|
||||
if (Lcl_current_lang < NUM_BUILTIN_LANGUAGES)
|
||||
return Lcl_current_lang;
|
||||
|
||||
return LCL_DEFAULT;
|
||||
}
|
||||
|
||||
// initialize localization, if no language is passed - use the language specified in the registry
|
||||
void lcl_init(int lang_init)
|
||||
{
|
||||
@@ -164,13 +153,13 @@ void lcl_init(int lang_init)
|
||||
lang = LCL_DEFAULT;
|
||||
}
|
||||
} else {
|
||||
Assert(lang_init == LCL_UNTRANSLATED || lang_init == LCL_RETAIL_HYBRID || (lang_init >= 0 && lang_init < (int)Lcl_languages.size()));
|
||||
Assert(lang_init == LCL_UNTRANSLATED || (lang_init >= 0 && lang_init < (int)Lcl_languages.size()));
|
||||
lang = lang_init;
|
||||
}
|
||||
|
||||
// and after all that... the default language reverts to hybrid unless we are specifically translating it
|
||||
// and after all that... the default language reverts to untranslated unless we are specifically translating it
|
||||
if (lang == LCL_DEFAULT && !Use_tabled_strings_for_default_language) {
|
||||
lang = LCL_RETAIL_HYBRID;
|
||||
lang = LCL_UNTRANSLATED;
|
||||
}
|
||||
|
||||
// set the language (this function takes care of setting up file pointers)
|
||||
@@ -279,7 +268,6 @@ void parse_stringstbl_common(const char *filename, const bool external)
|
||||
int z, index;
|
||||
char *p_offset = NULL;
|
||||
int offset_lo = 0, offset_hi = 0;
|
||||
int lcl_index = lcl_get_current_lang_index();
|
||||
|
||||
try {
|
||||
read_file_text(filename, CF_TYPE_TABLES);
|
||||
@@ -288,10 +276,10 @@ void parse_stringstbl_common(const char *filename, const bool external)
|
||||
// move down to the proper section
|
||||
memset(language_tag, 0, sizeof(language_tag));
|
||||
strcpy_s(language_tag, "#");
|
||||
if (external && (lcl_index == LCL_DEFAULT)) {
|
||||
if (external && (Lcl_current_lang == LCL_UNTRANSLATED || Lcl_current_lang == LCL_DEFAULT)) {
|
||||
strcat_s(language_tag, "default");
|
||||
} else {
|
||||
strcat_s(language_tag, Lcl_languages[lcl_index].lang_name);
|
||||
strcat_s(language_tag, Lcl_languages[Lcl_current_lang == LCL_UNTRANSLATED ? LCL_DEFAULT : Lcl_current_lang].lang_name);
|
||||
}
|
||||
|
||||
if ( skip_to_string(language_tag) != 1 ) {
|
||||
@@ -524,10 +512,6 @@ void lcl_set_language(int lang)
|
||||
nprintf(("General", "Setting language to UNTRANSLATED\n"));
|
||||
// but for the purposes of array access, we use the default
|
||||
lang = LCL_DEFAULT;
|
||||
} else if (lang == LCL_RETAIL_HYBRID) {
|
||||
nprintf(("General", "Setting language to RETAIL HYBRID\n"));
|
||||
// but for the purposes of array access, we use the default
|
||||
lang = LCL_DEFAULT;
|
||||
} else {
|
||||
nprintf(("General", "Setting language to %s\n", Lcl_languages[lang].lang_name));
|
||||
}
|
||||
@@ -553,7 +537,10 @@ void lcl_set_language(int lang)
|
||||
|
||||
ubyte lcl_get_font_index(int font_num)
|
||||
{
|
||||
int lang = lcl_get_current_lang_index();
|
||||
int lang = Lcl_current_lang;
|
||||
if (Lcl_current_lang == LCL_UNTRANSLATED) {
|
||||
lang = LCL_DEFAULT;
|
||||
}
|
||||
|
||||
if (Unicode_text_mode) {
|
||||
// In Unicode mode there are no special characters. Some of the code still uses this function in that mode so
|
||||
@@ -570,7 +557,10 @@ ubyte lcl_get_font_index(int font_num)
|
||||
// maybe add localized directory to full path with file name when opening a localized file
|
||||
int lcl_add_dir_to_path_with_filename(char *current_path, size_t path_max)
|
||||
{
|
||||
int lang = lcl_get_current_lang_index();
|
||||
int lang = Lcl_current_lang;
|
||||
if (Lcl_current_lang == LCL_UNTRANSLATED) {
|
||||
lang = LCL_DEFAULT;
|
||||
}
|
||||
|
||||
// if the disk extension is 0 length, don't add anything
|
||||
if (strlen(Lcl_languages[lang].lang_ext) <= 0) {
|
||||
@@ -607,9 +597,9 @@ int lcl_add_dir_to_path_with_filename(char *current_path, size_t path_max)
|
||||
|
||||
// externalization of table/mission files -----------------------
|
||||
|
||||
void lcl_replace_stuff(char *text, size_t max_len, bool force)
|
||||
void lcl_replace_stuff(char *text, size_t max_len)
|
||||
{
|
||||
if (Fred_running && !force)
|
||||
if (Fred_running)
|
||||
return;
|
||||
|
||||
Assert(text); // Goober5000
|
||||
@@ -628,17 +618,16 @@ void lcl_replace_stuff(char *text, size_t max_len, bool force)
|
||||
// now will also replace $quote with double quotation marks
|
||||
// now will also replace $semicolon with semicolon mark
|
||||
// now will also replace $slash and $backslash
|
||||
void lcl_replace_stuff(SCP_string &text, bool force)
|
||||
void lcl_replace_stuff(SCP_string &text)
|
||||
{
|
||||
if (Fred_running && !force)
|
||||
if (Fred_running)
|
||||
return;
|
||||
|
||||
if (!Fred_running && Player != nullptr)
|
||||
if (Player != NULL)
|
||||
{
|
||||
replace_all(text, "$callsign", Player->callsign);
|
||||
replace_all(text, "$rank", Ranks[Player->stats.rank].name);
|
||||
}
|
||||
|
||||
replace_all(text, "$quote", "\"");
|
||||
replace_all(text, "$semicolon", ";");
|
||||
replace_all(text, "$slash", "/");
|
||||
@@ -750,7 +739,7 @@ bool lcl_ext_localize_sub(const char *in, char *text_str, char *out, size_t max_
|
||||
}
|
||||
|
||||
// if the localization file is not open, or there's no entry, or we're not translating, return the original string
|
||||
if ( !Xstr_inited || (str_id < 0) || (Lcl_current_lang == LCL_UNTRANSLATED) || (Lcl_current_lang == LCL_RETAIL_HYBRID) ) {
|
||||
if ( !Xstr_inited || (str_id < 0) || (Lcl_current_lang == LCL_UNTRANSLATED) ) {
|
||||
if ( strlen(text_str) > max_len && !Lcl_unexpected_tstring_check )
|
||||
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", text_str, strlen(text_str), max_len);
|
||||
|
||||
@@ -836,7 +825,7 @@ bool lcl_ext_localize_sub(const SCP_string &in, SCP_string &text_str, SCP_string
|
||||
}
|
||||
|
||||
// if the localization file is not open, or there's no entry, or we're not translating, return the original string
|
||||
if ( !Xstr_inited || (str_id < 0) || (Lcl_current_lang == LCL_UNTRANSLATED) || (Lcl_current_lang == LCL_RETAIL_HYBRID) ) {
|
||||
if ( !Xstr_inited || (str_id < 0) || (Lcl_current_lang == LCL_UNTRANSLATED) ) {
|
||||
out = text_str;
|
||||
|
||||
if (id != NULL)
|
||||
@@ -955,18 +944,8 @@ const char *XSTR(const char *str, int index, bool force_lookup)
|
||||
// return translation of string
|
||||
if (Xstr_table[index].str)
|
||||
return Xstr_table[index].str;
|
||||
#ifndef NDEBUG
|
||||
else
|
||||
{
|
||||
// make sure missing strings are only logged once
|
||||
static SCP_unordered_set<int> Warned_xstr_indexes;
|
||||
if (Warned_xstr_indexes.count(index) == 0)
|
||||
{
|
||||
Warned_xstr_indexes.insert(index);
|
||||
mprintf(("No XSTR entry in strings.tbl for index %d\n", index));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
mprintf(("No XSTR entry in strings.tbl for index %d\n", index));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1219,7 +1198,9 @@ int lcl_ext_get_id(const SCP_string &xstr, int *out)
|
||||
|
||||
void lcl_get_language_name(char *lang_name)
|
||||
{
|
||||
int lang = lcl_get_current_lang_index();
|
||||
int lang = Lcl_current_lang;
|
||||
if (Lcl_current_lang == LCL_UNTRANSLATED)
|
||||
lang = LCL_DEFAULT;
|
||||
|
||||
Assert(lang >= 0 && lang < (int)Lcl_languages.size());
|
||||
strcpy(lang_name, Lcl_languages[lang].lang_name);
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#define LCL_POLISH 3
|
||||
|
||||
#define LCL_UNTRANSLATED 10 // this should be higher than the highest builtin language
|
||||
#define LCL_RETAIL_HYBRID 11 // ditto; this is the weird retail behavior where internal is translated but external isn't
|
||||
#define LCL_DEFAULT 0
|
||||
|
||||
// for language name strings
|
||||
@@ -71,10 +70,6 @@ extern bool *Lcl_unexpected_tstring_check;
|
||||
// LOCALIZE FUNCTIONS
|
||||
//
|
||||
|
||||
// get an index we can use to look into the array, since we now have three different ways of using English
|
||||
// (translated, untranslated, and hybrid: internal translated, external untranslated)
|
||||
int lcl_get_current_lang_index();
|
||||
|
||||
// initialize localization, if no language is passed - use the language specified in the registry
|
||||
void lcl_init(int lang = -1);
|
||||
|
||||
@@ -105,8 +100,8 @@ ubyte lcl_get_font_index(int font_num);
|
||||
int lcl_add_dir_to_path_with_filename(char *current_path, size_t path_max);
|
||||
|
||||
// Goober5000
|
||||
void lcl_replace_stuff(char *text, size_t max_len, bool force = false);
|
||||
void lcl_replace_stuff(SCP_string &text, bool force = false);
|
||||
void lcl_replace_stuff(char *text, size_t max_len);
|
||||
void lcl_replace_stuff(SCP_string &text);
|
||||
|
||||
// Karajorma
|
||||
void lcl_fred_replace_stuff(char *text, size_t max_len);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "io/timer.h"
|
||||
#include "math/floating.h"
|
||||
#include "math/staticrand.h"
|
||||
|
||||
|
||||
/**
|
||||
@@ -72,15 +71,3 @@ int rand_chance(float frametime, float chance)
|
||||
|
||||
return frand() < (frametime * (chance + 1.0f));
|
||||
}
|
||||
|
||||
static float accum_golden_ratio_rand_seed = 0.0f;
|
||||
|
||||
// generates a quasirandom, low discrepancy number sequence
|
||||
// acts very much like a random number generator, but has the property of being very well distributed
|
||||
// use if you need quasirandom numbers, but don't want ugly 'runs' or 'clumps' of similar numbers
|
||||
float golden_ratio_rand() {
|
||||
accum_golden_ratio_rand_seed += GOLDEN_RATIO;
|
||||
if (accum_golden_ratio_rand_seed >= 1.0f)
|
||||
accum_golden_ratio_rand_seed -= 1.0f;
|
||||
return accum_golden_ratio_rand_seed;
|
||||
}
|
||||
@@ -113,8 +113,4 @@ extern float fl_roundoff(float x, int multiple);
|
||||
*/
|
||||
#define IS_NEAR_ZERO(x, e) (fl_abs(x) < (float)(e))
|
||||
|
||||
const float GOLDEN_RATIO = 0.618033989f;
|
||||
|
||||
float golden_ratio_rand();
|
||||
|
||||
#endif
|
||||
@@ -20,9 +20,8 @@ void init_semirand()
|
||||
{
|
||||
Semirand_inited = true;
|
||||
|
||||
// Originally this made a 30-bit rand by sticking two 15-bit rands from myrand() together. Instead we trim rand32() down to size.
|
||||
for (auto & number : Semirand)
|
||||
number = (unsigned) (rand32() & STATIC_RAND_MAX);
|
||||
number = (static_cast<unsigned>(myrand()) << 15u) + myrand();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
constexpr unsigned int SEMIRAND_MAX_LOG = 4;
|
||||
constexpr unsigned int SEMIRAND_MAX = (2u << SEMIRAND_MAX_LOG); //Do not change this! Change SEMIRAND_MAX_LOG!
|
||||
|
||||
// Static rand has a max value independent of myrand()
|
||||
#define STATIC_RAND_MAX 0x3fffffff
|
||||
|
||||
extern void init_semirand();
|
||||
extern int static_rand(int num);
|
||||
extern float static_randf(int num);
|
||||
|
||||
+15
-30
@@ -100,7 +100,7 @@ float atan2_safe(float y, float x)
|
||||
float vm_vec_projection_parallel(vec3d *component, const vec3d *src, const vec3d *unit_vec)
|
||||
{
|
||||
float mag;
|
||||
Assertion(vm_vec_is_normalized(unit_vec), "unit_vec must be normalized!");
|
||||
Assert( vm_vec_mag(unit_vec) > 0.999f && vm_vec_mag(unit_vec) < 1.001f );
|
||||
|
||||
mag = vm_vec_dot(src, unit_vec);
|
||||
vm_vec_copy_scale(component, unit_vec, mag);
|
||||
@@ -115,7 +115,7 @@ float vm_vec_projection_parallel(vec3d *component, const vec3d *src, const vec3d
|
||||
void vm_vec_projection_onto_plane(vec3d *projection, const vec3d *src, const vec3d *unit_normal)
|
||||
{
|
||||
float mag;
|
||||
Assertion(vm_vec_is_normalized(unit_normal), "unit_normal must be normalized!");
|
||||
Assert( vm_vec_mag(unit_normal) > 0.999f && vm_vec_mag(unit_normal) < 1.001f );
|
||||
|
||||
mag = vm_vec_dot(src, unit_normal);
|
||||
*projection = *src;
|
||||
@@ -132,7 +132,7 @@ void vm_project_point_onto_plane(vec3d *new_point, const vec3d *point, const vec
|
||||
{
|
||||
float D; // plane constant in Ax+By+Cz+D = 0 or dot(X,n) - dot(Xp,n) = 0, so D = -dot(Xp,n)
|
||||
float dist;
|
||||
Assertion(vm_vec_is_normalized(plane_normal), "plane_normal must be normalized!");
|
||||
Assert( vm_vec_mag(plane_normal) > 0.999f && vm_vec_mag(plane_normal) < 1.001f );
|
||||
|
||||
D = -vm_vec_dot(plane_point, plane_normal);
|
||||
dist = vm_vec_dot(point, plane_normal) + D;
|
||||
@@ -399,12 +399,6 @@ float vm_vec_dist(const vec3d *v0, const vec3d *v1)
|
||||
return t1;
|
||||
}
|
||||
|
||||
bool vm_vec_is_normalized(const vec3d *v)
|
||||
{
|
||||
// By the standards of FSO, it is sufficient to check that the magnitude is close to 1.
|
||||
return vm_vec_mag(v) > 0.999f && vm_vec_mag(v) < 1.001f;
|
||||
}
|
||||
|
||||
//normalize a vector. returns mag of source vec (always greater than zero)
|
||||
float vm_vec_copy_normalize(vec3d *dest, const vec3d *src)
|
||||
{
|
||||
@@ -2256,39 +2250,30 @@ void vm_vec_random_cone(vec3d *out, const vec3d *in, float min_angle, float max_
|
||||
|
||||
// given a start vector, an orientation, and a radius, generate a point on the plane of the circle
|
||||
// if on_edge is true, the point will be on the edge of the circle
|
||||
void vm_vec_random_in_circle(vec3d *out, const vec3d *in, const matrix *orient, float radius, bool on_edge, bool bias_towards_center)
|
||||
void vm_vec_random_in_circle(vec3d *out, const vec3d *in, const matrix *orient, float radius, bool on_edge)
|
||||
{
|
||||
vec3d temp;
|
||||
float scalar = frand();
|
||||
|
||||
// sqrt because scaling inward increases the probability density by the square of its proximity towards the center
|
||||
if (!bias_towards_center)
|
||||
scalar = sqrtf(scalar);
|
||||
|
||||
// point somewhere in the plane, maybe scaled inward
|
||||
vm_vec_scale_add(&temp, in, &orient->vec.rvec, on_edge ? radius : scalar * radius);
|
||||
// point somewhere in the plane
|
||||
vm_vec_scale_add(&temp, in, &orient->vec.rvec, on_edge ? radius : frand_range(0.0f, radius));
|
||||
|
||||
// rotate to a random point on the circle
|
||||
vm_rot_point_around_line(out, &temp, fl_radians(frand_range(0.0f, 360.0f)), in, &orient->vec.fvec);
|
||||
vm_rot_point_around_line(out, &temp, fl_radians(frand_range(0.0f, 359.0f)), in, &orient->vec.fvec);
|
||||
}
|
||||
|
||||
// given a start vector and a radius, generate a point in a spherical volume
|
||||
// if on_surface is true, the point will be on the surface of the sphere
|
||||
void vm_vec_random_in_sphere(vec3d *out, const vec3d *in, float radius, bool on_surface, bool bias_towards_center)
|
||||
void vm_vec_random_in_sphere(vec3d *out, const vec3d *in, float radius, bool on_surface)
|
||||
{
|
||||
vec3d temp;
|
||||
|
||||
float z = util::UniformFloatRange(-1, 1).next(); // Take a 2-sphere slice
|
||||
float phi = util::UniformFloatRange(0.0f, PI2).next();
|
||||
vm_vec_make(&temp, sqrtf(1.0f - z * z) * cosf(phi), sqrtf(1.0f - z * z) * sinf(phi), z); // Using the z-vec as the starting point
|
||||
// Uniformly distributing each coordinate of a vector then normalizing results in a uniform sphere distribution
|
||||
util::UniformFloatRange coords(-1.0f,1.0f);
|
||||
vm_vec_make( &temp, coords.next(), coords.next(), coords.next() );
|
||||
vm_vec_normalize(&temp);
|
||||
|
||||
float scalar = util::UniformFloatRange(0.0f, 1.0f).next();
|
||||
|
||||
// cube root because scaling inward increases the probability density by the cube of its proximity towards the center
|
||||
if (!bias_towards_center)
|
||||
scalar = powf(scalar, 0.333f);
|
||||
|
||||
vm_vec_scale_add(out, in, &temp, on_surface ? radius : scalar * radius);
|
||||
// We then add the scaled result to the initial position to get the final position
|
||||
vm_vec_scale_add(out, in, &temp, on_surface ? radius : util::UniformFloatRange(0.0f, radius).next());
|
||||
}
|
||||
|
||||
// find the nearest point on the line to p. if dist is non-NULL, it is filled in
|
||||
@@ -2860,6 +2845,6 @@ void vm_interpolate_angles_quick(angles *dest0, angles *src0, angles *src1, floa
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const vec3d& vec)
|
||||
{
|
||||
os << "vec3d<" << vec.xyz.x << ", " << vec.xyz.y << ", " << vec.xyz.z << ">";
|
||||
os << "vec3d<" << vec.xyz.x << ", " << vec.xyz.y << ", " << vec.xyz.z << ")";
|
||||
return os;
|
||||
}
|
||||
+2
-6
@@ -186,8 +186,6 @@ float vm_vec_dist(const vec3d *v0, const vec3d *v1);
|
||||
#define vm_vec_normalized_dir_quick vm_vec_normalized_dir
|
||||
#define vm_vec_rand_vec_quick vm_vec_rand_vec
|
||||
|
||||
bool vm_vec_is_normalized(const vec3d *v);
|
||||
|
||||
//normalize a vector. returns mag of source vec
|
||||
float vm_vec_copy_normalize(vec3d *dest, const vec3d *src);
|
||||
float vm_vec_normalize(vec3d *v);
|
||||
@@ -486,13 +484,11 @@ void vm_vec_random_cone(vec3d *out, const vec3d *in, float min_angle, float max_
|
||||
|
||||
// given a start vector, an orientation, and a radius, generate a point on the plane of the circle
|
||||
// if on_edge is true, the point will be on the edge of the circle
|
||||
// if bias_towards_center is true, the probability will be higher towards the center
|
||||
void vm_vec_random_in_circle(vec3d *out, const vec3d *in, const matrix *orient, float radius, bool on_edge, bool bias_towards_center = false);
|
||||
void vm_vec_random_in_circle(vec3d *out, const vec3d *in, const matrix *orient, float radius, bool on_edge);
|
||||
|
||||
// given a start vector and a radius, generate a point in a spherical volume
|
||||
// if on_surface is true, the point will be on the surface of the sphere
|
||||
// if bias_towards_center is true, the probability will be higher towards the center
|
||||
void vm_vec_random_in_sphere(vec3d *out, const vec3d *in, float radius, bool on_surface, bool bias_towards_center = false);
|
||||
void vm_vec_random_in_sphere(vec3d *out, const vec3d *in, float radius, bool on_surface);
|
||||
|
||||
// find the nearest point on the line to p. if dist is non-NULL, it is filled in
|
||||
// returns 0 if the point is inside the line segment, -1 if "before" the line segment and 1 ir "after" the line segment
|
||||
|
||||
@@ -2576,7 +2576,7 @@ void parse_one_main_hall(bool replace, int num_resolutions, int &hall_idx, int &
|
||||
if (res_idx >= num_resolutions)
|
||||
{
|
||||
res_idx = 0;
|
||||
hall_idx = (int)Main_hall_defines.size(); // assume the next main hall is going to be a new one
|
||||
hall_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -413,12 +413,6 @@ void player_select_do()
|
||||
gr_set_bitmap(Player_select_background_bitmap);
|
||||
gr_bitmap(0,0,GR_RESIZE_MENU);
|
||||
|
||||
//skip this if pilot is given through cmdline, assuming single-player
|
||||
if (Cmdline_pilot) {
|
||||
player_finish_select(Cmdline_pilot, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// press the accept button
|
||||
if (Player_select_autoaccept) {
|
||||
Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button.press_button();
|
||||
@@ -1450,7 +1444,7 @@ void player_finish_select(const char* callsign, bool is_multi) {
|
||||
|
||||
// now read in a the pilot data
|
||||
if ( !Pilot.load_player(callsign, Player) ) {
|
||||
Error(LOCATION,"Couldn't load pilot file for pilot \"%s\", bailing", callsign);
|
||||
Error(LOCATION,"Couldn't load pilot file, bailing");
|
||||
Player = nullptr;
|
||||
} else {
|
||||
// NOTE: this may fail if there is no current campaign, it's not fatal
|
||||
|
||||
@@ -275,7 +275,7 @@ int hash_filename(const char *filename) {
|
||||
|
||||
// Don't hash .fsm extension, convert all to upper case
|
||||
for (int i=0; i < ((signed int)(strlen(filename)) - 4); i++) {
|
||||
hash_val = (hash_val << 4) + SCP_toupper(*ptr++);
|
||||
hash_val = (hash_val << 4) + toupper(*ptr++);
|
||||
}
|
||||
|
||||
return int(hash_val % CAMPAIGN_MISSION_HASH_SIZE);
|
||||
|
||||
+227
-123
@@ -5,8 +5,7 @@
|
||||
* or otherwise commercially exploit the source or things you created based on the
|
||||
* source.
|
||||
*
|
||||
*/
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +42,7 @@
|
||||
|
||||
#define NUM_BUTTONS 16
|
||||
#define NUM_TABS 3
|
||||
#define LIST_BUTTONS_MAX 50
|
||||
#define LIST_BUTTONS_MAX 42
|
||||
|
||||
#define SHIPS_DATA_MODE (1<<0)
|
||||
#define WEAPONS_DATA_MODE (1<<1)
|
||||
@@ -236,10 +235,14 @@ typedef struct {
|
||||
int textures_loaded; // if the model has textures loaded for it or not (hacky mem management)
|
||||
} tech_list_entry;
|
||||
|
||||
static SCP_vector<tech_list_entry> Ship_list;
|
||||
static SCP_vector<tech_list_entry> Weapon_list;
|
||||
static SCP_vector<tech_list_entry> Intel_list;
|
||||
static SCP_vector<tech_list_entry>* Current_list = &Ship_list; // A pointer to the current display list
|
||||
static tech_list_entry *Ship_list = nullptr;
|
||||
static int Ship_list_size = 0;
|
||||
static tech_list_entry *Weapon_list = nullptr;
|
||||
static int Weapon_list_size = 0;
|
||||
static tech_list_entry *Intel_list = nullptr;
|
||||
static int Intel_list_size = 0;
|
||||
static tech_list_entry *Current_list; // points to currently valid display list
|
||||
static int Current_list_size = 0;
|
||||
|
||||
// slider stuff
|
||||
static UI_SLIDER2 Tech_slider;
|
||||
@@ -271,63 +274,64 @@ void techroom_init_desc(const char *src, int w)
|
||||
|
||||
void techroom_unload_animation()
|
||||
{
|
||||
//clear everything, just in case, it will get loaded when needed later
|
||||
for (auto& list_entry : Weapon_list) {
|
||||
if (list_entry.animation.type != BM_TYPE_NONE && list_entry.has_anim != 0) {
|
||||
generic_anim_unload(&list_entry.animation);
|
||||
}
|
||||
int i;
|
||||
|
||||
if (list_entry.bitmap >= 0) {
|
||||
bm_release(list_entry.bitmap);
|
||||
list_entry.bitmap = -1;
|
||||
//clear everything, just in case, it will get loaded when needed later
|
||||
if (Weapon_list != NULL) {
|
||||
for (i = 0; i < Weapon_list_size; i++) {
|
||||
if (Weapon_list[i].animation.num_frames != 0) {
|
||||
generic_anim_unload(&Weapon_list[i].animation);
|
||||
}
|
||||
|
||||
if (Weapon_list[i].bitmap >= 0) {
|
||||
bm_release(Weapon_list[i].bitmap);
|
||||
Weapon_list[i].bitmap = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & intel_entry : Intel_list) {
|
||||
if (intel_entry.animation.type != BM_TYPE_NONE && intel_entry.has_anim != 0) {
|
||||
generic_anim_unload(&intel_entry.animation);
|
||||
for (i = 0; i < Intel_list_size; i++) {
|
||||
if (Intel_list[i].animation.num_frames != 0) {
|
||||
generic_anim_unload(&Intel_list[i].animation);
|
||||
}
|
||||
|
||||
if (intel_entry.bitmap >= 0) {
|
||||
bm_release(intel_entry.bitmap);
|
||||
intel_entry.bitmap = -1;
|
||||
if (Intel_list[i].bitmap >= 0) {
|
||||
bm_release(Intel_list[i].bitmap);
|
||||
Intel_list[i].bitmap = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void techroom_select_new_entry()
|
||||
{
|
||||
if (Current_list->empty()) {
|
||||
Assert(Current_list != NULL);
|
||||
if (Current_list == NULL || Current_list_size <= 0) {
|
||||
Cur_entry_index = Cur_entry = -1;
|
||||
techroom_init_desc(nullptr,0);
|
||||
techroom_init_desc(NULL,0);
|
||||
return;
|
||||
}
|
||||
|
||||
Assert(Cur_entry < static_cast<int>(Current_list->size()));
|
||||
|
||||
Cur_entry_index = Current_list->at(Cur_entry).index;
|
||||
Cur_entry_index = Current_list[Cur_entry].index;
|
||||
Assert( Cur_entry_index >= 0 );
|
||||
|
||||
// if we are in the ships tab, load the ship model
|
||||
if (Tab == SHIPS_DATA_TAB) {
|
||||
ship_info *sip = &Ship_info[Cur_entry_index];
|
||||
|
||||
int i = 0;
|
||||
// little memory management, kinda hacky but it should keep the techroom at around
|
||||
// 100meg rather than the 700+ it can get to with all ships loaded - taylor
|
||||
for (auto & list_entry : *Current_list) {
|
||||
if ((list_entry.model_num > -1) && (list_entry.textures_loaded)) {
|
||||
for (int i=0; i<Current_list_size; i++) {
|
||||
if ((Current_list[i].model_num > -1) && (Current_list[i].textures_loaded)) {
|
||||
// don't unload any spot within 5 of current
|
||||
if ((i < Cur_entry + 5) && (i > Cur_entry - 5) )
|
||||
if ( (i < Cur_entry + 5) && (i > Cur_entry - 5) )
|
||||
continue;
|
||||
|
||||
mprintf(("TECH ROOM: Dumping excess ship textures...\n"));
|
||||
|
||||
model_page_out_textures(list_entry.model_num);
|
||||
model_page_out_textures(Current_list[i].model_num);
|
||||
|
||||
list_entry.textures_loaded = 0;
|
||||
Current_list[i].textures_loaded = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
Techroom_ship_modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
|
||||
@@ -337,14 +341,12 @@ void techroom_select_new_entry()
|
||||
}
|
||||
Techroom_ship_model_instance = model_create_instance(true, Techroom_ship_modelnum);
|
||||
|
||||
model_set_up_techroom_instance(sip, Techroom_ship_model_instance);
|
||||
|
||||
Current_list->at(Cur_entry).model_num = Techroom_ship_modelnum;
|
||||
Current_list[Cur_entry].model_num = Techroom_ship_modelnum;
|
||||
|
||||
// page in ship textures properly (takes care of nondimming pixels)
|
||||
model_page_in_textures(Techroom_ship_modelnum, Cur_entry_index);
|
||||
|
||||
Current_list->at(Cur_entry).textures_loaded = 1;
|
||||
Current_list[Cur_entry].textures_loaded = 1;
|
||||
} else {
|
||||
Techroom_ship_modelnum = -1;
|
||||
|
||||
@@ -356,19 +358,19 @@ void techroom_select_new_entry()
|
||||
Trackball_mode = 0;
|
||||
|
||||
// load animation here, we now only have one loaded
|
||||
int stream_result = generic_anim_init_and_stream(&Current_list->at(Cur_entry).animation, Current_list->at(Cur_entry).tech_anim_filename, bm_get_type(Tech_background_bitmap), true);
|
||||
int stream_result = generic_anim_init_and_stream(&Current_list[Cur_entry].animation, Current_list[Cur_entry].tech_anim_filename, bm_get_type(Tech_background_bitmap), true);
|
||||
|
||||
if (stream_result >= 0) {
|
||||
Current_list->at(Cur_entry).has_anim = 1;
|
||||
Current_list[Cur_entry].has_anim = 1;
|
||||
} else {
|
||||
// we've failed to load any animation
|
||||
// load an image and treat it like a 1 frame animation
|
||||
Current_list->at(Cur_entry).bitmap = bm_load(Current_list->at(Cur_entry).tech_anim_filename);
|
||||
Current_list[Cur_entry].bitmap = bm_load(Current_list[Cur_entry].tech_anim_filename);
|
||||
}
|
||||
}
|
||||
|
||||
techroom_init_desc(Current_list->at(Cur_entry).desc, Tech_desc_coords[gr_screen.res][SHIP_W_COORD]);
|
||||
fsspeech_play(FSSPEECH_FROM_TECHROOM, Current_list->at(Cur_entry).desc);
|
||||
techroom_init_desc(Current_list[Cur_entry].desc, Tech_desc_coords[gr_screen.res][SHIP_W_COORD]);
|
||||
fsspeech_play(FSSPEECH_FROM_TECHROOM, Current_list[Cur_entry].desc);
|
||||
}
|
||||
|
||||
// write out the current description in the bottom window
|
||||
@@ -430,7 +432,7 @@ void tech_common_render()
|
||||
y = 0;
|
||||
z = List_offset;
|
||||
while (y + font_height <= Tech_list_coords[gr_screen.res][SHIP_H_COORD]) {
|
||||
if ((z - List_offset) >= LIST_BUTTONS_MAX || z >= static_cast<int>(Current_list->size())) {
|
||||
if (z >= Current_list_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -443,7 +445,7 @@ void tech_common_render()
|
||||
}
|
||||
|
||||
memset( buf, 0, sizeof(buf) );
|
||||
strncpy(buf, Current_list->at(z).name, sizeof(buf) - 1);
|
||||
strncpy(buf, Current_list[z].name, sizeof(buf) - 1);
|
||||
|
||||
if (Lcl_gr && !Disable_built_in_translations)
|
||||
lcl_translate_ship_name_gr(buf);
|
||||
@@ -465,6 +467,8 @@ void tech_common_render()
|
||||
}
|
||||
}
|
||||
|
||||
void light_set_all_relevent();
|
||||
|
||||
void techroom_ships_render(float frametime)
|
||||
{
|
||||
// render all the common stuff
|
||||
@@ -476,6 +480,7 @@ void techroom_ships_render(float frametime)
|
||||
// now render the trackball ship, which is unique to the ships tab
|
||||
float rev_rate = REVOLUTION_RATE;
|
||||
angles rot_angles, view_angles;
|
||||
int i, j;
|
||||
ship_info *sip = &Ship_info[Cur_entry_index];
|
||||
model_render_params render_info;
|
||||
|
||||
@@ -546,6 +551,31 @@ void techroom_ships_render(float frametime)
|
||||
model_clear_instance(Techroom_ship_modelnum);
|
||||
render_info.set_detail_level_lock(0);
|
||||
|
||||
polymodel *pm = model_get(Techroom_ship_modelnum);
|
||||
polymodel_instance *pmi = model_get_instance(Techroom_ship_model_instance);
|
||||
|
||||
for (i = 0; i < sip->n_subsystems; i++) {
|
||||
model_subsystem *msp = &sip->subsystems[i];
|
||||
if (msp->type == SUBSYSTEM_TURRET) {
|
||||
|
||||
float p = 0.0f;
|
||||
float h = 0.0f;
|
||||
|
||||
for (j = 0; j < msp->n_triggers; j++) {
|
||||
|
||||
// special case for turrets
|
||||
p = msp->triggers[j].angle.xyz.x;
|
||||
h = msp->triggers[j].angle.xyz.y;
|
||||
}
|
||||
if ( msp->subobj_num >= 0 ) {
|
||||
model_set_instance_techroom(pm, pmi, msp->subobj_num, 0.0f, h );
|
||||
}
|
||||
if ( (msp->subobj_num != msp->turret_gun_sobj) && (msp->turret_gun_sobj >= 0) ) {
|
||||
model_set_instance_techroom(pm, pmi, msp->turret_gun_sobj, p, 0.0f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sip->replacement_textures.size() > 0)
|
||||
{
|
||||
render_info.set_replacement_textures(Techroom_ship_modelnum, sip->replacement_textures);
|
||||
@@ -555,12 +585,10 @@ void techroom_ships_render(float frametime)
|
||||
{
|
||||
gr_reset_clip();
|
||||
|
||||
auto pm = model_get(Techroom_ship_modelnum);
|
||||
|
||||
shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -sip->closeup_pos.xyz.z + pm->rad, -sip->closeup_pos.xyz.z + pm->rad + 200.0f, -sip->closeup_pos.xyz.z + pm->rad + 2000.0f, -sip->closeup_pos.xyz.z + pm->rad + 10000.0f);
|
||||
render_info.set_flags(MR_NO_TEXTURING | MR_NO_LIGHTING | MR_AUTOCENTER);
|
||||
|
||||
model_render_immediate(&render_info, Techroom_ship_modelnum, Techroom_ship_model_instance, &Techroom_ship_orient, &vmd_zero_vector);
|
||||
model_render_immediate(&render_info, Techroom_ship_modelnum, &Techroom_ship_orient, &vmd_zero_vector);
|
||||
shadows_end_render();
|
||||
|
||||
gr_set_clip(Tech_ship_display_coords[gr_screen.res][SHIP_X_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_Y_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_W_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_H_COORD], GR_RESIZE_MENU);
|
||||
@@ -576,7 +604,7 @@ void techroom_ships_render(float frametime)
|
||||
|
||||
render_info.set_flags(render_flags);
|
||||
|
||||
model_render_immediate(&render_info, Techroom_ship_modelnum, Techroom_ship_model_instance, &Techroom_ship_orient, &vmd_zero_vector);
|
||||
model_render_immediate(&render_info, Techroom_ship_modelnum, &Techroom_ship_orient, &vmd_zero_vector);
|
||||
|
||||
Glowpoint_use_depth_buffer = true;
|
||||
|
||||
@@ -598,7 +626,7 @@ void tech_prev_entry()
|
||||
|
||||
Cur_entry--;
|
||||
if (Cur_entry < 0) {
|
||||
Cur_entry = static_cast<int>(Current_list->size() - 1);
|
||||
Cur_entry = Current_list_size - 1;
|
||||
|
||||
// scroll to end of list
|
||||
List_offset = Cur_entry - Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() + 1;
|
||||
@@ -626,7 +654,7 @@ void tech_next_entry()
|
||||
techroom_unload_animation();
|
||||
|
||||
Cur_entry++;
|
||||
if (Cur_entry >= static_cast<int>(Current_list->size())) {
|
||||
if (Cur_entry >= Current_list_size) {
|
||||
Cur_entry = 0;
|
||||
|
||||
// scroll to beginning of list
|
||||
@@ -682,7 +710,7 @@ void tech_scroll_list_up()
|
||||
|
||||
void tech_scroll_list_down()
|
||||
{
|
||||
if (List_offset + Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() < static_cast<int>(Current_list->size())) {
|
||||
if (List_offset + Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() < Current_list_size) {
|
||||
List_offset++;
|
||||
gamesnd_play_iface(InterfaceSounds::SCROLL);
|
||||
} else {
|
||||
@@ -707,27 +735,27 @@ void techroom_anim_render(float frametime)
|
||||
tech_common_render();
|
||||
|
||||
// exit now if there are no entries to show
|
||||
if (Current_list->empty() || Cur_entry < 0 || Cur_entry >= static_cast<int>(Current_list->size()))
|
||||
if (Current_list_size == 0 || Cur_entry < 0)
|
||||
return;
|
||||
|
||||
// render the animation
|
||||
if(Current_list->at(Cur_entry).animation.num_frames > 0)
|
||||
if(Current_list[Cur_entry].animation.num_frames > 0)
|
||||
{
|
||||
//grab dimensions
|
||||
bm_get_info((Current_list->at(Cur_entry).animation.streaming) ? Current_list->at(Cur_entry).animation.bitmap_id : Current_list->at(Cur_entry).animation.first_frame, &x, &y, nullptr, nullptr, nullptr);
|
||||
bm_get_info((Current_list[Cur_entry].animation.streaming) ? Current_list[Cur_entry].animation.bitmap_id : Current_list[Cur_entry].animation.first_frame, &x, &y, NULL, NULL, NULL);
|
||||
//get the centre point - adjust
|
||||
x = Tech_ani_centre_coords[gr_screen.res][0] - x / 2;
|
||||
y = Tech_ani_centre_coords[gr_screen.res][1] - y / 2;
|
||||
generic_anim_render(&Current_list->at(Cur_entry).animation, frametime, x, y, true);
|
||||
generic_anim_render(&Current_list[Cur_entry].animation, frametime, x, y, true);
|
||||
}
|
||||
// if our active item has a bitmap instead of an animation, draw it
|
||||
else if((Cur_entry >= 0) && (Current_list->at(Cur_entry).bitmap >= 0)){
|
||||
else if((Cur_entry >= 0) && (Current_list[Cur_entry].bitmap >= 0)){
|
||||
//grab dimensions
|
||||
bm_get_info(Current_list->at(Cur_entry).bitmap, &x, &y, nullptr, nullptr, nullptr);
|
||||
bm_get_info(Current_list[Cur_entry].bitmap, &x, &y, NULL, NULL, NULL);
|
||||
//get the centre point - adjust
|
||||
x = Tech_ani_centre_coords[gr_screen.res][0] - x / 2;
|
||||
y = Tech_ani_centre_coords[gr_screen.res][1] - y / 2;
|
||||
gr_set_bitmap(Current_list->at(Cur_entry).bitmap);
|
||||
gr_set_bitmap(Current_list[Cur_entry].bitmap);
|
||||
gr_bitmap(x, y, GR_RESIZE_MENU);
|
||||
}
|
||||
}
|
||||
@@ -764,40 +792,50 @@ void techroom_change_tab(int num)
|
||||
|
||||
// load ship info if necessary
|
||||
if ( !Ships_loaded ) {
|
||||
if (Ship_list.empty()) {
|
||||
Ship_list.reserve(Ship_info.size());
|
||||
if (Ship_list == NULL) {
|
||||
Ship_list = new tech_list_entry[Ship_info.size()];
|
||||
}
|
||||
|
||||
tech_list_entry temp_entry;
|
||||
|
||||
// we always initially set these values, so keep them outside the loop.
|
||||
temp_entry.bitmap = -1;
|
||||
temp_entry.animation.num_frames = 0; // no anim for ships
|
||||
temp_entry.has_anim = 0; // no anim for ships
|
||||
temp_entry.model_num = -1;
|
||||
temp_entry.textures_loaded = 0;
|
||||
Ship_list_size = 0;
|
||||
|
||||
for (auto it = Ship_info.begin(); it != Ship_info.end(); ++it)
|
||||
{
|
||||
if (Techroom_show_all || (it->flags & si_mask).any_set())
|
||||
{
|
||||
// this ship should be displayed, fill out the entry struct
|
||||
temp_entry.index = (int)std::distance(Ship_info.begin(), it);
|
||||
temp_entry.name = *it->tech_title ? it->tech_title : it->get_display_name();
|
||||
temp_entry.desc = it->tech_desc;
|
||||
Ship_list[Ship_list_size].bitmap = -1;
|
||||
Ship_list[Ship_list_size].index = (int)std::distance(Ship_info.begin(), it);
|
||||
Ship_list[Ship_list_size].animation.num_frames = 0; // no anim for ships
|
||||
Ship_list[Ship_list_size].has_anim = 0; // no anim for ships
|
||||
Ship_list[Ship_list_size].name = *it->tech_title ? it->tech_title : it->get_display_name();
|
||||
Ship_list[Ship_list_size].desc = it->tech_desc;
|
||||
Ship_list[Ship_list_size].model_num = -1;
|
||||
Ship_list[Ship_list_size].textures_loaded = 0;
|
||||
|
||||
Ship_list.push_back(temp_entry);
|
||||
Ship_list_size++;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure that at least the default entry is cleared out if we didn't grab anything
|
||||
if (!Ship_info.empty() && !Ship_list_size) {
|
||||
Ship_list[0].index = -1;
|
||||
Ship_list[0].desc = NULL;
|
||||
Ship_list[0].name = NULL;
|
||||
Ship_list[0].bitmap = -1;
|
||||
Ship_list[0].has_anim = 0;
|
||||
Ship_list[0].animation.num_frames = 0;
|
||||
Ship_list[0].model_num = -1;
|
||||
Ship_list[0].textures_loaded = 0;
|
||||
}
|
||||
|
||||
Ships_loaded = true;
|
||||
}
|
||||
|
||||
Current_list = &Ship_list;
|
||||
Current_list = Ship_list;
|
||||
Current_list_size = Ship_list_size;
|
||||
|
||||
font_height = gr_get_font_height();
|
||||
max_num_entries_viewable = Tech_list_coords[gr_screen.res][SHIP_H_COORD] / font_height;
|
||||
Tech_slider.set_numberItems((int)Current_list->size() > max_num_entries_viewable ? (int)Current_list->size()-max_num_entries_viewable : 0);
|
||||
Tech_slider.set_numberItems(Current_list_size > max_num_entries_viewable ? Current_list_size-max_num_entries_viewable : 0);
|
||||
|
||||
// no anim to start here
|
||||
break;
|
||||
@@ -806,45 +844,57 @@ void techroom_change_tab(int num)
|
||||
|
||||
// load weapon info & anims if necessary
|
||||
if ( !Weapons_loaded ) {
|
||||
Weapon_list.reserve(Weapon_info.size());
|
||||
if (Weapon_list == NULL) {
|
||||
Weapon_list = new tech_list_entry[Weapon_info.size()];
|
||||
}
|
||||
Weapon_list_size = 0;
|
||||
|
||||
wi_mask.set(multi ? Weapon::Info_Flags::Player_allowed : Weapon::Info_Flags::In_tech_database);
|
||||
wi_mask.set(Weapon::Info_Flags::Default_in_tech_database);
|
||||
|
||||
int i = 0;
|
||||
tech_list_entry temp_entry;
|
||||
|
||||
// we always initially set these values, so keep them outside the loop.
|
||||
temp_entry.has_anim = 1;
|
||||
temp_entry.bitmap = -1;
|
||||
temp_entry.animation.num_frames = 0;
|
||||
temp_entry.model_num = -1;
|
||||
temp_entry.textures_loaded = 0;
|
||||
|
||||
for (auto &wi : Weapon_info)
|
||||
{
|
||||
if (Techroom_show_all || (wi.wi_flags & wi_mask).any_set())
|
||||
{
|
||||
// we have a weapon that should be in the tech db, so fill out specific info
|
||||
temp_entry.index = i;
|
||||
temp_entry.desc = wi.tech_desc;
|
||||
temp_entry.name = wi.tech_title[0] ? wi.tech_title : wi.get_display_name();
|
||||
// we have a weapon that should be in the tech db, so fill out the entry struct
|
||||
Weapon_list[Weapon_list_size].index = i;
|
||||
Weapon_list[Weapon_list_size].desc = wi.tech_desc;
|
||||
Weapon_list[Weapon_list_size].has_anim = 1;
|
||||
Weapon_list[Weapon_list_size].name = wi.tech_title[0] ? wi.tech_title : wi.get_display_name();
|
||||
Weapon_list[Weapon_list_size].bitmap = -1;
|
||||
Weapon_list[Weapon_list_size].animation.num_frames = 0;
|
||||
Weapon_list[Weapon_list_size].model_num = -1;
|
||||
Weapon_list[Weapon_list_size].textures_loaded = 0;
|
||||
// copy the weapon animation filename
|
||||
strncpy(temp_entry.tech_anim_filename, wi.tech_anim_filename, MAX_FILENAME_LEN - 1);
|
||||
|
||||
Weapon_list.push_back(temp_entry);
|
||||
strncpy(Weapon_list[Weapon_list_size].tech_anim_filename, wi.tech_anim_filename, MAX_FILENAME_LEN - 1);
|
||||
|
||||
++Weapon_list_size;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
// make sure that at least the default entry is cleared out if we didn't grab anything
|
||||
if (!Weapon_info.empty() && !Weapon_list_size) {
|
||||
Weapon_list[0].index = -1;
|
||||
Weapon_list[0].desc = NULL;
|
||||
Weapon_list[0].name = NULL;
|
||||
Weapon_list[0].bitmap = -1;
|
||||
Weapon_list[0].has_anim = 0;
|
||||
Weapon_list[0].animation.num_frames = 0;
|
||||
Weapon_list[0].model_num = -1;
|
||||
Weapon_list[0].textures_loaded = 0;
|
||||
}
|
||||
|
||||
Weapons_loaded = true;
|
||||
}
|
||||
|
||||
Current_list = &Weapon_list;
|
||||
Current_list = Weapon_list;
|
||||
Current_list_size = Weapon_list_size;
|
||||
|
||||
font_height = gr_get_font_height();
|
||||
max_num_entries_viewable = Tech_list_coords[gr_screen.res][SHIP_H_COORD] / font_height;
|
||||
Tech_slider.set_numberItems(static_cast<int>(Current_list->size()) > max_num_entries_viewable ? static_cast<int>(Current_list->size())-max_num_entries_viewable : 0);
|
||||
Tech_slider.set_numberItems(Current_list_size > max_num_entries_viewable ? Current_list_size-max_num_entries_viewable : 0);
|
||||
|
||||
break;
|
||||
|
||||
@@ -852,46 +902,60 @@ void techroom_change_tab(int num)
|
||||
|
||||
// load intel if necessary
|
||||
if ( !Intel_loaded ) {
|
||||
if (Intel_list.empty()) {
|
||||
Intel_list.reserve(Intel_info.size());
|
||||
if (Intel_list == nullptr) {
|
||||
Intel_list = new tech_list_entry[Intel_info.size()];
|
||||
}
|
||||
|
||||
Intel_list_size = 0;
|
||||
|
||||
int i = 0;
|
||||
tech_list_entry temp_entry;
|
||||
|
||||
// we always initially set these values, so keep them outside the loop.
|
||||
temp_entry.has_anim = 0;
|
||||
temp_entry.model_num = -1;
|
||||
temp_entry.textures_loaded = 0;
|
||||
|
||||
for (auto &ii : Intel_info) {
|
||||
|
||||
if (Techroom_show_all || (ii.flags & IIF_IN_TECH_DATABASE)) {
|
||||
// leave option for no animation if string == "none"
|
||||
if (!strcmp(ii.anim_filename, "none")) {
|
||||
temp_entry.animation.num_frames = 0;
|
||||
Intel_list[Intel_list_size].has_anim = 0;
|
||||
Intel_list[Intel_list_size].animation.num_frames = 0;
|
||||
} else {
|
||||
// try and load as an animation
|
||||
temp_entry.bitmap = -1;
|
||||
strncpy(temp_entry.tech_anim_filename, ii.anim_filename, NAME_LENGTH - 1);
|
||||
Intel_list[Intel_list_size].has_anim = 0;
|
||||
Intel_list[Intel_list_size].bitmap = -1;
|
||||
strncpy(Intel_list[Intel_list_size].tech_anim_filename, ii.anim_filename, NAME_LENGTH - 1);
|
||||
}
|
||||
|
||||
temp_entry.desc = ii.desc.c_str();
|
||||
temp_entry.index = i;
|
||||
temp_entry.name = ii.name;
|
||||
Intel_list[Intel_list_size].desc = ii.desc.c_str();
|
||||
Intel_list[Intel_list_size].index = i;
|
||||
Intel_list[Intel_list_size].name = ii.name;
|
||||
Intel_list[Intel_list_size].model_num = -1;
|
||||
Intel_list[Intel_list_size].textures_loaded = 0;
|
||||
|
||||
Intel_list.push_back(temp_entry);
|
||||
Intel_list_size++;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
// make sure that at least the default entry is cleared out if we didn't grab anything
|
||||
if (!Intel_info.empty() && !Intel_list_size) {
|
||||
Intel_list[0].index = -1;
|
||||
Intel_list[0].desc = NULL;
|
||||
Intel_list[0].name = NULL;
|
||||
Intel_list[0].bitmap = -1;
|
||||
Intel_list[0].has_anim = 0;
|
||||
Intel_list[0].animation.num_frames = 0;
|
||||
Intel_list[0].model_num = -1;
|
||||
Intel_list[0].textures_loaded = 0;
|
||||
}
|
||||
|
||||
Intel_loaded = true;
|
||||
}
|
||||
|
||||
Current_list = &Intel_list;
|
||||
// index lookup on intel is a pretty pointless, but it keeps everything
|
||||
// consistent and doesn't really hurt anything
|
||||
Current_list = Intel_list;
|
||||
Current_list_size = Intel_list_size;
|
||||
|
||||
font_height = gr_get_font_height();
|
||||
max_num_entries_viewable = Tech_list_coords[gr_screen.res][SHIP_H_COORD] / font_height;
|
||||
Tech_slider.set_numberItems(static_cast<int>(Current_list->size()) > max_num_entries_viewable ? static_cast<int>(Current_list->size())-max_num_entries_viewable : 0);
|
||||
Tech_slider.set_numberItems(Current_list_size > max_num_entries_viewable ? Current_list_size-max_num_entries_viewable : 0);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1059,7 +1123,7 @@ void techroom_intel_reset()
|
||||
|
||||
void techroom_init()
|
||||
{
|
||||
int i;
|
||||
int i, idx;
|
||||
techroom_buttons *b;
|
||||
|
||||
Ships_loaded = false;
|
||||
@@ -1148,9 +1212,9 @@ void techroom_init()
|
||||
Tech_slider.create(&Ui_window, Tech_slider_coords[gr_screen.res][SHIP_X_COORD], Tech_slider_coords[gr_screen.res][SHIP_Y_COORD], Tech_slider_coords[gr_screen.res][SHIP_W_COORD], Tech_slider_coords[gr_screen.res][SHIP_H_COORD], (int)Ship_info.size(), Tech_slider_filename[gr_screen.res], &tech_scroll_list_up, &tech_scroll_list_down, &tech_ship_scroll_capture);
|
||||
|
||||
// zero intel anim/bitmap stuff
|
||||
for(auto & intel_item : Intel_list){
|
||||
intel_item.animation.num_frames = 0;
|
||||
intel_item.bitmap = -1;
|
||||
for(idx=0; idx<Intel_list_size; idx++){
|
||||
Intel_list[idx].animation.num_frames = 0;
|
||||
Intel_list[idx].bitmap = -1;
|
||||
}
|
||||
|
||||
mprintf(("Techroom successfully initialized, now changing tab...\n"));
|
||||
@@ -1159,23 +1223,63 @@ void techroom_init()
|
||||
|
||||
void techroom_lists_reset()
|
||||
{
|
||||
int i;
|
||||
|
||||
//unload the current animation, we load another one for the new current entry
|
||||
if (Tab != SHIPS_DATA_TAB)
|
||||
if(Tab != SHIPS_DATA_TAB)
|
||||
techroom_unload_animation();
|
||||
|
||||
Current_list = NULL;
|
||||
Current_list_size = 0;
|
||||
|
||||
model_free_all();
|
||||
Techroom_ship_modelnum = -1;
|
||||
Techroom_ship_model_instance = -1;
|
||||
|
||||
// This can be cleared immediately because there are no anims or bitmaps associated.
|
||||
Ship_list.clear();
|
||||
if (Ship_list != NULL) {
|
||||
delete[] Ship_list;
|
||||
Ship_list = NULL;
|
||||
}
|
||||
|
||||
Ship_list_size = 0;
|
||||
Ships_loaded = false;
|
||||
|
||||
// now that we're sure all the bitmaps are released, clear the vectors.
|
||||
Weapon_list.clear();
|
||||
if (Weapon_list != NULL) {
|
||||
for (i = 0; i < Weapon_list_size; i++) {
|
||||
if (Weapon_list[i].animation.num_frames != 0) {
|
||||
generic_anim_unload(&Weapon_list[i].animation);
|
||||
}
|
||||
|
||||
if (Weapon_list[i].bitmap >= 0) {
|
||||
bm_release(Weapon_list[i].bitmap);
|
||||
Weapon_list[i].bitmap = -1;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] Weapon_list;
|
||||
Weapon_list = NULL;
|
||||
}
|
||||
|
||||
Weapon_list_size = 0;
|
||||
Weapons_loaded = false;
|
||||
|
||||
Intel_list.clear();
|
||||
if (Intel_list != nullptr) {
|
||||
for (i = 0; i < Intel_list_size; i++) {
|
||||
if (Intel_list[i].animation.num_frames != 0) {
|
||||
generic_anim_unload(&Intel_list[i].animation);
|
||||
}
|
||||
|
||||
if (Intel_list[i].bitmap >= 0) {
|
||||
bm_release(Intel_list[i].bitmap);
|
||||
Intel_list[i].bitmap = -1;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] Intel_list;
|
||||
Intel_list = nullptr;
|
||||
}
|
||||
|
||||
Intel_list_size = 0;
|
||||
Intel_loaded = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,14 +25,13 @@ namespace Mission {
|
||||
Red_alert, // a red-alert mission - Goober5000
|
||||
Scramble, // a scramble mission - Goober5000
|
||||
No_builtin_command, // turns off Command without turning off pilots - Karajorma
|
||||
Player_start_ai, // Player starts mission under AI Control (NOT MULTI COMPATABLE) - Kazan
|
||||
Player_start_ai, // Player Starts mission under AI Control (NOT MULTI COMPATABLE) - Kazan
|
||||
All_attack, // all teams at war - Goober5000
|
||||
Use_ap_cinematics, // use autopilot cinematics - Kazan
|
||||
Deactivate_ap, // deactivate autopilot - KeldorKatarn (patch approved by Kazan)
|
||||
Always_show_goals, // Show the mission goals, even for training missions - Karajorma
|
||||
End_to_mainhall, // Return to the mainhall after debrief - niffiwan
|
||||
Override_hashcommand, // Override #Command with the Command info in Mission Specs - Goober5000
|
||||
Player_start_chase_view, // Player starts mission in chase view - Goober5000
|
||||
|
||||
NUM_VALUES
|
||||
};
|
||||
|
||||
@@ -274,160 +274,79 @@ void brief_set_text_color(char color_tag);
|
||||
extern void get_camera_limits(const matrix *start_camera, const matrix *end_camera, float time, vec3d *acc_max, vec3d *w_max);
|
||||
int brief_text_wipe_finished();
|
||||
|
||||
// parses, fills and returns a briefing_icon_info struct
|
||||
briefing_icon_info parse_briefing_icons() {
|
||||
char name[MAX_FILENAME_LEN];
|
||||
briefing_icon_info bii;
|
||||
|
||||
// parse regular frames
|
||||
required_string("$Name:");
|
||||
stuff_string(name, F_NAME, MAX_FILENAME_LEN);
|
||||
generic_anim_init(&bii.regular, name);
|
||||
|
||||
// parse fade frames
|
||||
required_string("$Name:");
|
||||
stuff_string(name, F_NAME, MAX_FILENAME_LEN);
|
||||
hud_anim_init(&bii.fade, 0, 0, name);
|
||||
|
||||
// parse highlighting frames
|
||||
required_string("$Name:");
|
||||
stuff_string(name, F_NAME, MAX_FILENAME_LEN);
|
||||
hud_anim_init(&bii.highlight, 0, 0, name);
|
||||
|
||||
return bii;
|
||||
}
|
||||
|
||||
// parses and adds a briefing_icon_info into the global vector
|
||||
int add_briefing_icons() {
|
||||
briefing_icon_info bii = parse_briefing_icons();
|
||||
|
||||
Briefing_icon_info.push_back(bii);
|
||||
|
||||
return (int)Briefing_icon_info.size() - 1;
|
||||
}
|
||||
|
||||
void brief_icon_parse_cleanup() {
|
||||
|
||||
// assign "borrows" icons
|
||||
for (species_info &spinfo : Species_info) {
|
||||
if (spinfo.borrows_bii_index_species < 0)
|
||||
continue;
|
||||
|
||||
int lender_species = spinfo.borrows_bii_index_species;
|
||||
|
||||
for (int i = 0; i < MIN_BRIEF_ICONS; i++) {
|
||||
spinfo.bii_indices[i] = Species_info[lender_species].bii_indices[i];
|
||||
}
|
||||
}
|
||||
|
||||
SCP_string errormsg = "The following species are missing briefing icons:\n";
|
||||
bool missing_icons = false;
|
||||
|
||||
for (species_info spinfo : Species_info) {
|
||||
bool missing_icon_species = false;
|
||||
for (int i = 0; i < MIN_BRIEF_ICONS; i++) {
|
||||
if (spinfo.bii_indices[i] < 0) {
|
||||
mprintf(("Species %s missing %s icon\n", spinfo.species_name, Icon_names[i]));
|
||||
missing_icons = missing_icon_species = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (missing_icon_species) {
|
||||
errormsg += spinfo.species_name;
|
||||
errormsg += "\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (missing_icons) {
|
||||
errormsg += "An exact list of the missing entries has been logged.";
|
||||
Warning(LOCATION, "%s", errormsg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// brief_parse_icon_tbl()
|
||||
//
|
||||
//
|
||||
void brief_parse_icon_tbl(const char* filename)
|
||||
void brief_parse_icon_tbl()
|
||||
{
|
||||
int icon;
|
||||
size_t species;
|
||||
char name[MAX_FILENAME_LEN];
|
||||
|
||||
Assert(!Species_info.empty());
|
||||
const size_t max_icons = Species_info.size() * MIN_BRIEF_ICONS;
|
||||
|
||||
try
|
||||
{
|
||||
read_file_text(filename, CF_TYPE_TABLES);
|
||||
read_file_text("icons.tbl", CF_TYPE_TABLES);
|
||||
reset_parse();
|
||||
|
||||
required_string("#Start");
|
||||
|
||||
// get all the species who are using unique icons (i.e. not borrowing from another species)
|
||||
SCP_vector<int> unique_icons_species;
|
||||
for (int i = 0; i < (int)Species_info.size(); i++) {
|
||||
if (Species_info[i].borrows_bii_index_species == -1)
|
||||
unique_icons_species.push_back(i);
|
||||
}
|
||||
|
||||
bool new_style_parsing = false;
|
||||
if (check_for_string("$Species:"))
|
||||
new_style_parsing = true;
|
||||
|
||||
if (new_style_parsing) {
|
||||
while (optional_string("$Species:")) {
|
||||
stuff_string(name, F_NAME, MAX_FILENAME_LEN);
|
||||
int spinfo_index = species_info_lookup(name);
|
||||
|
||||
if (spinfo_index < 0) {
|
||||
Warning(LOCATION, "Unable to find species %s, in icons.tbl.", name);
|
||||
}
|
||||
|
||||
while (optional_string("$Icon Type:")) {
|
||||
stuff_string(name, F_NAME, MAX_FILENAME_LEN);
|
||||
// find the matching icon type
|
||||
int icon_type = [&]() {
|
||||
for (int i = 0; i < MIN_BRIEF_ICONS; i++) {
|
||||
if (strcmp(Icon_names[i], name) == 0) {
|
||||
// found it
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// couldnt find it...
|
||||
return -1;
|
||||
}();
|
||||
|
||||
if (icon_type < 0) {
|
||||
Warning(LOCATION, "Icon Type %s is invalid, in icons.tbl.", name);
|
||||
}
|
||||
|
||||
int &existing_bii_index = Species_info[spinfo_index].bii_indices[icon_type];
|
||||
|
||||
if (existing_bii_index >= 0) { // overwriting
|
||||
briefing_icon_info bii = parse_briefing_icons();
|
||||
if (icon_type >= 0 && spinfo_index >= 0)
|
||||
Briefing_icon_info[existing_bii_index] = bii;
|
||||
} else { // adding a new one
|
||||
int new_bii_index = add_briefing_icons();
|
||||
if (icon_type >= 0 && spinfo_index >= 0)
|
||||
existing_bii_index = new_bii_index;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // old style
|
||||
for (int icon_type = 0; icon_type < MIN_BRIEF_ICONS; icon_type++) { // NOLINT(modernize-loop-convert)
|
||||
for (species = 0; species < unique_icons_species.size(); species++) {
|
||||
// if this check isn't true we're missing entries and will complain about it later in brief_icon_parse_cleanup()
|
||||
if (check_for_string("$Name:"))
|
||||
Species_info[unique_icons_species[species]].bii_indices[icon_type] = add_briefing_icons();
|
||||
}
|
||||
}
|
||||
|
||||
const size_t max_icons = unique_icons_species.size() * MIN_BRIEF_ICONS;
|
||||
if (!check_for_string("#End"))
|
||||
Briefing_icon_info.clear();
|
||||
while (required_string_either("#End", "$Name:"))
|
||||
{
|
||||
if (Briefing_icon_info.size() >= max_icons) {
|
||||
Warning(LOCATION, "Too many icons in icons.tbl; only the first " SIZE_T_ARG " will be used", max_icons);
|
||||
skip_to_start_of_string("#End");
|
||||
break;
|
||||
}
|
||||
|
||||
briefing_icon_info bii;
|
||||
|
||||
// parse regular frames
|
||||
required_string("$Name:");
|
||||
stuff_string(name, F_NAME, MAX_FILENAME_LEN);
|
||||
generic_anim_init(&bii.regular, name);
|
||||
|
||||
// parse fade frames
|
||||
required_string("$Name:");
|
||||
stuff_string(name, F_NAME, MAX_FILENAME_LEN);
|
||||
hud_anim_init(&bii.fade, 0, 0, name);
|
||||
|
||||
// parse highlighting frames
|
||||
required_string("$Name:");
|
||||
stuff_string(name, F_NAME, MAX_FILENAME_LEN);
|
||||
hud_anim_init(&bii.highlight, 0, 0, name);
|
||||
|
||||
// add it to the collection
|
||||
Briefing_icon_info.push_back(bii);
|
||||
}
|
||||
required_string("#End");
|
||||
|
||||
|
||||
// now assign the icons to their species
|
||||
const size_t num_species_covered = Briefing_icon_info.size() / MIN_BRIEF_ICONS;
|
||||
size_t bii_index = 0;
|
||||
for (icon = 0; icon < MIN_BRIEF_ICONS; icon++)
|
||||
{
|
||||
for (species = 0; species < num_species_covered; species++)
|
||||
Species_info[species].bii_index[icon] = (int)(bii_index++);
|
||||
}
|
||||
|
||||
// error check
|
||||
if (num_species_covered < Species_info.size())
|
||||
{
|
||||
SCP_string errormsg = "The following species are missing icon info in icons.tbl:\n";
|
||||
|
||||
for (species = num_species_covered; species < Species_info.size(); species++)
|
||||
{
|
||||
errormsg += Species_info[species].species_name;
|
||||
errormsg += "\n";
|
||||
}
|
||||
|
||||
Error(LOCATION, "%s", errormsg.c_str());
|
||||
}
|
||||
}
|
||||
catch (const parse::ParseException& e)
|
||||
@@ -436,16 +355,6 @@ void brief_parse_icon_tbl(const char* filename)
|
||||
}
|
||||
}
|
||||
|
||||
void brief_icons_init() {
|
||||
Briefing_icon_info.clear();
|
||||
|
||||
brief_parse_icon_tbl("icons.tbl");
|
||||
|
||||
parse_modular_table(NOX("*-ico.tbm"), brief_parse_icon_tbl);
|
||||
|
||||
brief_icon_parse_cleanup();
|
||||
}
|
||||
|
||||
void brief_set_icon_color(int team)
|
||||
{
|
||||
gr_set_color_fast(iff_get_color_by_team(team, -1, 0));
|
||||
@@ -554,7 +463,6 @@ void mission_brief_common_reset()
|
||||
memset( Briefings[i].stages[j].icons, 0, sizeof(brief_icon) * MAX_STAGE_ICONS );
|
||||
Briefings[i].stages[j].icons->ship_class = -1;
|
||||
Briefings[i].stages[j].icons->modelnum = -1;
|
||||
Briefings[i].stages[j].icons->model_instance_num = -1;
|
||||
Briefings[i].stages[j].icons->bitmap_id = -1;
|
||||
}
|
||||
|
||||
@@ -687,7 +595,7 @@ briefing_icon_info *brief_get_icon_info(brief_icon *bi)
|
||||
if (sip->species < 0)
|
||||
return NULL;
|
||||
|
||||
int bii_index = Species_info[sip->species].bii_indices[bi->type];
|
||||
int bii_index = Species_info[sip->species].bii_index[bi->type];
|
||||
if (bii_index < 0)
|
||||
return NULL;
|
||||
|
||||
|
||||
@@ -114,7 +114,6 @@ typedef struct brief_icon {
|
||||
int hold_x, hold_y; // 2D screen position of icon, used to place animations
|
||||
int ship_class;
|
||||
int modelnum;
|
||||
int model_instance_num;
|
||||
float radius;
|
||||
int type; // ICON_* defines from MissionBriefCommon.h
|
||||
int bitmap_id;
|
||||
@@ -298,7 +297,7 @@ void brief_camera_move(float frametime, int stage_num);
|
||||
void brief_render_icon(int stage_num, int icon_num, float frametime, int selected = 0, float w_scale_factor = 1.0f, float h_scale_factor = 1.0f);
|
||||
void brief_render_icon_line(int stage_num, int line_num);
|
||||
void brief_init_map();
|
||||
void brief_icons_init();
|
||||
void brief_parse_icon_tbl();
|
||||
void brief_common_close();
|
||||
void brief_reset_icons(int stage_num);
|
||||
void brief_restart_text_wipe();
|
||||
|
||||
@@ -379,7 +379,7 @@ void mission_campaign_get_sw_info()
|
||||
memset(Campaign.weapons_allowed, 0, sizeof(Campaign.weapons_allowed));
|
||||
|
||||
if (optional_string("+Starting Ships:")) {
|
||||
count = (int)stuff_int_list(ship_list, MAX_SHIP_CLASSES, SHIP_INFO_TYPE);
|
||||
count = stuff_int_list(ship_list, MAX_SHIP_CLASSES, SHIP_INFO_TYPE);
|
||||
|
||||
// now set the array elements stating which ships we are allowed
|
||||
for (i = 0; i < count; i++) {
|
||||
@@ -396,7 +396,7 @@ void mission_campaign_get_sw_info()
|
||||
}
|
||||
|
||||
if (optional_string("+Starting Weapons:")) {
|
||||
count = (int)stuff_int_list(weapon_list, MAX_WEAPON_TYPES, WEAPON_POOL_TYPE);
|
||||
count = stuff_int_list(weapon_list, MAX_WEAPON_TYPES, WEAPON_POOL_TYPE);
|
||||
|
||||
// now set the array elements stating which ships we are allowed
|
||||
for (i = 0; i < count; i++) {
|
||||
@@ -1588,7 +1588,8 @@ bool campaign_is_ignored(const char *filename)
|
||||
{
|
||||
SCP_string filename_no_ext = filename;
|
||||
drop_extension(filename_no_ext);
|
||||
SCP_tolower(filename_no_ext);
|
||||
std::transform(filename_no_ext.begin(), filename_no_ext.end(), filename_no_ext.begin(),
|
||||
[](char c) { return (char)::tolower(c); });
|
||||
|
||||
for (auto &ii: Ignored_campaigns) {
|
||||
if (ii == filename_no_ext) {
|
||||
|
||||
@@ -181,8 +181,6 @@ mission_event Mission_events[MAX_MISSION_EVENTS];
|
||||
mission_goal Mission_goals[MAX_GOALS]; // structure for the goals of this mission
|
||||
static goal_text Goal_text;
|
||||
|
||||
SCP_vector<event_annotation> Event_annotations;
|
||||
|
||||
#define DIRECTIVE_SOUND_DELAY 500 // time directive success sound effect is delayed
|
||||
#define DIRECTIVE_SPECIAL_DELAY 7000 // mark special directives as true after 7 seconds
|
||||
|
||||
@@ -888,11 +886,6 @@ void mission_event_unset_directive_special(int event)
|
||||
Mission_directive_special_timestamp = timestamp(-1);
|
||||
}
|
||||
|
||||
void mission_event_set_completion_sound_timestamp()
|
||||
{
|
||||
Mission_directive_sound_timestamp = timestamp(DIRECTIVE_SOUND_DELAY);
|
||||
}
|
||||
|
||||
// function which evaluates and processes the given event
|
||||
void mission_process_event( int event )
|
||||
{
|
||||
@@ -989,18 +982,13 @@ void mission_process_event( int event )
|
||||
// _argv[-1] - repeat_count of -1 would mean repeat indefinitely, so set to 0 instead.
|
||||
Mission_events[event].repeat_count = 0;
|
||||
Mission_events[event].formula = -1;
|
||||
|
||||
// Also send an update, if necessary.
|
||||
if(MULTIPLAYER_MASTER && ((store_flags != Mission_events[event].flags) || (sindex != Mission_events[event].formula) || (store_formula != Mission_events[event].formula) || (store_result != Mission_events[event].result) || (store_count != Mission_events[event].count)) ){
|
||||
send_event_update_packet(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (result && !Mission_events[event].satisfied_time) {
|
||||
Mission_events[event].satisfied_time = Missiontime;
|
||||
if ( Mission_events[event].objective_text ) {
|
||||
mission_event_set_completion_sound_timestamp();
|
||||
Mission_directive_sound_timestamp = timestamp(DIRECTIVE_SOUND_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -133,22 +133,6 @@ extern int Event_index; // used by sexp code to tell what event it came from
|
||||
extern bool Log_event;
|
||||
extern bool Snapshot_all_events;
|
||||
|
||||
|
||||
// only used in FRED
|
||||
struct event_annotation
|
||||
{
|
||||
void *handle = nullptr; // the handle of the tree node in the event editor. This is an HTREEITEM in FRED and TBD in qtFRED.
|
||||
int item_image = -1; // the previous image of the tree node (replaced by a comment icon when there is a comment)
|
||||
SCP_list<int> path; // a way to find the node that the annotation represents:
|
||||
// the first number is the event, the second number is the node on the first layer, etc.
|
||||
SCP_string comment;
|
||||
ubyte r = 255;
|
||||
ubyte g = 255;
|
||||
ubyte b = 255;
|
||||
};
|
||||
extern SCP_vector<event_annotation> Event_annotations;
|
||||
|
||||
|
||||
// prototypes
|
||||
void mission_init_goals( void );
|
||||
void mission_show_goals_init();
|
||||
@@ -181,13 +165,6 @@ void mission_goal_validation_change( int goal_num, bool valid );
|
||||
void mission_event_set_directive_special(int event);
|
||||
void mission_event_unset_directive_special(int event);
|
||||
|
||||
// Cyborg - set the directive completion sound timestamp
|
||||
void mission_event_set_completion_sound_timestamp();
|
||||
|
||||
// Maybe play a directive success sound... need to poll since the sound is delayed from when
|
||||
// the directive is actually satisfied.
|
||||
void mission_maybe_play_directive_success_sound();
|
||||
|
||||
void mission_goal_exit();
|
||||
|
||||
int ML_objectives_init(int x, int y, int w, int h);
|
||||
|
||||
@@ -82,7 +82,8 @@ bool mission_is_ignored(const char *filename)
|
||||
{
|
||||
SCP_string filename_no_ext = filename;
|
||||
drop_extension(filename_no_ext);
|
||||
SCP_tolower(filename_no_ext);
|
||||
std::transform(filename_no_ext.begin(), filename_no_ext.end(), filename_no_ext.begin(),
|
||||
[](char c) { return (char)::tolower(c); });
|
||||
|
||||
for (auto &ii: Ignored_missions) {
|
||||
if (ii == filename_no_ext) {
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "io/timer.h"
|
||||
#include "localization/localize.h"
|
||||
#include "mission/missiontraining.h"
|
||||
#include "mission/missiongoals.h"
|
||||
#include "mod_table/mod_table.h"
|
||||
#include "network/multi.h"
|
||||
#include "network/multimsgs.h"
|
||||
@@ -122,6 +121,18 @@ SCP_vector<message_extra> Message_waves;
|
||||
// variables to keep track of messages that are currently playing
|
||||
int Num_messages_playing; // number of is a message currently playing?
|
||||
|
||||
/*typedef struct pmessage {
|
||||
//anim_instance *anim; // handle of anim currently playing
|
||||
anim *anim_data; // animation data to be used by the talking head HUD gauge handler
|
||||
int start_frame; // the start frame needed to play the animation
|
||||
bool play_anim; // used to tell HUD gauges if they should be playing or not
|
||||
int wave; // handle of wave currently playing
|
||||
int id; // id of message currently playing
|
||||
int priority; // priority of message currently playing
|
||||
int shipnum; // shipnum of ship sending this message, -1 if from Terran command
|
||||
int builtin_type; // if a builtin message, type of the message
|
||||
} pmessage;*/
|
||||
|
||||
pmessage Playing_messages[MAX_PLAYING_MESSAGES];
|
||||
|
||||
int Message_shipnum; // ship number of who is sending message to player -- used outside this module
|
||||
@@ -145,7 +156,6 @@ typedef struct message_q {
|
||||
int flags; // should this message entry be converted to Terran Command head/wave file
|
||||
int min_delay_stamp; // minimum delay before this message will start playing
|
||||
int group; // message is part of a group, don't time it out
|
||||
int event_num_to_cancel; // Goober5000 - if this event is true, the message will not be played
|
||||
} message_q;
|
||||
|
||||
#define MAX_MESSAGE_Q 30
|
||||
@@ -176,7 +186,7 @@ int Default_command_persona;
|
||||
|
||||
// Goober5000
|
||||
// NOTE - these are truncated filenames, i.e. without extensions
|
||||
SCP_vector<SCP_string> Generic_message_filenames;
|
||||
SCP_vector<SCP_string> generic_message_filenames;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// used to distort incoming messages when comms are damaged
|
||||
@@ -612,13 +622,13 @@ void parse_msgtbl()
|
||||
|
||||
|
||||
// additional table part!
|
||||
Generic_message_filenames.clear();
|
||||
Generic_message_filenames.push_back("none");
|
||||
Generic_message_filenames.push_back("cuevoice");
|
||||
Generic_message_filenames.push_back("cue_voice");
|
||||
Generic_message_filenames.push_back("emptymsg");
|
||||
Generic_message_filenames.push_back("generic");
|
||||
Generic_message_filenames.push_back("msgstart");
|
||||
generic_message_filenames.clear();
|
||||
generic_message_filenames.push_back("none");
|
||||
generic_message_filenames.push_back("cuevoice");
|
||||
generic_message_filenames.push_back("cue_voice");
|
||||
generic_message_filenames.push_back("emptymsg");
|
||||
generic_message_filenames.push_back("generic");
|
||||
generic_message_filenames.push_back("msgstart");
|
||||
|
||||
if (optional_string("#Simulated Speech Overrides"))
|
||||
{
|
||||
@@ -648,7 +658,7 @@ void parse_msgtbl()
|
||||
*ptr = '\0';
|
||||
|
||||
// add truncated file name
|
||||
Generic_message_filenames.push_back(filename);
|
||||
generic_message_filenames.push_back(filename);
|
||||
}
|
||||
|
||||
required_string("#End");
|
||||
@@ -701,8 +711,7 @@ void messages_init()
|
||||
MessageQ[i].group = 0;
|
||||
|
||||
// Goober5000
|
||||
MessageQ[i].special_message = nullptr;
|
||||
MessageQ[i].event_num_to_cancel = -1;
|
||||
MessageQ[i].special_message = NULL;
|
||||
}
|
||||
|
||||
// this forces a reload of the AVI's and waves for builtin messages. Needed because the flic and
|
||||
@@ -983,7 +992,6 @@ void message_remove_from_queue(message_q *q)
|
||||
vm_free(q->special_message);
|
||||
q->special_message = NULL;
|
||||
}
|
||||
q->event_num_to_cancel = -1;
|
||||
|
||||
if ( MessageQ_num > 0 ) {
|
||||
insertion_sort(MessageQ, MAX_MESSAGE_Q, sizeof(message_q), message_queue_priority_compare);
|
||||
@@ -1032,9 +1040,9 @@ bool message_filename_is_generic(char *filename)
|
||||
*ptr = '\0';
|
||||
|
||||
// test against the list
|
||||
for (unsigned int i = 0; i < Generic_message_filenames.size(); i++)
|
||||
for (unsigned int i = 0; i < generic_message_filenames.size(); i++)
|
||||
{
|
||||
if (!stricmp(Generic_message_filenames[i].c_str(), truncated_filename))
|
||||
if (!stricmp(generic_message_filenames[i].c_str(), truncated_filename))
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1384,8 +1392,7 @@ void message_queue_process()
|
||||
// preprocess message queue and remove anything on the queue that is too old. If next message on
|
||||
// the queue can be played, then break out of the loop. Otherwise, loop until nothing on the queue
|
||||
while ( MessageQ_num > 0 ) {
|
||||
q = &MessageQ[0];
|
||||
// message is outside its time window
|
||||
q = &MessageQ[0];
|
||||
if ( timestamp_valid(q->window_timestamp) && timestamp_elapsed(q->window_timestamp) && !q->group) {
|
||||
// remove message from queue and see if more to remove
|
||||
nprintf(("messaging", "Message %s didn't play because it didn't fit into time window.\n", Messages[q->message_num].name));
|
||||
@@ -1394,13 +1401,7 @@ void message_queue_process()
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// message can't be played after a certain event
|
||||
else if (q->event_num_to_cancel >= 0 && Mission_events[q->event_num_to_cancel].result) {
|
||||
message_remove_from_queue(q);
|
||||
}
|
||||
// message isn't too old
|
||||
else {
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1640,7 +1641,7 @@ all_done:
|
||||
}
|
||||
|
||||
// queues up a message to display to the player
|
||||
void message_queue_message( int message_num, int priority, int timing, const char *who_from, int source, int group, int delay, int builtin_type, int event_num_to_cancel )
|
||||
void message_queue_message( int message_num, int priority, int timing, const char *who_from, int source, int group, int delay, int builtin_type )
|
||||
{
|
||||
int i, m_persona;
|
||||
char temp_buf[MESSAGE_LENGTH];
|
||||
@@ -1689,7 +1690,6 @@ void message_queue_message( int message_num, int priority, int timing, const cha
|
||||
MessageQ[i].min_delay_stamp = timestamp(delay);
|
||||
MessageQ[i].group = group;
|
||||
strcpy_s(MessageQ[i].who_from, who_from);
|
||||
MessageQ[i].event_num_to_cancel = event_num_to_cancel;
|
||||
|
||||
// Goober5000 - this shouldn't happen, but let's be safe
|
||||
if (MessageQ[i].special_message != NULL)
|
||||
@@ -1862,7 +1862,7 @@ int message_filter_multi(int id)
|
||||
|
||||
// send_unique_to_player sends a mission unique (specific) message to the player (possibly a multiplayer
|
||||
// person). These messages are *not* the builtin messages
|
||||
void message_send_unique_to_player( const char *id, const void *data, int m_source, int priority, int group, int delay, int event_num_to_cancel )
|
||||
void message_send_unique_to_player( const char *id, const void *data, int m_source, int priority, int group, int delay )
|
||||
{
|
||||
int i, source;
|
||||
const char *who_from;
|
||||
@@ -1918,12 +1918,12 @@ void message_send_unique_to_player( const char *id, const void *data, int m_sour
|
||||
|
||||
// maybe filter it out altogether
|
||||
if (!message_filter_multi(i)) {
|
||||
message_queue_message( i, priority, MESSAGE_TIME_ANYTIME, who_from, source, group, delay, -1, event_num_to_cancel );
|
||||
message_queue_message( i, priority, MESSAGE_TIME_ANYTIME, who_from, source, group, delay );
|
||||
}
|
||||
|
||||
// send a message packet to a player if destined for everyone or only a specific person
|
||||
if ( MULTIPLAYER_MASTER ){
|
||||
send_mission_message_packet( i, who_from, priority, MESSAGE_TIME_SOON, source, -1, -1, -1, delay, event_num_to_cancel );
|
||||
send_mission_message_packet( i, who_from, priority, MESSAGE_TIME_SOON, source, -1, -1, -1, delay);
|
||||
}
|
||||
|
||||
return; // all done with displaying
|
||||
@@ -2145,7 +2145,7 @@ void message_send_builtin_to_player( int type, ship *shipp, int priority, int ti
|
||||
|
||||
// if this filter matches mine
|
||||
if( (multi_team_filter < 0) || !(Netgame.type_flags & NG_TYPE_TEAM) || ((Net_player != NULL) && (Net_player->p_info.team == multi_team_filter)) ){
|
||||
message_queue_message( message_index, priority, timing, who_from, source, group, delay, type, -1 );
|
||||
message_queue_message( message_index, priority, timing, who_from, source, group, delay, type );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -164,8 +164,6 @@ extern int Num_messages;
|
||||
extern int Num_builtin_messages; // from messages.tbl -- index of message location to load mission specific messages into
|
||||
extern int Message_shipnum; // used to display info on hud when message is sent
|
||||
|
||||
extern SCP_vector<SCP_string> Generic_message_filenames;
|
||||
|
||||
// variable, etc for persona information
|
||||
#define MAX_PERSONA_TYPES 4
|
||||
|
||||
@@ -205,10 +203,10 @@ int message_is_playing();
|
||||
void message_maybe_distort();
|
||||
void message_kill_all( int kill_all );
|
||||
|
||||
void message_queue_message(int message_num, int priority, int timing, const char *who_from, int source, int group, int delay, int builtin_type, int event_num_to_cancel);
|
||||
void message_queue_message( int message_num, int priority, int timing, const char *who_from, int source, int group, int delay, int builtin_type=-1 );
|
||||
|
||||
// functions which send messages to player -- called externally
|
||||
void message_send_unique_to_player( const char *id, const void *data, int source, int priority, int group, int delay, int event_num_to_cancel = -1);
|
||||
void message_send_unique_to_player( const char *id, const void *data, int source, int priority, int group, int delay);
|
||||
void message_send_builtin_to_player( int type, ship *shipp, int priority, int timing, int group, int delay, int multi_target, int multi_team_filter );
|
||||
|
||||
// functions to deal with personas
|
||||
|
||||
+59
-156
@@ -839,8 +839,8 @@ void parse_player_info(mission *pm)
|
||||
|
||||
void parse_player_info2(mission *pm)
|
||||
{
|
||||
int nt, i;
|
||||
SCP_vector<loadout_row> list, list2;
|
||||
char str[NAME_LENGTH];
|
||||
int nt, i, total, list[MAX_SHIP_CLASSES * 4], list2[MAX_WEAPON_TYPES * 4];
|
||||
team_data *ptr;
|
||||
|
||||
// read in a ship/weapon pool for each team.
|
||||
@@ -854,49 +854,46 @@ void parse_player_info2(mission *pm)
|
||||
stuff_string( Player_start_shipname, F_NAME, NAME_LENGTH );
|
||||
|
||||
required_string("$Ship Choices:");
|
||||
stuff_loadout_list(list, MISSION_LOADOUT_SHIP_LIST);
|
||||
total = stuff_loadout_list(list, MAX_SHIP_CLASSES * 4, MISSION_LOADOUT_SHIP_LIST);
|
||||
|
||||
// make sure we have a count which is divisible by four since four values are added for each ship
|
||||
Assert((total%4) == 0);
|
||||
|
||||
num_choices = 0;
|
||||
|
||||
// check ship class loadout entries
|
||||
for (auto &sc : list) {
|
||||
// only every 4th entry is actually a ship class.
|
||||
for (i=0; i<total; i += 4) {
|
||||
// in a campaign, see if the player is allowed the ships or not. Remove them from the
|
||||
// pool if they are not allowed
|
||||
if (Game_mode & GM_CAMPAIGN_MODE || (MULTIPLAYER_CLIENT)) {
|
||||
if ( !Campaign.ships_allowed[sc.index] )
|
||||
if ( !Campaign.ships_allowed[list[i]] )
|
||||
continue;
|
||||
}
|
||||
if (sc.index < 0 || sc.index >= weapon_info_size())
|
||||
continue;
|
||||
|
||||
ptr->ship_list[num_choices] = sc.index;
|
||||
|
||||
ptr->ship_list[num_choices] = list[i];
|
||||
// if the list isn't set by a variable leave the variable name empty
|
||||
if (sc.index_sexp_var == NOT_SET_BY_SEXP_VARIABLE) {
|
||||
if (list[i+1] == -1) {
|
||||
strcpy_s(ptr->ship_list_variables[num_choices], "") ;
|
||||
}
|
||||
else {
|
||||
strcpy_s(ptr->ship_list_variables[num_choices], Sexp_variables[sc.index_sexp_var].variable_name);
|
||||
strcpy_s(ptr->ship_list_variables[num_choices],Sexp_variables[list[i+1]].variable_name);
|
||||
}
|
||||
|
||||
ptr->ship_count[num_choices] = sc.count;
|
||||
ptr->loadout_total += sc.count;
|
||||
ptr->ship_count[num_choices] = list[i+2];
|
||||
ptr->loadout_total += list[i+2];
|
||||
|
||||
// if the list isn't set by a variable leave the variable name empty
|
||||
if (sc.count_sexp_var == NOT_SET_BY_SEXP_VARIABLE) {
|
||||
if (list[i+3] == -1) {
|
||||
strcpy_s(ptr->ship_count_variables[num_choices], "");
|
||||
}
|
||||
else {
|
||||
strcpy_s(ptr->ship_count_variables[num_choices], Sexp_variables[sc.count_sexp_var].variable_name);
|
||||
strcpy_s(ptr->ship_count_variables[num_choices], Sexp_variables[list[i+3]].variable_name);
|
||||
}
|
||||
|
||||
num_choices++;
|
||||
}
|
||||
ptr->num_ship_choices = num_choices;
|
||||
|
||||
ptr->default_ship = -1;
|
||||
if (optional_string("+Default_ship:")) {
|
||||
char str[NAME_LENGTH];
|
||||
stuff_string(str, F_NAME, NAME_LENGTH);
|
||||
ptr->default_ship = ship_info_lookup(str);
|
||||
if (-1 == ptr->default_ship) {
|
||||
@@ -922,49 +919,49 @@ void parse_player_info2(mission *pm)
|
||||
ptr->default_ship = ptr->ship_list[0];
|
||||
|
||||
required_string("+Weaponry Pool:");
|
||||
stuff_loadout_list(list2, MISSION_LOADOUT_WEAPON_LIST);
|
||||
total = stuff_loadout_list(list2, MAX_WEAPON_TYPES * 4, MISSION_LOADOUT_WEAPON_LIST);
|
||||
|
||||
// make sure we have a count which is divisible by four since four values are added for each ship
|
||||
Assert((total%4) == 0);
|
||||
num_choices = 0;
|
||||
|
||||
// check weapon class loadout entries
|
||||
for (auto &wc : list2) {
|
||||
for (i = 0; i < total; i += 4) {
|
||||
// in a campaign, see if the player is allowed the weapons or not. Remove them from the
|
||||
// pool if they are not allowed
|
||||
if (Game_mode & GM_CAMPAIGN_MODE || (MULTIPLAYER_CLIENT)) {
|
||||
if ( !Campaign.weapons_allowed[wc.index] ) {
|
||||
if ( !Campaign.weapons_allowed[list2[i]] ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (wc.index < 0 || wc.index >= weapon_info_size())
|
||||
continue;
|
||||
|
||||
// always allow the pool to be added in FRED, it is a verbal warning
|
||||
// to let the mission dev know about the problem
|
||||
if ( !(Weapon_info[wc.index].wi_flags[Weapon::Info_Flags::Player_allowed]) && !Fred_running ) {
|
||||
WarningEx(LOCATION, "Weapon '%s' in weapon pool isn't allowed on player loadout! Ignoring it ...\n", Weapon_info[wc.index].name);
|
||||
continue;
|
||||
}
|
||||
if ( (list2[i] >= 0) && (list2[i] < weapon_info_size()) ) {
|
||||
// always allow the pool to be added in FRED, it is a verbal warning
|
||||
// to let the mission dev know about the problem
|
||||
if ( (Weapon_info[list2[i]].wi_flags[Weapon::Info_Flags::Player_allowed]) || Fred_running ) {
|
||||
ptr->weaponry_pool[num_choices] = list2[i];
|
||||
ptr->weaponry_count[num_choices] = list2[i+2];
|
||||
|
||||
ptr->weaponry_pool[num_choices] = wc.index;
|
||||
ptr->weaponry_count[num_choices] = wc.count;
|
||||
// if the list isn't set by a variable leave the variable name empty
|
||||
if (list2[i+1] == -1) {
|
||||
strcpy_s(ptr->weaponry_pool_variable[num_choices], "");
|
||||
}
|
||||
else {
|
||||
strcpy_s(ptr->weaponry_pool_variable[num_choices], Sexp_variables[list2[i+1]].variable_name);
|
||||
}
|
||||
|
||||
// if the list isn't set by a variable leave the variable name empty
|
||||
if (wc.index_sexp_var == NOT_SET_BY_SEXP_VARIABLE) {
|
||||
strcpy_s(ptr->weaponry_pool_variable[num_choices], "");
|
||||
// if the list isn't set by a variable leave the variable name empty
|
||||
if (list2[i+3] == -1) {
|
||||
strcpy_s(ptr->weaponry_amount_variable[num_choices], "");
|
||||
}
|
||||
else {
|
||||
strcpy_s(ptr->weaponry_amount_variable[num_choices], Sexp_variables[list2[i+3]].variable_name);
|
||||
}
|
||||
num_choices++;
|
||||
}
|
||||
else {
|
||||
WarningEx(LOCATION, "Weapon '%s' in weapon pool isn't allowed on player loadout! Ignoring it ...\n", Weapon_info[i].name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
strcpy_s(ptr->weaponry_pool_variable[num_choices], Sexp_variables[wc.index_sexp_var].variable_name);
|
||||
}
|
||||
|
||||
// if the list isn't set by a variable leave the variable name empty
|
||||
if (wc.count_sexp_var == NOT_SET_BY_SEXP_VARIABLE) {
|
||||
strcpy_s(ptr->weaponry_amount_variable[num_choices], "");
|
||||
}
|
||||
else {
|
||||
strcpy_s(ptr->weaponry_amount_variable[num_choices], Sexp_variables[wc.count_sexp_var].variable_name);
|
||||
}
|
||||
|
||||
num_choices++;
|
||||
}
|
||||
ptr->num_weapon_choices = num_choices;
|
||||
|
||||
@@ -973,7 +970,7 @@ void parse_player_info2(mission *pm)
|
||||
{
|
||||
int num_weapons;
|
||||
int weapon_list_buf[MAX_WEAPON_TYPES];
|
||||
num_weapons = (int)stuff_int_list(weapon_list_buf, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
|
||||
num_weapons = stuff_int_list(weapon_list_buf, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
|
||||
|
||||
for (i = 0; i < num_weapons; i++)
|
||||
ptr->weapon_required[weapon_list_buf[i]] = true;
|
||||
@@ -1512,8 +1509,6 @@ void parse_briefing(mission * /*pm*/, int flags)
|
||||
find_and_stuff("$team:", &bi->team, F_NAME, temp_team_names, Num_iffs, "team name");
|
||||
|
||||
find_and_stuff("$class:", &bi->ship_class, F_NAME, Ship_class_names, Ship_info.size(), "ship class");
|
||||
bi->modelnum = -1;
|
||||
bi->model_instance_num = -1;
|
||||
|
||||
// Goober5000 - import
|
||||
if (flags & MPF_IMPORT_FSM)
|
||||
@@ -2466,14 +2461,6 @@ int parse_create_object_sub(p_object *p_objp)
|
||||
Script_system.SetHookObjects(2, "Ship", &Objects[objnum], "Parent", anchor_objp);
|
||||
Script_system.RunCondition(CHA_ONSHIPARRIVE, &Objects[objnum]);
|
||||
Script_system.RemHookVars({"Ship", "Parent"});
|
||||
|
||||
if (Ship_info[shipp->ship_info_index].is_big_or_huge() && !brought_in_docked_wing) {
|
||||
float mission_time = f2fl(Missiontime);
|
||||
int minutes = (int)(mission_time / 60);
|
||||
int seconds = (int)mission_time % 60;
|
||||
|
||||
mprintf(("%s arrived at %02d:%02d\n", shipp->ship_name, minutes, seconds));
|
||||
}
|
||||
}
|
||||
|
||||
return objnum;
|
||||
@@ -4236,13 +4223,9 @@ int parse_wing_create_ships( wing *wingp, int num_to_create, int force, int spec
|
||||
|
||||
// also, if multiplayer, set the parse object's net signature to be wing's net signature
|
||||
// base + total_arrived_count (before adding 1)
|
||||
// Cyborg -- The original ships in the wave have their net_signature set at mission parse
|
||||
// so only do this if this is a subsequent wave.
|
||||
if (Game_mode & GM_MULTIPLAYER && wingp->num_waves > 1)
|
||||
if (Game_mode & GM_MULTIPLAYER)
|
||||
{
|
||||
// Cyborg -- Also, then we need to subtract the original wave's number of fighters
|
||||
// and also subtract 1 to use the wing's starting signature
|
||||
p_objp->net_signature = (ushort) (wingp->net_signature + wingp->total_arrived_count - (wingp->wave_count + 1));
|
||||
p_objp->net_signature = (ushort) (wingp->net_signature + wingp->total_arrived_count);
|
||||
}
|
||||
|
||||
|
||||
@@ -4593,7 +4576,7 @@ void parse_wing(mission *pm)
|
||||
|
||||
// stores a list of all names of ships in the wing
|
||||
required_string("$Ships:");
|
||||
wingp->wave_count = (int)stuff_string_list( ship_names, MAX_SHIPS_PER_WING );
|
||||
wingp->wave_count = stuff_string_list( ship_names, MAX_SHIPS_PER_WING );
|
||||
wingp->current_count = 0;
|
||||
|
||||
// get the wings goals, if any
|
||||
@@ -4608,8 +4591,9 @@ void parse_wing(mission *pm)
|
||||
}
|
||||
|
||||
if (optional_string("+Flags:")) {
|
||||
auto count = (int)stuff_string_list( wing_flag_strings, PARSEABLE_WING_FLAGS);
|
||||
int count;
|
||||
|
||||
count = stuff_string_list( wing_flag_strings, PARSEABLE_WING_FLAGS);
|
||||
for (i = 0; i < count; i++) {
|
||||
if (!stricmp(wing_flag_strings[i], NOX("ignore-count")))
|
||||
wingp->flags.set(Ship::Wing_Flags::Ignore_count);
|
||||
@@ -4687,8 +4671,7 @@ void parse_wing(mission *pm)
|
||||
int next_signature;
|
||||
|
||||
wingp->net_signature = multi_assign_network_signature( MULTI_SIG_SHIP );
|
||||
// Cyborg -- Subtract one because the original wave already has its signatures set.
|
||||
next_signature = wingp->net_signature + (wingp->wave_count * (wingp->num_waves - 1));
|
||||
next_signature = wingp->net_signature + (wingp->wave_count * wingp->num_waves);
|
||||
if ( next_signature > SHIP_SIG_MAX )
|
||||
Error(LOCATION, "Too many total ships in mission (%d) for network signature assignment", SHIP_SIG_MAX);
|
||||
multi_set_network_signature( (ushort)next_signature, MULTI_SIG_SHIP );
|
||||
@@ -5065,9 +5048,6 @@ void parse_event(mission * /*pm*/)
|
||||
}
|
||||
}
|
||||
|
||||
// Need to set this to zero so that we don't accidentally reuse old data.
|
||||
event->flags = 0;
|
||||
|
||||
if (optional_string("+Event Flags:")) {
|
||||
parse_string_flag_list(&event->flags, Mission_event_flags, Num_mission_event_flags);
|
||||
}
|
||||
@@ -5088,47 +5068,6 @@ void parse_event(mission * /*pm*/)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (optional_string("$Annotations Start")) {
|
||||
// annotations are only used in FRED
|
||||
if (Fred_running) {
|
||||
while (check_for_string("+Comment:") || check_for_string("+Background Color:") || check_for_string("+Path:")) {
|
||||
event_annotation ea;
|
||||
ea.path.push_back(Num_mission_events);
|
||||
|
||||
if (optional_string("+Comment:")) {
|
||||
stuff_string(ea.comment, F_MULTITEXT);
|
||||
lcl_replace_stuff(ea.comment, true);
|
||||
}
|
||||
|
||||
if (optional_string("+Background Color:")) {
|
||||
stuff_ubyte(&ea.r);
|
||||
if (*Mp == ',')
|
||||
Mp++;
|
||||
stuff_ubyte(&ea.g);
|
||||
if (*Mp == ',')
|
||||
Mp++;
|
||||
stuff_ubyte(&ea.b);
|
||||
}
|
||||
|
||||
if (optional_string("+Path:")) {
|
||||
int num;
|
||||
while (true) {
|
||||
ignore_gray_space();
|
||||
if (stuff_int_optional(&num) != 2) {
|
||||
break;
|
||||
}
|
||||
ea.path.push_back(num);
|
||||
}
|
||||
}
|
||||
|
||||
Event_annotations.push_back(std::move(ea));
|
||||
}
|
||||
required_string("$Annotations End");
|
||||
} else {
|
||||
skip_to_string("$Annotations End");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parse_events(mission *pm)
|
||||
@@ -5488,21 +5427,11 @@ void parse_bitmaps(mission *pm)
|
||||
Mission_palette = 1;
|
||||
|
||||
// neb2 info
|
||||
strcpy_s(Neb2_texture_name, "");
|
||||
strcpy_s(Neb2_texture_name, "Eraseme3");
|
||||
Neb2_poof_flags = ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5));
|
||||
bool nebula = false;
|
||||
if (optional_string("+Neb2:")) {
|
||||
nebula = true;
|
||||
if(optional_string("+Neb2:")){
|
||||
stuff_string(Neb2_texture_name, F_NAME, MAX_FILENAME_LEN);
|
||||
} else if (optional_string("+Neb2Color:")) {
|
||||
nebula = true;
|
||||
int neb_colors[3];
|
||||
stuff_int_list(neb_colors, 3, RAW_INTEGER_TYPE);
|
||||
Neb2_fog_color[0] = (ubyte)neb_colors[0];
|
||||
Neb2_fog_color[1] = (ubyte)neb_colors[1];
|
||||
Neb2_fog_color[2] = (ubyte)neb_colors[2];
|
||||
}
|
||||
if (nebula) {
|
||||
|
||||
required_string("+Neb2Flags:");
|
||||
stuff_int(&Neb2_poof_flags);
|
||||
|
||||
@@ -5662,17 +5591,6 @@ void parse_asteroid_fields(mission *pm)
|
||||
}
|
||||
}
|
||||
|
||||
bool invalid_asteroids = false;
|
||||
for (int& ast_type : Asteroid_field.field_debris_type) {
|
||||
if (ast_type >= (int)Asteroid_info.size()) {
|
||||
invalid_asteroids = true;
|
||||
ast_type = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalid_asteroids)
|
||||
Warning(LOCATION, "The Asteroid field contains invalid entries!");
|
||||
|
||||
// backward compatibility
|
||||
if ( (Asteroid_field.debris_genre == DG_ASTEROID) && (count == 0) ) {
|
||||
Asteroid_field.field_debris_type[0] = 0;
|
||||
@@ -6882,8 +6800,8 @@ int mission_set_arrival_location(int anchor, int location, int dist, int objnum,
|
||||
} else {
|
||||
// in multiplayer, use the static rand functions so that all clients can get the
|
||||
// same information.
|
||||
r1 = static_rand(Objects[objnum].net_signature) < STATIC_RAND_MAX / 2 ? -1 : 1;
|
||||
r2 = static_rand(Objects[objnum].net_signature+1) < STATIC_RAND_MAX / 2 ? -1 : 1;
|
||||
r1 = static_rand(Objects[objnum].net_signature) < RAND_MAX_2 ? -1 : 1;
|
||||
r2 = static_rand(Objects[objnum].net_signature+1) < RAND_MAX_2 ? -1 : 1;
|
||||
}
|
||||
|
||||
vm_vec_copy_scale(&t1, &(Objects[anchor_objnum].orient.vec.fvec), x);
|
||||
@@ -7675,7 +7593,7 @@ int add_path_restriction()
|
||||
// parse it
|
||||
path_restriction_t temp;
|
||||
temp.cached_mask = (1 << MAX_SHIP_BAY_PATHS); // uninitialized value (too high)
|
||||
temp.num_paths = (int)stuff_string_list(temp.path_names, MAX_SHIP_BAY_PATHS);
|
||||
temp.num_paths = stuff_string_list(temp.path_names, MAX_SHIP_BAY_PATHS);
|
||||
|
||||
// no restriction?
|
||||
if (temp.num_paths == 0)
|
||||
@@ -8167,21 +8085,6 @@ void mission_parse_reset_alt()
|
||||
Mission_alt_type_count = 0;
|
||||
}
|
||||
|
||||
// For compatibility purposes some mods want alt names to truncate at the hash symbol. But we can't actually do that at mission load,
|
||||
// since we have to save them again in FRED. So this function processes the names just before the mission starts.
|
||||
// To further complicate things, some mods actually want the hash to be displayed. So this function uses the double-hash as an escape sequence for a single hash.
|
||||
void mission_process_alt_types()
|
||||
{
|
||||
for (int i = 0; i < Mission_alt_type_count; ++i)
|
||||
{
|
||||
// truncate at a single hash
|
||||
end_string_at_first_hash_symbol(Mission_alt_types[i], true);
|
||||
|
||||
// ## -> #
|
||||
consolidate_double_characters(Mission_alt_types[i], '#');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callsign stuff
|
||||
*/
|
||||
|
||||
@@ -518,6 +518,9 @@ int get_parse_name_index(const char *name);
|
||||
// called from freespace game level loop
|
||||
void mission_parse_eval_stuff();
|
||||
|
||||
// function to set the ramaing time left in the mission
|
||||
void mission_parse_set_end_time( int seconds );
|
||||
|
||||
// code to bring in a repair ship.
|
||||
void mission_bring_in_support_ship( object *requester_objp );
|
||||
int mission_is_support_ship_arriving( void );
|
||||
@@ -532,7 +535,6 @@ const char *mission_parse_lookup_alt_index(int index);
|
||||
int mission_parse_add_alt(const char *name);
|
||||
void mission_parse_remove_alt(const char *name);
|
||||
void mission_parse_reset_alt();
|
||||
void mission_process_alt_types();
|
||||
|
||||
// callsign stuff
|
||||
int mission_parse_lookup_callsign(const char *name);
|
||||
|
||||
@@ -575,14 +575,6 @@ void training_check_objectives()
|
||||
// now sort list of events
|
||||
// sort on EVENT_CURRENT and born on date, for other events (EVENT_SATISFIED, EVENT_FAILED) sort on born on date
|
||||
sort_training_objectives();
|
||||
|
||||
// Cyborg - Multiplayer clients will not run the other directive functions, so just run this quick check to see
|
||||
// if the directive success sound should be played.
|
||||
if (MULTIPLAYER_CLIENT) {
|
||||
if ( !hud_disabled() && hud_gauge_active(HUD_DIRECTIVES_VIEW) ) {
|
||||
mission_maybe_play_directive_success_sound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -663,7 +655,7 @@ SCP_string message_translate_tokens(const char *text)
|
||||
if ( The_mission.game_type & MISSION_TYPE_TRAINING ) {
|
||||
r = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, XSTR( "&Bind Control", 424), XSTR( "&Abort mission", 425),
|
||||
XSTR( "Warning\nYou have no control bound to the action \"%s\". You must do so before you can continue with your training.", 426),
|
||||
XSTR(Control_config[Failed_key_index].text.c_str(), CONTROL_CONFIG_XSTR + Failed_key_index));
|
||||
XSTR(Control_config[Failed_key_index].text, CONTROL_CONFIG_XSTR + Failed_key_index));
|
||||
|
||||
if (r) { // do they want to abort the mission?
|
||||
gameseq_post_event(GS_EVENT_END_GAME);
|
||||
|
||||
@@ -704,13 +704,8 @@ brief_icon *brief_get_closeup_icon()
|
||||
// stop showing the closeup view of an icon
|
||||
void brief_turn_off_closeup_icon()
|
||||
{
|
||||
// turn off closeup
|
||||
// turn off closup
|
||||
if ( Closeup_icon != NULL ) {
|
||||
if (Closeup_icon->model_instance_num >= 0) {
|
||||
model_delete_instance(Closeup_icon->model_instance_num);
|
||||
Closeup_icon->model_instance_num = -1;
|
||||
}
|
||||
|
||||
gamesnd_play_iface(InterfaceSounds::BRIEF_ICON_SELECT);
|
||||
Closeup_icon = NULL;
|
||||
Closeup_close_button.disable();
|
||||
@@ -1080,7 +1075,7 @@ void brief_render_closeup(int ship_class, float frametime)
|
||||
render_info.set_flags(MR_AUTOCENTER);
|
||||
}
|
||||
|
||||
model_render_immediate( &render_info, Closeup_icon->modelnum, Closeup_icon->model_instance_num, &Closeup_orient, &Closeup_pos );
|
||||
model_render_immediate( &render_info, Closeup_icon->modelnum, &Closeup_orient, &Closeup_pos );
|
||||
|
||||
The_mission.flags.set(Mission::Mission_Flags::Fullneb, neb_restore);
|
||||
|
||||
@@ -1203,6 +1198,28 @@ void brief_set_closeup_pos(brief_icon * /*bi*/)
|
||||
Closeup_x1 = (int)std::lround(320 - Closeup_coords[gr_screen.res][BRIEF_W_COORD]/2.0f);
|
||||
}
|
||||
|
||||
void brief_get_closeup_ship_modelnum(brief_icon *ci)
|
||||
{
|
||||
object *objp;
|
||||
ship *sp;
|
||||
|
||||
// find the model number for the ship to display
|
||||
for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
|
||||
|
||||
if ( objp == &obj_used_list || objp->type != OBJ_SHIP ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sp = &Ships[objp->instance];
|
||||
if ( sp->ship_info_index == ci->ship_class ) {
|
||||
ci->ship_class = sp->ship_info_index;
|
||||
ci->modelnum = Ship_info[sp->ship_info_index].model_num;
|
||||
ci->radius = objp->radius;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// brief_setup_closeup()
|
||||
//
|
||||
@@ -1217,7 +1234,6 @@ int brief_setup_closeup(brief_icon *bi)
|
||||
Closeup_icon = bi;
|
||||
Closeup_icon->ship_class = bi->ship_class;
|
||||
Closeup_icon->modelnum = -1;
|
||||
Closeup_icon->model_instance_num = -1;
|
||||
|
||||
Closeup_one_revolution_time = ONE_REV_TIME;
|
||||
|
||||
@@ -1254,6 +1270,7 @@ int brief_setup_closeup(brief_icon *bi)
|
||||
Closeup_zoom = 0.5f;
|
||||
break;
|
||||
default:
|
||||
brief_get_closeup_ship_modelnum(Closeup_icon);
|
||||
Assert( Closeup_icon->ship_class != -1 );
|
||||
sip = &Ship_info[Closeup_icon->ship_class];
|
||||
|
||||
@@ -1267,17 +1284,12 @@ int brief_setup_closeup(brief_icon *bi)
|
||||
break;
|
||||
}
|
||||
|
||||
if ( Closeup_icon->modelnum < 0 ) {
|
||||
if ( Closeup_icon->modelnum == -1 ) {
|
||||
if ( sip == NULL ) {
|
||||
Closeup_icon->modelnum = model_load(pof_filename, 0, NULL);
|
||||
} else {
|
||||
Closeup_icon->modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
|
||||
Closeup_icon->model_instance_num = model_create_instance(true, Closeup_icon->modelnum);
|
||||
model_set_up_techroom_instance(sip, Closeup_icon->model_instance_num);
|
||||
}
|
||||
}
|
||||
|
||||
if ( Closeup_icon->modelnum >= 0 ) {
|
||||
Closeup_icon->radius = model_get_radius(Closeup_icon->modelnum);
|
||||
}
|
||||
|
||||
|
||||
@@ -2414,10 +2414,12 @@ int create_wings()
|
||||
ss_wing_info *wb;
|
||||
ss_slot_info *ws;
|
||||
wing *wp;
|
||||
p_object *p_objp;
|
||||
|
||||
int shipnum, objnum, slot_index;
|
||||
int cleanup_ship_index[MAX_WING_SLOTS];
|
||||
int i,j,k;
|
||||
int found_pobj;
|
||||
|
||||
Assert( (Ss_wings != NULL) && (Wss_slots != NULL) );
|
||||
|
||||
@@ -2449,22 +2451,21 @@ int create_wings()
|
||||
objnum = OBJ_INDEX(Player_obj);
|
||||
shipnum = Objects[objnum].instance;
|
||||
} else {
|
||||
// We should always update the parse object information, even if the ship is present at start,
|
||||
// because the wing might have more than one wave or scripting functions might need accurate data
|
||||
bool found_pobj = false;
|
||||
for ( auto &p_obj: Parse_objects ) {
|
||||
if ( p_obj.wingnum == WING_INDEX(wp) ) {
|
||||
if ( p_obj.pos_in_wing == j ) {
|
||||
p_obj.ship_class = Wss_slots[slot_index].ship_class;
|
||||
wl_update_parse_object_weapons(&p_obj, &Wss_slots[i*MAX_WING_SLOTS+j]);
|
||||
found_pobj = true;
|
||||
break;
|
||||
if ( wb->is_late) {
|
||||
found_pobj = 0;
|
||||
for ( p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp) ) {
|
||||
if ( p_objp->wingnum == WING_INDEX(wp) ) {
|
||||
if ( ws->sa_index == POBJ_INDEX(p_objp) ) {
|
||||
p_objp->ship_class = Wss_slots[slot_index].ship_class;
|
||||
wl_update_parse_object_weapons(p_objp, &Wss_slots[i*MAX_WING_SLOTS+j]);
|
||||
found_pobj = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert(found_pobj);
|
||||
}
|
||||
Assert(found_pobj);
|
||||
|
||||
if (!wb->is_late) {
|
||||
else {
|
||||
// AL 10/04/97
|
||||
// Change the ship type of the ship if different than current.
|
||||
// NOTE: This will reset the weapons for this ship. I think this is
|
||||
|
||||
@@ -62,14 +62,9 @@ bool Use_engine_wash_intensity;
|
||||
bool Framerate_independent_turning; // an in-depth explanation how this flag is supposed to work can be found in #2740 PR description
|
||||
bool Ai_respect_tabled_turntime_rotdamp;
|
||||
bool Swarmers_lead_targets;
|
||||
bool Chase_view_default;
|
||||
SCP_vector<gr_capability> Required_render_ext;
|
||||
float Weapon_SS_Threshold_Turret_Inaccuracy;
|
||||
bool Render_player_mflash;
|
||||
bool Neb_affects_beams;
|
||||
bool Neb_affects_weapons;
|
||||
bool Neb_affects_particles;
|
||||
bool Neb_affects_fireballs;
|
||||
|
||||
SCP_vector<std::pair<SCP_string, gr_capability>> req_render_ext_pairs = {
|
||||
std::make_pair("BPTC Texture Compression", CAPABILITY_BPTC)
|
||||
@@ -157,7 +152,8 @@ void parse_mod_table(const char *filename)
|
||||
}
|
||||
|
||||
// we want case-insensitive matching, so make this lowercase
|
||||
SCP_tolower(campaign_name);
|
||||
std::transform(campaign_name.begin(), campaign_name.end(), campaign_name.begin(),
|
||||
[](char c) { return (char)::tolower(c); });
|
||||
|
||||
Ignored_campaigns.push_back(campaign_name);
|
||||
}
|
||||
@@ -176,7 +172,8 @@ void parse_mod_table(const char *filename)
|
||||
}
|
||||
|
||||
// we want case-insensitive matching, so make this lowercase
|
||||
SCP_tolower(mission_name);
|
||||
std::transform(mission_name.begin(), mission_name.end(), mission_name.begin(),
|
||||
[](char c) { return (char)::tolower(c); });
|
||||
|
||||
Ignored_missions.push_back(mission_name);
|
||||
}
|
||||
@@ -381,22 +378,6 @@ void parse_mod_table(const char *filename)
|
||||
stuff_boolean(&Render_player_mflash);
|
||||
}
|
||||
|
||||
if (optional_string("$Beams affected by nebula visibility:")) {
|
||||
stuff_boolean(&Neb_affects_beams);
|
||||
}
|
||||
|
||||
if (optional_string("$Weapons affected by nebula visibility:")) {
|
||||
stuff_boolean(&Neb_affects_weapons);
|
||||
}
|
||||
|
||||
if (optional_string("$Particles affected by nebula visibility:")) {
|
||||
stuff_boolean(&Neb_affects_particles);
|
||||
}
|
||||
|
||||
if (optional_string("$Fireballs affected by nebula visibility:")) {
|
||||
stuff_boolean(&Neb_affects_fireballs);
|
||||
}
|
||||
|
||||
optional_string("#NETWORK SETTINGS");
|
||||
|
||||
if (optional_string("$FS2NetD port:")) {
|
||||
@@ -598,11 +579,7 @@ void parse_mod_table(const char *filename)
|
||||
Warning(LOCATION, "\'AI respect tabled turn time and rotdamp\' requires \'AI use framerate independent turning\' in order to function.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (optional_string("$Player starts in third person/chase view by default:")) {
|
||||
stuff_boolean(&Chase_view_default);
|
||||
}
|
||||
|
||||
|
||||
required_string("#END");
|
||||
}
|
||||
catch (const parse::ParseException& e)
|
||||
@@ -679,13 +656,8 @@ void mod_table_reset()
|
||||
Use_engine_wash_intensity = false;
|
||||
Framerate_independent_turning = true;
|
||||
Ai_respect_tabled_turntime_rotdamp = false;
|
||||
Chase_view_default = false;
|
||||
Swarmers_lead_targets = false;
|
||||
Required_render_ext.clear();
|
||||
Weapon_SS_Threshold_Turret_Inaccuracy = 0.7f; // Defaults to retail value of 0.7 --wookieejedi
|
||||
Render_player_mflash = false;
|
||||
Neb_affects_beams = false;
|
||||
Neb_affects_weapons = false;
|
||||
Neb_affects_particles = false;
|
||||
Neb_affects_fireballs = false;
|
||||
}
|
||||
@@ -56,12 +56,7 @@ extern SCP_vector<gr_capability> Required_render_ext;
|
||||
extern float Weapon_SS_Threshold_Turret_Inaccuracy;
|
||||
extern bool Framerate_independent_turning;
|
||||
extern bool Ai_respect_tabled_turntime_rotdamp;
|
||||
extern bool Chase_view_default;
|
||||
extern bool Render_player_mflash;
|
||||
extern bool Neb_affects_beams;
|
||||
extern bool Neb_affects_weapons;
|
||||
extern bool Neb_affects_particles;
|
||||
extern bool Neb_affects_fireballs;
|
||||
|
||||
void mod_table_init();
|
||||
|
||||
|
||||
Loaded 100 of 278 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user