Compare commits

..
Author SHA1 Message Date
asarium 1588644fec Add variables concept to expressions
This also includes the necessary setup work for using "position" and
"direction" in action expressions.
2021-01-10 22:47:49 +01:00
asarium 0abd592038 Rework actions to use definitions
This introduces action definitions which can then be instantiated to
form actual actions with expressions as their parameters.
2021-01-10 12:22:00 +01:00
asarium 909e799bdd Allow implicit conversion from int to float
This is generally a safe operation and will make the system a bit easier
to use.
2021-01-07 21:36:28 +01:00
asarium 5a9f181aff Rework function handling 2021-01-07 13:34:10 +01:00
asarium b0ba22a43d Add Action expressions
This is a parser for a very simple language to allow for execution of
dynamic expressions with return values for use as action parameters. The
only "dynamic" part this currently supports are random ranges but more
features are planned for the future.
2021-01-06 19:13:12 +01:00
asarium c89faebe4e Update GTest 2021-01-06 18:49:48 +01:00
asarium 059e149590 Add dynamic action programming system
This system adds the code necessary for letting content creators specify
complex events by specifying a simple, program-like sequence of
operations that will be executed by the engine when a certain event is
triggered.
2021-01-05 18:26:10 +01:00
278 changed files with 6296 additions and 21601 deletions

No files matched your search

-2
View File
@@ -6,5 +6,3 @@ code/executor/ @asarium
code/scpui/ @asarium
code/scripting/ @asarium
code/controlconfig/ @z64555
+1 -3
View File
@@ -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
+1 -3
View File
@@ -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
+1 -3
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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)
-1
View File
@@ -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)
-1
View File
@@ -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
{
-1
View File
@@ -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;
+18 -15
View File
@@ -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
+1 -1
View File
@@ -19,7 +19,7 @@ class ActionDefinitionManager {
static const ActionDefinitionManager& instance();
private:
ActionDefinitionManager();
ActionDefinitionManager() = default;
void addDefinition(std::unique_ptr<ActionDefinition> def);
+2
View File
@@ -0,0 +1,2 @@
#include "BuiltinActionDefinition.h"
-1
View File
@@ -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
{
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -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:
+14 -11
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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: {
+173 -125
View File
@@ -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,53 +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;
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);
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;
}
}
// if the bbox points method didn't work (or fov_edge_checks isn't on)
// try the normal method
bool in_fov;
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;
}
/**
@@ -503,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) ) {
@@ -543,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;
}
}
@@ -1130,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) {
@@ -1176,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);
@@ -1251,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 {
@@ -1383,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
@@ -1421,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]) {
@@ -1491,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())
@@ -1617,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();
}
}
@@ -1714,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;
@@ -1779,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);
@@ -1812,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
@@ -1834,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;
}
@@ -1859,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;
@@ -1938,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);
@@ -1961,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)
{
@@ -1976,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.
@@ -2183,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));
@@ -2192,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);
@@ -2310,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;
}
@@ -2326,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;
}
@@ -2391,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
@@ -2402,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);
@@ -2684,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;
@@ -2719,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.
@@ -2742,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;
}
}
@@ -2766,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
View File
@@ -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);
-2
View File
@@ -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 ),
+5 -7
View File
@@ -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) {
+8 -47
View File
@@ -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;
}
+2 -19
View File
@@ -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
-206
View File
@@ -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;
}
-66
View File
@@ -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
+10 -9
View File
@@ -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;
+1 -1
View File
@@ -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;
File diff suppressed because it is too large. Load diff
+28 -336
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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);
}
}
+1 -5
View File
@@ -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
+1 -9
View File
@@ -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) {
+6 -4
View File
@@ -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
View File
@@ -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;
}
+2 -4
View File
@@ -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 */
+1 -1
View File
@@ -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);
+16 -8
View File
@@ -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)
+6 -7
View File
@@ -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);
}
-16
View File
@@ -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);
}
-166
View File
@@ -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();
}
-326
View File
@@ -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;
//! @}
};
+2 -76
View File
@@ -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>>>;
+1 -3
View File
@@ -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!
+37 -18
View File
@@ -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;
+1 -1
View File
@@ -284,7 +284,7 @@ struct fog_data {
vec3d fog_color;
float fog_start;
float fog_density;
float fog_scale;
float zNear;
float zFar;
+3 -3
View File
@@ -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:
+6 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+3 -3
View File
@@ -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;
}
+1 -1
View File
@@ -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);
+5 -5
View File
@@ -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));
}
}
+5 -6
View File
@@ -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) {
+4 -4
View File
@@ -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);
}
}
+6 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
+9
View File
@@ -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;
+1 -12
View File
@@ -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);
-2
View File
@@ -133,8 +133,6 @@ public:
int setAmbientFactor(int factor) {
ambientFactor = factor;
Cmdline_ambient_factor = factor;
gr_calculate_ambient_factor();
return factor;
}
+46 -117
View File
@@ -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) {
+2 -29
View File
@@ -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);
};
}
}
}
+25 -44
View File
@@ -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);
+2 -7
View File
@@ -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);
-13
View File
@@ -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;
}
-4
View File
@@ -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
+1 -2
View File
@@ -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();
}
/**
-3
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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++;
}
}
+1 -7
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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;
}
+1 -2
View File
@@ -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
};
+57 -149
View File
@@ -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;
+1 -2
View File
@@ -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();
+4 -3
View File
@@ -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) {
+1 -13
View File
@@ -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);
}
}
-23
View File
@@ -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);
+2 -1
View File
@@ -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) {
+31 -31
View File
@@ -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 );
}
}
+2 -4
View File
@@ -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
View File
@@ -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
*/
+3 -1
View File
@@ -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);
+1 -9
View File
@@ -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);
+26 -14
View File
@@ -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);
}
+14 -13
View File
@@ -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
+5 -33
View File
@@ -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;
}
-5
View File
@@ -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