mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
Compare commits
148
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b7fc0b6a3 | ||
|
|
2e4bef0d09 | ||
|
|
172952cb0f | ||
|
|
72c27bdbe8 | ||
|
|
bd4de16007 | ||
|
|
2e560f2ddb | ||
|
|
d35d3d25f8 | ||
|
|
2d5111f14b | ||
|
|
d95754d6b4 | ||
|
|
5e3d4c6cfd | ||
|
|
deca820c9e | ||
|
|
21c298271b | ||
|
|
6ec28f6de5 | ||
|
|
8667891fc2 | ||
|
|
5f74388790 | ||
|
|
067b250be9 | ||
|
|
775984ae36 | ||
|
|
3a7e604af9 | ||
|
|
d4c429f3b1 | ||
|
|
933f20efaa | ||
|
|
6ca29a79ba | ||
|
|
498e5ef42e | ||
|
|
d29e236250 | ||
|
|
880005f758 | ||
|
|
ec1a35f624 | ||
|
|
575573334b | ||
|
|
82b9a392e1 | ||
|
|
ab2a2d46d6 | ||
|
|
b0f812b808 | ||
|
|
7e8ffcec86 | ||
|
|
7a9bebecf6 | ||
|
|
c005aa5ce2 | ||
|
|
1d00c296dd | ||
|
|
dd4036d8df | ||
|
|
4b42829aa0 | ||
|
|
7f8eaa275c | ||
|
|
329fd023ec | ||
|
|
2e0a84010e | ||
|
|
9e0c37364c | ||
|
|
d83d1b41ee | ||
|
|
d26a12464c | ||
|
|
f80693213c | ||
|
|
dbc26548f5 | ||
|
|
1ef90d79c4 | ||
|
|
caf3eaad98 | ||
|
|
efbace48b8 | ||
|
|
d961fa1bb6 | ||
|
|
c6a999ab7a | ||
|
|
5df12322e5 | ||
|
|
f7d474cb77 | ||
|
|
764a74dbd2 | ||
|
|
1d471e254b | ||
|
|
771d0c7a38 | ||
|
|
94bfb2fe75 | ||
|
|
8fd2813ddd | ||
|
|
a0180fc004 | ||
|
|
7a6aa5334d | ||
|
|
d8f388996d | ||
|
|
da55499817 | ||
|
|
e1dbcf4dd6 | ||
|
|
cb5a1000a6 | ||
|
|
94bd8da5db | ||
|
|
03926b8dde | ||
|
|
764bbac99f | ||
|
|
280ff2d4f0 | ||
|
|
45775a0d97 | ||
|
|
c4d405e33e | ||
|
|
86e1ebaf08 | ||
|
|
82921467a5 | ||
|
|
c2820f10c8 | ||
|
|
d52bddf0dc | ||
|
|
cd88663a92 | ||
|
|
0e9a72a37b | ||
|
|
1c10479d2f | ||
|
|
b88ccfb924 | ||
|
|
11e9fa27c6 | ||
|
|
f4f6e74456 | ||
|
|
de2e9ada72 | ||
|
|
4005a60b32 | ||
|
|
2e6306d85f | ||
|
|
e1f231fcf0 | ||
|
|
86e898c9ea | ||
|
|
df1491fd9e | ||
|
|
0b8f203371 | ||
|
|
1d152fcc73 | ||
|
|
c9fb046024 | ||
|
|
c4b8020ebc | ||
|
|
a2c7a344da | ||
|
|
f8e5c9f7a3 | ||
|
|
bebb69e28f | ||
|
|
aef4c1ba3c | ||
|
|
801a6031e3 | ||
|
|
4c573e2989 | ||
|
|
aa4e300153 | ||
|
|
3695f0ff93 | ||
|
|
7dc38f7752 | ||
|
|
5ab611a8b1 | ||
|
|
0577b69d69 | ||
|
|
6897024b29 | ||
|
|
ffd5fdc764 | ||
|
|
e985961ed5 | ||
|
|
89a722d41a | ||
|
|
3ca187127d | ||
|
|
35e67d4574 | ||
|
|
8b7253cfc1 | ||
|
|
7ec425f341 | ||
|
|
e4b2791c4e | ||
|
|
e5c2b76469 | ||
|
|
4c5bc1fa6c | ||
|
|
857a622caa | ||
|
|
bdc6145b2c | ||
|
|
07ce8a9c70 | ||
|
|
e42ed47c61 | ||
|
|
ce3b9b62b1 | ||
|
|
794863c5e7 | ||
|
|
544029f66f | ||
|
|
38e04b6151 | ||
|
|
3c76cc6524 | ||
|
|
3c7f73f4d8 | ||
|
|
5366073356 | ||
|
|
23fbdf7e64 | ||
|
|
4cb3b393da | ||
|
|
9c44e77da0 | ||
|
|
d9d372121a | ||
|
|
6df90a8ec7 | ||
|
|
289defdeff | ||
|
|
a54ebc3114 | ||
|
|
4a5ff2860b | ||
|
|
f8f1b44405 | ||
|
|
f13faf4cba | ||
|
|
9dd6bcf81d | ||
|
|
3356edff2b | ||
|
|
07acdde69a | ||
|
|
c89ea8e8dd | ||
|
|
d6c1a3dec4 | ||
|
|
30d41eff8b | ||
|
|
cc39cfe6f2 | ||
|
|
d0daa43ccf | ||
|
|
e0cd2806f8 | ||
|
|
4f026a66ab | ||
|
|
28df82a03f | ||
|
|
06f1b7d872 | ||
|
|
1dfe3a8d9f | ||
|
|
aa46ae6639 | ||
|
|
c8b1f56a09 | ||
|
|
8cc9eb2352 | ||
|
|
3d08119734 | ||
|
|
6f5e500a1e |
No files matched your search
+13
@@ -14,6 +14,17 @@ misc-*,\
|
||||
-misc-static-assert,\
|
||||
-misc-unused-parameters,\
|
||||
-misc-non-private-member-variables-in-classes,\
|
||||
readability-*,\
|
||||
-readability-function-size,\
|
||||
-readability-identifier-naming,\
|
||||
-readability-implicit-bool-conversion,\
|
||||
-readability-inconsistent-declaration-parameter-name,\
|
||||
-readability-isolate-declaration,\
|
||||
-readability-magic-numbers,\
|
||||
-readability-named-parameter,\
|
||||
-readability-qualified-auto,\
|
||||
-readability-uppercase-literal-suffix,\
|
||||
-readability-else-after-return,\
|
||||
"
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: 'code/.*$|freespace2/.*$|qtfred/.*$|test/src/.*$|build/.*$'
|
||||
@@ -23,5 +34,7 @@ CheckOptions:
|
||||
value: 'assert,Assert,Assertion'
|
||||
- key: 'modernize-use-emplace.ContainersWithPushBack'
|
||||
value: 'std::vector;std::deque;std::list;SCP_vector;SCP_deque;SCP_list'
|
||||
- key: 'readability-braces-around-statements.ShortStatementLines'
|
||||
value: '4' # Avoid flagging simple if (...) return false; statements
|
||||
...
|
||||
|
||||
+15
-13
@@ -18,8 +18,7 @@
|
||||
#include "ship/ship.h"
|
||||
|
||||
|
||||
int Total_goal_target_names = 0;
|
||||
char Goal_target_names[MAX_GOAL_TARGET_NAMES][NAME_LENGTH];
|
||||
SCP_vector<char *> Goal_target_names;
|
||||
ai_info Ai_info[MAX_AI_INFO];
|
||||
ai_info *Player_ai;
|
||||
|
||||
@@ -82,22 +81,25 @@ void set_wingnum(int objnum, int wingnum)
|
||||
Ai_info[ai_index].wing = wingnum;
|
||||
}
|
||||
|
||||
char *ai_get_goal_target_name(const char *name, int *index)
|
||||
const char *ai_get_goal_target_name(const char *name, int *index)
|
||||
{
|
||||
Assert(name != NULL);
|
||||
Assert(index != NULL);
|
||||
int i;
|
||||
Assertion(name != nullptr && index != nullptr, "Arguments cannot be null!");
|
||||
|
||||
for (i=0; i < Total_goal_target_names; i++)
|
||||
for (int i = 0; i < static_cast<int>(Goal_target_names.size()); ++i) {
|
||||
if (!stricmp(name, Goal_target_names[i])) {
|
||||
*index = i;
|
||||
return Goal_target_names[i];
|
||||
}
|
||||
}
|
||||
|
||||
Assert(Total_goal_target_names < MAX_GOAL_TARGET_NAMES);
|
||||
Assertion(strlen(name) <= NAME_LENGTH - 1, "Goal target name %s is too long. Needs to be 31 characters or less.", name);
|
||||
i = Total_goal_target_names++;
|
||||
strcpy_s(Goal_target_names[i], name);
|
||||
*index = i;
|
||||
return Goal_target_names[i];
|
||||
*index = static_cast<int>(Goal_target_names.size());
|
||||
Goal_target_names.push_back(vm_strdup(name));
|
||||
return Goal_target_names[*index];
|
||||
}
|
||||
|
||||
void ai_clear_goal_target_names()
|
||||
{
|
||||
for (auto ptr : Goal_target_names)
|
||||
vm_free(ptr);
|
||||
Goal_target_names.clear();
|
||||
}
|
||||
+5
-8
@@ -83,7 +83,7 @@ typedef struct ai_goal {
|
||||
fix time; // time at which this goal was issued.
|
||||
int priority; // how important is this goal -- number 0 - 100
|
||||
|
||||
char *target_name; // name of the thing that this goal acts upon
|
||||
const char *target_name; // name of the thing that this goal acts upon
|
||||
int target_name_index; // index of goal_target_name in Goal_target_names[][]
|
||||
waypoint_list *wp_list; // waypoints that this ship might fly.
|
||||
int target_instance; // instance of thing this ship might be chasing (currently only used for weapons; note, not the same as objnum!)
|
||||
@@ -93,19 +93,17 @@ typedef struct ai_goal {
|
||||
// (AIGF_DOCKER_INDEX_VALID and AIGF_DOCKEE_INDEX_VALID tell us to use indexes; otherwise we use names)
|
||||
// these are the dockpoints used on the docker and dockee ships, not the ships themselves
|
||||
union {
|
||||
char *name;
|
||||
const char *name;
|
||||
int index;
|
||||
} docker;
|
||||
|
||||
union {
|
||||
char *name;
|
||||
const char *name;
|
||||
int index;
|
||||
} dockee;
|
||||
|
||||
} ai_goal;
|
||||
|
||||
#define MAX_GOAL_TARGET_NAMES 100
|
||||
|
||||
#define AIM_CHASE 0
|
||||
#define AIM_EVADE 1
|
||||
#define AIM_GET_BEHIND 2
|
||||
@@ -513,8 +511,6 @@ typedef struct {
|
||||
#define BURST_DURATION 500 // decay time over which Player->damage_this_burst falls from MAX_BURST_DAMAGE to 0
|
||||
|
||||
extern int Mission_all_attack; // !0 means all teams attack all teams.
|
||||
extern int Total_goal_target_names;
|
||||
extern char Goal_target_names[MAX_GOAL_TARGET_NAMES][NAME_LENGTH];
|
||||
|
||||
extern void update_ai_info_for_hit(int hitter_obj, int hit_obj);
|
||||
extern void ai_frame_all(void);
|
||||
@@ -556,7 +552,8 @@ void ai_process( object * obj, int ai_index, float frametime );
|
||||
int get_wingnum(int objnum);
|
||||
|
||||
void set_wingnum(int objnum, int wingnum);
|
||||
char *ai_get_goal_target_name(const char *name, int *index);
|
||||
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, ship_subsys *ssp, int ship_info_index = -1);
|
||||
|
||||
@@ -84,13 +84,17 @@ namespace AI {
|
||||
Assist_scoring_scales_with_damage,
|
||||
Beams_damage_weapons,
|
||||
Big_ships_can_attack_beam_turrets_on_untargeted_ships,
|
||||
Check_comms_for_non_player_ships,
|
||||
Disable_linked_fire_penalty,
|
||||
Disable_player_secondary_doublefire,
|
||||
Disable_ai_secondary_doublefire,
|
||||
Disable_weapon_damage_scaling,
|
||||
Dont_insert_random_turret_fire_delay,
|
||||
Fix_ai_class_bug,
|
||||
Fix_ai_path_order_bug,
|
||||
Fix_heat_seeker_stealth_bug,
|
||||
Fix_linked_primary_bug,
|
||||
Fix_ramming_stationary_targets_bug,
|
||||
Force_beam_turret_fov,
|
||||
Free_afterburner_use,
|
||||
Glide_decay_requires_thrust,
|
||||
|
||||
+28
-2
@@ -7,11 +7,11 @@
|
||||
|
||||
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "def_files/def_files.h"
|
||||
#include "ai/ai_profiles.h"
|
||||
#include "def_files/def_files.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "localization/localize.h"
|
||||
#include "mod_table/mod_table.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "ship/ship.h"
|
||||
#include "weapon/weapon.h"
|
||||
@@ -371,10 +371,16 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
|
||||
set_flag(profile, "$navigation subsystem governs warpout capability:", AI::Profile_Flags::Navigation_subsys_governs_warp);
|
||||
|
||||
set_flag(profile, "$check communications for non-player ships:", AI::Profile_Flags::Check_comms_for_non_player_ships);
|
||||
|
||||
set_flag(profile, "$ignore lower bound for minimum speed of docked ship:", AI::Profile_Flags::No_min_dock_speed_cap);
|
||||
|
||||
set_flag(profile, "$disable linked fire penalty:", AI::Profile_Flags::Disable_linked_fire_penalty);
|
||||
|
||||
set_flag(profile, "$disable player secondary doublefire:", AI::Profile_Flags::Disable_player_secondary_doublefire);
|
||||
|
||||
set_flag(profile, "$disable ai secondary doublefire:", AI::Profile_Flags::Disable_ai_secondary_doublefire);
|
||||
|
||||
set_flag(profile, "$disable weapon damage scaling:", AI::Profile_Flags::Disable_weapon_damage_scaling);
|
||||
|
||||
set_flag(profile, "$use additive weapon velocity:", AI::Profile_Flags::Use_additive_weapon_velocity);
|
||||
@@ -391,6 +397,8 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
|
||||
set_flag(profile, "$fix linked primary weapon decision bug:", AI::Profile_Flags::Fix_linked_primary_bug);
|
||||
|
||||
set_flag(profile, "$fix ramming stationary targets bug:", AI::Profile_Flags::Fix_ramming_stationary_targets_bug);
|
||||
|
||||
set_flag(profile, "$prevent turrets targeting too distant bombs:", AI::Profile_Flags::Prevent_targeting_bombs_beyond_range);
|
||||
|
||||
set_flag(profile, "$smart subsystem targeting for turrets:", AI::Profile_Flags::Smart_subsystem_targeting_for_turrets);
|
||||
@@ -624,4 +632,22 @@ void ai_profile_t::reset()
|
||||
for (int i = 0; i <= MAX_DETAIL_LEVEL; ++i) {
|
||||
detail_distance_mult[i] = 0;
|
||||
}
|
||||
|
||||
// via Github #2332, enable bugfixes if we are targeting version 20 and up
|
||||
if (mod_supports_version(20, 0, 0)) {
|
||||
flags.set(AI::Profile_Flags::Huge_turret_weapons_ignore_bombs);
|
||||
flags.set(AI::Profile_Flags::Fix_linked_primary_bug);
|
||||
flags.set(AI::Profile_Flags::Prevent_targeting_bombs_beyond_range);
|
||||
flags.set(AI::Profile_Flags::Fix_heat_seeker_stealth_bug);
|
||||
flags.set(AI::Profile_Flags::Allow_vertical_dodge);
|
||||
flags.set(AI::Profile_Flags::Fix_ai_class_bug);
|
||||
flags.set(AI::Profile_Flags::Ai_guards_specific_ship_in_wing);
|
||||
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);
|
||||
}
|
||||
// this flag has been enabled ever since 3.7.2
|
||||
if (mod_supports_version(3, 7, 2)) {
|
||||
flags.set(AI::Profile_Flags::Fix_ramming_stationary_targets_bug);
|
||||
}
|
||||
}
|
||||
+27
-8
@@ -66,6 +66,7 @@
|
||||
#include "ship/ship.h"
|
||||
#include "ship/shipfx.h"
|
||||
#include "ship/shiphit.h"
|
||||
#include "ship/subsysdamage.h"
|
||||
#include "weapon/beam.h"
|
||||
#include "weapon/flak.h"
|
||||
#include "weapon/swarm.h"
|
||||
@@ -274,7 +275,7 @@ typedef struct {
|
||||
int team;
|
||||
int weapon_index;
|
||||
int max_fire_count;
|
||||
char *shipname;
|
||||
const char *shipname;
|
||||
} huge_fire_info;
|
||||
|
||||
SCP_vector<huge_fire_info> Ai_huge_fire_info;
|
||||
@@ -5674,7 +5675,16 @@ int ai_fire_primary_weapon(object *objp)
|
||||
float dot;
|
||||
vec3d v2t;
|
||||
|
||||
if ( !(vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE ) && !(aip->submode == SM_AVOID) ) {
|
||||
bool condition;
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ramming_stationary_targets_bug]) {
|
||||
// fixed by Mantis 3147 - Avoid bashing orientation if trying to avoid a collision.
|
||||
condition = !(vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE) && (aip->submode != SM_AVOID);
|
||||
} else {
|
||||
// retail behavior
|
||||
condition = !(vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE);
|
||||
}
|
||||
|
||||
if ( condition ) {
|
||||
if ( vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
|
||||
vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
|
||||
dot = vm_vec_dot(&v2t, &objp->orient.vec.fvec);
|
||||
@@ -8224,9 +8234,15 @@ void ai_chase()
|
||||
if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f)) {
|
||||
predicted_enemy_pos = enemy_pos;
|
||||
} else if (aip->ai_flags[AI::AI_Flags::Seek_lock]) {
|
||||
set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &aip->last_aim_enemy_pos, &aip->last_aim_enemy_vel, aip); // Set G_fire_pos
|
||||
predicted_enemy_pos = enemy_pos;
|
||||
G_predicted_pos = predicted_enemy_pos;
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ramming_stationary_targets_bug]) {
|
||||
// fixed by Mantis 3147 - Bash orientation if aspect seekers are equipped.
|
||||
set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &aip->last_aim_enemy_pos, &aip->last_aim_enemy_vel, aip); // Set G_fire_pos
|
||||
predicted_enemy_pos = enemy_pos;
|
||||
G_predicted_pos = predicted_enemy_pos;
|
||||
} else {
|
||||
// retail behavior
|
||||
predicted_enemy_pos = enemy_pos;
|
||||
}
|
||||
} else {
|
||||
// Set predicted_enemy_pos.
|
||||
// See if attacking a subsystem.
|
||||
@@ -12552,13 +12568,13 @@ int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, int allowed_path_
|
||||
polymodel *pm = model_get( Ship_info[parent_shipp->ship_info_index].model_num );
|
||||
ship_bay *bay = pm->ship_bay;
|
||||
|
||||
if ( bay == NULL ) {
|
||||
WarningEx(LOCATION, "Ship %s was set to arrive from fighter bay on object %s, but no fighter bay exists on that ships' model (%s).\n", shipp->ship_name, parent_shipp->ship_name, pm->filename);
|
||||
if ( bay == nullptr ) {
|
||||
Warning(LOCATION, "Ship %s was set to arrive from fighter bay on object %s, but no fighter bay exists on that ships' model (%s).\n", shipp->ship_name, parent_shipp->ship_name, pm->filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( bay->num_paths <= 0 ) {
|
||||
WarningEx(LOCATION, "Ship %s was set to arrive from fighter bay on object %s, but no fighter bay paths exist on that ships' model (%s).\n", shipp->ship_name, parent_shipp->ship_name, pm->filename);
|
||||
Warning(LOCATION, "Ship %s was set to arrive from fighter bay on object %s, but no fighter bay paths exist on that ships' model (%s).\n", shipp->ship_name, parent_shipp->ship_name, pm->filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -14530,6 +14546,9 @@ void process_friendly_hit_message( int message, object *objp )
|
||||
if (Ships[objp->instance].flags[Ship::Ship_Flags::No_builtin_messages]) {
|
||||
index = -1;
|
||||
}
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(&Ships[objp->instance]) <= COMM_DAMAGED) {
|
||||
index = -1;
|
||||
}
|
||||
|
||||
// Karajorma - pick a random ship to send Command messages if command is silenced.
|
||||
if (index < 0 && (The_mission.flags[Mission::Mission_Flags::No_builtin_command]) ) {
|
||||
|
||||
+10
-14
@@ -310,7 +310,7 @@ void ai_clear_wing_goals( int wingnum )
|
||||
void ai_mission_wing_goal_complete( int wingnum, ai_goal *remove_goalp )
|
||||
{
|
||||
int mode, submode, priority, i;
|
||||
char *name;
|
||||
const char *name;
|
||||
ai_goal *aigp;
|
||||
wing *wingp;
|
||||
|
||||
@@ -388,7 +388,7 @@ void ai_goal_purge_invalid_goals( ai_goal *aigp, ai_goal *goal_list, ai_info *ai
|
||||
{
|
||||
int i, j;
|
||||
ai_goal *purge_goal;
|
||||
char *name;
|
||||
const char *name;
|
||||
int mode, ship_index, wingnum;
|
||||
|
||||
// get locals for easer access
|
||||
@@ -1413,21 +1413,17 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
{
|
||||
ship *shipp = &Ships[objp->instance];
|
||||
|
||||
// always valid if has subspace drive
|
||||
if (!(shipp->flags[Ship::Ship_Flags::No_subspace_drive]))
|
||||
// always valid if has working subspace drive, not disabled, and not limited by subsystem strength
|
||||
if ( ship_can_warp_full_check(shipp) )
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
|
||||
// if no subspace drive, only valid if our mothership is present
|
||||
|
||||
// check that we have a mothership and that we can depart to it
|
||||
if (shipp->departure_location == DEPART_AT_DOCK_BAY)
|
||||
{
|
||||
int anchor_shipnum = ship_name_lookup(Parse_names[shipp->departure_anchor]);
|
||||
if (anchor_shipnum >= 0 && ship_useful_for_departure(anchor_shipnum, shipp->departure_path_mask))
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
// if we can't warp, we can only depart if the ship (or its wing) departs to a fighter bay and the mothership is present
|
||||
if (ship_can_bay_depart(shipp)) {
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
}
|
||||
else {
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
}
|
||||
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
-11
@@ -2544,7 +2544,6 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
|
||||
// Ok, the turret is lined up... now line up a particular gun.
|
||||
bool ok_to_fire = false;
|
||||
bool something_was_ok_to_fire=false;
|
||||
vec3d tv2e; //so flak can get their jitter without screwing up other guns
|
||||
|
||||
if (in_fov ) {
|
||||
|
||||
@@ -2581,7 +2580,9 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
|
||||
ss->turret_next_fire_pos = ffp_pos;
|
||||
}
|
||||
|
||||
ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
|
||||
// We're ready to fire... now get down to specifics, like where is the
|
||||
// actual gun point and normal, not just the one for whole turret.
|
||||
ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
|
||||
|
||||
// Fire in the direction the turret is facing, not right at the target regardless of turret dir.
|
||||
vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
|
||||
@@ -2592,12 +2593,6 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
|
||||
wip = get_turret_weapon_wip(&ss->weapons, valid_weapons[0]);
|
||||
else
|
||||
wip = get_turret_weapon_wip(&ss->weapons, valid_weapons[i]);
|
||||
|
||||
// We're ready to fire... now get down to specifics, like where is the
|
||||
// actual gun point and normal, not just the one for whole turret.
|
||||
// moved here as if there are two weapons with indentical fire stamps
|
||||
// they would have shared the fire point.
|
||||
tv2e = gvec;
|
||||
|
||||
// make sure to reset this for current weapon
|
||||
ok_to_fire = false;
|
||||
@@ -2605,7 +2600,7 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
|
||||
// if the weapon is a flak gun, add some jitter to its aim so it fires in a "cone"
|
||||
// to make a cool visual effect and make them less lethal
|
||||
if(wip->wi_flags[Weapon::Info_Flags::Flak]){
|
||||
flak_jitter_aim(&tv2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS), wip);
|
||||
flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS), wip);
|
||||
}
|
||||
|
||||
// Fire if:
|
||||
@@ -2716,9 +2711,9 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
|
||||
|
||||
//Pass along which gun we are using
|
||||
if (tp->flags[Model::Subsystem_Flags::Turret_salvo])
|
||||
turret_fire_weapon(valid_weapons[0], ss, parent_objnum, &gpos, &tv2e, &predicted_enemy_pos);
|
||||
turret_fire_weapon(valid_weapons[0], ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);
|
||||
else
|
||||
turret_fire_weapon(valid_weapons[i], ss, parent_objnum, &gpos, &tv2e, &predicted_enemy_pos);
|
||||
turret_fire_weapon(valid_weapons[i], ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);
|
||||
} else {
|
||||
// make sure salvo fire mode does not turn into autofire
|
||||
if ((tp->flags[Model::Subsystem_Flags::Turret_salvo]) && ((i + 1) == number_of_firings)) {
|
||||
|
||||
@@ -57,7 +57,7 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
|
||||
{ CF_TYPE_MAPS, "data" DIR_SEPARATOR_STR "maps", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
|
||||
{ CF_TYPE_TEXT, "data" DIR_SEPARATOR_STR "text", ".txt .net", CF_TYPE_DATA },
|
||||
{ CF_TYPE_MODELS, "data" DIR_SEPARATOR_STR "models", ".pof", CF_TYPE_DATA },
|
||||
{ CF_TYPE_TABLES, "data" DIR_SEPARATOR_STR "tables", ".tbl .tbm", CF_TYPE_DATA },
|
||||
{ CF_TYPE_TABLES, "data" DIR_SEPARATOR_STR "tables", ".tbl .tbm .lua", CF_TYPE_DATA },
|
||||
{ CF_TYPE_SOUNDS, "data" DIR_SEPARATOR_STR "sounds", ".wav .ogg", CF_TYPE_DATA },
|
||||
{ CF_TYPE_SOUNDS_8B22K, "data" DIR_SEPARATOR_STR "sounds" DIR_SEPARATOR_STR "8b22k", ".wav .ogg", CF_TYPE_SOUNDS },
|
||||
{ CF_TYPE_SOUNDS_16B11K, "data" DIR_SEPARATOR_STR "sounds" DIR_SEPARATOR_STR "16b11k", ".wav .ogg", CF_TYPE_SOUNDS },
|
||||
|
||||
+72
-33
@@ -162,8 +162,7 @@ Flag exe_params[] =
|
||||
{ "-3dshockwave", "Enable 3D shockwaves", true, EASY_ALL_ON | EASY_HI_MEM_ON, EASY_DEFAULT | EASY_HI_MEM_OFF, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-3dshockwave" },
|
||||
{ "-no_post_process", "Disable post-processing", true, EASY_DEFAULT | EASY_HI_MEM_OFF, EASY_ALL_ON | EASY_HI_MEM_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_post_process" },
|
||||
{ "-soft_particles", "Enable soft particles", true, EASY_ALL_ON, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-soft_particles" },
|
||||
{ "-fxaa", "Enable FXAA anti-aliasing", true, EASY_ALL_ON | EASY_HI_MEM_ON, EASY_DEFAULT | EASY_HI_MEM_OFF, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fxaa" },
|
||||
{ "-smaa", "Enable SMAA anti-aliasing", true, EASY_ALL_ON | EASY_HI_MEM_ON, EASY_DEFAULT | EASY_HI_MEM_OFF, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-smaa" },
|
||||
{ "-aa", "Enable Post-process anti-aliasing", true, EASY_ALL_ON | EASY_HI_MEM_ON, EASY_DEFAULT | EASY_HI_MEM_OFF, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-aa" },
|
||||
{ "-nolightshafts", "Disable lightshafts", true, EASY_DEFAULT | EASY_HI_MEM_OFF, EASY_ALL_ON | EASY_HI_MEM_ON, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-nolightshafts"},
|
||||
{ "-fb_explosions", "Enable Framebuffer Shockwaves", true, EASY_ALL_ON, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fb_explosions", },
|
||||
{ "-fb_thrusters", "Enable Framebuffer Thrusters", true, EASY_ALL_ON, EASY_DEFAULT, "Graphics", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fb_thrusters", },
|
||||
@@ -231,7 +230,8 @@ Flag exe_params[] =
|
||||
{ "-pos", "Show position of camera", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-pos", },
|
||||
{ "-stats", "Show statistics", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-stats", },
|
||||
{ "-coords", "Show coordinates", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-coords", },
|
||||
{ "-pofspew", "Generate all ibx files immediately", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-pofspew", },
|
||||
{ "-pofspew", "Dump model information to pofspew.txt", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-pofspew", },
|
||||
{ "-weaponspew", "Dump weapon stats and spreadsheets", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-weaponspew", },
|
||||
{ "-tablecrcs", "Dump table CRCs for multi validation", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-tablecrcs", },
|
||||
{ "-missioncrcs", "Dump mission CRCs for multi validation", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-missioncrcs", },
|
||||
{ "-dis_collisions", "Disable collisions", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-dis_collisions", },
|
||||
@@ -275,6 +275,7 @@ 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_NONE); // Cmdline_spew_weapon_stats
|
||||
cmdline_parm mouse_coords("-coords", NULL, AT_NONE); // Cmdline_mouse_coords
|
||||
cmdline_parm timeout("-timeout", "Multiplayer network timeout (secs)", AT_INT); // Cmdline_timeout
|
||||
cmdline_parm bit32_arg("-32bit", "Deprecated", AT_NONE); // (only here for retail compatibility reasons, doesn't actually do anything)
|
||||
@@ -296,6 +297,7 @@ int Cmdline_multi_stream_chat_to_file = 0;
|
||||
int Cmdline_network_port = -1;
|
||||
int Cmdline_restricted_game = 0;
|
||||
int Cmdline_spew_pof_info = 0;
|
||||
bool Cmdline_spew_weapon_stats = false;
|
||||
int Cmdline_start_netgame = 0;
|
||||
int Cmdline_timeout = -1;
|
||||
int Cmdline_use_last_pilot = 0;
|
||||
@@ -322,10 +324,12 @@ cmdline_parm enable_3d_shockwave_arg("-3dshockwave", NULL, AT_NONE);
|
||||
cmdline_parm softparticles_arg("-soft_particles", NULL, AT_NONE);
|
||||
cmdline_parm no_postprocess_arg("-no_post_process", "Disables post-processing", AT_NONE);
|
||||
cmdline_parm bloom_intensity_arg("-bloom_intensity", "Set bloom intensity, requires post-processing", AT_INT);
|
||||
cmdline_parm fxaa_arg("-fxaa", NULL, AT_NONE);
|
||||
cmdline_parm fxaa_preset_arg("-fxaa_preset", "FXAA quality (0-9), requires post-processing and -fxaa", AT_INT);
|
||||
cmdline_parm smaa_arg("-smaa", nullptr, AT_NONE);
|
||||
cmdline_parm smaa_preset_arg("-smaa_preset", "SMAA quality (0-9), requires post-processing and -smaa", AT_INT);
|
||||
cmdline_parm post_process_aa_arg("-aa", "Enables post-process antialiasing", AT_NONE);
|
||||
cmdline_parm post_process_aa_preset_arg("-aa_preset", "Sets the AA effect to use. See the wiki for details", AT_INT);
|
||||
cmdline_parm deprecated_fxaa_arg("-fxaa", nullptr, AT_NONE);
|
||||
cmdline_parm deprecated_fxaa_preset_arg("-fxaa_preset", "FXAA quality (0-2), requires post-processing and -fxaa", AT_INT);
|
||||
cmdline_parm deprecated_smaa_arg("-smaa", nullptr, AT_NONE);
|
||||
cmdline_parm deprecated_smaa_preset_arg("-smaa_preset", "SMAA quality (0-3), requires post-processing and -smaa", AT_INT);
|
||||
cmdline_parm fb_explosions_arg("-fb_explosions", NULL, AT_NONE);
|
||||
cmdline_parm fb_thrusters_arg("-fb_thrusters", NULL, AT_NONE);
|
||||
cmdline_parm flightshaftsoff_arg("-nolightshafts", NULL, AT_NONE);
|
||||
@@ -1667,6 +1671,11 @@ bool SetCmdlineParams()
|
||||
Cmdline_spew_pof_info = 1;
|
||||
}
|
||||
|
||||
// spew weapon stats
|
||||
if (weapon_spew.found()) {
|
||||
Cmdline_spew_weapon_stats = true;
|
||||
}
|
||||
|
||||
// mouse coords
|
||||
if(mouse_coords.found()){
|
||||
Cmdline_mouse_coords = 1;
|
||||
@@ -1863,40 +1872,31 @@ bool SetCmdlineParams()
|
||||
if ( height_arg.found() ) {
|
||||
Cmdline_height = 0;
|
||||
}
|
||||
|
||||
if (fxaa_arg.found() ) {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
|
||||
|
||||
if (fxaa_preset_arg.found()) {
|
||||
auto val = fxaa_preset_arg.get_int();
|
||||
if (val > 6) {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_High;
|
||||
} else if (val > 3) {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
|
||||
} else {
|
||||
if (post_process_aa_arg.found() || post_process_aa_preset_arg.found()) {
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_Low;
|
||||
|
||||
if (post_process_aa_preset_arg.found()) {
|
||||
switch (post_process_aa_preset_arg.get_int()) {
|
||||
case 0:
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Low;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (smaa_arg.found()) {
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_Medium;
|
||||
|
||||
if (smaa_preset_arg.found()) {
|
||||
switch (smaa_preset_arg.get_int()) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
|
||||
break;
|
||||
case 2:
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_High;
|
||||
break;
|
||||
case 3:
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_Low;
|
||||
break;
|
||||
case 1:
|
||||
case 4:
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_Medium;
|
||||
break;
|
||||
case 2:
|
||||
case 5:
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_High;
|
||||
break;
|
||||
case 3:
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_Ultra;
|
||||
break;
|
||||
default:
|
||||
case 6:
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_Ultra;
|
||||
break;
|
||||
}
|
||||
@@ -2210,6 +2210,45 @@ bool SetCmdlineParams()
|
||||
if (deprecated_postprocess_arg.found()) {
|
||||
Cmdline_deprecated_postprocess = true;
|
||||
}
|
||||
|
||||
if (deprecated_fxaa_arg.found() ) {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
|
||||
|
||||
if (deprecated_fxaa_preset_arg.found()) {
|
||||
auto val = deprecated_fxaa_preset_arg.get_int();
|
||||
if (val > 6) {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_High;
|
||||
} else if (val > 3) {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
|
||||
} else {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Low;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deprecated_smaa_arg.found()) {
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_Medium;
|
||||
|
||||
if (deprecated_smaa_preset_arg.found()) {
|
||||
switch (deprecated_smaa_preset_arg.get_int()) {
|
||||
case 0:
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_Low;
|
||||
break;
|
||||
case 1:
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_Medium;
|
||||
break;
|
||||
case 2:
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_High;
|
||||
break;
|
||||
case 3:
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_Ultra;
|
||||
break;
|
||||
default:
|
||||
Gr_aa_mode = AntiAliasMode::SMAA_Ultra;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -149,5 +149,7 @@ extern bool Cmdline_show_video_info;
|
||||
extern bool Cmdline_debug_window;
|
||||
extern bool Cmdline_graphics_debug_output;
|
||||
extern bool Cmdline_log_to_stdout;
|
||||
extern bool Cmdline_spew_weapon_stats;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -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_current_lang];
|
||||
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_current_lang];
|
||||
auto& current_language = Lcl_languages[Lcl_current_lang == LCL_UNTRANSLATED ? LCL_DEFAULT : Lcl_current_lang];
|
||||
|
||||
for (auto ext : CHECKED_SUBT_EXTENSIONS) {
|
||||
SCP_string fileName;
|
||||
|
||||
@@ -240,11 +240,12 @@ void debris_process_post(object * obj, float frame_time)
|
||||
MONITOR_INC(NumSmallDebris,1);
|
||||
}
|
||||
|
||||
if ( db->lifeleft >= 0.0f) {
|
||||
if (db->lifeleft >= 0.0f) {
|
||||
db->lifeleft -= frame_time;
|
||||
if ( db->lifeleft < 0.0f ) {
|
||||
debris_start_death_roll(obj, db);
|
||||
}
|
||||
}
|
||||
|
||||
if ( db->lifeleft < 0.0f ) {
|
||||
debris_start_death_roll(obj, db);
|
||||
}
|
||||
|
||||
maybe_delete_debris(db); // Make this debris go away if it's very far away.
|
||||
|
||||
@@ -45,7 +45,7 @@ void main()
|
||||
fragDepthLinear = ( 2.0 * farZ * nearZ ) / ( farZ + nearZ - gl_FragCoord.z * (farZ-nearZ) );
|
||||
}
|
||||
// assume UV of 0.5, 0.5 is the centroid of this sphere volume
|
||||
float depthOffset = sqrt(pow(fragRadius, 2.0) - pow(offset_len, 2.0));
|
||||
float depthOffset = sqrt((fragRadius*fragRadius) - (offset_len*offset_len));
|
||||
float frontDepth = fragDepthLinear - depthOffset;
|
||||
float backDepth = fragDepthLinear + depthOffset;
|
||||
float intensity = smoothstep(max(nearZ, frontDepth), backDepth, sceneDepthLinear);
|
||||
|
||||
@@ -55,7 +55,7 @@ struct version {
|
||||
|
||||
version() = default;
|
||||
|
||||
version(int major, int minor, int build, int revision);
|
||||
version(int major, int minor = 0, int build = 0, int revision = 0);
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
|
||||
@@ -254,25 +254,29 @@ void gr_opengl_post_process_begin()
|
||||
Post_in_frame = true;
|
||||
}
|
||||
|
||||
SCP_vector<shader_type> get_aa_shader_types(AntiAliasMode aa_mode) {
|
||||
auto stypes = SCP_vector<shader_type>();
|
||||
|
||||
if (gr_is_fxaa_mode(aa_mode)) {
|
||||
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_FXAA);
|
||||
}
|
||||
else if (gr_is_smaa_mode(aa_mode)) {
|
||||
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT);
|
||||
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_EDGE);
|
||||
stypes.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING);
|
||||
}
|
||||
|
||||
return stypes;
|
||||
}
|
||||
|
||||
void recompile_aa_shader() {
|
||||
auto stype = SCP_vector<shader_type>();
|
||||
|
||||
if (gr_is_fxaa_mode(Gr_aa_mode)) {
|
||||
stype.push_back(shader_type::SDR_TYPE_POST_PROCESS_FXAA);
|
||||
}
|
||||
else if (gr_is_smaa_mode(Gr_aa_mode)) {
|
||||
stype.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT);
|
||||
stype.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_EDGE);
|
||||
stype.push_back(shader_type::SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING);
|
||||
}
|
||||
|
||||
mprintf(("Recompiling AA shader(s)...\n"));
|
||||
|
||||
for (auto sdr : stype) {
|
||||
// start recompile by grabbing deleting the current shader we have, assuming it's already created
|
||||
for (auto sdr : get_aa_shader_types(Gr_aa_mode_last_frame)) {
|
||||
opengl_delete_shader( gr_opengl_maybe_create_shader(sdr, 0) );
|
||||
}
|
||||
|
||||
// then recreate it again. shader loading code will be updated with the new AA presets
|
||||
for (auto sdr : get_aa_shader_types(Gr_aa_mode)) {
|
||||
gr_opengl_maybe_create_shader(sdr, 0);
|
||||
}
|
||||
|
||||
@@ -1251,7 +1255,7 @@ static bool opengl_post_init_framebuffer()
|
||||
|
||||
opengl_setup_bloom_textures();
|
||||
|
||||
if (gr_is_smaa_mode(Gr_aa_mode)) {
|
||||
if (Gr_aa_mode != AntiAliasMode::None) {
|
||||
setup_smaa_resources();
|
||||
}
|
||||
|
||||
|
||||
+1050
-21
File diff suppressed because it is too large.
Load diff
@@ -18,6 +18,7 @@ void hud_init_missile_lock();
|
||||
void hud_calculate_lock_position(float frametime);
|
||||
void hud_calculate_lock_start_pos();
|
||||
void hud_do_lock_indicator(float frametime);
|
||||
void hud_do_lock_indicators(float frametime);
|
||||
void hud_stop_looped_locking_sounds();
|
||||
void hud_lock_reset(float lock_time_scale=1.0f);
|
||||
|
||||
@@ -54,7 +55,9 @@ public:
|
||||
void initLoopLockedAnim(bool loop);
|
||||
|
||||
void render(float frametime) override;
|
||||
void renderOld(float frametime);
|
||||
void renderLockTriangles(int center_x, int center_y, float frametime);
|
||||
void renderLockTrianglesNew(int center_x, int center_y, int start_timestamp);
|
||||
void renderLockTrianglesOld(int center_x, int center_y, int radius);
|
||||
void pageIn() override;
|
||||
void initialize() override;
|
||||
|
||||
+44
-83
@@ -238,8 +238,9 @@ SCP_vector<squadmsg_history> Squadmsg_history;
|
||||
// forward declarations
|
||||
void hud_add_issued_order(const char *name, int order);
|
||||
void hud_update_last_order(char *target, int order_source, int special_index);
|
||||
int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip = NULL );
|
||||
int hud_squadmsg_ship_order_valid( int shipnum, int order );
|
||||
bool hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip = nullptr);
|
||||
bool hud_squadmsg_ship_valid(ship *shipp, object *objp = nullptr);
|
||||
bool hud_squadmsg_ship_order_valid( int shipnum, int order );
|
||||
|
||||
// function to set up variables needed when messaging mode is started
|
||||
void hud_squadmsg_start()
|
||||
@@ -300,31 +301,15 @@ bool hud_squadmsg_exist_fighters( )
|
||||
{
|
||||
objp = &Objects[so->objnum];
|
||||
shipp = &Ships[objp->instance];
|
||||
Assert ( shipp->objnum != -1 );
|
||||
Assertion(shipp->objnum != -1, "hud_squadmsg_exist_fighters() discovered that ship #%d ('%s') has an objnum of -1. Since the ship was retrieved from its object number (%d), this should be impossible; get a coder!\n", objp->instance, shipp->ship_name, so->objnum);
|
||||
|
||||
// check fighter is accepting orders
|
||||
if (shipp->orders_accepted == 0)
|
||||
continue;
|
||||
|
||||
// be sure ship is on correct team
|
||||
#ifdef NDEBUG
|
||||
if (shipp->team != Player_ship->team)
|
||||
continue;
|
||||
#else
|
||||
if (!Msg_enemies && (shipp->team != Player_ship->team))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// cannot be my ship or an instructor
|
||||
if ((objp == Player_obj) || is_instructor(objp))
|
||||
continue;
|
||||
|
||||
// ship must be a fighter/bomber
|
||||
if ( !(Ship_info[shipp->ship_info_index].is_fighter_bomber()) )
|
||||
if (!(Ship_info[shipp->ship_info_index].is_fighter_bomber()))
|
||||
continue;
|
||||
|
||||
// this ship satisfies everything
|
||||
return true;
|
||||
if (hud_squadmsg_ship_valid(shipp, objp))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -337,7 +322,7 @@ bool hud_squadmsg_ship_valid(ship *shipp, object *objp)
|
||||
if (!objp) // For simplicity's sake, allow it to be called with just the ship pointer
|
||||
objp = &Objects[shipp->objnum];
|
||||
|
||||
// ships must be able to receive a message
|
||||
// ships must be able to receive orders
|
||||
if ( Ship_info[shipp->ship_info_index].class_type < 0 || !(Ship_types[Ship_info[shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::AI_accept_player_orders]) )
|
||||
return false;
|
||||
|
||||
@@ -374,6 +359,10 @@ bool hud_squadmsg_ship_valid(ship *shipp, object *objp)
|
||||
return false;
|
||||
}
|
||||
|
||||
// maybe check comm system
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(shipp) != COMM_OK)
|
||||
return false;
|
||||
|
||||
// If we got to this point, the ship must be valid.
|
||||
return true;
|
||||
}
|
||||
@@ -420,14 +409,14 @@ int hud_squadmsg_count_ships(int add_to_menu)
|
||||
}
|
||||
|
||||
// routine to return true if a wing should be put onto the messaging menu
|
||||
int hud_squadmsg_wing_valid(wing *wingp)
|
||||
bool hud_squadmsg_wing_valid(wing *wingp)
|
||||
{
|
||||
int idx, ship_num;
|
||||
|
||||
// a couple of special cases to account for before adding to count (or to menu). Don't count
|
||||
// wings that are leaving or left.
|
||||
if ( wingp->flags[Ship::Wing_Flags::Gone, Ship::Wing_Flags::Departing] )
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
// Goober5000 - instead of checking wing leader, let's check all ships in wing;
|
||||
// there are several significant cases when e.g. wings contain ships of different IFFs
|
||||
@@ -436,34 +425,13 @@ int hud_squadmsg_wing_valid(wing *wingp)
|
||||
ship_num = wingp->ship_index[idx];
|
||||
Assert(ship_num >= 0);
|
||||
|
||||
// don't check player
|
||||
if (ship_num == SHIP_INDEX(Player_ship))
|
||||
continue;
|
||||
|
||||
// be sure ship is on same team
|
||||
#ifdef NDEBUG
|
||||
if (Ships[ship_num].team != Player_ship->team)
|
||||
continue;
|
||||
#else
|
||||
if (!Msg_enemies && (Ships[ship_num].team != Player_ship->team))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// check if ship is accepting orders
|
||||
if (Ships[ship_num].orders_accepted == 0)
|
||||
continue;
|
||||
|
||||
// if doing a message shortcut is being used, be sure the ship can "accept" the command.
|
||||
if ( Msg_shortcut_command >= 0 && !(Ships[ship_num].orders_accepted & Msg_shortcut_command) )
|
||||
return 0;
|
||||
|
||||
|
||||
// at least one ship in this wing is valid, and that's all we need
|
||||
return 1;
|
||||
// if at least one ship in this wing is valid, that's all we need
|
||||
if (hud_squadmsg_ship_valid(&Ships[ship_num]))
|
||||
return true;
|
||||
}
|
||||
|
||||
// no ships in the wing were valid
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// function like above, except for wings
|
||||
@@ -766,8 +734,8 @@ void hud_squadmsg_repair_rearm_abort( int toggle_state, object *obj)
|
||||
}
|
||||
|
||||
// Goober5000 - redone and added a bit
|
||||
// returns 1 if an order is valid for a ship. Applies to things like departure when engines are blown, etc.
|
||||
int hud_squadmsg_ship_order_valid( int shipnum, int order )
|
||||
// returns true if an order is valid for a ship. Applies to things like departure when engines are blown, etc.
|
||||
bool hud_squadmsg_ship_order_valid( int shipnum, int order )
|
||||
{
|
||||
// Goober5000
|
||||
Assert( shipnum >= 0 && shipnum < MAX_SHIPS );
|
||||
@@ -778,31 +746,24 @@ int hud_squadmsg_ship_order_valid( int shipnum, int order )
|
||||
case DEPART_ITEM:
|
||||
// disabled ships can't depart.
|
||||
if (shipp->flags[Ship::Ship_Flags::Disabled])
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
// Goober5000: also can't depart if no subspace drives and no valid mothership
|
||||
if (shipp->flags[Ship::Ship_Flags::No_subspace_drive])
|
||||
if ( !(ship_can_warp_full_check(shipp)) )
|
||||
{
|
||||
// check that we have a mothership and that we can depart to it
|
||||
if (shipp->departure_location == DEPART_AT_DOCK_BAY)
|
||||
{
|
||||
int anchor_shipnum = ship_name_lookup(Parse_names[shipp->departure_anchor]);
|
||||
if (anchor_shipnum >= 0 && ship_useful_for_departure(anchor_shipnum, shipp->departure_path_mask))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
// function accounts for ship's wing and returns true if ship has bay departure and mothership present, false otherwise
|
||||
return ship_can_bay_depart(shipp);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns true or false if the Players target is valid for the given order
|
||||
// find_order is true when we need to search the comm_orders array for the order entry. We have
|
||||
// to do this action in some cases since all we know is the actual "value" of the order
|
||||
int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip )
|
||||
bool hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip )
|
||||
{
|
||||
int target_objnum, i;
|
||||
ship *shipp, *ordering_shipp;
|
||||
@@ -823,22 +784,22 @@ int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip )
|
||||
|
||||
// orders which don't operate on targets are always valid
|
||||
if ( !(Comm_orders[order].item & TARGET_MESSAGES) )
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
target_objnum = aip->target_objnum;
|
||||
|
||||
// order isn't valid if there is no player target
|
||||
if ( target_objnum == -1 ) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
objp = &Objects[target_objnum];
|
||||
|
||||
ordering_shipp = &Ships[aip->shipnum];
|
||||
|
||||
// target isn't a ship, then return 0
|
||||
// target isn't a ship, then return false
|
||||
if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_WEAPON) )
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
// if it's a weapon, then it needs to be a WIF_BOMB weapon. Only attack order valid, and only
|
||||
// valid on bombs not on the player's team
|
||||
@@ -846,54 +807,54 @@ int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip )
|
||||
if ( objp->type == OBJ_WEAPON ) {
|
||||
|
||||
if (Weapons[objp->instance].lssm_stage==3){
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( (Comm_orders[order].item == ATTACK_TARGET_ITEM )
|
||||
&& ((Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) || (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Fighter_Interceptable]))
|
||||
&& (Weapons[objp->instance].team != ordering_shipp->team) )
|
||||
&& (Weapons[objp->instance].team != ordering_shipp->team) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
Assert( objp->type == OBJ_SHIP );
|
||||
|
||||
shipp = &Ships[objp->instance];
|
||||
|
||||
// if target is a navbouy, return 0
|
||||
// if target is a navbouy, return false
|
||||
if ( Ship_info[shipp->ship_info_index].flags[Ship::Info_Flags::Navbuoy] ){
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we are messaging a ship, and that ship is our target, no target type orders are ever active
|
||||
if ( (Squad_msg_mode == SM_MODE_SHIP_COMMAND) && (Objects[target_objnum].instance == Msg_instance) ){
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the order is a disable order or depart, and the ship is disabled, order isn't active
|
||||
if ( (Comm_orders[order].item == DISABLE_TARGET_ITEM) && (shipp->flags[Ship::Ship_Flags::Disabled]) ){
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// same as above except for disarmed.
|
||||
if ( (Comm_orders[order].item == DISARM_TARGET_ITEM) && ((shipp->subsys_info[SUBSYSTEM_TURRET].type_count == 0) || (shipp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f)) ){
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if order is disable subsystem, and no subsystem targeted or no hits, then order not valid
|
||||
if ( (Comm_orders[order].item == DISABLE_SUBSYSTEM_ITEM) && ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) ){
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check based on target's and player's team
|
||||
if ( (shipp->team == ordering_shipp->team) && (FRIENDLY_TARGET_MESSAGES & Comm_orders[order].item) ){
|
||||
return 1;
|
||||
return true;
|
||||
} else if ( (shipp->team != ordering_shipp->team) && (ENEMY_TARGET_MESSAGES & Comm_orders[order].item) ){
|
||||
return 1;
|
||||
return true;
|
||||
} else {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-13
@@ -3431,6 +3431,7 @@ void hud_show_targeting_gauges(float frametime)
|
||||
vec3d target_pos;
|
||||
|
||||
hud_show_hostile_triangle();
|
||||
hud_do_lock_indicators(frametime);
|
||||
|
||||
if (Player_ai->target_objnum == -1)
|
||||
return;
|
||||
@@ -3480,8 +3481,6 @@ void hud_show_targeting_gauges(float frametime)
|
||||
|
||||
// display the lock indicator
|
||||
if (!Player->target_is_dying) {
|
||||
hud_do_lock_indicator(frametime);
|
||||
|
||||
// update and render artillery
|
||||
hud_artillery_update();
|
||||
hud_artillery_render();
|
||||
@@ -3975,12 +3974,14 @@ void HudGaugeLeadIndicator::renderLeadCurrentTarget()
|
||||
}
|
||||
|
||||
frame_offset = pickFrame(prange, srange, dist_to_target);
|
||||
if ( frame_offset < 0 ) {
|
||||
if ( frame_offset < 0 && srange != -1.0f ) {
|
||||
return;
|
||||
}
|
||||
|
||||
hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
|
||||
renderIndicator(frame_offset, targetp, &lead_target_pos);
|
||||
if (frame_offset >= 0) {
|
||||
hud_calculate_lead_pos(&lead_target_pos, &target_pos, targetp, wip, dist_to_target);
|
||||
renderIndicator(frame_offset, targetp, &lead_target_pos);
|
||||
}
|
||||
|
||||
//do dumbfire lead indicator - color is orange (255,128,0) - bright, (192,96,0) - dim
|
||||
//phreak changed 9/01/02
|
||||
@@ -4556,7 +4557,7 @@ int hud_sensors_ok(ship *sp, int show_msg)
|
||||
}
|
||||
}
|
||||
|
||||
int hud_communications_state(ship *sp)
|
||||
int hud_communications_state(ship *sp, bool for_death_scream)
|
||||
{
|
||||
float str;
|
||||
|
||||
@@ -4570,11 +4571,7 @@ int hud_communications_state(ship *sp)
|
||||
if ((sp == Player_ship) && (sp->flags[Ship::Ship_Flags::Dying]))
|
||||
return COMM_OK;
|
||||
|
||||
// Goober5000 - check for scrambled communications
|
||||
if ( emp_active_local() || sp->flags[Ship::Ship_Flags::Scramble_messages] )
|
||||
return COMM_SCRAMBLED;
|
||||
|
||||
str = ship_get_subsystem_strength( sp, SUBSYSTEM_COMMUNICATION );
|
||||
str = ship_get_subsystem_strength( sp, SUBSYSTEM_COMMUNICATION, for_death_scream );
|
||||
|
||||
if ( (str <= 0.01) || ship_subsys_disrupted(sp, SUBSYSTEM_COMMUNICATION) ) {
|
||||
return COMM_DESTROYED;
|
||||
@@ -4582,6 +4579,10 @@ int hud_communications_state(ship *sp)
|
||||
return COMM_DAMAGED;
|
||||
}
|
||||
|
||||
// Goober5000 - check for scrambled communications
|
||||
if (emp_active_local() || sp->flags[Ship::Ship_Flags::Scramble_messages])
|
||||
return COMM_SCRAMBLED;
|
||||
|
||||
return COMM_OK;
|
||||
}
|
||||
|
||||
@@ -6074,7 +6075,10 @@ void HudGaugeWeapons::render(float /*frametime*/)
|
||||
renderPrintf(position[0] + Weapon_sunlinked_offset_x, name_y, EG_NULL, "%c", Weapon_link_icon);
|
||||
|
||||
// indicate if this is linked
|
||||
if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] ) {
|
||||
// don't draw the link indicator if the fire can't be fired link.
|
||||
// the link flag is ignored rather than cleared so the player can cycle past a no-doublefire weapon without the setting being cleared
|
||||
if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] && !wip->wi_flags[Weapon::Info_Flags::No_doublefire] &&
|
||||
!The_mission.ai_profile->flags[AI::Profile_Flags::Disable_player_secondary_doublefire] ) {
|
||||
renderPrintf(position[0] + Weapon_slinked_offset_x, name_y, EG_NULL, "%c", Weapon_link_icon);
|
||||
}
|
||||
|
||||
@@ -6992,7 +6996,10 @@ void HudGaugeSecondaryWeapons::render(float /*frametime*/)
|
||||
renderPrintf(position[0] + _sunlinked_offset_x, position[1] + text_y_offset, EG_NULL, "%c", Weapon_link_icon);
|
||||
|
||||
// indicate if this is linked
|
||||
if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] ) {
|
||||
// don't draw the link indicator if the fire can't be fired link.
|
||||
// the link flag is ignored rather than cleared so the player can cycle past a no-doublefire weapon without the setting being cleared
|
||||
if ( Player_ship->flags[Ship::Ship_Flags::Secondary_dual_fire] && !wip->wi_flags[Weapon::Info_Flags::No_doublefire] &&
|
||||
!The_mission.ai_profile->flags[AI::Profile_Flags::Disable_player_secondary_doublefire] ) {
|
||||
renderPrintf(position[0] + _slinked_offset_x, position[1] + text_y_offset, EG_NULL, "%c", Weapon_link_icon);
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ void hud_update_weapon_flash();
|
||||
void hud_process_homing_missiles(void);
|
||||
|
||||
int hud_sensors_ok(ship *sp, int show_msg = 1);
|
||||
int hud_communications_state(ship *sp);
|
||||
int hud_communications_state(ship *sp, bool for_death_scream = false);
|
||||
|
||||
int hud_get_best_primary_bank(float *range);
|
||||
void hud_target_toggle_hidden_from_sensors();
|
||||
|
||||
@@ -360,6 +360,23 @@ void iff_init()
|
||||
num_observed_colors[cur_iff]++;
|
||||
}
|
||||
|
||||
// get F3 override
|
||||
iff->hotkey_team = IFF_hotkey_team::Default;
|
||||
if (optional_string("+Hotkey Team:"))
|
||||
{
|
||||
char temp[NAME_LENGTH];
|
||||
stuff_string(temp, F_NAME, NAME_LENGTH);
|
||||
|
||||
if (!stricmp(temp, "Friendly"))
|
||||
iff->hotkey_team = IFF_hotkey_team::Friendly;
|
||||
else if (!stricmp(temp, "Hostile") || !stricmp(temp, "Enemy"))
|
||||
iff->hotkey_team = IFF_hotkey_team::Hostile;
|
||||
else if (!stricmp(temp, "None"))
|
||||
iff->hotkey_team = IFF_hotkey_team::None;
|
||||
else
|
||||
Warning(LOCATION, "Unrecognized +Hotkey Tean: %s\n", temp);
|
||||
}
|
||||
|
||||
|
||||
// get flags ----------------------------------------------------------
|
||||
|
||||
@@ -560,10 +577,8 @@ int iff_get_attacker_mask(int attackee_team)
|
||||
*
|
||||
* @param team_x Team of attacker
|
||||
* @param team_y Team of attackee
|
||||
*
|
||||
* @return >0 if true, 0 if false
|
||||
*/
|
||||
int iff_x_attacks_y(int team_x, int team_y)
|
||||
bool iff_x_attacks_y(int team_x, int team_y)
|
||||
{
|
||||
return iff_matches_mask(team_y, iff_get_attackee_mask(team_x));
|
||||
}
|
||||
@@ -583,12 +598,10 @@ int iff_get_mask(int team)
|
||||
*
|
||||
* @param team Team to test
|
||||
* @param mask Mask
|
||||
*
|
||||
* @return 1 if matches, 0 if does not match
|
||||
*/
|
||||
int iff_matches_mask(int team, int mask)
|
||||
bool iff_matches_mask(int team, int mask)
|
||||
{
|
||||
return (iff_get_mask(team) & mask) ? 1 : 0;
|
||||
return (iff_get_mask(team) & mask) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,8 @@ class object;
|
||||
#define IFF_COLOR_TAGGED 2
|
||||
#define MAX_IFF_COLORS (MAX_IFFS + 3)
|
||||
|
||||
enum IFF_hotkey_team { Default = -1, None = 0, Friendly, Hostile };
|
||||
|
||||
// iff flags
|
||||
#define IFFF_SUPPORT_ALLOWED (1 << 0) // this IFF can call for support
|
||||
#define IFFF_EXEMPT_FROM_ALL_TEAMS_AT_WAR (1 << 1) // self-explanatory
|
||||
@@ -41,6 +43,7 @@ typedef struct iff_info {
|
||||
int attackee_bitmask; // treat this as private and use iff_get_attackee_mask or iff_x_attacks_y
|
||||
int attackee_bitmask_all_teams_at_war; // treat this as private and use iff_get_attackee_mask or iff_x_attacks_y
|
||||
int observed_color_index[MAX_IFFS]; // treat this as private and use iff_get_color or iff_get_color_by_team
|
||||
IFF_hotkey_team hotkey_team;
|
||||
|
||||
// flags
|
||||
int flags;
|
||||
@@ -74,11 +77,11 @@ extern int iff_lookup(const char *iff_name);
|
||||
// If he fires at you, you don't react unless you are coded to attack him, because you are oblivious.
|
||||
extern int iff_get_attackee_mask(int attacker_team);
|
||||
extern int iff_get_attacker_mask(int attackee_team);
|
||||
extern int iff_x_attacks_y(int team_x, int team_y);
|
||||
extern bool iff_x_attacks_y(int team_x, int team_y);
|
||||
|
||||
// mask stuff
|
||||
extern int iff_get_mask(int team);
|
||||
extern int iff_matches_mask(int team, int mask);
|
||||
extern bool iff_matches_mask(int team, int mask);
|
||||
|
||||
// get color stuff
|
||||
extern color *iff_get_color(int color_index, int is_bright);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "io/joy.h"
|
||||
#include "io/timer.h"
|
||||
#include "ship/ship.h"
|
||||
#include "ship/ship_flags.h"
|
||||
#include "playerman/player.h"
|
||||
#include "weapon/weapon.h"
|
||||
#include "hud/hud.h"
|
||||
@@ -1095,6 +1096,57 @@ void process_debug_keys(int k)
|
||||
break;
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
case KEY_DEBUGGED + KEY_SHIFTED + KEY_R:
|
||||
case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_R:
|
||||
{
|
||||
// toggle support for this mission
|
||||
if (The_mission.support_ships.max_support_ships == 0)
|
||||
{
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Setting maximum number of support ships to infinite.", 1643));
|
||||
The_mission.support_ships.max_support_ships = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Setting maximum number of support ships to zero.", 1644));
|
||||
The_mission.support_ships.max_support_ships = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
case KEY_DEBUGGED + KEY_SHIFTED + KEY_U:
|
||||
case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_U:
|
||||
{
|
||||
// toggle the current target between scanned and unscanned
|
||||
if (Player_ai->target_objnum >= 0)
|
||||
{
|
||||
object *objp = &Objects[Player_ai->target_objnum];
|
||||
if (objp->type == OBJ_SHIP)
|
||||
{
|
||||
ship *targeted_shipp = &Ships[objp->instance];
|
||||
|
||||
if (Player_ai->targeted_subsys == nullptr)
|
||||
{
|
||||
if (targeted_shipp->flags[Ship::Ship_Flags::Cargo_revealed])
|
||||
ship_do_cargo_hidden(targeted_shipp);
|
||||
else
|
||||
ship_do_cargo_revealed(targeted_shipp);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Player_ai->targeted_subsys->flags[Ship::Subsystem_Flags::Cargo_revealed])
|
||||
ship_do_cap_subsys_cargo_hidden(targeted_shipp, Player_ai->targeted_subsys);
|
||||
else
|
||||
ship_do_cap_subsys_cargo_revealed(targeted_shipp, Player_ai->targeted_subsys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_DEBUGGED + KEY_SHIFTED + KEY_UP:
|
||||
Game_detail_level++;
|
||||
HUD_printf( XSTR( "Detail level set to %+d\n", 22), Game_detail_level );
|
||||
|
||||
+88
-3
@@ -25,9 +25,12 @@
|
||||
#include "nebula/neb.h"
|
||||
#include "object/objectsnd.h"
|
||||
#include "playerman/managepilot.h"
|
||||
#include "particle/particle.h"
|
||||
#include "render/3d.h"
|
||||
#include "render/batching.h"
|
||||
#include "ship/ship.h"
|
||||
#include "ship/shiphit.h"
|
||||
#include "debris/debris.h"
|
||||
#include "species_defs/species_defs.h"
|
||||
#include "starfield/nebula.h"
|
||||
#include "starfield/starfield.h"
|
||||
@@ -47,6 +50,7 @@
|
||||
#define LAB_FLAG_SHOW_WEAPONS (1 << 6) // determines if external weapons models are displayed
|
||||
#define LAB_FLAG_INITIAL_ROTATION (1 << 7) // initial rotation setting
|
||||
#define LAB_FLAG_DESTROYED_SUBSYSTEMS (1 << 8) // render model as if all subsystems are destroyed
|
||||
#define LAB_FLAG_SHIP_EXPLODING (1 << 9) // execute the ship kill stuff
|
||||
|
||||
// modes
|
||||
#define LAB_MODE_NONE 0 // not showing anything
|
||||
@@ -64,6 +68,7 @@ static Window* Lab_material_override_window = nullptr;
|
||||
static Window* Lab_variables_window = nullptr;
|
||||
static Window* Lab_description_window = nullptr;
|
||||
static Window* Lab_background_window = nullptr;
|
||||
static Window* Lab_actions_window = nullptr;
|
||||
static Text* Lab_description_text = nullptr;
|
||||
static TreeItem** Lab_species_nodes = nullptr;
|
||||
|
||||
@@ -349,7 +354,7 @@ void labviewer_render_model(float frametime)
|
||||
|
||||
if (dx || dy) {
|
||||
// Rotate the ship
|
||||
if (Trackball_mode == 1) {
|
||||
if (Trackball_mode == 1 && !(Lab_viewer_flags & LAB_FLAG_SHIP_EXPLODING)) {
|
||||
angles rot_angle;
|
||||
vm_extract_angles_matrix_alternate(&rot_angle, &Lab_model_orient);
|
||||
|
||||
@@ -436,10 +441,16 @@ void labviewer_render_model(float frametime)
|
||||
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_heightmap, Lab_Heightmap_override);
|
||||
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_miscmap, Lab_Miscmap_override);
|
||||
|
||||
ship_process_post(obj, frametime);
|
||||
ship_model_update_instance(obj);
|
||||
|
||||
Ships[obj->instance].team_name = Lab_team_color;
|
||||
|
||||
if (Lab_viewer_flags & LAB_FLAG_LIGHTNING_ARCS) {
|
||||
obj->hull_strength = 1.0f;
|
||||
}
|
||||
else {
|
||||
obj->hull_strength = Ship_info[Ships[obj->instance].ship_info_index].max_hull_strength;
|
||||
}
|
||||
}
|
||||
|
||||
if (Lab_render_wireframe)
|
||||
@@ -460,6 +471,11 @@ void labviewer_render_model(float frametime)
|
||||
Ships[obj->instance].flags.set(Ship::Ship_Flags::No_thrusters);
|
||||
}
|
||||
|
||||
obj_move_all(frametime);
|
||||
particle::move_all(frametime);
|
||||
particle::ParticleManager::get()->doFrame(frametime);
|
||||
shockwave_move_all(frametime);
|
||||
|
||||
Trail_render_override = true;
|
||||
game_render_frame(Lab_cam);
|
||||
Trail_render_override = false;
|
||||
@@ -1574,7 +1590,30 @@ void labviewer_change_ship_lod(Tree* caller)
|
||||
}
|
||||
|
||||
Lab_selected_object = ship_create(&vmd_identity_matrix, &Lab_model_pos, ship_index);
|
||||
Objects[Lab_selected_object].flags.set(Object::Object_Flags::Player_ship);
|
||||
auto ship_objp = &Ships[Objects[Lab_selected_object].instance];
|
||||
auto ship_infop = &Ship_info[ship_objp->ship_info_index];
|
||||
|
||||
// This is normally set during mission parse
|
||||
ship_objp->special_exp_damage = -1;
|
||||
|
||||
// If the ship class defines replacement textures, load them and apply them to the ship
|
||||
// load the texture
|
||||
auto replacements = SCP_vector<texture_replace>();
|
||||
for (auto tr : ship_infop->replacement_textures) {
|
||||
if (!stricmp(tr.new_texture, "invisible"))
|
||||
{
|
||||
// invisible is a special case
|
||||
tr.new_texture_id = REPLACE_WITH_INVISIBLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// try to load texture or anim as normal
|
||||
tr.new_texture_id = bm_load_either(tr.new_texture);
|
||||
}
|
||||
replacements.push_back(tr);
|
||||
}
|
||||
|
||||
ship_objp->apply_replacement_textures(replacements);
|
||||
|
||||
lab_cam_distance = Objects[Lab_selected_object].radius * 1.6f;
|
||||
|
||||
@@ -1599,6 +1638,8 @@ void labviewer_change_ship_lod(Tree* caller)
|
||||
labviewer_update_flags_window();
|
||||
labviewer_update_variables_window();
|
||||
labviewer_recalc_camera();
|
||||
|
||||
Lab_viewer_flags &= ~LAB_FLAG_SHIP_EXPLODING;
|
||||
}
|
||||
|
||||
void labviewer_change_ship(Tree* caller)
|
||||
@@ -2046,6 +2087,34 @@ void labviewer_make_background_window(Button* /*caller*/)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------- Actions window --------------------------------
|
||||
void lab_actions_window_close(GUIObject* /*caller*/) {
|
||||
Lab_actions_window = nullptr;
|
||||
}
|
||||
|
||||
void labviewer_actions_destroy_ship(Button* /*caller*/) {
|
||||
auto obj = &Objects[Lab_selected_object];
|
||||
|
||||
if (obj->type == OBJ_SHIP) {
|
||||
obj->flags.remove(Object::Object_Flags::Player_ship);
|
||||
ship_self_destruct(obj);
|
||||
Lab_viewer_flags |= LAB_FLAG_SHIP_EXPLODING;
|
||||
}
|
||||
}
|
||||
|
||||
void labviewer_make_actions_window(Button* /*caller*/)
|
||||
{
|
||||
if (Lab_actions_window != nullptr)
|
||||
return;
|
||||
|
||||
Lab_actions_window = (Window*)Lab_screen->Add(
|
||||
new Window("Actions", gr_screen.center_offset_x + 250, gr_screen.center_offset_y + 50)
|
||||
);
|
||||
Lab_actions_window->SetCloseFunction(lab_actions_window_close);
|
||||
|
||||
Lab_actions_window->AddChild(new Button("Destroy Ship", 0, 0, labviewer_actions_destroy_ship));
|
||||
}
|
||||
|
||||
// ----------------------------- Lab functions ---------------------------------
|
||||
bool is_same_obj_type(int curr, int next)
|
||||
{
|
||||
@@ -2178,6 +2247,9 @@ void lab_init()
|
||||
x += cbp->GetWidth() + 10;
|
||||
cbp = Lab_toolbar->AddChild(new Button("Backgrounds", x, 0, labviewer_make_background_window));
|
||||
|
||||
x += cbp->GetWidth() + 10;
|
||||
cbp = Lab_toolbar->AddChild(new Button("Actions", x, 0, labviewer_make_actions_window));
|
||||
|
||||
x += cbp->GetWidth() + 20;
|
||||
cbp = Lab_toolbar->AddChild(new Button("Exit", x, 0, labviewer_exit));
|
||||
|
||||
@@ -2247,6 +2319,15 @@ void lab_init()
|
||||
|
||||
Lab_selected_mission = "None";
|
||||
Lab_skybox_orientation = vmd_identity_matrix;
|
||||
|
||||
fireball_init();
|
||||
debris_init();
|
||||
extern void debris_page_in();
|
||||
debris_page_in();
|
||||
shockwave_level_init();
|
||||
particle::init();
|
||||
|
||||
ai_paused = 1;
|
||||
}
|
||||
|
||||
#include "lab.h"
|
||||
@@ -2500,4 +2581,8 @@ void lab_close()
|
||||
cam_delete(Lab_cam);
|
||||
|
||||
obj_init();
|
||||
|
||||
shockwave_level_close();
|
||||
|
||||
ai_paused = 0;
|
||||
}
|
||||
+156
-74
@@ -27,13 +27,13 @@
|
||||
// general language/localization data ---------------------
|
||||
|
||||
// current language
|
||||
int Lcl_current_lang = FS2_OPEN_DEFAULT_LANGUAGE;
|
||||
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
|
||||
|
||||
lang_info Lcl_builtin_languages[NUM_BUILTIN_LANGUAGES] = {
|
||||
{ "English", "", {127,0,176,0,0}, 589986744}, // English
|
||||
{ "English", "", {127,0,176,0,0}, 589986744}, // English ("" is correct; the game data files do not use a language extension for English)
|
||||
{ "German", "gr", {164,0,176,0,0}, -1132430286 }, // German
|
||||
{ "French", "fr", {164,0,176,0,0}, 0 }, // French
|
||||
{ "Polish", "pl", {127,0,176,0,0}, -1131728960}, // Polish
|
||||
@@ -46,7 +46,9 @@ int Lcl_special_chars;
|
||||
int Lcl_fr = 0;
|
||||
int Lcl_gr = 0;
|
||||
int Lcl_pl = 0;
|
||||
int Lcl_english = 1;
|
||||
int Lcl_en = 1;
|
||||
|
||||
bool *Lcl_unexpected_tstring_check = nullptr;
|
||||
|
||||
|
||||
// executable string localization data --------------------
|
||||
@@ -56,7 +58,7 @@ int Lcl_english = 1;
|
||||
// the english version (in the code) to a foreign version (in the table). Thus, if you
|
||||
// add a new string to the code, you must assign it a new index. Use the number below for
|
||||
// that index and increase the number below by one.
|
||||
#define XSTR_SIZE 1643
|
||||
#define XSTR_SIZE 1645
|
||||
|
||||
|
||||
// struct to allow for strings.tbl-determined x offset
|
||||
@@ -109,8 +111,8 @@ void lcl_init(int lang_init)
|
||||
// initialize encryption
|
||||
encrypt_init();
|
||||
|
||||
// setup English
|
||||
Lcl_languages.push_back(Lcl_builtin_languages[FS2_OPEN_DEFAULT_LANGUAGE]);
|
||||
// set up the first language (which should be English)
|
||||
Lcl_languages.push_back(Lcl_builtin_languages[0]);
|
||||
|
||||
// check string.tbl to see which languages we support
|
||||
try
|
||||
@@ -124,8 +126,8 @@ void lcl_init(int lang_init)
|
||||
|
||||
parse_modular_table(NOX("*-lcl.tbm"), parse_stringstbl_quick);
|
||||
|
||||
// if the only language we have at this point is English, we need to setup the builtin languages as we might be dealing with an old style strings.tbl
|
||||
// which doesn't support anything beyond the builtin languages. Note, we start at i = 1 because we added English above.
|
||||
// if we only have one language at this point, we need to setup the builtin languages as we might be dealing with an old style strings.tbl
|
||||
// which doesn't support anything beyond the builtin languages. Note, we start at i = 1 because we added the first language above.
|
||||
if ((int)Lcl_languages.size() == 1) {
|
||||
for (i=1; i<NUM_BUILTIN_LANGUAGES; i++) {
|
||||
Lcl_languages.push_back(Lcl_builtin_languages[i]);
|
||||
@@ -133,18 +135,12 @@ void lcl_init(int lang_init)
|
||||
}
|
||||
|
||||
// read the language from the registry
|
||||
if(lang_init < 0){
|
||||
if (lang_init < 0) {
|
||||
memset(lang_string, 0, 128);
|
||||
// default to DEFAULT_LANGUAGE (which should be English so we don't have to put German text
|
||||
// default to DEFAULT_LANGUAGE (which should be English so we don't have to put German text
|
||||
// in tstrings in the #default section)
|
||||
ret = os_config_read_string(NULL, "Language", Lcl_languages[FS2_OPEN_DEFAULT_LANGUAGE].lang_name);
|
||||
|
||||
if(ret == NULL){
|
||||
Error(LOCATION, "Default language not found.");
|
||||
}
|
||||
|
||||
strcpy_s(lang_string, ret);
|
||||
|
||||
ret = os_config_read_string(nullptr, "Language", Lcl_languages[LCL_DEFAULT].lang_name);
|
||||
strcpy_s(lang_string, ret);
|
||||
|
||||
// look it up
|
||||
lang = -1;
|
||||
@@ -155,13 +151,18 @@ void lcl_init(int lang_init)
|
||||
}
|
||||
}
|
||||
if(lang < 0){
|
||||
lang = 0;
|
||||
}
|
||||
lang = LCL_DEFAULT;
|
||||
}
|
||||
} else {
|
||||
Assert((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 untranslated unless we are specifically translating it
|
||||
if (lang == LCL_DEFAULT && !Use_tabled_strings_for_default_language) {
|
||||
lang = LCL_UNTRANSLATED;
|
||||
}
|
||||
|
||||
// set the language (this function takes care of setting up file pointers)
|
||||
lcl_set_language(lang);
|
||||
}
|
||||
@@ -250,14 +251,14 @@ 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_current_lang == FS2_OPEN_DEFAULT_LANGUAGE){
|
||||
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_current_lang].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 ) {
|
||||
mprintf(("Current language not found in %s\n", filename));
|
||||
mprintf(("Language tag %s not found in %s\n", language_tag, filename));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -482,46 +483,62 @@ void lcl_set_language(int lang)
|
||||
{
|
||||
Lcl_current_lang = lang;
|
||||
|
||||
nprintf(("General", "Setting language to %s\n", Lcl_languages[lang].lang_name));
|
||||
if (lang == LCL_UNTRANSLATED) {
|
||||
nprintf(("General", "Setting language to UNTRANSLATED\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));
|
||||
}
|
||||
|
||||
Assertion((Lcl_current_lang >= 0) && (Lcl_current_lang < (int)Lcl_languages.size()), "Attempt to set language to an invalid language");
|
||||
Assertion((lang >= 0) && (lang < (int)Lcl_languages.size()), "Attempt to set language to an invalid language");
|
||||
|
||||
// flag the proper language as being active
|
||||
Lcl_special_chars = Lcl_languages[Lcl_current_lang].special_char_indexes[0];
|
||||
Lcl_special_chars = Lcl_languages[lang].special_char_indexes[0];
|
||||
Lcl_fr = 0;
|
||||
Lcl_gr = 0;
|
||||
Lcl_pl = 0;
|
||||
Lcl_english = 0;
|
||||
if (!strcmp(Lcl_languages[Lcl_current_lang].lang_name, Lcl_builtin_languages[LCL_ENGLISH].lang_name)) {
|
||||
Lcl_english = 1;
|
||||
} else if (!strcmp(Lcl_languages[Lcl_current_lang].lang_name, Lcl_builtin_languages[LCL_FRENCH].lang_name)) {
|
||||
Lcl_en = 0;
|
||||
if (!strcmp(Lcl_languages[lang].lang_name, Lcl_builtin_languages[LCL_ENGLISH].lang_name)) {
|
||||
Lcl_en = 1;
|
||||
} else if (!strcmp(Lcl_languages[lang].lang_name, Lcl_builtin_languages[LCL_FRENCH].lang_name)) {
|
||||
Lcl_fr = 1;
|
||||
} else if (!strcmp(Lcl_languages[Lcl_current_lang].lang_name, Lcl_builtin_languages[LCL_GERMAN].lang_name)) {
|
||||
} else if (!strcmp(Lcl_languages[lang].lang_name, Lcl_builtin_languages[LCL_GERMAN].lang_name)) {
|
||||
Lcl_gr = 1;
|
||||
} else if (!strcmp(Lcl_languages[Lcl_current_lang].lang_name, Lcl_builtin_languages[LCL_POLISH].lang_name)) {
|
||||
} else if (!strcmp(Lcl_languages[lang].lang_name, Lcl_builtin_languages[LCL_POLISH].lang_name)) {
|
||||
Lcl_pl = 1;
|
||||
}
|
||||
}
|
||||
|
||||
ubyte lcl_get_font_index(int font_num)
|
||||
{
|
||||
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
|
||||
// we just return 0 to signify that there are no special characters in this font
|
||||
return 0;
|
||||
} else {
|
||||
Assertion((font_num >= 0) && (font_num < LCL_MAX_FONTS), "Passed an invalid font index");
|
||||
Assertion((Lcl_current_lang >= 0) && (Lcl_current_lang < (int)Lcl_languages.size()), "Current language is not valid, can't get font indexes");
|
||||
Assertion((lang >= 0) && (lang < (int)Lcl_languages.size()), "Current language is not valid, can't get font indexes");
|
||||
|
||||
return Lcl_languages[Lcl_current_lang].special_char_indexes[font_num];
|
||||
return Lcl_languages[lang].special_char_indexes[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_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[Lcl_current_lang].lang_ext) <= 0) {
|
||||
if (strlen(Lcl_languages[lang].lang_ext) <= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -542,7 +559,7 @@ int lcl_add_dir_to_path_with_filename(char *current_path, size_t path_max)
|
||||
}
|
||||
|
||||
// add extension
|
||||
strcat_s(current_path, path_max, Lcl_languages[Lcl_current_lang].lang_ext);
|
||||
strcat_s(current_path, path_max, Lcl_languages[lang].lang_ext);
|
||||
strcat_s(current_path, path_max, DIR_SEPARATOR_STR );
|
||||
|
||||
// copy rest of filename from temp
|
||||
@@ -626,15 +643,18 @@ void lcl_fred_replace_stuff(SCP_string &text)
|
||||
// XSTR("whee", 20)
|
||||
// and these should cover all the externalized string cases
|
||||
// fills in id if non-NULL. a value of -2 indicates it is not an external string
|
||||
void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
|
||||
// returns true if we were able to extract the XSTR elements (text_str and maybe id are populated)
|
||||
bool lcl_ext_localize_sub(const char *in, char *text_str, char *out, size_t max_len, int *id)
|
||||
{
|
||||
char text_str[PARSE_BUF_SIZE]="";
|
||||
int str_id;
|
||||
size_t str_len;
|
||||
|
||||
Assert(in);
|
||||
Assert(out);
|
||||
|
||||
// NOTE: "Token too long" warnings are disabled when Lcl_unexpected_tstring_check is active,
|
||||
// because in such cases we actually anticipate that the length might be exceeded.
|
||||
|
||||
// default (non-external string) value
|
||||
if (id != NULL) {
|
||||
*id = -2;
|
||||
@@ -644,7 +664,7 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
|
||||
|
||||
// if the string is < 9 chars, it can't be an XSTR("",) tag, so just copy it
|
||||
if (str_len < 9) {
|
||||
if (str_len > max_len)
|
||||
if (str_len > max_len && !Lcl_unexpected_tstring_check)
|
||||
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", in, str_len, max_len);
|
||||
|
||||
strncpy(out, in, max_len);
|
||||
@@ -652,13 +672,13 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
|
||||
if (id != NULL)
|
||||
*id = -2;
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise, check to see if it's an XSTR() tag
|
||||
if (strnicmp(in, "XSTR", 4) != 0) {
|
||||
// NOT an XSTR() tag
|
||||
if (str_len > max_len)
|
||||
if (str_len > max_len && !Lcl_unexpected_tstring_check)
|
||||
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", in, str_len, max_len);
|
||||
|
||||
strncpy(out, in, max_len);
|
||||
@@ -666,12 +686,12 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
|
||||
if (id != NULL)
|
||||
*id = -2;
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// at this point we _know_ its an XSTR() tag, so split off the strings and id sections
|
||||
if (!lcl_ext_get_text(in, text_str)) {
|
||||
if (str_len > max_len)
|
||||
if (str_len > max_len && !Lcl_unexpected_tstring_check)
|
||||
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", in, str_len, max_len);
|
||||
|
||||
strncpy(out, in, max_len);
|
||||
@@ -679,10 +699,10 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
|
||||
if (id != NULL)
|
||||
*id = -1;
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!lcl_ext_get_id(in, &str_id)) {
|
||||
if (str_len > max_len)
|
||||
if (str_len > max_len && !Lcl_unexpected_tstring_check)
|
||||
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", in, str_len, max_len);
|
||||
|
||||
strncpy(out, in, max_len);
|
||||
@@ -690,12 +710,12 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
|
||||
if (id != NULL)
|
||||
*id = -1;
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the localization file is not open, or we're running in the default language, return the original string
|
||||
if ( !Xstr_inited || (str_id < 0) || (Lcl_current_lang == FS2_OPEN_DEFAULT_LANGUAGE) ) {
|
||||
if ( strlen(text_str) > max_len )
|
||||
// if the localization file is not open, or there's no entry, return the original string
|
||||
if ( !Xstr_inited || (str_id < 0) ) {
|
||||
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);
|
||||
|
||||
strncpy(out, text_str, max_len);
|
||||
@@ -703,20 +723,20 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
|
||||
if (id != NULL)
|
||||
*id = str_id;
|
||||
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the string if it exists
|
||||
if (Lcl_ext_str.find(str_id) != Lcl_ext_str.end()) {
|
||||
// copy to the outgoing string
|
||||
if ( strlen(Lcl_ext_str[str_id]) > max_len )
|
||||
if ( strlen(Lcl_ext_str[str_id]) > max_len && !Lcl_unexpected_tstring_check )
|
||||
error_display(0, "Token too long: [%s]. Length = " SIZE_T_ARG ". Max is " SIZE_T_ARG ".\n", Lcl_ext_str[str_id], strlen(Lcl_ext_str[str_id]), max_len);
|
||||
|
||||
strncpy(out, Lcl_ext_str[str_id], max_len);
|
||||
}
|
||||
// otherwise use what we have - probably should Int3() or assert here
|
||||
else {
|
||||
if ( strlen(text_str) > max_len )
|
||||
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);
|
||||
|
||||
strncpy(out, text_str, max_len);
|
||||
@@ -726,12 +746,13 @@ void lcl_ext_localize_sub(const char *in, char *out, size_t max_len, int *id)
|
||||
if (id != NULL) {
|
||||
*id = str_id;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ditto for SCP_string
|
||||
void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
|
||||
bool lcl_ext_localize_sub(const SCP_string &in, SCP_string &text_str, SCP_string &out, int *id)
|
||||
{
|
||||
SCP_string text_str = "";
|
||||
int str_id;
|
||||
|
||||
// default (non-external string) value
|
||||
@@ -746,7 +767,7 @@ void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
|
||||
if (id != NULL)
|
||||
*id = -2;
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise, check to see if it's an XSTR() tag
|
||||
@@ -757,7 +778,7 @@ void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
|
||||
if (id != NULL)
|
||||
*id = -2;
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// at this point we _know_ its an XSTR() tag, so split off the strings and id sections
|
||||
@@ -767,7 +788,7 @@ void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
|
||||
if (id != NULL)
|
||||
*id = -1;
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!lcl_ext_get_id(in, &str_id)) {
|
||||
out = in;
|
||||
@@ -775,17 +796,17 @@ void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
|
||||
if (id != NULL)
|
||||
*id = -1;
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the localization file is not open, or we're running in the default language, return the original string
|
||||
if ( !Xstr_inited || (str_id < 0) || (Lcl_current_lang == FS2_OPEN_DEFAULT_LANGUAGE) ) {
|
||||
// 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) ) {
|
||||
out = text_str;
|
||||
|
||||
if (id != NULL)
|
||||
*id = str_id;
|
||||
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the string if it exists
|
||||
@@ -802,15 +823,43 @@ void lcl_ext_localize_sub(const SCP_string &in, SCP_string &out, int *id)
|
||||
if (id != NULL){
|
||||
*id = str_id;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Goober5000 - wrapper for lcl_ext_localize_sub; used because lcl_replace_stuff has to
|
||||
// be called *after* the translation is done, and the original function returned in so
|
||||
// many places that it would be messy to call lcl_replace_stuff everywhere
|
||||
// Addendum: Now, of course, it provides a handy way to encapsulate the unexpected tstring check.
|
||||
void lcl_ext_localize(const char *in, char *out, size_t max_len, int *id)
|
||||
{
|
||||
// do XSTR translation
|
||||
lcl_ext_localize_sub(in, out, max_len, id);
|
||||
// buffer for the untranslated string inside the XSTR tag
|
||||
char text_str[PARSE_BUF_SIZE] = "";
|
||||
|
||||
// if we're doing this extra check, then we have to compare the untranslated string with the default language string and see if they're different
|
||||
if (Lcl_unexpected_tstring_check)
|
||||
{
|
||||
int saved_language = Lcl_current_lang;
|
||||
Lcl_current_lang = LCL_DEFAULT;
|
||||
bool extracted = lcl_ext_localize_sub(in, text_str, out, max_len, id);
|
||||
|
||||
// the untranslated and default-translated strings should always be identical, so if they're different, it might mean we have some data from a different mod
|
||||
if (extracted && strcmp(text_str, out) != 0)
|
||||
*Lcl_unexpected_tstring_check = true;
|
||||
|
||||
// at this point, we go back to our usual language and do the translation for real
|
||||
if (saved_language != Lcl_current_lang)
|
||||
{
|
||||
Lcl_current_lang = saved_language;
|
||||
lcl_ext_localize_sub(in, text_str, out, max_len, id);
|
||||
}
|
||||
}
|
||||
// most of the time we're not going to do the check, so localize as normal
|
||||
else
|
||||
{
|
||||
// do XSTR translation
|
||||
lcl_ext_localize_sub(in, text_str, out, max_len, id);
|
||||
}
|
||||
|
||||
// do translation of $callsign, $rank, etc.
|
||||
lcl_replace_stuff(out, max_len);
|
||||
@@ -819,8 +868,33 @@ void lcl_ext_localize(const char *in, char *out, size_t max_len, int *id)
|
||||
// ditto for SCP_string
|
||||
void lcl_ext_localize(const SCP_string &in, SCP_string &out, int *id)
|
||||
{
|
||||
// do XSTR translation
|
||||
lcl_ext_localize_sub(in, out, id);
|
||||
// buffer for the untranslated string inside the XSTR tag
|
||||
SCP_string text_str = "";
|
||||
|
||||
// if we're doing this extra check, then we have to compare the untranslated string with the default language string and see if they're different
|
||||
if (Lcl_unexpected_tstring_check)
|
||||
{
|
||||
int saved_language = Lcl_current_lang;
|
||||
Lcl_current_lang = LCL_DEFAULT;
|
||||
bool extracted = lcl_ext_localize_sub(in, text_str, out, id);
|
||||
|
||||
// the untranslated and default-translated strings should always be identical, so if they're different, it might mean we have some data from a different mod
|
||||
if (extracted && text_str != out)
|
||||
*Lcl_unexpected_tstring_check = true;
|
||||
|
||||
// at this point, we go back to our usual language and do the translation for real
|
||||
if (saved_language != Lcl_current_lang)
|
||||
{
|
||||
Lcl_current_lang = saved_language;
|
||||
lcl_ext_localize_sub(in, text_str, out, id);
|
||||
}
|
||||
}
|
||||
// most of the time we're not going to do the check, so localize as normal
|
||||
else
|
||||
{
|
||||
// do XSTR translation
|
||||
lcl_ext_localize_sub(in, text_str, out, id);
|
||||
}
|
||||
|
||||
// do translation of $callsign, $rank, etc.
|
||||
lcl_replace_stuff(out);
|
||||
@@ -835,15 +909,20 @@ const char *XSTR(const char *str, int index)
|
||||
return str;
|
||||
}
|
||||
|
||||
// perform a lookup
|
||||
if (index >= 0 && index < XSTR_SIZE)
|
||||
if (Lcl_current_lang != LCL_UNTRANSLATED)
|
||||
{
|
||||
// return translation of string
|
||||
if (Xstr_table[index].str)
|
||||
return Xstr_table[index].str;
|
||||
// perform a lookup
|
||||
if (index >= 0 && index < XSTR_SIZE)
|
||||
{
|
||||
// return translation of string
|
||||
if (Xstr_table[index].str)
|
||||
return Xstr_table[index].str;
|
||||
else
|
||||
mprintf(("No XSTR entry in strings.tbl for index %d\n", index));
|
||||
}
|
||||
}
|
||||
|
||||
// can't translate; return original english string
|
||||
// can't translate; return original English string
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -1092,9 +1171,12 @@ int lcl_ext_get_id(const SCP_string &xstr, int *out)
|
||||
|
||||
void lcl_get_language_name(char *lang_name)
|
||||
{
|
||||
Assert(Lcl_current_lang < (int)Lcl_languages.size());
|
||||
int lang = Lcl_current_lang;
|
||||
if (Lcl_current_lang == LCL_UNTRANSLATED)
|
||||
lang = LCL_DEFAULT;
|
||||
|
||||
strcpy(lang_name, Lcl_languages[Lcl_current_lang].lang_name);
|
||||
Assert(lang >= 0 && lang < (int)Lcl_languages.size());
|
||||
strcpy(lang_name, Lcl_languages[lang].lang_name);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
// LOCALIZE DEFINES/VARS
|
||||
//
|
||||
|
||||
// language defines
|
||||
// language defines (English should always be index 0)
|
||||
#define LCL_ENGLISH 0
|
||||
#define LCL_GERMAN 1
|
||||
#define LCL_FRENCH 2
|
||||
#define LCL_POLISH 3
|
||||
|
||||
#define FS2_OPEN_DEFAULT_LANGUAGE 0
|
||||
#define LCL_UNTRANSLATED 10 // this should be higher than the highest builtin language
|
||||
#define LCL_DEFAULT 0
|
||||
|
||||
// for language name strings
|
||||
#define LCL_LANG_NAME_LEN 32
|
||||
@@ -56,11 +57,14 @@ extern int Lcl_special_chars;
|
||||
extern int Lcl_fr;
|
||||
extern int Lcl_gr;
|
||||
extern int Lcl_pl;
|
||||
extern int Lcl_english;
|
||||
extern int Lcl_en;
|
||||
|
||||
// The currently active language. Index into Lcl_languages.
|
||||
extern int Lcl_current_lang;
|
||||
|
||||
// special check for misplaced mod data (see Mantis #2942)
|
||||
extern bool *Lcl_unexpected_tstring_check;
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
// LOCALIZE FUNCTIONS
|
||||
@@ -110,8 +114,8 @@ void lcl_fred_replace_stuff(SCP_string &text);
|
||||
// XSTR("whee", 20)
|
||||
// and these should cover all the externalized string cases
|
||||
// fills in id if non-NULL. a value of -2 indicates it is not an external string
|
||||
void lcl_ext_localize(const char *in, char *out, size_t max_len, int *id = NULL);
|
||||
void lcl_ext_localize(const SCP_string &in, SCP_string &out, int *id = NULL);
|
||||
void lcl_ext_localize(const char *in, char *out, size_t max_len, int *id = nullptr);
|
||||
void lcl_ext_localize(const SCP_string &in, SCP_string &out, int *id = nullptr);
|
||||
|
||||
// translate the specified string based upon the current language
|
||||
const char *XSTR(const char *str, int index);
|
||||
|
||||
@@ -450,6 +450,7 @@ int build_standalone_mission_list_do_frame()
|
||||
int font_height = gr_get_font_height();
|
||||
char filename[MAX_FILENAME_LEN];
|
||||
char str[256];
|
||||
bool lcl_weirdness = false;
|
||||
|
||||
// When no standalone missions in data directory
|
||||
if (Num_standalone_missions == 0) {
|
||||
@@ -473,8 +474,16 @@ int build_standalone_mission_list_do_frame()
|
||||
// tack on an extension
|
||||
strcat_s(filename, FS_MISSION_FILE_EXT);
|
||||
|
||||
// check if we can list the mission and if loading basic info didn't return an error code
|
||||
if (!mission_is_ignored(filename) && !get_mission_info(filename)) {
|
||||
// activate tstrings check
|
||||
Lcl_unexpected_tstring_check = &lcl_weirdness;
|
||||
|
||||
// check if we can list the mission, if loading basic info didn't return an error code, and if we didn't find an unexpected XSTR mismatch
|
||||
bool condition = !mission_is_ignored(filename) && !get_mission_info(filename) && !lcl_weirdness;
|
||||
|
||||
// deactivate tstrings check
|
||||
Lcl_unexpected_tstring_check = nullptr;
|
||||
|
||||
if (condition) {
|
||||
Standalone_mission_names[Num_standalone_missions_with_info] = vm_strdup(The_mission.name);
|
||||
Standalone_mission_flags[Num_standalone_missions_with_info] = The_mission.game_type;
|
||||
int y = Num_lines * (font_height + 2);
|
||||
@@ -1099,7 +1108,17 @@ void sim_room_init()
|
||||
memset(wild_card, 0, 256);
|
||||
strcpy_s(wild_card, NOX("*"));
|
||||
strcat_s(wild_card, FS_MISSION_FILE_EXT);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Activate the check while we fill the mission list so that we don't potentially end up with dozens of string length warnings.
|
||||
// We don't actually remove mismatched missions from the list here; that will happen in build_standalone_mission_list_do_frame().
|
||||
bool dummy_buffer;
|
||||
Lcl_unexpected_tstring_check = &dummy_buffer;
|
||||
#endif
|
||||
Num_standalone_missions = cf_get_file_list(MAX_MISSIONS, Mission_filenames, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME);
|
||||
#ifndef NDEBUG
|
||||
Lcl_unexpected_tstring_check = nullptr;
|
||||
#endif
|
||||
|
||||
// set up slider with 0 items to start
|
||||
Sim_room_slider.create(&Ui_window, Sim_room_slider_coords[gr_screen.res][X_COORD], Sim_room_slider_coords[gr_screen.res][Y_COORD], Sim_room_slider_coords[gr_screen.res][W_COORD], Sim_room_slider_coords[gr_screen.res][H_COORD], 0, Sim_room_slider_filename[gr_screen.res], &sim_room_scroll_screen_up, &sim_room_scroll_screen_down, &sim_room_scroll_capture);
|
||||
|
||||
@@ -569,63 +569,88 @@ int hotkey_build_team_listing(int enemy_team_mask, int y, bool list_enemies)
|
||||
const char *str = NULL;
|
||||
int i, j, s, z, start, team_mask;
|
||||
int font_height = gr_get_font_height();
|
||||
IFF_hotkey_team hotkey_team;
|
||||
|
||||
if (list_enemies)
|
||||
{
|
||||
str = XSTR( "Enemy ships", 403);
|
||||
team_mask = enemy_team_mask;
|
||||
hotkey_team = IFF_hotkey_team::Hostile;
|
||||
}
|
||||
else
|
||||
{
|
||||
str = XSTR( "Friendly ships", 402);
|
||||
team_mask = ~enemy_team_mask;
|
||||
hotkey_team = IFF_hotkey_team::Friendly;
|
||||
}
|
||||
|
||||
for (i=0; i<MAX_SHIPS; i++)
|
||||
if (iff_matches_mask(Ships[i].team, team_mask))
|
||||
break;
|
||||
|
||||
if (i >= MAX_SHIPS)
|
||||
return y;
|
||||
|
||||
hotkey_line_add(str, HOTKEY_LINE_HEADING, 0, y);
|
||||
y += 2;
|
||||
|
||||
start = Num_lines;
|
||||
|
||||
for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
|
||||
int shipnum;
|
||||
bool add_it;
|
||||
|
||||
// don't process non-ships, or the player ship
|
||||
if ( (Game_mode & GM_NORMAL) && (so->objnum == OBJ_INDEX(Player_obj)) )
|
||||
continue;
|
||||
|
||||
shipnum = Objects[so->objnum].instance;
|
||||
int shipnum = Objects[so->objnum].instance;
|
||||
ship *shipp = &Ships[shipnum];
|
||||
|
||||
// filter out ships in wings
|
||||
if ( shipp->wingnum >= 0 )
|
||||
continue;
|
||||
|
||||
// filter out cargo containers, navbouys, etc, and non-ships
|
||||
if ( Ship_info[Ships[shipnum].ship_info_index].class_type < 0 || !(Ship_types[Ship_info[Ships[shipnum].ship_info_index].class_type].flags[Ship::Type_Info_Flags::Hotkey_on_list] ))
|
||||
if ( Ship_info[shipp->ship_info_index].class_type < 0 || !(Ship_types[Ship_info[shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::Hotkey_on_list]) )
|
||||
continue;
|
||||
|
||||
// don't process ships invisible to sensors, dying or departing
|
||||
if ( Ships[shipnum].flags[Ship::Ship_Flags::Hidden_from_sensors] || Ships[shipnum].is_dying_or_departing() )
|
||||
if ( shipp->flags[Ship::Ship_Flags::Hidden_from_sensors] || shipp->is_dying_or_departing() )
|
||||
continue;
|
||||
|
||||
// if a ship's hotkey is the last hotkey on the list, then maybe make the hotkey -1 if
|
||||
// we are now in mission. Otherwise, skip this ship
|
||||
if ( Ships[shipnum].hotkey == MAX_KEYED_TARGETS ) {
|
||||
if ( shipp->hotkey == MAX_KEYED_TARGETS ) {
|
||||
if ( !(Game_mode & GM_IN_MISSION) )
|
||||
continue; // skip to next ship
|
||||
Ships[shipnum].hotkey = -1;
|
||||
shipp->hotkey = -1;
|
||||
}
|
||||
|
||||
// be sure this ship isn't in a wing, and that the teams match
|
||||
if ( iff_matches_mask(Ships[shipnum].team, team_mask) && (Ships[shipnum].wingnum < 0) ) {
|
||||
hotkey_line_add_sorted(Ships[shipnum].get_display_string(), HOTKEY_LINE_SHIP, shipnum, start);
|
||||
// check IFF override and team mask
|
||||
if (Iff_info[shipp->team].hotkey_team == IFF_hotkey_team::Default) {
|
||||
add_it = iff_matches_mask(shipp->team, team_mask);
|
||||
} else {
|
||||
add_it = (Iff_info[shipp->team].hotkey_team == hotkey_team);
|
||||
}
|
||||
|
||||
// add it if the teams match or if the IFF says to
|
||||
if (add_it) {
|
||||
hotkey_line_add_sorted(shipp->get_display_string(), HOTKEY_LINE_SHIP, shipnum, start);
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<Num_wings; i++) {
|
||||
if (Wings[i].current_count && (Wings[i].ship_index[Wings[i].special_ship] >= 0) && iff_matches_mask(Ships[Wings[i].ship_index[Wings[i].special_ship]].team, team_mask)) {
|
||||
bool add_it;
|
||||
|
||||
// the wing has to be valid
|
||||
if (Wings[i].current_count && Wings[i].ship_index[Wings[i].special_ship] >= 0) {
|
||||
ship *shipp = &Ships[Wings[i].ship_index[Wings[i].special_ship]];
|
||||
|
||||
// check IFF override and team mask
|
||||
if (Iff_info[shipp->team].hotkey_team == IFF_hotkey_team::Default) {
|
||||
add_it = iff_matches_mask(shipp->team, team_mask);
|
||||
} else {
|
||||
add_it = (Iff_info[shipp->team].hotkey_team == hotkey_team);
|
||||
}
|
||||
} else {
|
||||
add_it = false;
|
||||
}
|
||||
|
||||
// add it if the teams match or if the IFF says to
|
||||
if (add_it) {
|
||||
// special check for the player's wing. If he's in a wing, and the only guy left, don't
|
||||
// do anything
|
||||
if ( (Player_ship->wingnum == i) && (Wings[i].current_count == 1) )
|
||||
@@ -652,12 +677,21 @@ int hotkey_build_team_listing(int enemy_team_mask, int y, bool list_enemies)
|
||||
if (Wings[i].flags[Ship::Wing_Flags::Expanded]) {
|
||||
for (j=0; j<Wings[i].current_count; j++) {
|
||||
s = Wings[i].ship_index[j];
|
||||
z = hotkey_line_insert(z + 1, Ships[s].get_display_string(), HOTKEY_LINE_SUBSHIP, s);
|
||||
if (!Ships[s].is_dying_or_departing()) {
|
||||
z = hotkey_line_insert(z + 1, Ships[s].get_display_string(), HOTKEY_LINE_SUBSHIP, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// see if we actually added any lines
|
||||
if (start == Num_lines) {
|
||||
// roll back the heading, then return as if nothing happened
|
||||
Num_lines--;
|
||||
return y - 2;
|
||||
}
|
||||
|
||||
for (i=start; i<Num_lines; i++) {
|
||||
if (Hotkey_lines[i].type == HOTKEY_LINE_SUBSHIP)
|
||||
y += font_height;
|
||||
|
||||
@@ -233,8 +233,8 @@ const auto OnMessageReceivedHook = scripting::Hook::Factory(
|
||||
});
|
||||
|
||||
// forward declarations
|
||||
void message_maybe_distort_text(char *text, int shipnum);
|
||||
int comm_between_player_and_ship(int other_shipnum);
|
||||
void message_maybe_distort_text(char *text, int shipnum, bool for_death_scream);
|
||||
int comm_between_player_and_ship(int other_shipnum, bool for_death_scream);
|
||||
|
||||
// following functions to parse messages.tbl -- code pretty much ripped from weapon/ship table parsing code
|
||||
|
||||
@@ -625,6 +625,7 @@ void parse_msgtbl()
|
||||
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");
|
||||
@@ -864,9 +865,8 @@ void message_kill_all( int kill_all )
|
||||
|
||||
if ( kill_all ) {
|
||||
Num_messages_playing = 0;
|
||||
fsspeech_stop();
|
||||
}
|
||||
|
||||
fsspeech_stop();
|
||||
}
|
||||
|
||||
// function to kill nth playing message
|
||||
@@ -1145,7 +1145,7 @@ void message_calc_anim_start_frame(int time, generic_anim *ani, int reverse)
|
||||
}
|
||||
|
||||
if ( start_frame < 0 ) {
|
||||
Int3();
|
||||
mprintf(("Calculated start frame for animation %s was less than 0, setting to 0.", ani->filename));
|
||||
start_frame=0;
|
||||
}
|
||||
|
||||
@@ -1450,6 +1450,47 @@ void message_queue_process()
|
||||
Assert ( q->priority != -1 );
|
||||
Assert ( q->time_added != -1 );
|
||||
|
||||
int provisional_message_shipnum = -1;
|
||||
|
||||
// Do some checks to see if we are actually going to play this message at all. These checks have been moved above the
|
||||
// "if ( Num_messages_playing )" block because we don't want to cancel an existing message if we have nothing to replace it with.
|
||||
|
||||
// Goober5000 - argh, don't conflate special sources with ships!
|
||||
// NOTA BENE: don't check for != HUD_SOURCE_TERRAN_CMD, because with the new command persona code, Command could be a ship
|
||||
if ( q->source != HUD_SOURCE_IMPORTANT ) {
|
||||
provisional_message_shipnum = ship_name_lookup( q->who_from );
|
||||
|
||||
// see if we need to check if sending ship is alive
|
||||
if ( (provisional_message_shipnum < 0) && (q->flags & MQF_CHECK_ALIVE) ) {
|
||||
goto all_done;
|
||||
}
|
||||
}
|
||||
|
||||
// AL: added 07/14/97.. only play avi/sound if in gameplay
|
||||
if ( gameseq_get_state() != GS_STATE_GAME_PLAY )
|
||||
goto all_done;
|
||||
|
||||
// AL 4-7-98: Can't receive messages if comm is destroyed
|
||||
if ( hud_communications_state(Player_ship) == COMM_DESTROYED ) {
|
||||
goto all_done;
|
||||
}
|
||||
// G5K 4-26-20: Can't send messages if comm is destroyed
|
||||
if ( The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(&Ships[provisional_message_shipnum], (q->builtin_type == MESSAGE_WINGMAN_SCREAM)) == COMM_DESTROYED ) {
|
||||
goto all_done;
|
||||
}
|
||||
|
||||
// Don't play death scream unless a small ship.
|
||||
if ( q->builtin_type == MESSAGE_WINGMAN_SCREAM ) {
|
||||
if (provisional_message_shipnum < 0) {
|
||||
goto all_done;
|
||||
}
|
||||
if (!(Ship_info[Ships[provisional_message_shipnum].ship_info_index].is_small_ship() || (Ships[provisional_message_shipnum].flags[Ship::Ship_Flags::Always_death_scream])) ) {
|
||||
goto all_done;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we think we're going to play the queued message.
|
||||
|
||||
if ( Num_messages_playing ) {
|
||||
// peek at the first message on the queue to see if it should interrupt, or overlap a currently
|
||||
// playing message. Mission specific messages will always interrupt builtin messages. They
|
||||
@@ -1509,30 +1550,23 @@ void message_queue_process()
|
||||
if ( Num_messages_playing == MAX_PLAYING_MESSAGES )
|
||||
return;
|
||||
|
||||
// Goober5000 - argh, don't conflate special sources with ships!
|
||||
// NOTA BENE: don't check for != HUD_SOURCE_TERRAN_CMD, because with the new command persona code, Command could be a ship
|
||||
if ( q->source != HUD_SOURCE_IMPORTANT ) {
|
||||
Message_shipnum = ship_name_lookup( q->who_from );
|
||||
|
||||
// see if we need to check if sending ship is alive
|
||||
if ( (Message_shipnum < 0) && (q->flags & MQF_CHECK_ALIVE) ) {
|
||||
goto all_done;
|
||||
}
|
||||
}
|
||||
|
||||
// if this is a ship, then don't play anything if this ship is already talking
|
||||
if ( Message_shipnum != -1 ) {
|
||||
if ( provisional_message_shipnum != -1 ) {
|
||||
for ( i = 0; i < Num_messages_playing; i++ ) {
|
||||
if ( (Playing_messages[i].shipnum != -1) && (Playing_messages[i].shipnum == Message_shipnum) ){
|
||||
if ( (Playing_messages[i].shipnum != -1) && (Playing_messages[i].shipnum == provisional_message_shipnum) ){
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we are definitely going to play the queued message.
|
||||
|
||||
Message_shipnum = provisional_message_shipnum;
|
||||
|
||||
// set up module globals for this message
|
||||
m = &Messages[q->message_num];
|
||||
Playing_messages[Num_messages_playing].anim_data = NULL;
|
||||
Playing_messages[Num_messages_playing].wave = sound_handle::invalid();
|
||||
Playing_messages[Num_messages_playing].wave = sound_handle::invalid();
|
||||
Playing_messages[Num_messages_playing].id = q->message_num;
|
||||
Playing_messages[Num_messages_playing].priority = q->priority;
|
||||
Playing_messages[Num_messages_playing].shipnum = Message_shipnum;
|
||||
@@ -1547,24 +1581,6 @@ void message_queue_process()
|
||||
message_translate_tokens(buf, q->special_message);
|
||||
|
||||
Message_expire = timestamp(static_cast<int>(42 * strlen(buf)));
|
||||
// AL: added 07/14/97.. only play avi/sound if in gameplay
|
||||
if ( gameseq_get_state() != GS_STATE_GAME_PLAY )
|
||||
goto all_done;
|
||||
|
||||
// AL 4-7-98: Can't receive messages if comm is destroyed
|
||||
if ( hud_communications_state(Player_ship) == COMM_DESTROYED ) {
|
||||
goto all_done;
|
||||
}
|
||||
|
||||
// Don't play death scream unless a small ship.
|
||||
if ( q->builtin_type == MESSAGE_WINGMAN_SCREAM ) {
|
||||
if (Message_shipnum < 0) {
|
||||
goto all_done;
|
||||
}
|
||||
if (!(Ship_info[Ships[Message_shipnum].ship_info_index].is_small_ship() || (Ships[Message_shipnum].flags[Ship::Ship_Flags::Always_death_scream])) ) {
|
||||
goto all_done;
|
||||
}
|
||||
}
|
||||
|
||||
// play wave first, since need to know duration for picking anim start frame
|
||||
if(message_play_wave(q) == false) {
|
||||
@@ -1575,7 +1591,7 @@ void message_queue_process()
|
||||
message_play_anim(q);
|
||||
|
||||
// distort the message if comms system is damaged
|
||||
message_maybe_distort_text(buf, Message_shipnum);
|
||||
message_maybe_distort_text(buf, Message_shipnum, (q->builtin_type == MESSAGE_WINGMAN_SCREAM));
|
||||
|
||||
#ifndef NDEBUG
|
||||
// debug only -- if the message is a builtin message, put in parens whether or not the voice played
|
||||
@@ -1620,8 +1636,8 @@ void message_queue_process()
|
||||
scripting::hook_param("Builtin", 'b', builtinMessage),
|
||||
scripting::hook_param("Sender", 'o', sender)));
|
||||
|
||||
Num_messages_playing++; // this has to be done at the end because some sound functions use it to index into the array
|
||||
all_done:
|
||||
Num_messages_playing++;
|
||||
message_remove_from_queue(q);
|
||||
}
|
||||
|
||||
@@ -2195,7 +2211,7 @@ void message_maybe_distort()
|
||||
|
||||
was_muted = 0;
|
||||
|
||||
if ( comm_between_player_and_ship(Playing_messages[i].shipnum) != COMM_OK) {
|
||||
if ( comm_between_player_and_ship(Playing_messages[i].shipnum, Playing_messages[i].builtin_type == MESSAGE_WINGMAN_SCREAM) != COMM_OK ) {
|
||||
was_muted = Message_wave_muted;
|
||||
if ( timestamp_elapsed(Next_mute_time) ) {
|
||||
Next_mute_time = fl2i(Distort_patterns[Distort_num][Distort_next++] * Message_wave_duration);
|
||||
@@ -2226,11 +2242,11 @@ void message_maybe_distort()
|
||||
// Blank out portions of the sound based on Distort_num, this this is that same
|
||||
// data that will be used to blank out portions of the audio playback
|
||||
//
|
||||
void message_maybe_distort_text(char *text, int shipnum)
|
||||
void message_maybe_distort_text(char *text, int shipnum, bool for_death_scream)
|
||||
{
|
||||
int voice_duration;
|
||||
|
||||
if (comm_between_player_and_ship(shipnum) == COMM_OK) {
|
||||
if (comm_between_player_and_ship(shipnum, for_death_scream) == COMM_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2371,31 +2387,28 @@ bool change_message(const char* name, const char* message, int persona_index, in
|
||||
}
|
||||
|
||||
/**
|
||||
* Ideally, this would return the minimum of the comm state between the player and the other ship. In practice, retail has no checks whatsoever on a ship's ability
|
||||
* to send messages unless that ship is the player, so such a change would require an AI profiles option and we must default to the player's state. However, we
|
||||
* have a bit of wiggle room with COMM_SCRAMBLED, because EMP effects are either transient or set by the newly enhanced scramble-messages SEXP. Thus any comm
|
||||
* dropout does not cause an unanticipated deviation in the mission design.
|
||||
* This returns the minimum of the comm state between the player and the other ship. In practice, retail has no checks whatsoever on a ship's ability
|
||||
* to send messages unless that ship is the player, so such a change requires an AI profiles option and we must default to the player's state. However, we
|
||||
* have a bit of wiggle room with COMM_SCRAMBLED, because EMP effects are either transient or set by the scramble-messages SEXP.
|
||||
*/
|
||||
int comm_between_player_and_ship(int other_shipnum)
|
||||
int comm_between_player_and_ship(int other_shipnum, bool for_death_scream)
|
||||
{
|
||||
int player_comm_state = hud_communications_state(Player_ship);
|
||||
|
||||
if (other_shipnum < 0)
|
||||
return player_comm_state;
|
||||
|
||||
int other_comm_state = hud_communications_state(&Ships[other_shipnum]);
|
||||
int other_comm_state = hud_communications_state(&Ships[other_shipnum], for_death_scream);
|
||||
|
||||
/* here is where you would check the flag
|
||||
if (hypothetical_ai_profiles_flag)
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships])
|
||||
{
|
||||
return MIN(player_comm_state, other_comm_state);
|
||||
}
|
||||
else
|
||||
*/
|
||||
{
|
||||
if (player_comm_state == COMM_OK && other_comm_state == COMM_OK)
|
||||
return COMM_OK;
|
||||
else if (player_comm_state == COMM_SCRAMBLED || other_comm_state == COMM_SCRAMBLED)
|
||||
else if (player_comm_state == COMM_OK && other_comm_state == COMM_SCRAMBLED)
|
||||
return COMM_SCRAMBLED;
|
||||
else
|
||||
return player_comm_state;
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
#include "object/waypoint.h"
|
||||
#include "parse/generic_log.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "scripting/hook_api.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "playerman/player.h"
|
||||
#include "popup/popup.h"
|
||||
@@ -406,6 +407,11 @@ void convertFSMtoFS2();
|
||||
MONITOR(NumShipArrivals)
|
||||
MONITOR(NumShipDepartures)
|
||||
|
||||
const std::shared_ptr<scripting::Hook> OnDepartureStartedHook = scripting::Hook::Factory(
|
||||
"On Departure Started", "Called when a ship starts the departure process.",
|
||||
{
|
||||
{"Ship", "ship", "The ship that has began the depture process."},
|
||||
});
|
||||
|
||||
// Goober5000
|
||||
void parse_custom_bitmap(const char *expected_string_640, const char *expected_string_1024, char *string_field_640, char *string_field_1024)
|
||||
@@ -2024,29 +2030,7 @@ int parse_create_object_sub(p_object *p_objp)
|
||||
shipp->base_texture_anim_frametime = game_get_overall_frametime();
|
||||
|
||||
// handle the replacement textures
|
||||
if (!p_objp->replacement_textures.empty())
|
||||
{
|
||||
shipp->ship_replacement_textures = (int *) vm_malloc( MAX_REPLACEMENT_TEXTURES * sizeof(int));
|
||||
|
||||
for (i = 0; i < MAX_REPLACEMENT_TEXTURES; i++)
|
||||
shipp->ship_replacement_textures[i] = -1;
|
||||
}
|
||||
|
||||
// now fill them in
|
||||
for (SCP_vector<texture_replace>::iterator tr = p_objp->replacement_textures.begin(); tr != p_objp->replacement_textures.end(); ++tr)
|
||||
{
|
||||
pm = model_get(sip->model_num);
|
||||
|
||||
// look for textures
|
||||
for (j = 0; j < pm->n_textures; j++)
|
||||
{
|
||||
texture_map *tmap = &pm->maps[j];
|
||||
|
||||
int tnum = tmap->FindTexture(tr->old_texture);
|
||||
if(tnum > -1)
|
||||
shipp->ship_replacement_textures[j * TM_NUM_TYPES + tnum] = tr->new_texture_id;
|
||||
}
|
||||
}
|
||||
shipp->apply_replacement_textures(p_objp->replacement_textures);
|
||||
|
||||
// Copy across the alt classes (if any) for FRED
|
||||
if (Fred_running) {
|
||||
@@ -6011,15 +5995,18 @@ void parse_init(bool basic)
|
||||
{
|
||||
reset_parse();
|
||||
|
||||
for (int i = 0; i < MAX_CARGO; i++)
|
||||
Cargo_names[i] = Cargo_names_buf[i]; // make a pointer array for compatibility
|
||||
// if we just want basic info then we don't need some of this initialization
|
||||
if (!basic)
|
||||
{
|
||||
for (int i = 0; i < MAX_CARGO; i++)
|
||||
Cargo_names[i] = Cargo_names_buf[i]; // make a pointer array for compatibility
|
||||
|
||||
Total_goal_target_names = 0;
|
||||
ai_clear_goal_target_names();
|
||||
|
||||
// if we are just wanting basic info then we shouldn't need sexps
|
||||
// (prevents memory fragmentation with the now dynamic Sexp_nodes[])
|
||||
if ( !basic )
|
||||
// if we are just wanting basic info then we shouldn't need sexps
|
||||
// (prevents memory fragmentation with the now dynamic Sexp_nodes[])
|
||||
init_sexp();
|
||||
}
|
||||
}
|
||||
|
||||
// main parse routine for parsing a mission. The default parameter flags tells us which information
|
||||
@@ -6643,7 +6630,8 @@ int mission_set_arrival_location(int anchor, int location, int dist, int objnum,
|
||||
|
||||
// if we get an error, just let the ship arrive(?)
|
||||
if ( ai_acquire_emerge_path(&Objects[objnum], anchor_objnum, path_mask, &pos, &fvec) == -1 ) {
|
||||
Int3(); // get MWA or AL -- not sure what to do here when we cannot acquire a path
|
||||
// get MWA or AL -- not sure what to do here when we cannot acquire a path
|
||||
mprintf(("Unable to acquire arrival path on anchor ship %s\n", Ships[shipnum].ship_name));
|
||||
return -1;
|
||||
}
|
||||
Objects[objnum].pos = pos;
|
||||
@@ -7115,21 +7103,6 @@ void mission_eval_arrivals()
|
||||
Mission_arrival_timestamp = timestamp(ARRIVAL_TIMESTAMP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the warp drive; we might be able to depart some other way (e.g. by entering a docking bay)
|
||||
*/
|
||||
int ship_can_use_warp_drive(ship *shipp)
|
||||
{
|
||||
// must *have* a subspace drive
|
||||
if (shipp->flags[Ship::Ship_Flags::No_subspace_drive])
|
||||
return 0;
|
||||
|
||||
// navigation must work
|
||||
if (!ship_navigation_ok_to_warp(shipp))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to make object objp depart. Rewritten and expanded by Goober5000.
|
||||
@@ -7143,6 +7116,11 @@ int mission_do_departure(object *objp, bool goal_is_to_warp)
|
||||
|
||||
mprintf(("Entered mission_do_departure() for %s\n", shipp->ship_name));
|
||||
|
||||
// add scripting hook for 'On Depature Started' --wookieejedi
|
||||
// hook is placed at the begining of this function to allow the scripter to
|
||||
// actually have access to the ship's departure decisions before they are all executed
|
||||
OnDepartureStartedHook->run(scripting::hook_param_list(scripting::hook_param("Ship", 'o', objp)));
|
||||
|
||||
// abort rearm, because if we entered this function we're either going to depart via hyperspace, depart via bay,
|
||||
// or revert to our default behavior
|
||||
ai_abort_rearm_request(objp);
|
||||
@@ -7157,7 +7135,7 @@ int mission_do_departure(object *objp, bool goal_is_to_warp)
|
||||
mprintf(("Looks like we were ordered to depart; initiating the standard departure logic\n"));
|
||||
}
|
||||
// since our goal is to warp, then if we can warp, jump directly to the warping part
|
||||
else if (ship_can_use_warp_drive(shipp))
|
||||
else if (ship_can_warp_full_check(shipp))
|
||||
{
|
||||
mprintf(("Our current goal is to warp! Trying to warp...\n"));
|
||||
goto try_to_warp;
|
||||
@@ -7227,7 +7205,7 @@ int mission_do_departure(object *objp, bool goal_is_to_warp)
|
||||
try_to_warp:
|
||||
|
||||
// make sure we can actually warp
|
||||
if (ship_can_use_warp_drive(shipp))
|
||||
if (ship_can_warp_full_check(shipp))
|
||||
{
|
||||
mprintf(("Setting mode to warpout\n"));
|
||||
|
||||
@@ -7280,7 +7258,8 @@ void mission_eval_departures()
|
||||
// don't process a ship that is already departing or dying or disabled
|
||||
// AL 12-30-97: Added SF_CANNOT_WARP to check
|
||||
// Goober5000 - fixed so that it WILL eval when SF_CANNOT_WARP if departing to dockbay
|
||||
if ( shipp->is_dying_or_departing() || (shipp->cannot_warp() && (shipp->departure_location != DEPART_AT_DOCK_BAY)) || ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE) ) {
|
||||
// wookieejedi - fixed so it accounts for break and never warp too
|
||||
if ( shipp->is_dying_or_departing() || ( !(ship_can_warp_full_check(shipp)) && (shipp->departure_location != DEPART_AT_DOCK_BAY)) || ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1992,7 +1992,7 @@ int brief_only_allow_briefing()
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( The_mission.flags[Mission::Mission_Flags::Scramble] || The_mission.flags[Mission::Mission_Flags::Red_alert] ) {
|
||||
if ( The_mission.flags[Mission::Mission_Flags::Scramble, Mission::Mission_Flags::Red_alert] ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1853,29 +1853,51 @@ bool is_weapon_carried(int weapon_index)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_a_weapon_slot_empty()
|
||||
bool check_for_gaps_in_weapon_slots()
|
||||
{
|
||||
for (int slot = 0; slot < MAX_WING_BLOCKS*MAX_WING_SLOTS; ++slot)
|
||||
{
|
||||
// a ship must exist in this slot
|
||||
if (Wss_slots[slot].ship_class >= 0)
|
||||
{
|
||||
// if the player can't modify the weapons, then an empty bank is not his fault
|
||||
auto ss_slot = &Ss_wings[slot / MAX_WING_SLOTS].ss_slots[slot % MAX_WING_SLOTS];
|
||||
if (ss_slot->status & WING_SLOT_WEAPONS_DISABLED)
|
||||
continue;
|
||||
|
||||
ship_info *sip = &Ship_info[Wss_slots[slot].ship_class];
|
||||
bool empty_slot = false;
|
||||
|
||||
// check primary banks
|
||||
for (int bank = 0; bank < sip->num_primary_banks; ++bank) // NOLINT(modernize-loop-convert)
|
||||
{
|
||||
// is the slot empty?
|
||||
if (Wss_slots[slot].wep_count[bank] <= 0)
|
||||
{
|
||||
empty_slot = true;
|
||||
}
|
||||
// is there a full slot following a gap?
|
||||
else if (empty_slot)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
empty_slot = false;
|
||||
|
||||
// check secondary banks
|
||||
for (int bank = 0; bank < sip->num_secondary_banks; ++bank)
|
||||
{
|
||||
// is the slot empty?
|
||||
if (Wss_slots[slot].wep_count[MAX_SHIP_PRIMARY_BANKS+bank] <= 0)
|
||||
if (Wss_slots[slot].wep_count[MAX_SHIP_PRIMARY_BANKS + bank] <= 0)
|
||||
{
|
||||
empty_slot = true;
|
||||
}
|
||||
// is there a full slot following a gap?
|
||||
else if (empty_slot)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1950,12 +1972,13 @@ void commit_pressed()
|
||||
}
|
||||
}
|
||||
|
||||
// Goober5000 - check that all weapon slots are filled (Mantis 2715)
|
||||
// Goober5000 - check that there are no gaps in weapon slots (Mantis 2715)
|
||||
// Any incomplete loadouts that make it through the loadout screen will be condensed so that gaps are moved to the bottom. This check adds that responsibility to the player.
|
||||
// Note: don't check for training, scramble, or red-alert missions
|
||||
// Note2: don't check missions without briefings either
|
||||
if (is_a_weapon_slot_empty() && !(The_mission.game_type & MISSION_TYPE_TRAINING) && !The_mission.flags[Mission::Mission_Flags::Scramble] && !The_mission.flags[Mission::Mission_Flags::Red_alert] && !The_mission.flags[Mission::Mission_Flags::No_briefing])
|
||||
if (check_for_gaps_in_weapon_slots() && !(The_mission.game_type & MISSION_TYPE_TRAINING) && !The_mission.flags[Mission::Mission_Flags::Scramble, Mission::Mission_Flags::Red_alert, Mission::Mission_Flags::No_briefing])
|
||||
{
|
||||
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("At least one ship has an empty weapon bank. All weapon banks must have weapons assigned.", 1642), weapon_list.c_str());
|
||||
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("At least one ship has an empty weapon bank before a full weapon bank.\n\nAll weapon banks must have weapons assigned, or if there are any gaps, they must be at the bottom of the set of banks.", 1642), weapon_list.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2197,7 +2220,7 @@ void draw_wing_block(int wb_num, int hot_slot, int selected_slot, int class_sele
|
||||
color_to_draw = &Icon_colors[ICON_FRAME_DISABLED];
|
||||
|
||||
// in multiplayer, determine if this it the special case where the slot is disabled, and
|
||||
// it is also _my_ slot (ie, team capatains/host have not locked players yet)
|
||||
// it is also _my_ slot (ie, team captains/hosts have not locked players yet)
|
||||
if((Game_mode & GM_MULTIPLAYER) && multi_ts_disabled_high_slot(slot_index)){
|
||||
if(icon->model_index == -1)
|
||||
bitmap_to_draw = icon->icon_bmaps[ICON_FRAME_DISABLED_HIGH];
|
||||
|
||||
@@ -42,6 +42,7 @@ float Shield_pain_flash_factor;
|
||||
gameversion::version Targetted_version; // Defaults to retail
|
||||
SCP_string Window_title;
|
||||
bool Unicode_text_mode;
|
||||
bool Use_tabled_strings_for_default_language;
|
||||
SCP_string Movie_subtitle_font;
|
||||
bool Enable_scripts_in_fred; // By default FRED does not initialize the scripting system
|
||||
SCP_string Window_icon_path;
|
||||
@@ -99,6 +100,12 @@ void parse_mod_table(const char *filename)
|
||||
mprintf(("Game settings table: Unicode mode: %s\n", Unicode_text_mode ? "yes" : "no"));
|
||||
}
|
||||
|
||||
if (optional_string("$Use tabled strings for the default language:")) {
|
||||
stuff_boolean(&Use_tabled_strings_for_default_language);
|
||||
|
||||
mprintf(("Game settings table: Use tabled strings (translations) for the default language: %s\n", Use_tabled_strings_for_default_language ? "yes" : "no"));
|
||||
}
|
||||
|
||||
optional_string("#CAMPAIGN SETTINGS");
|
||||
|
||||
if (optional_string("$Default Campaign File Name:")) {
|
||||
@@ -535,11 +542,14 @@ void mod_table_init()
|
||||
// parse any modular tables
|
||||
parse_modular_table("*-mod.tbm", parse_mod_table);
|
||||
}
|
||||
bool mod_supports_version(int major, int minor, int build) {
|
||||
|
||||
bool mod_supports_version(int major, int minor, int build)
|
||||
{
|
||||
return Targetted_version >= gameversion::version(major, minor, build, 0);
|
||||
}
|
||||
void mod_table_reset() {
|
||||
|
||||
void mod_table_reset()
|
||||
{
|
||||
Directive_wait_time = 3000;
|
||||
True_loop_argument_sexps = false;
|
||||
Fixed_turret_collisions = false;
|
||||
@@ -565,6 +575,7 @@ void mod_table_reset() {
|
||||
Targetted_version = gameversion::version(2, 0, 0, 0); // Defaults to retail
|
||||
Window_title = "";
|
||||
Unicode_text_mode = false;
|
||||
Use_tabled_strings_for_default_language = false;
|
||||
Movie_subtitle_font = "font01.vf";
|
||||
Enable_scripts_in_fred = false;
|
||||
Window_icon_path = "app_icon_sse";
|
||||
|
||||
@@ -33,6 +33,7 @@ extern float Generic_pain_flash_factor;
|
||||
extern float Shield_pain_flash_factor;
|
||||
extern SCP_string Window_title;
|
||||
extern bool Unicode_text_mode;
|
||||
extern bool Use_tabled_strings_for_default_language;
|
||||
extern SCP_string Movie_subtitle_font;
|
||||
extern bool Enable_scripts_in_fred;
|
||||
extern SCP_string Window_icon_path;
|
||||
|
||||
+1
-1
@@ -371,7 +371,7 @@ typedef struct mp_vert {
|
||||
} mp_vert;
|
||||
|
||||
typedef struct model_path {
|
||||
char name[MAX_NAME_LEN]; // name of the subsystem. Probably displayed on HUD
|
||||
char name[MAX_NAME_LEN]; // name of the path. Should be unique
|
||||
char parent_name[MAX_NAME_LEN]; // parent name of submodel that path is linked to in POF
|
||||
int parent_submodel;
|
||||
int nverts;
|
||||
|
||||
@@ -2273,7 +2273,15 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
|
||||
memset( pm->paths, 0, sizeof(model_path) * pm->n_paths );
|
||||
|
||||
for (i=0; i<pm->n_paths; i++ ) {
|
||||
cfread_string_len(pm->paths[i].name , MAX_NAME_LEN-1, fp);
|
||||
cfread_string_len(pm->paths[i].name, MAX_NAME_LEN-1, fp);
|
||||
|
||||
// check for reused path names... not fatal, but maybe problematic
|
||||
for (j = 0; j < i; j++) {
|
||||
if (!stricmp(pm->paths[i].name, pm->paths[j].name)) {
|
||||
Warning(LOCATION, "Path '%s' in model %s has a name that is not unique!", pm->paths[i].name, pm->filename);
|
||||
}
|
||||
}
|
||||
|
||||
if ( pm->version >= 2002 ) {
|
||||
// store the sub_model name number of the parent
|
||||
cfread_string_len(pm->paths[i].parent_name , MAX_NAME_LEN-1, fp);
|
||||
@@ -3073,6 +3081,8 @@ void model_set_bay_path_nums(polymodel *pm)
|
||||
pm->ship_bay->arrive_flags = 0; // bitfield, set to 1 when that path number is reserved for an arrival
|
||||
pm->ship_bay->depart_flags = 0; // bitfield, set to 1 when that path number is reserved for a departure
|
||||
|
||||
// sanity part 1
|
||||
memset(pm->ship_bay->path_indexes, -1, MAX_SHIP_BAY_PATHS * sizeof(int));
|
||||
|
||||
// iterate through the paths that exist in the polymodel, searching for $bayN pathnames
|
||||
bool too_many_paths = false;
|
||||
@@ -3108,6 +3118,16 @@ void model_set_bay_path_nums(polymodel *pm)
|
||||
{
|
||||
Warning(LOCATION, "Model '%s' has too many bay paths - max is %d", pm->filename, MAX_SHIP_BAY_PATHS);
|
||||
}
|
||||
|
||||
// sanity part 2
|
||||
for (i = 0; i < pm->ship_bay->num_paths; i++)
|
||||
{
|
||||
if (pm->ship_bay->path_indexes[i] < 0)
|
||||
{
|
||||
Warning(LOCATION, "Model '%s' does not have a '$bay%.2d' path specified! A total of %d bay paths were counted. Either there is a gap in the path sequence, or a path has a duplicate name.", pm->filename, i + 1, pm->ship_bay->num_paths);
|
||||
pm->ship_bay->path_indexes[i] = 0; // avoid crashes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get "parent" submodel for live debris submodel
|
||||
@@ -3991,7 +4011,7 @@ int model_rotate_gun(int model_num, model_subsystem *turret, matrix *orient, ang
|
||||
{
|
||||
base_delta = vm_delta_from_interp_angle( base_angles->h, desired_angles.h );
|
||||
gun_delta = vm_delta_from_interp_angle( gun_angles->p, desired_angles.p );
|
||||
ss->points_to_target = sqrt( pow(base_delta,2) + pow(gun_delta,2));
|
||||
ss->points_to_target = sqrt((base_delta*base_delta) + (gun_delta*gun_delta));
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -4449,9 +4449,10 @@ void multi_create_list_load_missions()
|
||||
}
|
||||
|
||||
for (idx = 0; idx < file_count; idx++) {
|
||||
int flags,max_players;
|
||||
int flags, max_players;
|
||||
char *filename;
|
||||
uint m_respawn;
|
||||
bool lcl_weirdness = false;
|
||||
|
||||
fname = file_list[idx];
|
||||
|
||||
@@ -4462,10 +4463,16 @@ void multi_create_list_load_missions()
|
||||
std_gen_set_text(filename, 2);
|
||||
}
|
||||
|
||||
// activate tstrings check
|
||||
Lcl_unexpected_tstring_check = &lcl_weirdness;
|
||||
|
||||
flags = mission_parse_is_multi(filename, mission_name);
|
||||
|
||||
// deactivate tstrings check
|
||||
Lcl_unexpected_tstring_check = nullptr;
|
||||
|
||||
// if the mission is a multiplayer mission, and we can list it, then add it to the mission list
|
||||
if (flags && !mission_is_ignored(filename)) {
|
||||
if (flags && !mission_is_ignored(filename) && !lcl_weirdness) {
|
||||
max_players = mission_parse_get_multi_mission_info( filename );
|
||||
m_respawn = The_mission.num_respawns;
|
||||
|
||||
|
||||
@@ -2157,7 +2157,7 @@ void multi_warpout_all_players()
|
||||
|
||||
// if we're an observer, or we're respawning, or we can't warp out. so just jump into the debrief state
|
||||
if((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Net_player->flags & NETINFO_FLAG_RESPAWNING) ||
|
||||
((Player_obj->type == OBJ_SHIP) && ( Player_ship->cannot_warp())) ){
|
||||
((Player_obj->type == OBJ_SHIP) && !(ship_can_warp_full_check(Player_ship)))){
|
||||
|
||||
multi_handle_sudden_mission_end();
|
||||
|
||||
|
||||
@@ -781,10 +781,16 @@ void obj_player_fire_stuff( object *objp, control_info ci )
|
||||
// single player and multiplayer masters do all of the following
|
||||
if ( !MULTIPLAYER_CLIENT ) {
|
||||
if ( ci.fire_secondary_count ) {
|
||||
ship_fire_secondary( objp );
|
||||
if ( !ship_start_secondary_fire(objp) ) {
|
||||
ship_fire_secondary( objp );
|
||||
}
|
||||
|
||||
// kill the secondary count
|
||||
ci.fire_secondary_count = 0;
|
||||
} else {
|
||||
if ( ship_stop_secondary_fire(objp) ) {
|
||||
ship_fire_secondary( objp );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,8 +89,6 @@ namespace
|
||||
int Global_warning_count = 0;
|
||||
int Global_error_count = 0;
|
||||
|
||||
extern lua_Debug Ade_debug_info;
|
||||
|
||||
namespace os
|
||||
{
|
||||
namespace dialogs
|
||||
@@ -166,17 +164,6 @@ namespace os
|
||||
msgStream << "\n";
|
||||
msgStream << "\n";
|
||||
|
||||
msgStream << Separator;
|
||||
msgStream << "ADE Debug:";
|
||||
msgStream << "\n";
|
||||
|
||||
msgStream << Separator;
|
||||
LuaDebugPrint(msgStream, Ade_debug_info);
|
||||
msgStream << Separator;
|
||||
|
||||
msgStream << "\n";
|
||||
msgStream << "\n";
|
||||
|
||||
msgStream << Separator;
|
||||
|
||||
// Get the stack via the debug.traceback() function
|
||||
|
||||
+58
-54
@@ -244,7 +244,7 @@ int get_line_num()
|
||||
while (p < stoploc)
|
||||
{
|
||||
if (*p == '\0')
|
||||
Assert(0);
|
||||
Warning(LOCATION, "Unexpected end-of-file while looking for error line!");
|
||||
|
||||
if ( !incomment && (*p == COMMENT_CHAR) )
|
||||
incomment = 1;
|
||||
@@ -314,8 +314,6 @@ void advance_to_eoln(const char *more_terminators)
|
||||
terminators[1] = 0;
|
||||
if (more_terminators != NULL)
|
||||
strcat_s(terminators, more_terminators);
|
||||
else
|
||||
terminators[1] = 0;
|
||||
|
||||
while (strchr(terminators, *Mp) == NULL)
|
||||
Mp++;
|
||||
@@ -324,7 +322,7 @@ void advance_to_eoln(const char *more_terminators)
|
||||
// Advance Mp to the next white space (ignoring white space inside of " marks)
|
||||
void advance_to_next_white()
|
||||
{
|
||||
int in_quotes = 0;
|
||||
bool in_quotes = false;
|
||||
|
||||
while ((*Mp != EOLN) && (*Mp != '\0')) {
|
||||
if (*Mp == '\"')
|
||||
@@ -765,8 +763,6 @@ void copy_to_eoln(char *outstr, const char *more_terminators, const char *instr,
|
||||
terminators[1] = 0;
|
||||
if (more_terminators != NULL)
|
||||
strcat_s(terminators, more_terminators);
|
||||
else
|
||||
terminators[1] = 0;
|
||||
|
||||
while (((ch = *instr++) != 0) && (strchr(terminators, ch) == NULL) && (count < max)) {
|
||||
*outstr++ = ch;
|
||||
@@ -791,8 +787,6 @@ void copy_to_eoln(SCP_string &outstr, const char *more_terminators, const char *
|
||||
terminators[1] = 0;
|
||||
if (more_terminators != NULL)
|
||||
strcat_s(terminators, more_terminators);
|
||||
else
|
||||
terminators[1] = 0;
|
||||
|
||||
outstr = "";
|
||||
while (((ch = *instr++) != 0) && (strchr(terminators, ch) == NULL)) {
|
||||
@@ -802,10 +796,10 @@ void copy_to_eoln(SCP_string &outstr, const char *more_terminators, const char *
|
||||
|
||||
// Copy characters from instr to outstr until next white space is found, or until max
|
||||
// characters have been copied (including terminator).
|
||||
void copy_to_next_white(char *outstr, char *instr, int max)
|
||||
void copy_to_next_white(char *outstr, const char *instr, int max)
|
||||
{
|
||||
int count = 0;
|
||||
int in_quotes = 0;
|
||||
bool in_quotes = false;
|
||||
char ch;
|
||||
|
||||
while (((ch = *instr++)>0) && (ch != EOLN) && (ch != '\0') && (count < max)) {
|
||||
@@ -831,9 +825,9 @@ void copy_to_next_white(char *outstr, char *instr, int max)
|
||||
}
|
||||
|
||||
// Ditto for SCP_string.
|
||||
void copy_to_next_white(SCP_string &outstr, char *instr)
|
||||
void copy_to_next_white(SCP_string &outstr, const char *instr)
|
||||
{
|
||||
int in_quotes = 0;
|
||||
bool in_quotes = false;
|
||||
char ch;
|
||||
|
||||
outstr = "";
|
||||
@@ -854,10 +848,10 @@ void copy_to_next_white(SCP_string &outstr, char *instr)
|
||||
}
|
||||
|
||||
//Returns a null-terminated character string allocated with vm_malloc() with the data
|
||||
char* alloc_text_until(char* instr, char* endstr)
|
||||
char* alloc_text_until(const char* instr, const char* endstr)
|
||||
{
|
||||
Assert(instr && endstr);
|
||||
char *foundstr = stristr(instr, endstr);
|
||||
auto foundstr = stristr(instr, endstr);
|
||||
|
||||
if(foundstr == NULL)
|
||||
{
|
||||
@@ -888,12 +882,11 @@ char* alloc_text_until(char* instr, char* endstr)
|
||||
// Copy text until a certain string is matched.
|
||||
// For example, this is used to copy mission notes, scanning until $END NOTES:
|
||||
// is found.
|
||||
void copy_text_until(char *outstr, char *instr, const char *endstr, int max_chars)
|
||||
void copy_text_until(char *outstr, const char *instr, const char *endstr, int max_chars)
|
||||
{
|
||||
char *foundstr;
|
||||
Assert(outstr && instr && endstr);
|
||||
|
||||
foundstr = stristr(instr, endstr);
|
||||
auto foundstr = stristr(instr, endstr);
|
||||
|
||||
if (foundstr == NULL) {
|
||||
nprintf(("Error", "Error. Looking for [%s], but never found it.\n", endstr));
|
||||
@@ -915,12 +908,11 @@ void copy_text_until(char *outstr, char *instr, const char *endstr, int max_char
|
||||
}
|
||||
|
||||
// Ditto for SCP_string.
|
||||
void copy_text_until(SCP_string &outstr, char *instr, const char *endstr)
|
||||
void copy_text_until(SCP_string &outstr, const char *instr, const char *endstr)
|
||||
{
|
||||
char *foundstr;
|
||||
Assert(instr && endstr);
|
||||
|
||||
foundstr = stristr(instr, endstr);
|
||||
auto foundstr = stristr(instr, endstr);
|
||||
|
||||
if (foundstr == NULL) {
|
||||
nprintf(("Error", "Error. Looking for [%s], but never found it.\n", endstr));
|
||||
@@ -1367,7 +1359,7 @@ void stuff_string_line(SCP_string &outstr)
|
||||
// Exactly the same as stuff string only Malloc's the buffer.
|
||||
// Supports various FreeSpace primitive types. If 'len' is supplied, it will override
|
||||
// the default string length if using the F_NAME case.
|
||||
char *stuff_and_malloc_string(int type, char *terminators)
|
||||
char *stuff_and_malloc_string(int type, const char *terminators)
|
||||
{
|
||||
SCP_string tmp_result;
|
||||
|
||||
@@ -1380,7 +1372,7 @@ char *stuff_and_malloc_string(int type, char *terminators)
|
||||
return vm_strdup(tmp_result.c_str());
|
||||
}
|
||||
|
||||
void stuff_malloc_string(char **dest, int type, char *terminators)
|
||||
void stuff_malloc_string(char **dest, int type, const char *terminators)
|
||||
{
|
||||
Assert(dest != NULL); //wtf?
|
||||
|
||||
@@ -1397,53 +1389,62 @@ void stuff_malloc_string(char **dest, int type, char *terminators)
|
||||
}
|
||||
|
||||
// After reading a multitext string, you can call this function to convert any newlines into
|
||||
// spaces, so it's a one paragraph string (I.e. as in MS-Word).
|
||||
//
|
||||
// spaces, so it's a one paragraph string (i.e. as in MS-Word).
|
||||
void compact_multitext_string(char *str)
|
||||
{
|
||||
size_t i;
|
||||
size_t len = strlen(str);
|
||||
size_t num_cr = 0;
|
||||
auto p_dest = str;
|
||||
auto p_src = str;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
while (*p_src)
|
||||
{
|
||||
char ch = *p_src;
|
||||
p_src++;
|
||||
|
||||
// skip CR
|
||||
// convert LF to space
|
||||
// copy characters backwards if any CRs previously encountered
|
||||
if (str[i] == '\r')
|
||||
num_cr++;
|
||||
else if (str[i] == '\n')
|
||||
str[i-num_cr] = ' ';
|
||||
else if (num_cr > 0)
|
||||
str[i-num_cr] = str[i];
|
||||
if (ch != '\r')
|
||||
{
|
||||
if (ch == '\n')
|
||||
*p_dest = ' ';
|
||||
else if (p_dest != p_src)
|
||||
*p_dest = *p_src;
|
||||
|
||||
p_dest++;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_cr > 0)
|
||||
str[len-num_cr] = 0;
|
||||
if (p_dest != p_src)
|
||||
*p_dest = 0;
|
||||
}
|
||||
|
||||
// ditto for SCP_string
|
||||
void compact_multitext_string(SCP_string &str)
|
||||
{
|
||||
size_t i;
|
||||
size_t len = str.length();
|
||||
size_t num_cr = 0;
|
||||
auto p_dest = str.begin();
|
||||
auto p_src = str.begin();
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
while (p_src != str.end())
|
||||
{
|
||||
char ch = *p_src;
|
||||
p_src++;
|
||||
|
||||
// skip CR
|
||||
// convert LF to space
|
||||
// copy characters backwards if any CRs previously encountered
|
||||
if (str[i] == '\r')
|
||||
num_cr++;
|
||||
else if (str[i] == '\n')
|
||||
str[i-num_cr] = ' ';
|
||||
else if (num_cr > 0)
|
||||
str[i-num_cr] = str[i];
|
||||
if (ch != '\r')
|
||||
{
|
||||
if (ch == '\n')
|
||||
*p_dest = ' ';
|
||||
else if (p_dest != p_src)
|
||||
*p_dest = *p_src;
|
||||
|
||||
p_dest++;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_cr > 0)
|
||||
str.resize(len-num_cr);
|
||||
if (p_dest != p_src)
|
||||
str.erase(p_dest);
|
||||
}
|
||||
|
||||
// Converts a character from Windows-1252 to CP437.
|
||||
@@ -1987,13 +1988,12 @@ void strip_comments(char *line, bool &in_quote, bool &in_multiline_comment_a, bo
|
||||
}
|
||||
}
|
||||
|
||||
int parse_get_line(char *lineout, int max_line_len, char *start, int max_size, char *cur)
|
||||
int parse_get_line(char *lineout, int max_line_len, const char *start, int max_size, const char *cur)
|
||||
{
|
||||
char * t = lineout;
|
||||
int i, num_chars_read=0;
|
||||
char c;
|
||||
|
||||
|
||||
for ( i = 0; i < max_line_len-1; i++ ) {
|
||||
do {
|
||||
if ( (cur - start) >= max_size ) {
|
||||
@@ -2019,7 +2019,7 @@ int parse_get_line(char *lineout, int max_line_len, char *start, int max_size, c
|
||||
// Read mission text, stripping comments.
|
||||
// When a comment is found, it is removed. If an entire line
|
||||
// consisted of a comment, a blank line is left in the input file.
|
||||
// Goober5000 - added ability to read somewhere other than Mission_text
|
||||
// Goober5000 - added ability to read somewhere other than Parse_text
|
||||
void read_file_text(const char *filename, int mode, char *processed_text, char *raw_text)
|
||||
{
|
||||
// copy the filename
|
||||
@@ -2134,7 +2134,7 @@ void allocate_parse_text(size_t size)
|
||||
Parse_text_raw = (char *) vm_malloc(sizeof(char) * size, memory::quiet_alloc);
|
||||
|
||||
if ( (Parse_text == nullptr) || (Parse_text_raw == nullptr) ) {
|
||||
Error(LOCATION, "Unable to allocate enough memory for Mission_text! Aborting...\n");
|
||||
Error(LOCATION, "Unable to allocate enough memory for Parse_text! Aborting...\n");
|
||||
}
|
||||
|
||||
memset( Parse_text, 0, sizeof(char) * size );
|
||||
@@ -2170,7 +2170,7 @@ void read_raw_file_text(const char *filename, int mode, char *raw_text)
|
||||
// that if we have control over the raw_text pointer which is only the case if it's null.
|
||||
auto can_reallocate = raw_text == nullptr;
|
||||
if (raw_text == nullptr) {
|
||||
// allocate, or reallocate, memory for Mission_text and Mission_text_raw based on size we need now
|
||||
// allocate, or reallocate, memory for Parse_text and Parse_text_raw based on size we need now
|
||||
allocate_parse_text((size_t) (file_len + 1));
|
||||
// NOTE: this always has to be done *after* the allocate_mission_text() call!!
|
||||
raw_text = Parse_text_raw;
|
||||
@@ -4309,8 +4309,12 @@ int parse_modular_table(const char *name_check, void (*parse_callback)(const cha
|
||||
|
||||
Parsing_modular_table = true;
|
||||
|
||||
const auto ext = strrchr(name_check, '.');
|
||||
|
||||
for (i = 0; i < num_files; i++){
|
||||
tbl_file_names[i] += ".tbm";
|
||||
if (ext != nullptr) {
|
||||
tbl_file_names[i] += ext;
|
||||
}
|
||||
mprintf(("TBM => Starting parse of '%s' ...\n", tbl_file_names[i].c_str()));
|
||||
(*parse_callback)(tbl_file_names[i].c_str());
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ extern int required_string_one_of(int arg_count, ...);
|
||||
|
||||
// stuff
|
||||
extern void copy_to_eoln(char *outstr, const char *more_terminators, const char *instr, int max);
|
||||
extern void copy_text_until(char *outstr, char *instr, const char *endstr, int max_chars);
|
||||
extern void copy_text_until(char *outstr, const char *instr, const char *endstr, int max_chars);
|
||||
extern void stuff_string_white(char *outstr, int len = 0);
|
||||
extern void stuff_string_until(char *outstr, const char *endstr, int len = 0);
|
||||
extern void stuff_string(char *outstr, int type, int len, const char *terminators = NULL);
|
||||
@@ -124,7 +124,7 @@ extern void stuff_string_line(char *outstr, int len);
|
||||
|
||||
// SCP_string stuff
|
||||
extern void copy_to_eoln(SCP_string &outstr, const char *more_terminators, const char *instr);
|
||||
extern void copy_text_until(SCP_string &outstr, char *instr, const char *endstr);
|
||||
extern void copy_text_until(SCP_string &outstr, const char *instr, const char *endstr);
|
||||
extern void stuff_string_white(SCP_string &outstr);
|
||||
extern void stuff_string_until(SCP_string &outstr, const char *endstr);
|
||||
extern void stuff_string(SCP_string &outstr, int type, const char *terminators = NULL);
|
||||
@@ -136,8 +136,8 @@ extern char* alloc_block(const char* startstr, const char* endstr, int extra_cha
|
||||
// Exactly the same as stuff string only Malloc's the buffer.
|
||||
// Supports various FreeSpace primitive types. If 'len' is supplied, it will override
|
||||
// the default string length if using the F_NAME case.
|
||||
extern char *stuff_and_malloc_string(int type, char *terminators = NULL);
|
||||
extern void stuff_malloc_string(char **dest, int type, char *terminators = NULL);
|
||||
extern char *stuff_and_malloc_string(int type, const char *terminators = nullptr);
|
||||
extern void stuff_malloc_string(char **dest, int type, const char *terminators = nullptr);
|
||||
extern void stuff_float(float *f);
|
||||
extern int stuff_float_optional(float *f, bool raw = false);
|
||||
extern int stuff_int_optional(int *i, bool raw = false);
|
||||
@@ -252,7 +252,7 @@ extern void reset_parse(char *text = NULL);
|
||||
extern void display_parse_diagnostics();
|
||||
extern void pause_parse();
|
||||
extern void unpause_parse();
|
||||
// stop parsing, basically just free's up the memory from Mission_text and Mission_text_raw
|
||||
// stop parsing, basically just frees up the memory from Parse_text and Parse_text_raw
|
||||
extern void stop_parse();
|
||||
|
||||
// utility
|
||||
|
||||
@@ -26480,6 +26480,7 @@ int eval_sexp(int cur_node, int referenced_node)
|
||||
return sexp_val;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// now, reconcile positive and negative - Goober5000
|
||||
if (sexp_val < 0 && sexp_val > SEXP_UNLIKELY_RETURN_VALUE_BOUND)
|
||||
{
|
||||
@@ -26494,10 +26495,12 @@ int eval_sexp(int cur_node, int referenced_node)
|
||||
// if we need a positive value, make it positive
|
||||
if (query_operator_argument_type(get_operator_index(parent_node), arg_num) == OPF_POSITIVE)
|
||||
{
|
||||
Warning(LOCATION, "Parent node %s, argument %d (value %d) is negative, but is required to be positive!", Sexp_nodes[parent_node].text, arg_num + 1, sexp_val);
|
||||
sexp_val *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( sexp_val ){
|
||||
Sexp_nodes[cur_node].value = SEXP_TRUE;
|
||||
|
||||
@@ -1038,7 +1038,7 @@ void read_player_controls(object *objp, float frametime)
|
||||
target_warpout_speed = ship_get_warpout_speed(objp);
|
||||
|
||||
// check if warp ability has been disabled
|
||||
if (!(Warpout_forced) && (Ships[objp->instance].flags[Ship::Ship_Flags::Warp_broken] || Ships[objp->instance].flags[Ship::Ship_Flags::Warp_never]) ) {
|
||||
if (!(Warpout_forced) && !(ship_can_warp_full_check(&Ships[objp->instance]))) {
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Cannot warp out at this time.", 81));
|
||||
snd_play(gamesnd_get_game_sound(GameSounds::PLAYER_WARP_FAIL));
|
||||
gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP );
|
||||
|
||||
@@ -323,7 +323,8 @@ ADE_FUNC(getCurrentLanguage,
|
||||
"Determines the language that is being used by the engine. This returns the full name of the language (e.g. \"English\").",
|
||||
"string",
|
||||
"The current game language") {
|
||||
return ade_set_args(L, "s", Lcl_languages[Lcl_current_lang].lang_name);
|
||||
auto lang_name = (Lcl_current_lang == LCL_UNTRANSLATED) ? "UNTRANSLATED" : Lcl_languages[Lcl_current_lang].lang_name;
|
||||
return ade_set_args(L, "s", lang_name);
|
||||
}
|
||||
|
||||
ADE_FUNC(getCurrentLanguageExtension,
|
||||
@@ -334,7 +335,8 @@ ADE_FUNC(getCurrentLanguageExtension,
|
||||
"This will return an empty string for the default language.",
|
||||
"string",
|
||||
"The current game language") {
|
||||
return ade_set_args(L, "s", Lcl_languages[Lcl_current_lang].lang_ext);
|
||||
int lang = (Lcl_current_lang == LCL_UNTRANSLATED) ? LCL_DEFAULT : Lcl_current_lang;
|
||||
return ade_set_args(L, "s", Lcl_languages[lang].lang_ext);
|
||||
}
|
||||
|
||||
ADE_FUNC(getVersionString, l_Base, nullptr,
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
//
|
||||
|
||||
#include "engine.h"
|
||||
|
||||
#include "scripting/scripting.h"
|
||||
|
||||
namespace scripting {
|
||||
namespace api {
|
||||
|
||||
//**********LIBRARY: Engine
|
||||
ADE_LIB(l_Engine, "Engine", "engine", "Basic engine access functions");
|
||||
|
||||
ADE_FUNC(addHook, l_Engine,
|
||||
"string name, function hookFunction, [table conditionals = {}], [function override_func = return false]",
|
||||
"Adds a function to be called from the specified game hook", "boolean",
|
||||
"true if hook was installed properly, false otherwise")
|
||||
{
|
||||
using namespace luacpp;
|
||||
|
||||
const char* hook_name;
|
||||
LuaFunction hook;
|
||||
LuaTable conditionals;
|
||||
LuaFunction override_func;
|
||||
if (!ade_get_args(L, "su|tu", &hook_name, &hook, &conditionals, &override_func)) {
|
||||
return ADE_RETURN_FALSE;
|
||||
}
|
||||
|
||||
if (!hook.isValid()) {
|
||||
LuaError(L, "Hook function is invalid!");
|
||||
return ADE_RETURN_FALSE;
|
||||
}
|
||||
|
||||
ConditionedHook cond_hook;
|
||||
|
||||
script_action action;
|
||||
action.action_type = scripting_string_to_action(hook_name);
|
||||
|
||||
if (action.action_type == CHA_NONE) {
|
||||
LuaError(L, "Invalid hook name '%s'!", hook_name);
|
||||
return ADE_RETURN_FALSE;
|
||||
}
|
||||
|
||||
action.hook.hook_function.language = SC_LUA;
|
||||
action.hook.hook_function.function = hook;
|
||||
|
||||
if (override_func.isValid()) {
|
||||
action.hook.override_function.language = SC_LUA;
|
||||
action.hook.override_function.function = override_func;
|
||||
}
|
||||
|
||||
cond_hook.AddAction(&action);
|
||||
|
||||
if (conditionals.isValid()) {
|
||||
for (const auto& tableEntry : conditionals) {
|
||||
const auto& key = tableEntry.first;
|
||||
const auto& value = tableEntry.second;
|
||||
|
||||
if (!key.is(ValueType::STRING)) {
|
||||
LuaError(L, "Conditional key '%s' is not a string", key.getValue<SCP_string>().c_str());
|
||||
return ADE_RETURN_FALSE;
|
||||
}
|
||||
|
||||
if (!value.is(ValueType::STRING)) {
|
||||
LuaError(L, "Conditional value '%s' for key '%s' is not a string", value.getValue<SCP_string>().c_str(),
|
||||
key.getValue<SCP_string>().c_str());
|
||||
return ADE_RETURN_FALSE;
|
||||
}
|
||||
|
||||
script_condition cond;
|
||||
cond.condition_type = scripting_string_to_condition(key.getValue<SCP_string>().c_str());
|
||||
cond.condition_string = value.getValue<SCP_string>();
|
||||
|
||||
cond_hook.AddCondition(&cond);
|
||||
}
|
||||
}
|
||||
|
||||
Script_system.AddConditionedHook(std::move(cond_hook));
|
||||
return ADE_RETURN_TRUE;
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
} // namespace scripting
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "scripting/ade_api.h"
|
||||
|
||||
namespace scripting {
|
||||
namespace api {
|
||||
|
||||
DECLARE_ADE_LIB(l_Engine);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1470,7 +1470,7 @@ ADE_FUNC(warpOut, l_Ship, NULL, "Warps ship out", "boolean", "True if successful
|
||||
return ADE_RETURN_TRUE;
|
||||
}
|
||||
|
||||
ADE_FUNC(canWarp, l_Ship, NULL, "Checks whether ship has a working subspace drive and is allowed to use it", "boolean", "True if successful, or nil if ship handle is invalid")
|
||||
ADE_FUNC(canWarp, l_Ship, nullptr, "Checks whether ship has a working subspace drive, is allowed to use it, and is not disabled or limited by subsystem strength.", "boolean", "True if successful, or nil if ship handle is invalid")
|
||||
{
|
||||
object_h *objh;
|
||||
if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
|
||||
@@ -1480,11 +1480,28 @@ ADE_FUNC(canWarp, l_Ship, NULL, "Checks whether ship has a working subspace driv
|
||||
return ADE_RETURN_NIL;
|
||||
|
||||
ship *shipp = &Ships[objh->objp->instance];
|
||||
if(shipp->flags[Ship::Ship_Flags::No_subspace_drive]){
|
||||
return ADE_RETURN_FALSE;
|
||||
if( ship_can_warp_full_check(shipp) ){
|
||||
return ADE_RETURN_TRUE;
|
||||
}
|
||||
|
||||
return ADE_RETURN_TRUE;
|
||||
return ADE_RETURN_FALSE;
|
||||
}
|
||||
|
||||
ADE_FUNC(canBayDepart, l_Ship, nullptr, "Checks whether ship has a bay departure location and if its mother ship is present.", "boolean", "True if successful, or nil if ship handle is invalid")
|
||||
{
|
||||
object_h *objh;
|
||||
if(!ade_get_args(L, "o", l_Ship.GetPtr(&objh)))
|
||||
return ADE_RETURN_NIL;
|
||||
|
||||
if(!objh->IsValid())
|
||||
return ADE_RETURN_NIL;
|
||||
|
||||
ship *shipp = &Ships[objh->objp->instance];
|
||||
if( ship_can_bay_depart(shipp) ){
|
||||
return ADE_RETURN_TRUE;
|
||||
}
|
||||
|
||||
return ADE_RETURN_FALSE;
|
||||
}
|
||||
|
||||
// Aardwolf's function for finding if a ship should be drawn as blue on the radar/minimap
|
||||
|
||||
@@ -73,7 +73,7 @@ ADE_FUNC(attacks, l_Team, "team", "Checks the IFF status of another team", "bool
|
||||
{
|
||||
int x, y;
|
||||
ade_get_args(L, "oo", l_Team.Get(&x), l_Team.Get(&y));
|
||||
if (iff_x_attacks_y(x, y) > 0)
|
||||
if (iff_x_attacks_y(x, y))
|
||||
return ADE_RETURN_TRUE;
|
||||
|
||||
return ADE_RETURN_FALSE;
|
||||
|
||||
+1
-51
@@ -58,6 +58,7 @@
|
||||
#include "scripting/api/libs/base.h"
|
||||
#include "scripting/api/libs/bitops.h"
|
||||
#include "scripting/api/libs/cfile.h"
|
||||
#include "scripting/api/libs/engine.h"
|
||||
#include "scripting/api/libs/graphics.h"
|
||||
#include "scripting/api/libs/hookvars.h"
|
||||
#include "scripting/api/libs/hud.h"
|
||||
@@ -76,46 +77,6 @@ using namespace scripting;
|
||||
using namespace scripting::api;
|
||||
|
||||
// *************************Housekeeping*************************
|
||||
//WMC - The miraculous lines of code that make Lua debugging worth something.
|
||||
lua_Debug Ade_debug_info;
|
||||
char debug_stack[4][32];
|
||||
|
||||
void ade_debug_ret(lua_State *L, lua_Debug *ar)
|
||||
{
|
||||
Assert(L != NULL);
|
||||
Assert(ar != NULL);
|
||||
lua_getstack(L, 1, ar);
|
||||
lua_getinfo(L, "nSlu", ar);
|
||||
memcpy(&Ade_debug_info, ar, sizeof(lua_Debug));
|
||||
|
||||
int n;
|
||||
for (n = 0; n < 4; n++) {
|
||||
debug_stack[n][0] = '\0';
|
||||
}
|
||||
|
||||
for (n = 0; n < 4; n++) {
|
||||
if (lua_getstack(L,n+1, ar) == 0)
|
||||
break;
|
||||
lua_getinfo(L,"n", ar);
|
||||
if (ar->name == NULL)
|
||||
break;
|
||||
strcpy_s(debug_stack[n],ar->name);
|
||||
}
|
||||
}
|
||||
|
||||
//WMC - because the behavior of the return keyword
|
||||
//was changed, I now have to use this in hooks.
|
||||
static int ade_return_hack(lua_State *L)
|
||||
{
|
||||
int i = 0;
|
||||
int num = lua_gettop(L);
|
||||
for(i = 0; i < num; i++)
|
||||
{
|
||||
lua_pushvalue(L, i+1);
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static void *vm_lua_alloc(void*, void *ptr, size_t, size_t nsize) {
|
||||
if (nsize == 0)
|
||||
@@ -165,11 +126,6 @@ int script_state::CreateLuaState()
|
||||
}
|
||||
lua_pop(L, 1); //os table
|
||||
|
||||
//*****SET DEBUG HOOKS
|
||||
#ifndef NDEBUG
|
||||
lua_sethook(L, ade_debug_ret, LUA_MASKRET, 0);
|
||||
#endif
|
||||
|
||||
//*****INITIALIZE ADE
|
||||
uint i;
|
||||
mprintf(("LUA: Beginning ADE initialization\n"));
|
||||
@@ -180,12 +136,6 @@ int script_state::CreateLuaState()
|
||||
ade_manager::getInstance()->getEntry(i).SetTable(L, LUA_GLOBALSINDEX, LUA_GLOBALSINDEX); //Oh the miracles of OOP.
|
||||
}
|
||||
|
||||
//*****INITIALIZE RETURN HACK FUNCTION
|
||||
lua_pushstring(L, "ade_return_hack");
|
||||
lua_pushboolean(L, 0);
|
||||
lua_pushcclosure(L, ade_return_hack, 2);
|
||||
lua_setglobal(L, "ade_return_hack");
|
||||
|
||||
//*****INITIALIZE ENUMERATION CONSTANTS
|
||||
mprintf(("ADE: Initializing enumeration constants...\n"));
|
||||
enum_h eh;
|
||||
|
||||
@@ -66,7 +66,7 @@ bool LuaFunction::setEnvironment(const LuaTable& table) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
LuaValueList LuaFunction::operator()(const LuaValueList& args) {
|
||||
LuaValueList LuaFunction::operator()(const LuaValueList& args) const {
|
||||
return this->call(args);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ void LuaFunction::setReference(const LuaReference& ref) {
|
||||
}
|
||||
}
|
||||
|
||||
LuaValueList LuaFunction::call(const LuaValueList& args) {
|
||||
LuaValueList LuaFunction::call(const LuaValueList& args) const {
|
||||
int err_idx = 0;
|
||||
int stackTop;
|
||||
|
||||
@@ -108,8 +108,8 @@ LuaValueList LuaFunction::call(const LuaValueList& args) {
|
||||
this->pushValue();
|
||||
|
||||
// Push the arguments onto the stack
|
||||
for (LuaValueList::const_iterator iter = args.begin(); iter != args.end(); ++iter) {
|
||||
iter->pushValue();
|
||||
for (const auto& arg : args) {
|
||||
arg.pushValue();
|
||||
}
|
||||
|
||||
// actually call the function now!
|
||||
|
||||
@@ -110,13 +110,13 @@ class LuaFunction: public LuaValue {
|
||||
* @exception LuaException If an error occurs while executing the function an exception is thrown
|
||||
* with the message of the error.
|
||||
*/
|
||||
LuaValueList call(const LuaValueList& arguments = LuaValueList());
|
||||
LuaValueList call(const LuaValueList& arguments = LuaValueList()) const;
|
||||
|
||||
/**
|
||||
* @brief Calls the function. See call().
|
||||
* @return Same as call().
|
||||
*/
|
||||
LuaValueList operator()(const LuaValueList& arguments = LuaValueList());
|
||||
LuaValueList operator()(const LuaValueList& arguments = LuaValueList()) const;
|
||||
private:
|
||||
LuaReference _errorFunction;
|
||||
};
|
||||
|
||||
+128
-56
@@ -174,6 +174,36 @@ void script_parse_table(const char* filename)
|
||||
return;
|
||||
}
|
||||
}
|
||||
void script_parse_lua_script(const char *filename) {
|
||||
using namespace luacpp;
|
||||
|
||||
CFILE *cfp = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_TABLES);
|
||||
if(cfp == nullptr)
|
||||
{
|
||||
Warning(LOCATION, "Could not open lua script file '%s'", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
int len = cfilelength(cfp);
|
||||
|
||||
SCP_string source;
|
||||
source.resize((size_t) len);
|
||||
cfread(&source[0], len, 1, cfp);
|
||||
cfclose(cfp);
|
||||
|
||||
try {
|
||||
auto function = LuaFunction::createFromCode(Script_system.GetLuaSession(), source, filename);
|
||||
function.setErrorFunction(LuaFunction::createFromCFunction(Script_system.GetLuaSession(), ade_friendly_error));
|
||||
|
||||
script_function func;
|
||||
func.language = SC_LUA;
|
||||
func.function = function;
|
||||
|
||||
Script_system.AddGameInitFunction(func);
|
||||
} catch (const LuaException& e) {
|
||||
LuaError(Script_system.GetLuaSession(), "Failed to parse %s: %s", filename, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
static void json_doc_generate_class(json_t* elObj, const DocumentationElementClass* lib)
|
||||
{
|
||||
@@ -375,6 +405,8 @@ void script_init()
|
||||
mprintf(("SCRIPTING: Beginning main hook parse sequence....\n"));
|
||||
script_parse_table("scripting.tbl");
|
||||
parse_modular_table(NOX("*-sct.tbm"), script_parse_table);
|
||||
mprintf(("SCRIPTING: Parsing pure Lua scripts\n"));
|
||||
parse_modular_table(NOX("*-sct.lua"), script_parse_lua_script);
|
||||
mprintf(("SCRIPTING: Inititialization complete.\n"));
|
||||
}
|
||||
/*
|
||||
@@ -436,7 +468,7 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
case CHC_STATE:
|
||||
if(gameseq_get_depth() < 0)
|
||||
return false;
|
||||
if(stricmp(GS_state_text[gameseq_get_state(0)], scp->data.name) != 0)
|
||||
if(stricmp(GS_state_text[gameseq_get_state(0)], scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
break;
|
||||
case CHC_SHIPTYPE:
|
||||
@@ -445,19 +477,19 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
sip = &Ship_info[Ships[objp->instance].ship_info_index];
|
||||
if(sip->class_type < 0)
|
||||
return false;
|
||||
if(stricmp(Ship_types[sip->class_type].name, scp->data.name) != 0)
|
||||
if(stricmp(Ship_types[sip->class_type].name, scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
break;
|
||||
case CHC_SHIPCLASS:
|
||||
if(objp == NULL || objp->type != OBJ_SHIP)
|
||||
return false;
|
||||
if(stricmp(Ship_info[Ships[objp->instance].ship_info_index].name, scp->data.name) != 0)
|
||||
if(stricmp(Ship_info[Ships[objp->instance].ship_info_index].name, scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
break;
|
||||
case CHC_SHIP:
|
||||
if(objp == NULL || objp->type != OBJ_SHIP)
|
||||
return false;
|
||||
if(stricmp(Ships[objp->instance].ship_name, scp->data.name) != 0)
|
||||
if(stricmp(Ships[objp->instance].ship_name, scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
break;
|
||||
case CHC_MISSION:
|
||||
@@ -470,7 +502,7 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
return false;
|
||||
if(len > 4 && !stricmp(&Mission_filename[len-4], ".fs2"))
|
||||
len -= 4;
|
||||
if(strnicmp(scp->data.name, Mission_filename, len) != 0)
|
||||
if(strnicmp(scp->condition_string.c_str(), Mission_filename, len) != 0)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@@ -481,21 +513,21 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
return false;
|
||||
if(len > 4 && !stricmp(&Mission_filename[len-4], ".fc2"))
|
||||
len -= 4;
|
||||
if(strnicmp(scp->data.name, Mission_filename, len) != 0)
|
||||
if(strnicmp(scp->condition_string.c_str(), Mission_filename, len) != 0)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case CHC_WEAPONCLASS:
|
||||
{
|
||||
if (action == CHA_COLLIDEWEAPON) {
|
||||
if (stricmp(Weapon_info[more_data].name, scp->data.name) != 0)
|
||||
if (stricmp(Weapon_info[more_data].name, scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
} else if (!(action == CHA_ONWPSELECTED || action == CHA_ONWPDESELECTED || action == CHA_ONWPEQUIPPED || action == CHA_ONWPFIRED || action == CHA_ONTURRETFIRED )) {
|
||||
if(objp == NULL || (objp->type != OBJ_WEAPON && objp->type != OBJ_BEAM))
|
||||
return false;
|
||||
else if (( objp->type == OBJ_WEAPON) && (stricmp(Weapon_info[Weapons[objp->instance].weapon_info_index].name, scp->data.name) != 0 ))
|
||||
else if (( objp->type == OBJ_WEAPON) && (stricmp(Weapon_info[Weapons[objp->instance].weapon_info_index].name, scp->condition_string.c_str()) != 0 ))
|
||||
return false;
|
||||
else if (( objp->type == OBJ_BEAM) && (stricmp(Weapon_info[Beams[objp->instance].weapon_info_index].name, scp->data.name) != 0 ))
|
||||
else if (( objp->type == OBJ_BEAM) && (stricmp(Weapon_info[Beams[objp->instance].weapon_info_index].name, scp->condition_string.c_str()) != 0 ))
|
||||
return false;
|
||||
} else if(objp == NULL || objp->type != OBJ_SHIP) {
|
||||
return false;
|
||||
@@ -509,27 +541,35 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
bool primary = false, secondary = false, prev_primary = false, prev_secondary = false;
|
||||
switch (action) {
|
||||
case CHA_ONWPSELECTED:
|
||||
if (shipp->weapons.current_primary_bank >= 0)
|
||||
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->data.name) == 0;
|
||||
if (shipp->weapons.current_secondary_bank >= 0)
|
||||
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->data.name) == 0;
|
||||
|
||||
if (!(primary || secondary))
|
||||
return false;
|
||||
if (shipp->weapons.current_primary_bank >= 0) {
|
||||
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->condition_string.c_str()) == 0;
|
||||
}
|
||||
if (shipp->weapons.current_secondary_bank >= 0) {
|
||||
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->condition_string.c_str()) == 0;
|
||||
}
|
||||
|
||||
if ((shipp->flags[Ship::Ship_Flags::Primary_linked]) && primary && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].wi_flags[Weapon::Info_Flags::Nolink]))
|
||||
if (!(primary || secondary)) {
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if ((shipp->flags[Ship::Ship_Flags::Primary_linked]) && primary && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].wi_flags[Weapon::Info_Flags::Nolink])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
case CHA_ONWPDESELECTED:
|
||||
if (shipp->weapons.current_primary_bank >= 0)
|
||||
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->data.name) == 0;
|
||||
if (shipp->weapons.previous_primary_bank >= 0)
|
||||
prev_primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.previous_primary_bank]].name, scp->data.name) == 0;
|
||||
if (shipp->weapons.current_secondary_bank >= 0)
|
||||
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->data.name) == 0;
|
||||
if (shipp->weapons.previous_secondary_bank >= 0)
|
||||
prev_secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.previous_secondary_bank]].name, scp->data.name) == 0;
|
||||
if (shipp->weapons.current_primary_bank >= 0) {
|
||||
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->condition_string.c_str()) == 0;
|
||||
}
|
||||
if (shipp->weapons.previous_primary_bank >= 0) {
|
||||
prev_primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.previous_primary_bank]].name, scp->condition_string.c_str()) == 0;
|
||||
}
|
||||
if (shipp->weapons.current_secondary_bank >= 0) {
|
||||
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->condition_string.c_str()) == 0;
|
||||
}
|
||||
if (shipp->weapons.previous_secondary_bank >= 0) {
|
||||
prev_secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.previous_secondary_bank]].name, scp->condition_string.c_str()) == 0;
|
||||
}
|
||||
|
||||
if ((shipp->flags[Ship::Ship_Flags::Primary_linked]) && prev_primary && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.previous_primary_bank]].wi_flags[Weapon::Info_Flags::Nolink]))
|
||||
return true;
|
||||
@@ -537,10 +577,10 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
if ( !prev_secondary && ! secondary && !prev_primary && !primary )
|
||||
return false;
|
||||
|
||||
if ( (!prev_secondary && !secondary) && (prev_primary && primary) )
|
||||
if ( (!prev_secondary && !secondary) && (prev_primary && primary) )
|
||||
return false;
|
||||
|
||||
if ( (!prev_secondary && !secondary) && (!prev_primary && primary) )
|
||||
if ( (!prev_secondary && !secondary) && (!prev_primary && primary) )
|
||||
return false;
|
||||
|
||||
if ( (!prev_primary && !primary) && (prev_secondary && secondary) )
|
||||
@@ -554,7 +594,8 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
bool equipped = false;
|
||||
for(int j = 0; j < MAX_SHIP_PRIMARY_BANKS; j++) {
|
||||
if (!equipped && (shipp->weapons.primary_bank_weapons[j] >= 0) && (shipp->weapons.primary_bank_weapons[j] < weapon_info_size()) ) {
|
||||
if ( !stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[j]].name, scp->data.name) ) {
|
||||
if (!stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[j]].name,
|
||||
scp->condition_string.c_str())) {
|
||||
equipped = true;
|
||||
break;
|
||||
}
|
||||
@@ -564,7 +605,8 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
if (!equipped) {
|
||||
for(int j = 0; j < MAX_SHIP_SECONDARY_BANKS; j++) {
|
||||
if (!equipped && (shipp->weapons.secondary_bank_weapons[j] >= 0) && (shipp->weapons.secondary_bank_weapons[j] < weapon_info_size()) ) {
|
||||
if ( !stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[j]].name, scp->data.name) ) {
|
||||
if (!stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[j]].name,
|
||||
scp->condition_string.c_str())) {
|
||||
equipped = true;
|
||||
break;
|
||||
}
|
||||
@@ -579,11 +621,11 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
}
|
||||
case CHA_ONWPFIRED: {
|
||||
if (more_data == 1) {
|
||||
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->data.name) == 0;
|
||||
primary = stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->condition_string.c_str()) == 0;
|
||||
secondary = false;
|
||||
} else {
|
||||
primary = false;
|
||||
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->data.name) == 0;
|
||||
secondary = stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->condition_string.c_str()) == 0;
|
||||
}
|
||||
|
||||
if ((shipp->flags[Ship::Ship_Flags::Primary_linked]) && primary && (Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].wi_flags[Weapon::Info_Flags::Nolink]))
|
||||
@@ -594,22 +636,22 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
break;
|
||||
}
|
||||
case CHA_ONTURRETFIRED: {
|
||||
if (! (stricmp(Weapon_info[shipp->last_fired_turret->last_fired_weapon_info_index].name, scp->data.name) == 0))
|
||||
if (! (stricmp(Weapon_info[shipp->last_fired_turret->last_fired_weapon_info_index].name, scp->condition_string.c_str()) == 0))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case CHA_PRIMARYFIRE: {
|
||||
if (stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->data.name) != 0)
|
||||
if (stricmp(Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name, scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case CHA_SECONDARYFIRE: {
|
||||
if (stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->data.name) != 0)
|
||||
if (stricmp(Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name, scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case CHA_BEAMFIRE: {
|
||||
if (!(stricmp(Weapon_info[more_data].name, scp->data.name) == 0))
|
||||
if (!(stricmp(Weapon_info[more_data].name, scp->condition_string.c_str()) == 0))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@@ -622,7 +664,7 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
case CHC_OBJECTTYPE:
|
||||
if(objp == NULL)
|
||||
return false;
|
||||
if(stricmp(Object_type_names[objp->type], scp->data.name) != 0)
|
||||
if(stricmp(Object_type_names[objp->type], scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
break;
|
||||
case CHC_KEYPRESS:
|
||||
@@ -633,7 +675,7 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
if(Current_key_down == 0)
|
||||
return false;
|
||||
//WMC - could be more efficient, but whatever.
|
||||
if(stricmp(textify_scancode(Current_key_down), scp->data.name) != 0)
|
||||
if(stricmp(textify_scancode(Current_key_down), scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@@ -644,7 +686,7 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
|
||||
int action_index = more_data;
|
||||
|
||||
if (action_index <= 0 || stricmp(scp->data.name, Control_config[action_index].text) != 0)
|
||||
if (action_index <= 0 || stricmp(scp->condition_string.c_str(), Control_config[action_index].text) != 0)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@@ -653,13 +695,13 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
// Goober5000: I'm going to assume scripting doesn't care about SVN revision
|
||||
char buf[32];
|
||||
sprintf(buf, "%i.%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD);
|
||||
if(stricmp(buf, scp->data.name) != 0)
|
||||
if(stricmp(buf, scp->condition_string.c_str()) != 0)
|
||||
{
|
||||
//In case some people are lazy and say "3.7" instead of "3.7.0" or something
|
||||
if(FS_VERSION_BUILD == 0)
|
||||
{
|
||||
sprintf(buf, "%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR);
|
||||
if(stricmp(buf, scp->data.name) != 0)
|
||||
if(stricmp(buf, scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@@ -673,12 +715,12 @@ bool ConditionedHook::ConditionsValid(int action, object *objp, int more_data)
|
||||
{
|
||||
if(Fred_running)
|
||||
{
|
||||
if(stricmp("FRED2_Open", scp->data.name) != 0 && stricmp("FRED2Open", scp->data.name) != 0 && stricmp("FRED 2", scp->data.name) != 0 && stricmp("FRED", scp->data.name) != 0)
|
||||
if(stricmp("FRED2_Open", scp->condition_string.c_str()) != 0 && stricmp("FRED2Open", scp->condition_string.c_str()) != 0 && stricmp("FRED 2", scp->condition_string.c_str()) != 0 && stricmp("FRED", scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(stricmp("FS2_Open", scp->data.name) != 0 && stricmp("FS2Open", scp->data.name) != 0 && stricmp("Freespace 2", scp->data.name) != 0 && stricmp("Freespace", scp->data.name) != 0)
|
||||
if(stricmp("FS2_Open", scp->condition_string.c_str()) != 0 && stricmp("FS2Open", scp->condition_string.c_str()) != 0 && stricmp("Freespace 2", scp->condition_string.c_str()) != 0 && stricmp("Freespace", scp->condition_string.c_str()) != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1282,13 +1324,13 @@ int script_state::RunBytecode(script_function& hd)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int script_parse_condition()
|
||||
ConditionalType script_parse_condition()
|
||||
{
|
||||
char buf[NAME_LENGTH];
|
||||
for (int i = 0; i < Num_script_conditions; i++) {
|
||||
sprintf(buf, "$%s:", Script_conditions[i].name);
|
||||
if (optional_string(buf))
|
||||
return Script_conditions[i].def;
|
||||
if(optional_string(buf))
|
||||
return static_cast<ConditionalType>(Script_conditions[i].def);
|
||||
}
|
||||
|
||||
return CHC_NONE;
|
||||
@@ -1305,7 +1347,29 @@ const scripting::HookBase* script_parse_action()
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
void script_state::ParseGlobalChunk(int hookType, const char* debug_str) {
|
||||
|
||||
int32_t scripting_string_to_action(const char* action)
|
||||
{
|
||||
for (const auto& hook : scripting::getHooks()) {
|
||||
if (hook->getHookName() == action)
|
||||
return hook->getHookId();
|
||||
}
|
||||
|
||||
return CHA_NONE;
|
||||
}
|
||||
|
||||
ConditionalType scripting_string_to_condition(const char* condition)
|
||||
{
|
||||
for (int i = 0; i < Num_script_conditions; i++) {
|
||||
if (!stricmp(Script_conditions[i].name, condition)) {
|
||||
return static_cast<ConditionalType>(Script_conditions[i].def);
|
||||
}
|
||||
}
|
||||
|
||||
return CHC_NONE;
|
||||
}
|
||||
|
||||
void script_state::ParseGlobalChunk(ConditionalActions hookType, const char* debug_str) {
|
||||
ConditionedHook hook;
|
||||
|
||||
script_action sat;
|
||||
@@ -1320,10 +1384,9 @@ void script_state::ParseGlobalChunk(int hookType, const char* debug_str) {
|
||||
}
|
||||
bool script_state::ParseCondition(const char *filename)
|
||||
{
|
||||
ConditionedHook *chp = NULL;
|
||||
int condition;
|
||||
ConditionedHook *chp = nullptr;
|
||||
|
||||
for(condition = script_parse_condition(); condition != CHC_NONE; condition = script_parse_condition())
|
||||
for(auto condition = script_parse_condition(); condition != CHC_NONE; condition = script_parse_condition())
|
||||
{
|
||||
script_condition sct;
|
||||
sct.condition_type = condition;
|
||||
@@ -1341,7 +1404,7 @@ bool script_state::ParseCondition(const char *filename)
|
||||
case CHC_VERSION:
|
||||
case CHC_APPLICATION:
|
||||
default:
|
||||
stuff_string(sct.data.name, F_NAME, CONDITION_LENGTH);
|
||||
stuff_string(sct.condition_string, F_NAME);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1368,13 +1431,10 @@ bool script_state::ParseCondition(const char *filename)
|
||||
sat.action_type = action->getHookId();
|
||||
|
||||
// WMC - build error string
|
||||
char* buf = (char*)vm_malloc(strlen(filename) + action->getHookName().size() + 4);
|
||||
SCP_string buf;
|
||||
sprintf(buf, "%s - %s", filename, action->getHookName().c_str());
|
||||
|
||||
ParseChunk(&sat.hook, buf);
|
||||
|
||||
// Free error string
|
||||
vm_free(buf);
|
||||
ParseChunk(&sat.hook, buf.c_str());
|
||||
|
||||
// Add the action
|
||||
if (chp->AddAction(&sat))
|
||||
@@ -1391,6 +1451,10 @@ bool script_state::ParseCondition(const char *filename)
|
||||
return true;
|
||||
}
|
||||
|
||||
void script_state::AddConditionedHook(ConditionedHook hook) { ConditionalHooks.push_back(std::move(hook)); }
|
||||
|
||||
void script_state::AddGameInitFunction(script_function func) { GameInitFunctions.push_back(std::move(func)); }
|
||||
|
||||
//*************************CLASS: script_state*************************
|
||||
bool script_state::IsOverride(script_hook &hd)
|
||||
{
|
||||
@@ -1403,6 +1467,14 @@ bool script_state::IsOverride(script_hook &hd)
|
||||
return b;
|
||||
}
|
||||
|
||||
void script_state::RunInitFunctions() {
|
||||
for (const auto& initFunc : GameInitFunctions) {
|
||||
initFunc.function();
|
||||
}
|
||||
// We don't need this anymore so no need to keep references to those functions around anymore
|
||||
GameInitFunctions.clear();
|
||||
}
|
||||
|
||||
void scripting_state_init()
|
||||
{
|
||||
// nothing to do here
|
||||
|
||||
+32
-30
@@ -43,20 +43,22 @@ extern bool script_hook_valid(script_hook *hook);
|
||||
|
||||
#define MAX_HOOK_CONDITIONS 8
|
||||
|
||||
//Conditionals
|
||||
#define CHC_NONE -1
|
||||
#define CHC_MISSION 0
|
||||
#define CHC_SHIP 1
|
||||
#define CHC_SHIPCLASS 2
|
||||
#define CHC_SHIPTYPE 3
|
||||
#define CHC_STATE 4
|
||||
#define CHC_CAMPAIGN 5
|
||||
#define CHC_WEAPONCLASS 6
|
||||
#define CHC_OBJECTTYPE 7
|
||||
#define CHC_KEYPRESS 8
|
||||
#define CHC_ACTION 9
|
||||
#define CHC_VERSION 10
|
||||
#define CHC_APPLICATION 11
|
||||
// Conditionals
|
||||
enum ConditionalType {
|
||||
CHC_NONE = -1,
|
||||
CHC_MISSION = 0,
|
||||
CHC_SHIP = 1,
|
||||
CHC_SHIPCLASS = 2,
|
||||
CHC_SHIPTYPE = 3,
|
||||
CHC_STATE = 4,
|
||||
CHC_CAMPAIGN = 5,
|
||||
CHC_WEAPONCLASS = 6,
|
||||
CHC_OBJECTTYPE = 7,
|
||||
CHC_KEYPRESS = 8,
|
||||
CHC_ACTION = 9,
|
||||
CHC_VERSION = 10,
|
||||
CHC_APPLICATION = 11,
|
||||
};
|
||||
|
||||
//Actions
|
||||
enum ConditionalActions : int32_t {
|
||||
@@ -116,25 +118,18 @@ void scripting_state_init();
|
||||
void scripting_state_close();
|
||||
void scripting_state_do_frame(float frametime);
|
||||
|
||||
class script_condition
|
||||
{
|
||||
public:
|
||||
int condition_type{CHC_NONE};
|
||||
union
|
||||
{
|
||||
char name[CONDITION_LENGTH];
|
||||
} data;
|
||||
int32_t scripting_string_to_action(const char* action);
|
||||
ConditionalType scripting_string_to_condition(const char* condition);
|
||||
|
||||
script_condition()
|
||||
{
|
||||
memset(data.name, 0, sizeof(data.name));
|
||||
}
|
||||
struct script_condition
|
||||
{
|
||||
ConditionalType condition_type = CHC_NONE;
|
||||
SCP_string condition_string;
|
||||
};
|
||||
|
||||
class script_action
|
||||
struct script_action
|
||||
{
|
||||
public:
|
||||
int action_type{CHA_NONE};
|
||||
int32_t action_type {CHA_NONE};
|
||||
script_hook hook;
|
||||
};
|
||||
|
||||
@@ -235,6 +230,8 @@ private:
|
||||
SCP_vector<image_desc> ScriptImages;
|
||||
SCP_vector<ConditionedHook> ConditionalHooks;
|
||||
|
||||
SCP_vector<script_function> GameInitFunctions;
|
||||
|
||||
private:
|
||||
|
||||
void ParseChunkSub(script_function& out_func, const char* debug_str=NULL);
|
||||
@@ -294,8 +291,11 @@ public:
|
||||
const char* debug_str = nullptr);
|
||||
bool EvalString(const char* string, const char* debug_str = nullptr);
|
||||
void ParseChunk(script_hook *dest, const char* debug_str=NULL);
|
||||
void ParseGlobalChunk(int hookType, const char* debug_str=NULL);
|
||||
void ParseGlobalChunk(ConditionalActions hookType, const char* debug_str=nullptr);
|
||||
bool ParseCondition(const char *filename="<Unknown>");
|
||||
void AddConditionedHook(ConditionedHook hook);
|
||||
|
||||
void AddGameInitFunction(script_function func);
|
||||
|
||||
//***Hook running functions
|
||||
template <typename T>
|
||||
@@ -305,6 +305,8 @@ public:
|
||||
int RunCondition(int condition, object* objp = nullptr, int more_data = 0);
|
||||
bool IsConditionOverride(int action, object *objp=NULL);
|
||||
|
||||
void RunInitFunctions();
|
||||
|
||||
//*****Other functions
|
||||
void EndFrame();
|
||||
|
||||
|
||||
+351
-83
@@ -446,6 +446,7 @@ flag_def_list_new<Weapon::Info_Flags> ai_tgt_weapon_flags[] = {
|
||||
{ "big ship", Weapon::Info_Flags::Big_only, true, false },
|
||||
{ "child", Weapon::Info_Flags::Child, true, false },
|
||||
{ "no dumbfire", Weapon::Info_Flags::No_dumbfire, true, false },
|
||||
{ "no doublefire", Weapon::Info_Flags::No_doublefire, true, false },
|
||||
{ "thruster", Weapon::Info_Flags::Thruster, true, false },
|
||||
{ "in tech database", Weapon::Info_Flags::In_tech_database, true, false },
|
||||
{ "player allowed", Weapon::Info_Flags::Player_allowed, true, false },
|
||||
@@ -1611,7 +1612,7 @@ ship_info::ship_info()
|
||||
|
||||
sup_hull_repair_rate = 0.15f;
|
||||
sup_shield_repair_rate = 0.20f;
|
||||
sup_subsys_repair_rate = 0.15f;
|
||||
sup_subsys_repair_rate = 0.15f; // The retail default is 0.10, but it actually used the hull rate of 0.15
|
||||
|
||||
vm_vec_zero(&closeup_pos);
|
||||
closeup_zoom = 0.5f;
|
||||
@@ -1819,7 +1820,7 @@ static void parse_ship(const char *filename, bool replace)
|
||||
}
|
||||
|
||||
//Remove @ symbol
|
||||
//these used to be used to denote weapons that would
|
||||
//these used to denote weapons that would
|
||||
//only be parsed in demo builds
|
||||
if ( fname[0] == '@' ) {
|
||||
backspace(fname);
|
||||
@@ -6036,6 +6037,33 @@ const char* ship::get_display_string() {
|
||||
}
|
||||
}
|
||||
|
||||
void ship::apply_replacement_textures(SCP_vector<texture_replace> &replacements)
|
||||
{
|
||||
if (!replacements.empty())
|
||||
{
|
||||
ship_replacement_textures = (int *) vm_malloc( MAX_REPLACEMENT_TEXTURES * sizeof(int));
|
||||
|
||||
for (auto i = 0; i < MAX_REPLACEMENT_TEXTURES; i++)
|
||||
ship_replacement_textures[i] = -1;
|
||||
}
|
||||
|
||||
// now fill them in
|
||||
for (auto tr : replacements)
|
||||
{
|
||||
auto pm = model_get(Ship_info[ship_info_index].model_num);
|
||||
|
||||
// look for textures
|
||||
for (auto j = 0; j < pm->n_textures; j++)
|
||||
{
|
||||
texture_map *tmap = &pm->maps[j];
|
||||
|
||||
int tnum = tmap->FindTexture(tr.old_texture);
|
||||
if(tnum > -1)
|
||||
ship_replacement_textures[j * TM_NUM_TYPES + tnum] = tr.new_texture_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ship_weapon::clear()
|
||||
{
|
||||
flags.reset();
|
||||
@@ -6899,9 +6927,16 @@ void ship_render_cockpit(object *objp)
|
||||
//Deal with the model
|
||||
model_clear_instance(sip->cockpit_model_num);
|
||||
|
||||
uint render_flags = MR_NORMAL;
|
||||
render_flags |= MR_NO_FOGGING;
|
||||
|
||||
if (shipp->flags[Ship::Ship_Flags::Glowmaps_disabled]) {
|
||||
render_flags |= MR_NO_GLOWMAPS;
|
||||
}
|
||||
|
||||
model_render_params render_info;
|
||||
render_info.set_detail_level_lock(0);
|
||||
render_info.set_flags(MR_NO_FOGGING);
|
||||
render_info.set_flags(render_flags);
|
||||
render_info.set_replacement_textures(Player_cockpit_textures);
|
||||
|
||||
model_render_immediate(&render_info, sip->cockpit_model_num, &eye_ori, &pos);
|
||||
@@ -6931,6 +6966,7 @@ void ship_render_show_ship_cockpit(object *objp)
|
||||
model_render_params render_info;
|
||||
|
||||
render_info.set_object_number(OBJ_INDEX(objp));
|
||||
render_info.set_replacement_textures(Ships[objp->instance].ship_replacement_textures);
|
||||
render_info.set_detail_level_lock(0);
|
||||
|
||||
model_render_immediate(&render_info, Ship_info[Ships[objp->instance].ship_info_index].model_num, &objp->orient,
|
||||
@@ -10610,8 +10646,6 @@ int ship_stop_fire_primary(object * obj)
|
||||
|
||||
int tracers[MAX_SHIPS][4][4];
|
||||
|
||||
float ship_get_subsystem_strength( ship *shipp, int type );
|
||||
|
||||
/**
|
||||
* Checks whether a ship would use autoaim if it were to fire its primaries at
|
||||
* the given object, taking lead into account.
|
||||
@@ -11810,33 +11844,34 @@ int ship_fire_secondary( object *obj, int allow_swarm )
|
||||
|
||||
// Ensure if this is a "require-lock" missile, that a lock actually exists
|
||||
if ( wip->wi_flags[Weapon::Info_Flags::No_dumbfire] ) {
|
||||
if ( aip->current_target_is_locked <= 0 ) {
|
||||
if ( obj == Player_obj ) {
|
||||
if ( !Weapon_energy_cheat ) {
|
||||
if (!aip->current_target_is_locked && !ship_lock_present(shipp) && !(shipp->missile_locks_firing.size() > 0)) {
|
||||
if (obj == Player_obj) {
|
||||
if (!Weapon_energy_cheat) {
|
||||
float max_dist;
|
||||
|
||||
max_dist = wip->lifetime * wip->max_speed;
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Local_ssm]){
|
||||
max_dist= wip->lssm_lock_range;
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Local_ssm]) {
|
||||
max_dist = wip->lssm_lock_range;
|
||||
}
|
||||
|
||||
if ((aip->target_objnum != -1) && (vm_vec_dist_quick(&obj->pos, &Objects[aip->target_objnum].pos) > max_dist)) {
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Too far from target to acquire lock", 487));
|
||||
if ((aip->target_objnum != -1) &&
|
||||
(vm_vec_dist_quick(&obj->pos, &Objects[aip->target_objnum].pos) > max_dist)) {
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Too far from target to acquire lock", 487));
|
||||
} else {
|
||||
char missile_name[NAME_LENGTH];
|
||||
strcpy_s(missile_name, wip->get_display_string());
|
||||
end_string_at_first_hash_symbol(missile_name);
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Cannot fire %s without a lock", 488), missile_name);
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("Cannot fire %s without a lock", 488), missile_name);
|
||||
}
|
||||
|
||||
snd_play( gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::OUT_OF_MISSLES)) );
|
||||
swp->next_secondary_fire_stamp[bank] = timestamp(800); // to avoid repeating messages
|
||||
snd_play(gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::OUT_OF_MISSLES)));
|
||||
swp->next_secondary_fire_stamp[bank] = timestamp(800); // to avoid repeating messages
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
// multiplayer clients should always fire the weapon here, so return only if not
|
||||
// a multiplayer client.
|
||||
if ( !MULTIPLAYER_CLIENT ) {
|
||||
if (!MULTIPLAYER_CLIENT) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -11867,16 +11902,21 @@ int ship_fire_secondary( object *obj, int allow_swarm )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( !allow_swarm && obj == Player_obj ) {
|
||||
ship_queue_missile_locks(shipp);
|
||||
}
|
||||
|
||||
// if trying to fire a swarm missile, make sure being called from right place
|
||||
if ( (wip->wi_flags[Weapon::Info_Flags::Swarm]) && !allow_swarm ) {
|
||||
Assert(wip->swarm_count > 0);
|
||||
if(wip->swarm_count <= 0){
|
||||
if (wip->multi_lock && obj == Player_obj) {
|
||||
shipp->num_swarm_missiles_to_fire = (int)shipp->missile_locks_firing.size();
|
||||
} else if(wip->swarm_count <= 0){
|
||||
shipp->num_swarm_missiles_to_fire = SWARM_DEFAULT_NUM_MISSILES_FIRED;
|
||||
} else {
|
||||
shipp->num_swarm_missiles_to_fire = wip->swarm_count;
|
||||
}
|
||||
|
||||
shipp->swarm_missile_bank = bank;
|
||||
return 1; // Note: Missiles didn't get fired, but the frame interval code will fire them.
|
||||
}
|
||||
@@ -11885,7 +11925,11 @@ int ship_fire_secondary( object *obj, int allow_swarm )
|
||||
if ( (wip->wi_flags[Weapon::Info_Flags::Corkscrew]) && !allow_swarm ) {
|
||||
//phreak 11-9-02
|
||||
//changed this from 4 to custom number defined in tables
|
||||
shipp->num_corkscrew_to_fire = (ubyte)(shipp->num_corkscrew_to_fire + (ubyte)wip->cs_num_fired);
|
||||
if (wip->multi_lock && obj == Player_obj) {
|
||||
shipp->num_corkscrew_to_fire = (ubyte)shipp->missile_locks_firing.size();
|
||||
} else {
|
||||
shipp->num_corkscrew_to_fire = (ubyte)(shipp->num_corkscrew_to_fire + (ubyte)wip->cs_num_fired);
|
||||
}
|
||||
shipp->corkscrew_missile_bank = bank;
|
||||
return 1; // Note: Missiles didn't get fired, but the frame interval code will fire them.
|
||||
}
|
||||
@@ -11899,7 +11943,7 @@ int ship_fire_secondary( object *obj, int allow_swarm )
|
||||
t = Weapon_info[weapon_idx].fire_wait; // They can fire 5 times a second
|
||||
swp->burst_counter[bank_adjusted] = 0;
|
||||
}
|
||||
swp->next_secondary_fire_stamp[bank] = timestamp((int) (t * 1000.0f));
|
||||
swp->next_secondary_fire_stamp[bank] = timestamp((int)(t * 1000.0f));
|
||||
swp->last_secondary_fire_stamp[bank] = timestamp();
|
||||
|
||||
// Here is where we check if weapons subsystem is capable of firing the weapon.
|
||||
@@ -11927,6 +11971,33 @@ int ship_fire_secondary( object *obj, int allow_swarm )
|
||||
return 0; // we can make a quick out here!!!
|
||||
}
|
||||
|
||||
int target_objnum;
|
||||
ship_subsys *target_subsys;
|
||||
int locked;
|
||||
|
||||
if ( obj == Player_obj ) {
|
||||
// use missile lock slots
|
||||
if ( shipp->missile_locks_firing.size() > 0 ) {
|
||||
lock_info lock_data = shipp->missile_locks_firing.back();
|
||||
|
||||
if ( wip->multi_lock ) {
|
||||
shipp->missile_locks_firing.pop_back();
|
||||
}
|
||||
|
||||
target_objnum = OBJ_INDEX(lock_data.obj);
|
||||
target_subsys = lock_data.subsys;
|
||||
locked = 1;
|
||||
} else {
|
||||
target_objnum = -1;
|
||||
target_subsys = nullptr;
|
||||
locked = 0;
|
||||
}
|
||||
} else {
|
||||
target_objnum = aip->target_objnum;
|
||||
target_subsys = aip->targeted_subsys;
|
||||
locked = aip->current_target_is_locked;
|
||||
}
|
||||
|
||||
num_slots = pm->missile_banks[bank].num_slots;
|
||||
|
||||
// determine if there is enough ammo left to fire weapons on this bank. As with primary
|
||||
@@ -11950,6 +12021,21 @@ int ship_fire_secondary( object *obj, int allow_swarm )
|
||||
goto done_secondary;
|
||||
}
|
||||
|
||||
// Handle the optional disabling of dual fire
|
||||
// dual fire/doublefire can be disabled for individual weapons, for players, or for AIs
|
||||
// if any of these apply to the current weapon, unset the dual fire flag on the ship
|
||||
// then proceed as normal.
|
||||
// This is only handled at firing time so dualfire isn't lost when cycling through weapons
|
||||
if (shipp->flags[Ship_Flags::Secondary_dual_fire] &&
|
||||
( wip->wi_flags[Weapon::Info_Flags::No_doublefire] ||
|
||||
( The_mission.ai_profile->flags[AI::Profile_Flags::Disable_ai_secondary_doublefire] &&
|
||||
shipp->objnum != OBJ_INDEX(Player_obj) ) ||
|
||||
( The_mission.ai_profile->flags[AI::Profile_Flags::Disable_player_secondary_doublefire] &&
|
||||
shipp->objnum == OBJ_INDEX(Player_obj) ))
|
||||
) {
|
||||
shipp->flags.remove(Ship_Flags::Secondary_dual_fire);
|
||||
}
|
||||
|
||||
int start_slot, end_slot;
|
||||
|
||||
if ( shipp->flags[Ship_Flags::Secondary_dual_fire] && num_slots > 1) {
|
||||
@@ -12022,7 +12108,7 @@ int ship_fire_secondary( object *obj, int allow_swarm )
|
||||
|
||||
// create the weapon -- for multiplayer, the net_signature is assigned inside
|
||||
// of weapon_create
|
||||
weapon_num = weapon_create( &firing_pos, &firing_orient, weapon_idx, OBJ_INDEX(obj), -1, aip->current_target_is_locked);
|
||||
weapon_num = weapon_create( &firing_pos, &firing_orient, weapon_idx, OBJ_INDEX(obj), -1, locked);
|
||||
|
||||
if (weapon_num == -1) {
|
||||
// Weapon most likely failed to fire
|
||||
@@ -12034,7 +12120,7 @@ int ship_fire_secondary( object *obj, int allow_swarm )
|
||||
|
||||
if (weapon_num >= 0) {
|
||||
weapon_idx = Weapons[Objects[weapon_num].instance].weapon_info_index;
|
||||
weapon_set_tracking_info(weapon_num, OBJ_INDEX(obj), aip->target_objnum, aip->current_target_is_locked, aip->targeted_subsys);
|
||||
weapon_set_tracking_info(weapon_num, OBJ_INDEX(obj), target_objnum, locked, target_subsys);
|
||||
has_fired = true;
|
||||
|
||||
// create the muzzle flash effect
|
||||
@@ -13303,7 +13389,7 @@ int ship_get_subsys_index(ship *shipp, ship_subsys *subsys)
|
||||
// 0.0 and 1.0 which is the relative combined strength of the given subsystem type. The number
|
||||
// calculated for the engines is slightly different. Once an engine reaches < 15% of its hits, its
|
||||
// output drops to that %. A dead engine has no output.
|
||||
float ship_get_subsystem_strength( ship *shipp, int type )
|
||||
float ship_get_subsystem_strength( ship *shipp, int type, bool skip_dying_check )
|
||||
{
|
||||
float strength;
|
||||
ship_subsys *ssp;
|
||||
@@ -13311,7 +13397,7 @@ float ship_get_subsystem_strength( ship *shipp, int type )
|
||||
Assert ( (type >= 0) && (type < SUBSYSTEM_MAX) );
|
||||
|
||||
// For a dying ship, all subsystem strengths are zero.
|
||||
if (Objects[shipp->objnum].hull_strength <= 0.0f)
|
||||
if (Objects[shipp->objnum].hull_strength <= 0.0f && !skip_dying_check)
|
||||
return 0.0f;
|
||||
|
||||
// short circuit 1
|
||||
@@ -13515,8 +13601,8 @@ float ship_calculate_rearm_duration( object *objp )
|
||||
//
|
||||
int ship_do_rearm_frame( object *objp, float frametime )
|
||||
{
|
||||
int i, banks_full, primary_banks_full, subsys_type, subsys_all_ok, last_ballistic_idx = -1;
|
||||
float shield_str, max_shield_str = 0.0f, repair_delta, repair_allocated, max_hull_repair, max_subsys_repair;
|
||||
int i, banks_full, primary_banks_full, subsys_type, last_ballistic_idx = -1;
|
||||
float shield_str, max_shield_str = 0.0f, repair_delta, repair_allocated, max_hull_repair = 0, max_subsys_repair;
|
||||
ship *shipp;
|
||||
ship_weapon *swp;
|
||||
ship_info *sip;
|
||||
@@ -13538,6 +13624,7 @@ int ship_do_rearm_frame( object *objp, float frametime )
|
||||
}
|
||||
swp->num_primary_banks = sip->num_primary_banks;
|
||||
}
|
||||
|
||||
// AL 12-30-97: Repair broken warp drive
|
||||
if ( shipp->flags[Ship_Flags::Warp_broken] ) {
|
||||
// TODO: maybe do something here like informing player warp is fixed?
|
||||
@@ -13546,7 +13633,7 @@ int ship_do_rearm_frame( object *objp, float frametime )
|
||||
shipp->flags.remove(Ship_Flags::Warp_broken);
|
||||
}
|
||||
|
||||
// AL 1-16-97: Replenish countermeasures
|
||||
// AL 1-16-98: Replenish countermeasures
|
||||
shipp->cmeasure_count = sip->cmeasure_max;
|
||||
|
||||
// Do shield repair here
|
||||
@@ -13567,53 +13654,36 @@ int ship_do_rearm_frame( object *objp, float frametime )
|
||||
}
|
||||
|
||||
// Repair the ship integrity (subsystems + hull). This works by applying the repair points
|
||||
// to the subsystems. Ships integrity is stored is objp->hull_strength, so that always is
|
||||
// to the subsystems. Ships integrity is stored in objp->hull_strength, so that always is
|
||||
// incremented by repair_allocated
|
||||
repair_allocated = shipp->ship_max_hull_strength * frametime * sip->sup_hull_repair_rate;
|
||||
|
||||
// AL 11-24-97: remove increase to hull integrity
|
||||
// Comments removed by PhReAk; Note that this is toggled on/off with a mission flag
|
||||
|
||||
//Figure out how much of the ship's hull we can repair
|
||||
max_hull_repair = shipp->ship_max_hull_strength * (The_mission.support_ships.max_hull_repair_val * 0.01f);
|
||||
|
||||
//Don't "reverse-repair" the hull if it's already above the max repair threshold
|
||||
if (objp->hull_strength > max_hull_repair)
|
||||
// AL 11-24-97: remove increase to hull integrity
|
||||
// Comments removed by PhReAk; Note that this is toggled on/off with a mission flag
|
||||
if (The_mission.flags[Mission::Mission_Flags::Support_repairs_hull])
|
||||
{
|
||||
max_hull_repair = objp->hull_strength;
|
||||
}
|
||||
|
||||
if(The_mission.flags[Mission::Mission_Flags::Support_repairs_hull])
|
||||
{
|
||||
objp->hull_strength += repair_allocated;
|
||||
if ( objp->hull_strength > max_hull_repair ) {
|
||||
objp->hull_strength = max_hull_repair;
|
||||
}
|
||||
|
||||
if ( objp->hull_strength > shipp->ship_max_hull_strength )
|
||||
//Figure out how much of the ship's hull we can repair
|
||||
//Don't "reverse-repair" the hull if it's already above the max repair threshold
|
||||
max_hull_repair = shipp->ship_max_hull_strength * (The_mission.support_ships.max_hull_repair_val * 0.01f);
|
||||
if (objp->hull_strength < max_hull_repair)
|
||||
{
|
||||
objp->hull_strength = shipp->ship_max_hull_strength;
|
||||
repair_allocated -= ( shipp->ship_max_hull_strength - objp->hull_strength);
|
||||
objp->hull_strength += shipp->ship_max_hull_strength * frametime * sip->sup_hull_repair_rate;
|
||||
if (objp->hull_strength > max_hull_repair)
|
||||
objp->hull_strength = max_hull_repair;
|
||||
}
|
||||
}
|
||||
|
||||
// figure out repairs for subsystems
|
||||
if(sip->sup_subsys_repair_rate == 0.0f)
|
||||
repair_allocated = 0.0f;
|
||||
else if(sip->sup_hull_repair_rate == 0.0f)
|
||||
repair_allocated = shipp->ship_max_hull_strength * frametime * sip->sup_subsys_repair_rate;
|
||||
else if(!(sip->sup_hull_repair_rate == sip->sup_subsys_repair_rate))
|
||||
repair_allocated = repair_allocated * sip->sup_subsys_repair_rate / sip->sup_hull_repair_rate;
|
||||
repair_allocated = shipp->ship_max_hull_strength * frametime * sip->sup_subsys_repair_rate;
|
||||
|
||||
// check the subsystems of the ship.
|
||||
subsys_all_ok = 1;
|
||||
bool subsys_all_ok = true;
|
||||
ssp = GET_FIRST(&shipp->subsys_list);
|
||||
while ( ssp != END_OF_LIST( &shipp->subsys_list ) ) {
|
||||
//Figure out how much we *can* repair the current subsystem -C
|
||||
max_subsys_repair = ssp->max_hits * (The_mission.support_ships.max_subsys_repair_val * 0.01f);
|
||||
|
||||
if ( ssp->current_hits < max_subsys_repair && repair_allocated > 0 ) {
|
||||
subsys_all_ok = 0;
|
||||
subsys_all_ok = false;
|
||||
subsys_type = ssp->system_info->type;
|
||||
|
||||
if ( objp == Player_obj ) {
|
||||
@@ -13779,7 +13849,8 @@ int ship_do_rearm_frame( object *objp, float frametime )
|
||||
|
||||
if ((aip->rearm_first_ballistic_primary == TRUE) && (i == last_ballistic_idx))
|
||||
{
|
||||
if (primary_banks_full != swp->num_primary_banks) {
|
||||
if (primary_banks_full != swp->num_primary_banks)
|
||||
{
|
||||
// Goober5000
|
||||
gamesnd_id sound_index;
|
||||
if (gamesnd_game_sound_valid(GameSounds::BALLISTIC_START_LOAD))
|
||||
@@ -13805,23 +13876,24 @@ int ship_do_rearm_frame( object *objp, float frametime )
|
||||
aip->rearm_first_ballistic_primary = TRUE;
|
||||
}
|
||||
|
||||
int shields_full = 0;
|
||||
int shields_full = false;
|
||||
if ( (objp->flags[Object::Object_Flags::No_shields]) ) {
|
||||
shields_full = 1;
|
||||
shields_full = true;
|
||||
} else {
|
||||
if ( shield_get_strength(objp) >= max_shield_str )
|
||||
shields_full = 1;
|
||||
shields_full = true;
|
||||
if (sip->sup_shield_repair_rate == 0.0f)
|
||||
shields_full = 1;
|
||||
shields_full = true;
|
||||
}
|
||||
|
||||
int hull_ok = 0;
|
||||
if(objp->hull_strength >= max_hull_repair)
|
||||
hull_ok = 1;
|
||||
|
||||
if(sip->sup_hull_repair_rate == 0.0f) {
|
||||
subsys_all_ok = 1;
|
||||
hull_ok = 1;
|
||||
bool hull_ok = false;
|
||||
if (!(The_mission.flags[Mission::Mission_Flags::Support_repairs_hull])) {
|
||||
hull_ok = true;
|
||||
} else {
|
||||
if (objp->hull_strength >= max_hull_repair)
|
||||
hull_ok = true;
|
||||
if (sip->sup_hull_repair_rate == 0.0f)
|
||||
hull_ok = true;
|
||||
}
|
||||
|
||||
// return 1 if at end of subsystem list, hull damage at 0, and shields full and all secondary banks full.
|
||||
@@ -13846,7 +13918,8 @@ int ship_do_rearm_frame( object *objp, float frametime )
|
||||
}
|
||||
}
|
||||
|
||||
if (objp == Player_obj) Player_rearm_eta -= frametime;
|
||||
if (objp == Player_obj)
|
||||
Player_rearm_eta -= frametime;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -14317,8 +14390,9 @@ static void ship_add_ship_type_kill_count( int ship_info_index )
|
||||
return;
|
||||
}
|
||||
|
||||
//Add it
|
||||
Ship_type_counts[type].killed++;
|
||||
//Add it if we are actually in gameplay
|
||||
if (Ship_type_counts.size() > static_cast<size_t>(type))
|
||||
Ship_type_counts[type].killed++;
|
||||
}
|
||||
|
||||
int ship_query_general_type(int ship)
|
||||
@@ -14411,14 +14485,16 @@ int ship_get_random_player_wing_ship( int flags, float max_dist, int persona_ind
|
||||
if ( Ships[ship_index].flags[Ship_Flags::Dying] ) {
|
||||
continue;
|
||||
}
|
||||
// see if ship meets our criterea
|
||||
|
||||
// see if ship meets our criteria
|
||||
if ( (flags == SHIP_GET_NO_PLAYERS || flags == SHIP_GET_UNSILENCED) && (Objects[Ships[ship_index].objnum].flags[Object::Object_Flags::Player_ship]) ){
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( (flags == SHIP_GET_UNSILENCED) && (Ships[ship_index].flags[Ship_Flags::No_builtin_messages]) )
|
||||
{
|
||||
continue;
|
||||
if (flags == SHIP_GET_UNSILENCED) {
|
||||
if (Ships[ship_index].flags[Ship_Flags::No_builtin_messages])
|
||||
continue;
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(&Ships[ship_index]) <= COMM_DAMAGED)
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't process ships on a different team
|
||||
@@ -14652,6 +14728,57 @@ int ship_navigation_ok_to_warp(ship *sp)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// wookieejedi
|
||||
// checks both the warp flags and ship_engine_ok_to_warp() and ship_navigation_ok_to_warp() --wookieejedi
|
||||
// returns true if ship is able to warp, otherwise return false
|
||||
bool ship_can_warp_full_check(ship* sp)
|
||||
{
|
||||
if (!(sp->cannot_warp_flags()) && ship_navigation_ok_to_warp(sp) && ship_engine_ok_to_warp(sp)) {
|
||||
// there are no warp flags that are preventing us from warping and the nav and engines are good
|
||||
// thus warp is allowed
|
||||
return true;
|
||||
} else {
|
||||
// warp is not allowed since one of those conditions was not met
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// wookieejedi
|
||||
// check to see if a ship can depart via bay
|
||||
// takes into account if the ship is in a wing
|
||||
// returns true if the ship has a bay departure and the mothership is present, false otherwise
|
||||
bool ship_can_bay_depart(ship* sp)
|
||||
{
|
||||
// if this ship belongs to a wing, then use the wing departure information
|
||||
int departure_location;
|
||||
int departure_anchor;
|
||||
int departure_path_mask;
|
||||
if (sp->wingnum >= 0)
|
||||
{
|
||||
wing *wingp = &Wings[sp->wingnum];
|
||||
departure_location = wingp->departure_location;
|
||||
departure_anchor = wingp->departure_anchor;
|
||||
departure_path_mask = wingp->departure_path_mask;
|
||||
} else {
|
||||
departure_location = sp->departure_location;
|
||||
departure_anchor = sp->departure_anchor;
|
||||
departure_path_mask = sp->departure_path_mask;
|
||||
}
|
||||
|
||||
if ( departure_location == DEPART_AT_DOCK_BAY )
|
||||
{
|
||||
Assertion( departure_anchor >= 0, "Ship %s must have a valid departure anchor", sp->ship_name );
|
||||
int anchor_shipnum = ship_name_lookup(Parse_names[departure_anchor]);
|
||||
if (anchor_shipnum >= 0 && ship_useful_for_departure(anchor_shipnum, departure_path_mask)) {
|
||||
// can bay depart at this time
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// cannot bay depart at this time
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate the normal vector from a subsystem position and its first path point
|
||||
// input: sp => pointer to ship that is parent of subsystem
|
||||
// ss => pointer to subsystem of interest
|
||||
@@ -15464,6 +15591,9 @@ void ship_maybe_praise_self(ship *deader_sp, ship *killer_sp)
|
||||
if ( killer_sp->flags[Ship_Flags::No_builtin_messages] ) {
|
||||
return;
|
||||
}
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(killer_sp) != COMM_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
message_send_builtin_to_player(MESSAGE_PRAISE_SELF, killer_sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, -1, -1);
|
||||
Player->praise_self_timestamp = timestamp(Builtin_messages[MESSAGE_PRAISE_SELF].min_delay);
|
||||
@@ -15559,8 +15689,12 @@ play_ask_help:
|
||||
if (!(Ship_info[sp->ship_info_index].is_fighter_bomber())) //If we're still here, only continue if we're a fighter or bomber.
|
||||
return;
|
||||
|
||||
if (!(sp->flags[Ship_Flags::No_builtin_messages])) // Karajorma - Only unsilenced ships should ask for help
|
||||
{
|
||||
// only unsilenced ships should ask for help
|
||||
if (sp->flags[Ship_Flags::No_builtin_messages])
|
||||
return;
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(&Ships[objp->instance]) <= COMM_DAMAGED)
|
||||
return;
|
||||
|
||||
message_send_builtin_to_player(MESSAGE_HELP, sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, multi_team_filter);
|
||||
Player->allow_ask_help_timestamp = timestamp(Builtin_messages[MESSAGE_HELP].min_delay);
|
||||
|
||||
@@ -15569,7 +15703,6 @@ play_ask_help:
|
||||
Player->allow_scream_timestamp = timestamp(15000);
|
||||
|
||||
Player->ask_help_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -15608,9 +15741,9 @@ void ship_scream(ship *sp)
|
||||
|
||||
// Bail if the ship is silenced
|
||||
if (sp->flags[Ship_Flags::No_builtin_messages])
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(sp, true) <= COMM_DAMAGED)
|
||||
return;
|
||||
|
||||
message_send_builtin_to_player(MESSAGE_WINGMAN_SCREAM, sp, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, multi_team_filter);
|
||||
Player->allow_scream_timestamp = timestamp(Builtin_messages[MESSAGE_WINGMAN_SCREAM].min_delay);
|
||||
@@ -15695,6 +15828,8 @@ void ship_maybe_tell_about_low_ammo(ship *sp)
|
||||
if (sp->flags[Ship_Flags::No_builtin_messages]) {
|
||||
return;
|
||||
}
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(sp) <= COMM_DAMAGED)
|
||||
return;
|
||||
|
||||
// don't mention low ammo if we're docked, for the same reason as in maybe_request_support
|
||||
if (object_is_docked(&Objects[sp->objnum]))
|
||||
@@ -15760,6 +15895,8 @@ void ship_maybe_tell_about_rearm(ship *sp)
|
||||
// Silent ships should remain just that
|
||||
if (sp->flags[Ship_Flags::No_builtin_messages])
|
||||
return;
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Check_comms_for_non_player_ships] && hud_communications_state(sp) <= COMM_DAMAGED)
|
||||
return;
|
||||
|
||||
// AL 1-4-98: If ship integrity is low, tell player you want to get repaired. Otherwise, tell
|
||||
// the player you want to get re-armed.
|
||||
@@ -19128,3 +19265,134 @@ bool ship::is_arriving(ship::warpstage stage, bool dock_leader_or_single)
|
||||
Assertion(false, "ship::is_arriving didn't handle all possible states; get a coder!");
|
||||
return false;
|
||||
}
|
||||
|
||||
void ship_clear_lock(lock_info *slot) {
|
||||
vec3d zero_vec = ZERO_VECTOR;
|
||||
|
||||
slot->accumulated_x_pixels = 0;
|
||||
slot->accumulated_y_pixels = 0;
|
||||
|
||||
slot->catch_up_distance = 0.0f;
|
||||
|
||||
slot->catching_up = 0;
|
||||
|
||||
slot->current_target_sx = -1;
|
||||
slot->current_target_sy = -1;
|
||||
|
||||
slot->dist_to_lock = 0.0f;
|
||||
|
||||
slot->indicator_start_x = -1;
|
||||
slot->indicator_start_y = -1;
|
||||
|
||||
slot->indicator_visible = false;
|
||||
|
||||
slot->indicator_x = -1;
|
||||
slot->indicator_y = -1;
|
||||
|
||||
slot->last_dist_to_target = 0.0f;
|
||||
|
||||
slot->locked_timestamp = 0;
|
||||
|
||||
slot->maintain_lock_count = 0;
|
||||
|
||||
slot->need_new_start_pos = false;
|
||||
|
||||
slot->target_in_lock_cone = false;
|
||||
|
||||
slot->world_pos = zero_vec;
|
||||
|
||||
slot->locked = false;
|
||||
|
||||
slot->obj = nullptr;
|
||||
slot->subsys = nullptr;
|
||||
|
||||
|
||||
slot->time_to_lock = -1.0f;
|
||||
}
|
||||
|
||||
void ship_queue_missile_locks(ship *shipp)
|
||||
{
|
||||
shipp->missile_locks_firing.clear();
|
||||
|
||||
// queue up valid missile locks
|
||||
std::copy_if(shipp->missile_locks.begin(), shipp->missile_locks.end(), std::back_inserter(shipp->missile_locks_firing), [](lock_info lock) { return lock.locked; });
|
||||
}
|
||||
|
||||
bool ship_lock_present(ship *shipp)
|
||||
{
|
||||
return std::any_of(shipp->missile_locks.begin(), shipp->missile_locks.end(), [](lock_info lock) { return lock.locked; });
|
||||
}
|
||||
|
||||
bool ship_start_secondary_fire(object* objp)
|
||||
{
|
||||
Assert( objp != nullptr);
|
||||
|
||||
if ( objp->type != OBJ_SHIP ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int n = objp->instance;
|
||||
|
||||
Assert( n >= 0 && n < MAX_SHIPS );
|
||||
Assert( Ships[n].objnum == OBJ_INDEX(objp) );
|
||||
|
||||
ship *shipp = &Ships[n];
|
||||
ship_info *sip = &Ship_info[shipp->ship_info_index];
|
||||
ship_weapon* swp = &shipp->weapons;
|
||||
|
||||
int bank = swp->current_secondary_bank;
|
||||
|
||||
if ( bank < 0 || bank >= sip->num_secondary_banks ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Assert( (swp->secondary_bank_weapons[bank] >= 0) && (swp->secondary_bank_weapons[bank] < weapon_info_size()) );
|
||||
|
||||
weapon_info *wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
|
||||
|
||||
if ( wip->trigger_lock ) {
|
||||
swp->flags.set(Ship::Weapon_Flags::Trigger_Lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ship_stop_secondary_fire(object* objp)
|
||||
{
|
||||
Assert( objp != nullptr);
|
||||
|
||||
if ( objp->type != OBJ_SHIP ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int n = objp->instance;
|
||||
|
||||
if ( n < 0 || n >= MAX_SHIPS ) {
|
||||
return false;
|
||||
}
|
||||
Assert( Ships[n].objnum == OBJ_INDEX(objp) );
|
||||
|
||||
ship *shipp = &Ships[n];
|
||||
ship_info *sip = &Ship_info[shipp->ship_info_index];
|
||||
ship_weapon *swp = &shipp->weapons;
|
||||
|
||||
int bank = swp->current_secondary_bank;
|
||||
|
||||
if ( bank < 0 || bank >= sip->num_secondary_banks ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Assert( (swp->secondary_bank_weapons[bank] >= 0) && (swp->secondary_bank_weapons[bank] < weapon_info_size()) );
|
||||
|
||||
weapon_info *wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
|
||||
|
||||
if ( wip->trigger_lock && swp->flags[Ship::Weapon_Flags::Trigger_Lock]) {
|
||||
swp->flags.remove(Ship::Weapon_Flags::Trigger_Lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
+52
-2
@@ -31,6 +31,7 @@
|
||||
#include "weapon/trails.h"
|
||||
#include "ship/ship_flags.h"
|
||||
#include "weapon/weapon_flags.h"
|
||||
#include "ai/ai.h"
|
||||
|
||||
#include <string>
|
||||
#include <particle/ParticleManager.h>
|
||||
@@ -283,6 +284,36 @@ typedef struct cockpit_display_info {
|
||||
int size[2];
|
||||
} cockpit_display_info;
|
||||
|
||||
// structure to keep track of ship locks
|
||||
typedef struct lock_info {
|
||||
object *obj;
|
||||
ship_subsys *subsys;
|
||||
|
||||
vec3d world_pos;
|
||||
|
||||
int current_target_sx;
|
||||
int current_target_sy;
|
||||
|
||||
bool locked;
|
||||
int maintain_lock_count;
|
||||
int indicator_x;
|
||||
int indicator_y;
|
||||
int indicator_start_x;
|
||||
int indicator_start_y;
|
||||
bool indicator_visible;
|
||||
float time_to_lock;
|
||||
float dist_to_lock;
|
||||
int catching_up;
|
||||
float catch_up_distance;
|
||||
float last_dist_to_target;
|
||||
double accumulated_x_pixels;
|
||||
double accumulated_y_pixels;
|
||||
bool need_new_start_pos;
|
||||
bool target_in_lock_cone;
|
||||
|
||||
int locked_timestamp;
|
||||
} lock_info;
|
||||
|
||||
// structure definition for a linked list of subsystems for a ship. Each subsystem has a pointer
|
||||
// to the static data for the subsystem. The obj_subsystem data is defined and read in the model
|
||||
// code. Other dynamic data (such as current_hits) should remain in this structure.
|
||||
@@ -560,6 +591,9 @@ public:
|
||||
vec3d wash_rot_axis;
|
||||
int wash_timestamp;
|
||||
|
||||
SCP_vector<lock_info> missile_locks;
|
||||
SCP_vector<lock_info> missile_locks_firing;
|
||||
|
||||
int num_swarm_missiles_to_fire; // number of swarm missiles that need to be launched
|
||||
int next_swarm_fire; // timestamp of next swarm missile to fire
|
||||
int next_swarm_path; // next path number for swarm missile to take
|
||||
@@ -722,11 +756,13 @@ public:
|
||||
//Helper functions
|
||||
bool is_arriving(ship::warpstage stage = ship::warpstage::BOTH, bool dock_leader_or_single = false);
|
||||
inline bool is_departing() { return flags[Ship::Ship_Flags::Depart_warp, Ship::Ship_Flags::Depart_dockbay]; }
|
||||
inline bool cannot_warp() { return flags[Ship::Ship_Flags::Warp_broken, Ship::Ship_Flags::Warp_never, Ship::Ship_Flags::Disabled]; }
|
||||
inline bool cannot_warp_flags() { return flags[Ship::Ship_Flags::Warp_broken, Ship::Ship_Flags::Warp_never, Ship::Ship_Flags::Disabled, Ship::Ship_Flags::No_subspace_drive]; }
|
||||
inline bool is_dying_or_departing() { return is_departing() || flags[Ship::Ship_Flags::Dying]; }
|
||||
|
||||
bool has_display_name();
|
||||
const char* get_display_string();
|
||||
|
||||
void apply_replacement_textures(SCP_vector<texture_replace> &replacements);
|
||||
};
|
||||
|
||||
struct ai_target_priority {
|
||||
@@ -1407,6 +1443,9 @@ extern bool in_autoaim_fov(ship *shipp, int bank_to_fire, object *obj);
|
||||
extern int ship_stop_fire_primary(object * obj);
|
||||
extern int ship_fire_primary(object * objp, int stream_weapons, int force = 0);
|
||||
extern int ship_fire_secondary(object * objp, int allow_swarm = 0 );
|
||||
bool ship_start_secondary_fire(object* objp);
|
||||
bool ship_stop_secondary_fire(object* objp);
|
||||
|
||||
extern int ship_launch_countermeasure(object *objp, int rand_val = -1);
|
||||
|
||||
// for special targeting lasers
|
||||
@@ -1495,7 +1534,7 @@ extern ship_subsys *ship_get_indexed_subsys( ship *sp, int index, vec3d *attacke
|
||||
extern int ship_get_index_from_subsys(ship_subsys *ssp, int objnum);
|
||||
extern int ship_get_subsys_index(ship *sp, const char* ss_name); // returns numerical index in linked list of subsystems
|
||||
extern int ship_get_subsys_index(ship *shipp, ship_subsys *subsys);
|
||||
extern float ship_get_subsystem_strength( ship *shipp, int type );
|
||||
extern float ship_get_subsystem_strength( ship *shipp, int type, bool skip_dying_check = false );
|
||||
extern ship_subsys *ship_get_subsys(ship *shipp, const char *subsys_name);
|
||||
extern int ship_get_num_subsys(ship *shipp);
|
||||
extern ship_subsys *ship_get_closest_subsys_in_sight(ship *sp, int subsys_type, vec3d *attacker_pos);
|
||||
@@ -1536,6 +1575,8 @@ int ship_secondary_bank_has_ammo(int shipnum); // check if current secondary ban
|
||||
|
||||
int ship_engine_ok_to_warp(ship *sp); // check if ship has engine power to warp
|
||||
int ship_navigation_ok_to_warp(ship *sp); // check if ship has navigation power to warp
|
||||
bool ship_can_warp_full_check(ship *sp); // checks both the warp flags and ship_engine_ok_to_warp() and ship_navigation_ok_to_warp() --wookieejedi
|
||||
bool ship_can_bay_depart(ship *sp); // checks to see if a ship has a departure location as a bay and if the mothership is present
|
||||
|
||||
int ship_return_subsys_path_normal(ship *sp, ship_subsys *ss, vec3d *gsubpos, vec3d *norm);
|
||||
int ship_subsystem_in_sight(object* objp, ship_subsys* subsys, vec3d *eye_pos, vec3d* subsys_pos, int do_facing_check=1, float *dot_out=NULL, vec3d *vec_out=NULL);
|
||||
@@ -1796,4 +1837,13 @@ extern void toggle_ignore_list_flag(Ship::Ship_Flags flag);
|
||||
|
||||
ship_subsys* ship_get_subsys_for_submodel(ship* shipp, int submodel);
|
||||
|
||||
// Clears a lock_info struct with defaults
|
||||
void ship_clear_lock(lock_info *slot);
|
||||
|
||||
// queues up locks
|
||||
void ship_queue_missile_locks(ship *shipp);
|
||||
|
||||
// snoops missile locks to see if any are ready to fire.
|
||||
bool ship_lock_present(ship *shipp);
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,7 @@ namespace Ship {
|
||||
Beam_Free = 0, // if this is a beam weapon, its free to fire
|
||||
Turret_Lock, // is this turret is free to fire or locked
|
||||
Tagged_Only, // only fire if target is tagged
|
||||
Trigger_Lock, // // indicates that the trigger is held down
|
||||
|
||||
NUM_VALUES
|
||||
};
|
||||
|
||||
@@ -1582,6 +1582,10 @@ void shipfx_queue_render_ship_halves_and_debris(model_draw_list *scene, clip_shi
|
||||
render_flags |= MR_NO_TEXTURING | MR_NO_LIGHTING;
|
||||
}
|
||||
|
||||
if (shipp->flags[Ship::Ship_Flags::Glowmaps_disabled]) {
|
||||
render_flags |= MR_NO_GLOWMAPS;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pm->num_debris_objects; i++ ) {
|
||||
// draw DEBRIS_FREE in test only
|
||||
if (half_ship->draw_debris[i] == DEBRIS_DRAW) {
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "object/objectshield.h"
|
||||
#include "object/objectsnd.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "scripting/hook_api.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "scripting/api/objs/subsystem.h"
|
||||
#include "playerman/player.h"
|
||||
@@ -72,6 +73,12 @@ vec3d Original_vec_to_deader;
|
||||
|
||||
static bool global_damage = false;
|
||||
|
||||
const std::shared_ptr<scripting::Hook> OnPainFlashHook = scripting::Hook::Factory(
|
||||
"On Pain Flash", "Called when a pain flash is displayed.",
|
||||
{
|
||||
{"Pain_Type", "number", "The type of pain flash displayed: shield = 0 and hull = 1."},
|
||||
});
|
||||
|
||||
//WMC - Camera rough draft stuff
|
||||
/*
|
||||
camid dead_get_camera()
|
||||
@@ -2637,6 +2644,7 @@ void ship_hit_pain(float damage, int quadrant)
|
||||
|
||||
if (!(Player_obj->flags[Object::Object_Flags::Invulnerable]))
|
||||
{
|
||||
int pain_type;
|
||||
if (Shield_pain_flash_factor != 0.0f && quadrant >= 0)
|
||||
{
|
||||
float effect = (Shield_pain_flash_factor * Player_obj->shield_quadrant[quadrant] * Player_obj->n_quadrants) / shield_get_max_strength(Player_obj);
|
||||
@@ -2645,9 +2653,15 @@ void ship_hit_pain(float damage, int quadrant)
|
||||
effect -= Shield_pain_flash_factor;
|
||||
|
||||
game_flash((sip->shield_color[0] * effect) / 255.0f, (sip->shield_color[1] * effect) / 255.0f, (sip->shield_color[2] * effect) / 255.0f);
|
||||
pain_type = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
game_flash(damage * Generic_pain_flash_factor / 15.0f, -damage * Generic_pain_flash_factor / 30.0f, -damage * Generic_pain_flash_factor / 30.0f);
|
||||
pain_type = 1;
|
||||
}
|
||||
// add scripting hook for 'On Pain Flash' --wookieejedi
|
||||
OnPainFlashHook->run(scripting::hook_param_list(scripting::hook_param("Pain_Type", 'i', pain_type)));
|
||||
}
|
||||
|
||||
// kill any active popups when you get hit.
|
||||
|
||||
@@ -56,7 +56,7 @@ extern int button_function(int n);
|
||||
extern void hud_squadmsg_msg_all_fighters();
|
||||
extern void hud_squadmsg_shortcut( int command );
|
||||
extern bool hud_squadmsg_ship_valid(ship *shipp, object *objp = nullptr);
|
||||
extern int hud_squadmsg_wing_valid(wing *wingp);
|
||||
extern bool hud_squadmsg_wing_valid(wing *wingp);
|
||||
|
||||
extern int Msg_instance;;
|
||||
extern int Msg_shortcut_command;
|
||||
|
||||
@@ -1073,6 +1073,8 @@ add_file_folder("Scripting\\\\Api\\\\Libs"
|
||||
scripting/api/libs/bitops.h
|
||||
scripting/api/libs/cfile.cpp
|
||||
scripting/api/libs/cfile.h
|
||||
scripting/api/libs/engine.cpp
|
||||
scripting/api/libs/engine.h
|
||||
scripting/api/libs/graphics.cpp
|
||||
scripting/api/libs/graphics.h
|
||||
scripting/api/libs/hookvars.cpp
|
||||
|
||||
+30
-1
@@ -38,6 +38,15 @@ extern int Num_weapon_subtypes;
|
||||
#define WRT_LASER 1
|
||||
#define WRT_POF 2
|
||||
|
||||
// constants for weapon lock acquire methods
|
||||
#define WLOCK_PIXEL 0
|
||||
#define WLOCK_TIMER 1
|
||||
|
||||
// constants for weapon lock restrictions
|
||||
#define LR_CURRENT_TARGET 0 // Only lock current target and subsystem
|
||||
#define LR_CURRENT_TARGET_SUBSYS 1 //
|
||||
#define LR_ANY_TARGETS 2
|
||||
|
||||
//particle names go here -nuke
|
||||
#define PSPEW_NONE -1 //used to disable a spew, useful for xmts
|
||||
#define PSPEW_DEFAULT 0 //std fs2 pspew
|
||||
@@ -315,11 +324,24 @@ struct weapon_info
|
||||
short swarm_count; // how many swarm missiles are fired for this weapon
|
||||
int SwarmWait; // *Swarm firewait, default is 150 -Et1
|
||||
|
||||
int target_restrict;
|
||||
bool multi_lock;
|
||||
int max_seeking; // how many seekers can be active at a time if multilock is enabled. A value of one will lock stuff up one by one.
|
||||
int max_seekers_per_target; // how many seekers can be attached to a target.
|
||||
|
||||
SCP_vector<int> ship_type_restrict;
|
||||
SCP_vector<SCP_string> ship_type_restrict_temp;
|
||||
|
||||
bool trigger_lock; // Trigger must be held down and released to lock and fire.
|
||||
bool launch_reset_locks; // Lock indicators reset after firing
|
||||
|
||||
// Specific to ASPECT homing missiles.
|
||||
int acquire_method;
|
||||
float min_lock_time; // minimum time (in seconds) to achieve lock
|
||||
int lock_pixels_per_sec; // pixels/sec moved while locking
|
||||
int catchup_pixels_per_sec; // pixels/sec moved while catching-up for a lock
|
||||
int catchup_pixel_penalty; // number of extra pixels to move while locking as a penalty for catching up for a lock
|
||||
int catchup_pixel_penalty; // number of extra pixels to move while locking as a penalty for catching up for a lock
|
||||
float lock_fov;
|
||||
|
||||
// Specific to HEAT homing missiles.
|
||||
float fov;
|
||||
@@ -629,4 +651,11 @@ void validate_SSM_entries();
|
||||
|
||||
void shield_impact_explosion(vec3d *hitpos, object *objp, float radius, int idx);
|
||||
|
||||
// Swifty - return number of max simultaneous locks
|
||||
int weapon_get_max_missile_seekers(weapon_info *wip);
|
||||
|
||||
// return if this weapon can lock on this ship type
|
||||
bool weapon_can_lock_on_ship_type(weapon_info *wip, int ship_type);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -23,6 +23,7 @@ namespace Weapon {
|
||||
Bomb, // Bomb-type missile, can be targeted
|
||||
Huge, // Huge damage (generally 500+), probably only fired at huge ships.
|
||||
No_dumbfire, // Missile cannot be fired dumbfire (ie requires aspect lock)
|
||||
No_doublefire, // Disables linked firing for secondaries - EatThePath
|
||||
Thruster, // Has thruster cone and/or glow
|
||||
In_tech_database,
|
||||
Player_allowed, // allowed to be on starting wing ships/in weaponry pool
|
||||
|
||||
+304
-5
@@ -115,6 +115,7 @@ flag_def_list_new<Weapon::Info_Flags> Weapon_Info_Flags[] = {
|
||||
{ "child", Weapon::Info_Flags::Child, true, false },
|
||||
{ "bomb", Weapon::Info_Flags::Bomb, true, false },
|
||||
{ "no dumbfire", Weapon::Info_Flags::No_dumbfire, true, false },
|
||||
{ "no doublefire", Weapon::Info_Flags::No_doublefire, true, false },
|
||||
{ "in tech database", Weapon::Info_Flags::In_tech_database, true, false },
|
||||
{ "player allowed", Weapon::Info_Flags::Player_allowed, true, false },
|
||||
{ "particle spew", Weapon::Info_Flags::Particle_spew, true, false },
|
||||
@@ -221,6 +222,7 @@ int Weapon_impact_timer; // timer, initialized at start of each mission
|
||||
#define REARM_NUM_BALLISTIC_PRIMARIES_PER_BATCH 100
|
||||
|
||||
extern int compute_num_homing_objects(object *target_objp);
|
||||
void weapon_spew_stats();
|
||||
|
||||
|
||||
weapon_explosions::weapon_explosions()
|
||||
@@ -808,7 +810,7 @@ int parse_weapon(int subtype, bool replace, const char *filename)
|
||||
}
|
||||
|
||||
//Remove @ symbol
|
||||
//these used to be used to denote weapons that would
|
||||
//these used to denote weapons that would
|
||||
//only be parsed in demo builds
|
||||
if ( fname[0] == '@' ) {
|
||||
backspace(fname);
|
||||
@@ -984,8 +986,10 @@ int parse_weapon(int subtype, bool replace, const char *filename)
|
||||
if ( optional_string("$Model file:") ) {
|
||||
stuff_string(wip->pofbitmap_name, F_NAME, MAX_FILENAME_LEN);
|
||||
|
||||
if ( VALID_FNAME(wip->pofbitmap_name) )
|
||||
if (VALID_FNAME(wip->pofbitmap_name))
|
||||
wip->render_type = WRT_POF;
|
||||
else
|
||||
wip->render_type = WRT_NONE;
|
||||
|
||||
diag_printf("Model pof file -- %s\n", wip->pofbitmap_name );
|
||||
}
|
||||
@@ -1016,7 +1020,7 @@ int parse_weapon(int subtype, bool replace, const char *filename)
|
||||
|
||||
if (wip->render_type == WRT_POF) {
|
||||
mprintf(("WARNING: Weapon '%s' has both LASER and POF render types! Will only use POF type!\n", wip->name));
|
||||
generic_anim_init(&wip->laser_bitmap, NULL);
|
||||
generic_anim_init(&wip->laser_bitmap, nullptr);
|
||||
} else {
|
||||
generic_anim_init(&wip->laser_bitmap, fname);
|
||||
wip->render_type = WRT_LASER;
|
||||
@@ -1394,6 +1398,56 @@ int parse_weapon(int subtype, bool replace, const char *filename)
|
||||
}
|
||||
}
|
||||
|
||||
if (optional_string("+Target Lock Restriction:")) {
|
||||
if (optional_string("current target, any subsystem")) {
|
||||
wip->target_restrict = LR_CURRENT_TARGET_SUBSYS;
|
||||
|
||||
}
|
||||
else if (optional_string("any target")) {
|
||||
wip->target_restrict = LR_ANY_TARGETS;
|
||||
|
||||
}
|
||||
else if (optional_string("current target only")) {
|
||||
wip->target_restrict = LR_CURRENT_TARGET;
|
||||
|
||||
}
|
||||
else {
|
||||
wip->target_restrict = LR_CURRENT_TARGET;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (optional_string("+Independent Seekers:")) {
|
||||
stuff_boolean(&wip->multi_lock);
|
||||
|
||||
}
|
||||
|
||||
if (optional_string("+Trigger Hold:")) {
|
||||
stuff_boolean(&wip->trigger_lock);
|
||||
|
||||
}
|
||||
|
||||
if (optional_string("+Reset On Launch:")) {
|
||||
stuff_boolean(&wip->launch_reset_locks);
|
||||
|
||||
}
|
||||
|
||||
if (optional_string("+Max Seekers Per Target:")) {
|
||||
stuff_int(&wip->max_seekers_per_target);
|
||||
|
||||
}
|
||||
|
||||
if (optional_string("+Max Active Seekers:")) {
|
||||
stuff_int(&wip->max_seeking);
|
||||
|
||||
}
|
||||
|
||||
if (optional_string("+Ship Types:")) {
|
||||
stuff_string_list(wip->ship_type_restrict_temp);
|
||||
|
||||
}
|
||||
|
||||
if (wip->is_locked_homing()) {
|
||||
// locked homing missiles have a much longer lifespan than the AI think they do
|
||||
wip->max_lifetime = wip->lifetime * LOCKED_HOMING_EXTENDED_LIFE_FACTOR;
|
||||
@@ -3546,6 +3600,9 @@ void weapon_init()
|
||||
}
|
||||
|
||||
weapon_level_init();
|
||||
|
||||
if (Cmdline_spew_weapon_stats)
|
||||
weapon_spew_stats();
|
||||
}
|
||||
|
||||
|
||||
@@ -3592,6 +3649,7 @@ void weapon_close()
|
||||
void weapon_level_init()
|
||||
{
|
||||
int i;
|
||||
extern int ships_inited;
|
||||
|
||||
// Reset everything between levels
|
||||
Num_weapons = 0;
|
||||
@@ -3603,6 +3661,19 @@ void weapon_level_init()
|
||||
for (i = 0; i < weapon_info_size(); i++) {
|
||||
Weapon_info[i].damage_type_idx = Weapon_info[i].damage_type_idx_sav;
|
||||
Weapon_info[i].shockwave.damage_type_idx = Weapon_info[i].shockwave.damage_type_idx_sav;
|
||||
|
||||
if ( ships_inited ) {
|
||||
// populate ship type lock restrictions
|
||||
for ( int j = 0; j < (int)Weapon_info[i].ship_type_restrict_temp.size(); ++j ) {
|
||||
int idx = ship_type_name_lookup((char*)Weapon_info[i].ship_type_restrict_temp[j].c_str());
|
||||
|
||||
if ( idx >= 0 ) {
|
||||
Weapon_info[i].ship_type_restrict.push_back(idx);
|
||||
}
|
||||
}
|
||||
|
||||
Weapon_info[i].ship_type_restrict_temp.clear();
|
||||
}
|
||||
}
|
||||
|
||||
trail_level_init(); // reset all missile trails
|
||||
@@ -7693,12 +7764,25 @@ void weapon_info::reset()
|
||||
// *Default is 150 -Et1
|
||||
this->SwarmWait = SWARM_MISSILE_DELAY;
|
||||
|
||||
this->target_restrict = LR_CURRENT_TARGET;
|
||||
this->multi_lock = false;
|
||||
this->trigger_lock = false;
|
||||
this->launch_reset_locks = false;
|
||||
|
||||
this->max_seeking = 1;
|
||||
this->max_seekers_per_target = 1;
|
||||
this->ship_type_restrict.clear();
|
||||
this->ship_type_restrict_temp.clear();
|
||||
|
||||
this->acquire_method = WLOCK_PIXEL;
|
||||
|
||||
this->min_lock_time = 0.0f;
|
||||
this->lock_pixels_per_sec = 50;
|
||||
this->catchup_pixels_per_sec = 50;
|
||||
this->catchup_pixel_penalty = 50;
|
||||
this->fov = 0; //should be cos(pi), not pi
|
||||
this->seeker_strength = 1.0f;
|
||||
this->lock_fov = 0.85f;
|
||||
|
||||
this->pre_launch_snd = gamesnd_id();
|
||||
this->pre_launch_snd_min_interval = 0;
|
||||
@@ -7889,13 +7973,228 @@ void weapon_info::reset()
|
||||
// Reset using default constructor
|
||||
this->impact_decal = decals::creation_info();
|
||||
}
|
||||
const char* weapon_info::get_display_string() {
|
||||
|
||||
const char* weapon_info::get_display_string()
|
||||
{
|
||||
if (has_alternate_name()) {
|
||||
return alt_name;
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
bool weapon_info::has_alternate_name() {
|
||||
|
||||
bool weapon_info::has_alternate_name()
|
||||
{
|
||||
return alt_name[0] != '\0';
|
||||
}
|
||||
|
||||
void weapon_spew_stats()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
// csv weapon stats for comparisons
|
||||
mprintf(("Name,Type,Velocity,Range,Damage Hull,DPS Hull,Damage Shield,DPS Shield,Damage Subsystem,DPS Subsystem,Power Use,Fire Wait,ROF,Reload,1/Reload,Area Effect,Shockwave\n"));
|
||||
for (auto &wi : Weapon_info)
|
||||
{
|
||||
if (wi.subtype != WP_LASER)
|
||||
continue;
|
||||
|
||||
weapon_info *wip_base = nullptr;
|
||||
if (strchr(wi.name, '#'))
|
||||
{
|
||||
char temp[NAME_LENGTH];
|
||||
strcpy_s(temp, wi.name);
|
||||
end_string_at_first_hash_symbol(temp);
|
||||
int idx = weapon_info_lookup(temp);
|
||||
if (idx >= 0)
|
||||
wip_base = &Weapon_info[idx];
|
||||
}
|
||||
|
||||
if (wi.wi_flags[Weapon::Info_Flags::Player_allowed] || (wip_base != nullptr && wip_base->wi_flags[Weapon::Info_Flags::Player_allowed]))
|
||||
{
|
||||
mprintf(("%s,%s,", wi.name, "Primary"));
|
||||
mprintf(("%.2f,%.2f,", wi.max_speed, wi.max_speed * wi.lifetime));
|
||||
|
||||
float multiplier = (wi.shockwave.speed > 0.0f) ? 2.0f : 1.0f;
|
||||
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.armor_factor, multiplier * wi.damage * wi.armor_factor / wi.fire_wait));
|
||||
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.shield_factor, multiplier * wi.damage * wi.shield_factor / wi.fire_wait));
|
||||
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.subsystem_factor, multiplier * wi.damage * wi.subsystem_factor / wi.fire_wait));
|
||||
|
||||
mprintf(("%.2f,", wi.energy_consumed / wi.fire_wait));
|
||||
mprintf(("%.2f,%.2f,", wi.fire_wait, 1.0f / wi.fire_wait));
|
||||
mprintf((",,")); // no reload for primaries
|
||||
|
||||
if (wi.shockwave.inner_rad > 0.0f || wi.shockwave.outer_rad > 0.0f)
|
||||
mprintf(("Yes,"));
|
||||
else
|
||||
mprintf((","));
|
||||
|
||||
if (wi.shockwave.speed > 0.0f)
|
||||
mprintf(("Yes\n"));
|
||||
else
|
||||
mprintf(("\n"));
|
||||
}
|
||||
}
|
||||
for (auto &wi : Weapon_info)
|
||||
{
|
||||
if (wi.subtype != WP_MISSILE)
|
||||
continue;
|
||||
|
||||
weapon_info *wip_base = nullptr;
|
||||
if (strchr(wi.name, '#'))
|
||||
{
|
||||
char temp[NAME_LENGTH];
|
||||
strcpy_s(temp, wi.name);
|
||||
end_string_at_first_hash_symbol(temp);
|
||||
int idx = weapon_info_lookup(temp);
|
||||
if (idx >= 0)
|
||||
wip_base = &Weapon_info[idx];
|
||||
}
|
||||
|
||||
if (wi.wi_flags[Weapon::Info_Flags::Player_allowed] || (wip_base != nullptr && wip_base->wi_flags[Weapon::Info_Flags::Player_allowed]) || wi.wi_flags[Weapon::Info_Flags::Child])
|
||||
{
|
||||
mprintf(("%s,%s,", wi.name, "Secondary"));
|
||||
mprintf(("%.2f,%.2f,", wi.max_speed, wi.max_speed * wi.lifetime));
|
||||
|
||||
float multiplier = (wi.shockwave.speed > 0.0f) ? 2.0f : 1.0f;
|
||||
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.armor_factor, multiplier * wi.damage * wi.armor_factor / wi.fire_wait));
|
||||
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.shield_factor, multiplier * wi.damage * wi.shield_factor / wi.fire_wait));
|
||||
mprintf(("%.2f,%.2f,", multiplier * wi.damage * wi.subsystem_factor, multiplier * wi.damage * wi.subsystem_factor / wi.fire_wait));
|
||||
|
||||
mprintf((",")); // no power use for secondaries
|
||||
mprintf(("%.2f,%.2f,", wi.fire_wait, 1.0f / wi.fire_wait));
|
||||
mprintf(("%.2f,%.2f,", wi.reloaded_per_batch / wi.rearm_rate, wi.rearm_rate / wi.reloaded_per_batch)); // rearm_rate is actually the reciprocal of what is in weapons.tbl
|
||||
|
||||
if (wi.shockwave.inner_rad > 0.0f || wi.shockwave.outer_rad > 0.0f)
|
||||
mprintf(("Yes,"));
|
||||
else
|
||||
mprintf((","));
|
||||
|
||||
if (wi.shockwave.speed > 0.0f)
|
||||
mprintf(("Yes\n"));
|
||||
else
|
||||
mprintf(("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// mvp-style stats
|
||||
mprintf(("\n"));
|
||||
for (auto &wi : Weapon_info)
|
||||
{
|
||||
if (wi.subtype != WP_LASER)
|
||||
continue;
|
||||
|
||||
weapon_info *wip_base = nullptr;
|
||||
if (strchr(wi.name, '#'))
|
||||
{
|
||||
char temp[NAME_LENGTH];
|
||||
strcpy_s(temp, wi.name);
|
||||
end_string_at_first_hash_symbol(temp);
|
||||
int idx = weapon_info_lookup(temp);
|
||||
if (idx >= 0)
|
||||
wip_base = &Weapon_info[idx];
|
||||
}
|
||||
|
||||
if (wi.wi_flags[Weapon::Info_Flags::Player_allowed] || (wip_base != nullptr && wip_base->wi_flags[Weapon::Info_Flags::Player_allowed]))
|
||||
{
|
||||
mprintf(("%s\n", wi.name));
|
||||
mprintf(("\tVelocity: %-11.0fRange: %.0f\n", wi.max_speed, wi.max_speed * wi.lifetime));
|
||||
|
||||
float multiplier = (wi.shockwave.inner_rad > 0.0f || wi.shockwave.outer_rad > 0.0f) ? 2.0f : 1.0f;
|
||||
mprintf(("\tDPS: "));
|
||||
mprintf(("%.0f Hull, ", multiplier * wi.damage * wi.armor_factor / wi.fire_wait));
|
||||
mprintf(("%.0f Shield, ", multiplier * wi.damage * wi.shield_factor / wi.fire_wait));
|
||||
mprintf(("%.0f Subsystem\n", multiplier * wi.damage * wi.subsystem_factor / wi.fire_wait));
|
||||
|
||||
char watts[NAME_LENGTH];
|
||||
sprintf(watts, "%.1f", wi.energy_consumed / wi.fire_wait);
|
||||
char *p = strstr(watts, ".0");
|
||||
if (p)
|
||||
*p = 0;
|
||||
strcat(watts, "W");
|
||||
|
||||
char rof[NAME_LENGTH];
|
||||
sprintf(rof, "%.1f", 1.0f / wi.fire_wait);
|
||||
p = strstr(rof, ".0");
|
||||
if (p)
|
||||
*p = 0;
|
||||
strcat(rof, "/s");
|
||||
|
||||
mprintf(("\tPower Use: %-10sROF: %s\n\n", watts, rof));
|
||||
}
|
||||
}
|
||||
for (auto &wi : Weapon_info)
|
||||
{
|
||||
if (wi.subtype != WP_MISSILE)
|
||||
continue;
|
||||
|
||||
weapon_info *wip_base = nullptr;
|
||||
if (strchr(wi.name, '#'))
|
||||
{
|
||||
char temp[NAME_LENGTH];
|
||||
strcpy_s(temp, wi.name);
|
||||
end_string_at_first_hash_symbol(temp);
|
||||
int idx = weapon_info_lookup(temp);
|
||||
if (idx >= 0)
|
||||
wip_base = &Weapon_info[idx];
|
||||
}
|
||||
|
||||
if (wi.wi_flags[Weapon::Info_Flags::Player_allowed] || (wip_base != nullptr && wip_base->wi_flags[Weapon::Info_Flags::Player_allowed]) || wi.wi_flags[Weapon::Info_Flags::Child])
|
||||
{
|
||||
mprintf(("%s\n", wi.name));
|
||||
mprintf(("\tVelocity: %-11.0fRange: %.0f\n", wi.max_speed, wi.max_speed * wi.lifetime));
|
||||
|
||||
float multiplier = (wi.shockwave.inner_rad > 0.0f || wi.shockwave.outer_rad > 0.0f) ? 2.0f : 1.0f;
|
||||
mprintf(("\tDamage: "));
|
||||
mprintf(("%.0f Hull, ", multiplier * wi.damage * wi.armor_factor));
|
||||
mprintf(("%.0f Shield, ", multiplier * wi.damage * wi.shield_factor));
|
||||
mprintf(("%.0f Subsystem\n", multiplier * wi.damage * wi.subsystem_factor));
|
||||
|
||||
char wait[NAME_LENGTH];
|
||||
sprintf(wait, "%.1f", wi.fire_wait);
|
||||
char *p = strstr(wait, ".0");
|
||||
if (p)
|
||||
*p = 0;
|
||||
strcat(wait, "s");
|
||||
|
||||
bool flip = wi.rearm_rate <= 1.0f;
|
||||
char flip_str[NAME_LENGTH];
|
||||
sprintf(flip_str, "%d/", wi.reloaded_per_batch);
|
||||
|
||||
char reload[NAME_LENGTH];
|
||||
sprintf(reload, "%.1f", flip ? wi.reloaded_per_batch / wi.rearm_rate : wi.rearm_rate);
|
||||
p = strstr(reload, ".0");
|
||||
if (p)
|
||||
*p = 0;
|
||||
|
||||
mprintf(("\tFire Wait: %-10sReload: %s%s%ss\n\n", wait, !flip ? flip_str : "", reload, flip ? "/" : ""));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Given a weapon, figure out how many independent locks we can have with it.
|
||||
int weapon_get_max_missile_seekers(weapon_info *wip)
|
||||
{
|
||||
int max_target_locks;
|
||||
|
||||
if ( wip->multi_lock ) {
|
||||
if ( wip->wi_flags[Weapon::Info_Flags::Swarm] ) {
|
||||
max_target_locks = wip->swarm_count;
|
||||
} else if ( wip->wi_flags[Weapon::Info_Flags::Corkscrew] ) {
|
||||
max_target_locks = wip->cs_num_fired;
|
||||
} else {
|
||||
max_target_locks = 1;
|
||||
}
|
||||
} else {
|
||||
max_target_locks = 1;
|
||||
}
|
||||
|
||||
return max_target_locks;
|
||||
}
|
||||
|
||||
bool weapon_can_lock_on_ship_type(weapon_info *wip, int ship_type)
|
||||
{
|
||||
// Determine if there are any restrictions, treating an empty list as true
|
||||
return (wip->ship_type_restrict.size() == 0) ||
|
||||
std::any_of(wip->ship_type_restrict.begin(), wip->ship_type_restrict.end(), [ship_type](int type) { return type == ship_type; });
|
||||
}
|
||||
+3
-6
@@ -231,9 +231,8 @@ BOOL CFREDApp::InitInstance() {
|
||||
Lighting_on = GetProfileInt("Preferences", "Lighting On", Lighting_on);
|
||||
|
||||
// Goober5000
|
||||
Format_fs2_open = GetProfileInt("Preferences", "FS2 open format", Format_fs2_open);
|
||||
Format_fs2_retail = GetProfileInt("Preferences", "FS2 retail format", Format_fs2_retail);
|
||||
Format_fs1_retail = GetProfileInt("Preferences", "FS1 retail format", Format_fs1_retail);
|
||||
Mission_save_format = GetProfileInt("Preferences", "FS2 open format", FSO_FORMAT_STANDARD);
|
||||
Mission_save_format = GetProfileInt("Preferences", "Mission save format", Mission_save_format);
|
||||
|
||||
read_window("Main window", &Main_wnd_data);
|
||||
read_window("Ship window", &Ship_wnd_data);
|
||||
@@ -493,9 +492,7 @@ void CFREDApp::write_ini_file(int degree) {
|
||||
WriteProfileInt("Preferences", "Lighting On", Lighting_on);
|
||||
|
||||
// Goober5000
|
||||
WriteProfileInt("Preferences", "FS2 open format", Format_fs2_open);
|
||||
WriteProfileInt("Preferences", "FS2 retail format", Format_fs2_retail);
|
||||
WriteProfileInt("Preferences", "FS1 retail format", Format_fs1_retail);
|
||||
WriteProfileInt("Preferences", "Mission save format", Mission_save_format);
|
||||
|
||||
if (!degree) {
|
||||
record_window_data(&Waypoint_wnd_data, &Waypoint_editor_dialog);
|
||||
|
||||
+9
-19
@@ -107,9 +107,7 @@ Marking_box marking_box;
|
||||
object_orient_pos rotation_backup[MAX_OBJECTS];
|
||||
|
||||
// Goober5000 (currently, FS1 retail not implemented)
|
||||
int Format_fs2_open = FSO_FORMAT_STANDARD;
|
||||
int Format_fs2_retail = 0;
|
||||
int Format_fs1_retail = 0;
|
||||
int Mission_save_format = FSO_FORMAT_STANDARD;
|
||||
|
||||
// used by error checker, but needed in more than just one function.
|
||||
char *names[MAX_OBJECTS], flags[MAX_OBJECTS];
|
||||
@@ -4523,9 +4521,7 @@ void CFREDView::OnDumpStats()
|
||||
|
||||
void CFREDView::OnFormatFs2Open()
|
||||
{
|
||||
Format_fs2_open = FSO_FORMAT_STANDARD;
|
||||
Format_fs2_retail = 0;
|
||||
Format_fs1_retail = 0;
|
||||
Mission_save_format = FSO_FORMAT_STANDARD;
|
||||
|
||||
theApp.write_ini_file();
|
||||
Update_window = 1;
|
||||
@@ -4533,14 +4529,12 @@ void CFREDView::OnFormatFs2Open()
|
||||
|
||||
void CFREDView::OnUpdateFormatFs2Open(CCmdUI* pCmdUI)
|
||||
{
|
||||
pCmdUI->SetCheck(Format_fs2_open == FSO_FORMAT_STANDARD);
|
||||
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_STANDARD);
|
||||
}
|
||||
|
||||
void CFREDView::OnFormatFs2OpenComp()
|
||||
{
|
||||
Format_fs2_open = FSO_FORMAT_COMPATIBILITY_MODE;
|
||||
Format_fs2_retail = 0;
|
||||
Format_fs1_retail = 0;
|
||||
Mission_save_format = FSO_FORMAT_COMPATIBILITY_MODE;
|
||||
|
||||
theApp.write_ini_file();
|
||||
Update_window = 1;
|
||||
@@ -4548,14 +4542,12 @@ void CFREDView::OnFormatFs2OpenComp()
|
||||
|
||||
void CFREDView::OnUpdateFormatFs2OpenComp(CCmdUI* pCmdUI)
|
||||
{
|
||||
pCmdUI->SetCheck(Format_fs2_open == FSO_FORMAT_COMPATIBILITY_MODE);
|
||||
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_COMPATIBILITY_MODE);
|
||||
}
|
||||
|
||||
void CFREDView::OnFormatFs2Retail()
|
||||
{
|
||||
Format_fs2_open = FSO_FORMAT_RETAIL;
|
||||
Format_fs2_retail = 1;
|
||||
Format_fs1_retail = 0;
|
||||
Mission_save_format = FSO_FORMAT_RETAIL;
|
||||
|
||||
theApp.write_ini_file();
|
||||
Update_window = 1;
|
||||
@@ -4563,14 +4555,12 @@ void CFREDView::OnFormatFs2Retail()
|
||||
|
||||
void CFREDView::OnUpdateFormatFs2Retail(CCmdUI* pCmdUI)
|
||||
{
|
||||
pCmdUI->SetCheck(Format_fs2_retail);
|
||||
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_RETAIL);
|
||||
}
|
||||
|
||||
void CFREDView::OnFormatFs1Retail()
|
||||
{
|
||||
Format_fs2_open = FSO_FORMAT_RETAIL;
|
||||
Format_fs2_retail = 0;
|
||||
Format_fs1_retail = 1;
|
||||
Mission_save_format = FSO_FORMAT_RETAIL;
|
||||
|
||||
theApp.write_ini_file();
|
||||
Update_window = 1;
|
||||
@@ -4578,7 +4568,7 @@ void CFREDView::OnFormatFs1Retail()
|
||||
|
||||
void CFREDView::OnUpdateFormatFs1Retail(CCmdUI* pCmdUI)
|
||||
{
|
||||
pCmdUI->SetCheck(Format_fs1_retail);
|
||||
pCmdUI->SetCheck(Mission_save_format == FSO_FORMAT_RETAIL);
|
||||
}
|
||||
|
||||
void CFREDView::OnEditorsSetGlobalShipFlags()
|
||||
|
||||
+1
-5
@@ -364,11 +364,7 @@ enum FSO_FORMAT
|
||||
FSO_FORMAT_STANDARD = 1,
|
||||
FSO_FORMAT_COMPATIBILITY_MODE = 2
|
||||
};
|
||||
|
||||
// Goober5000 (currently, FS1 retail not implemented)
|
||||
extern int Format_fs2_open;
|
||||
extern int Format_fs2_retail;
|
||||
extern int Format_fs1_retail;
|
||||
extern int Mission_save_format;
|
||||
|
||||
extern CFREDView *Fred_view_wnd;
|
||||
|
||||
|
||||
@@ -333,13 +333,13 @@ bool fred_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps)
|
||||
// Load game_settings.tbl
|
||||
mod_table_init();
|
||||
|
||||
// initialize localization module. Make sure this is done AFTER initialzing OS.
|
||||
// NOTE : Fred should ALWAYS run in English. Otherwise it might swap in another language
|
||||
// initialize localization module. Make sure this is done AFTER initializing OS.
|
||||
// NOTE: Fred should ALWAYS run without localization. Otherwise it might swap in another language
|
||||
// when saving - which would cause inconsistencies when externalizing to tstrings.tbl via Exstr
|
||||
// trust me on this :)
|
||||
lcl_init(FS2_OPEN_DEFAULT_LANGUAGE);
|
||||
lcl_init(LCL_UNTRANSLATED);
|
||||
|
||||
// Goober5000 - force init XSTRs (so they work, but only work in English, based on above comment)
|
||||
// Goober5000 - force init XSTRs (so they work, but only work untranslated, based on above comment)
|
||||
extern int Xstr_inited;
|
||||
Xstr_inited = 1;
|
||||
|
||||
@@ -466,6 +466,7 @@ bool fred_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps)
|
||||
Id_select_type_waypoint = (int)(Ship_info.size());
|
||||
Fred_main_wnd -> init_tools();
|
||||
|
||||
Script_system.RunInitFunctions();
|
||||
Script_system.RunCondition(CHA_GAMEINIT);
|
||||
|
||||
return true;
|
||||
|
||||
+187
-122
@@ -51,7 +51,8 @@
|
||||
|
||||
#include <freespace.h>
|
||||
|
||||
int CFred_mission_save::autosave_mission_file(char *pathname) {
|
||||
int CFred_mission_save::autosave_mission_file(char *pathname)
|
||||
{
|
||||
char backup_name[256], name2[256];
|
||||
int i;
|
||||
|
||||
@@ -73,7 +74,8 @@ int CFred_mission_save::autosave_mission_file(char *pathname) {
|
||||
return err;
|
||||
}
|
||||
|
||||
void CFred_mission_save::bypass_comment(const char *comment, const char *end) {
|
||||
void CFred_mission_save::bypass_comment(const char *comment, const char *end)
|
||||
{
|
||||
char *ch = strstr(raw_ptr, comment);
|
||||
if (ch != NULL) {
|
||||
if (end != NULL) {
|
||||
@@ -96,20 +98,23 @@ void CFred_mission_save::bypass_comment(const char *comment, const char *end) {
|
||||
}
|
||||
}
|
||||
|
||||
void CFred_mission_save::convert_special_tags_to_retail(char *text, int max_len) {
|
||||
void CFred_mission_save::convert_special_tags_to_retail(char *text, int max_len)
|
||||
{
|
||||
replace_all(text, "$quote", "''", max_len);
|
||||
replace_all(text, "$semicolon", ",", max_len);
|
||||
}
|
||||
|
||||
void CFred_mission_save::convert_special_tags_to_retail(SCP_string &text) {
|
||||
void CFred_mission_save::convert_special_tags_to_retail(SCP_string &text)
|
||||
{
|
||||
replace_all(text, "$quote", "''");
|
||||
replace_all(text, "$semicolon", ",");
|
||||
}
|
||||
|
||||
void CFred_mission_save::convert_special_tags_to_retail() {
|
||||
void CFred_mission_save::convert_special_tags_to_retail()
|
||||
{
|
||||
int i, team, stage;
|
||||
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL)
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL)
|
||||
return;
|
||||
|
||||
for (team = 0; team < Num_teams; team++) {
|
||||
@@ -134,7 +139,8 @@ void CFred_mission_save::convert_special_tags_to_retail() {
|
||||
}
|
||||
}
|
||||
|
||||
int CFred_mission_save::fout(char *format, ...) {
|
||||
int CFred_mission_save::fout(char *format, ...)
|
||||
{
|
||||
SCP_string str;
|
||||
va_list args;
|
||||
|
||||
@@ -150,7 +156,8 @@ int CFred_mission_save::fout(char *format, ...) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFred_mission_save::fout_ext(char *pre_str, char *format, ...) {
|
||||
int CFred_mission_save::fout_ext(char *pre_str, char *format, ...)
|
||||
{
|
||||
SCP_string str_scp;
|
||||
SCP_string str_out_scp;
|
||||
va_list args;
|
||||
@@ -252,7 +259,8 @@ int CFred_mission_save::fout_ext(char *pre_str, char *format, ...) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFred_mission_save::fout_version(char *format, ...) {
|
||||
int CFred_mission_save::fout_version(char *format, ...)
|
||||
{
|
||||
SCP_string str_scp;
|
||||
char *ch = NULL;
|
||||
va_list args;
|
||||
@@ -261,9 +269,14 @@ int CFred_mission_save::fout_version(char *format, ...) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// if we're saving in retail format, we can completely skip this
|
||||
if (Mission_save_format == FSO_FORMAT_RETAIL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// output the version first thing, but skip the special case where we use
|
||||
// fout_version() for multiline value strings (typically indicated by an initial space)
|
||||
if ((Format_fs2_open == FSO_FORMAT_COMPATIBILITY_MODE) && (*format != ' ') && !fso_ver_comment.empty()) {
|
||||
if ((Mission_save_format == FSO_FORMAT_COMPATIBILITY_MODE) && (*format != ' ') && !fso_ver_comment.empty()) {
|
||||
while (*format == '\n') {
|
||||
str_scp.append(1, *format);
|
||||
format++;
|
||||
@@ -284,7 +297,7 @@ int CFred_mission_save::fout_version(char *format, ...) {
|
||||
char *str_c = vm_strdup(str_scp.c_str());
|
||||
|
||||
// this could be a multi-line string, so we've got to handle it all properly
|
||||
if ((Format_fs2_open == FSO_FORMAT_COMPATIBILITY_MODE) && !fso_ver_comment.empty()) {
|
||||
if ((Mission_save_format == FSO_FORMAT_COMPATIBILITY_MODE) && !fso_ver_comment.empty()) {
|
||||
bool first_line = true;
|
||||
char *str_p = str_c;
|
||||
|
||||
@@ -341,7 +354,8 @@ int CFred_mission_save::fout_version(char *format, ...) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CFred_mission_save::parse_comments(int newlines) {
|
||||
void CFred_mission_save::parse_comments(int newlines)
|
||||
{
|
||||
char *comment_start = NULL;
|
||||
int state = 0, same_line = 0, first_comment = 1, tab = 0, flag = 0;
|
||||
bool version_added = false;
|
||||
@@ -501,7 +515,8 @@ void CFred_mission_save::parse_comments(int newlines) {
|
||||
return;
|
||||
}
|
||||
|
||||
void CFred_mission_save::save_ai_goals(ai_goal *goalp, int ship) {
|
||||
void CFred_mission_save::save_ai_goals(ai_goal *goalp, int ship)
|
||||
{
|
||||
char *str = NULL, buf[80];
|
||||
int i, valid, flag = 1;
|
||||
|
||||
@@ -654,7 +669,8 @@ void CFred_mission_save::save_ai_goals(ai_goal *goalp, int ship) {
|
||||
fso_comment_pop(true);
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_asteroid_fields() {
|
||||
int CFred_mission_save::save_asteroid_fields()
|
||||
{
|
||||
int i;
|
||||
|
||||
fred_parse_flag = 0;
|
||||
@@ -748,7 +764,8 @@ int CFred_mission_save::save_asteroid_fields() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_bitmaps() {
|
||||
int CFred_mission_save::save_bitmaps()
|
||||
{
|
||||
int i;
|
||||
uint j;
|
||||
|
||||
@@ -894,7 +911,8 @@ int CFred_mission_save::save_bitmaps() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_briefing() {
|
||||
int CFred_mission_save::save_briefing()
|
||||
{
|
||||
int i, j, k, nb;
|
||||
SCP_string sexp_out;
|
||||
brief_stage *bs;
|
||||
@@ -1027,13 +1045,13 @@ int CFred_mission_save::save_briefing() {
|
||||
parse_comments();
|
||||
fout(" %d", (bi->flags & BI_HIGHLIGHT) ? 1 : 0);
|
||||
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
required_string_fred("$mirror:");
|
||||
parse_comments();
|
||||
fout(" %d", (bi->flags & BI_MIRROR_ICON) ? 1 : 0);
|
||||
}
|
||||
|
||||
if ((Format_fs2_open != FSO_FORMAT_RETAIL) && (bi->flags & BI_USE_WING_ICON)) {
|
||||
if ((Mission_save_format != FSO_FORMAT_RETAIL) && (bi->flags & BI_USE_WING_ICON)) {
|
||||
required_string_fred("$use wing icon:");
|
||||
parse_comments();
|
||||
fout(" %d", (bi->flags & BI_USE_WING_ICON) ? 1 : 0);
|
||||
@@ -1070,7 +1088,8 @@ int CFred_mission_save::save_briefing() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_campaign_file(char *pathname) {
|
||||
int CFred_mission_save::save_campaign_file(char *pathname)
|
||||
{
|
||||
int i, j, m, flag;
|
||||
|
||||
Campaign_tree_formp->save_tree(); // flush all changes so they get saved.
|
||||
@@ -1109,7 +1128,7 @@ int CFred_mission_save::save_campaign_file(char *pathname) {
|
||||
}
|
||||
|
||||
// campaign flags - Goober5000
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
optional_string_fred("$Flags:");
|
||||
parse_comments();
|
||||
fout(" %d\n", Campaign.flags);
|
||||
@@ -1153,7 +1172,7 @@ int CFred_mission_save::save_campaign_file(char *pathname) {
|
||||
parse_comments();
|
||||
|
||||
// Goober5000
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
// don't save Bastion flag
|
||||
fout(" %d", Campaign.missions[m].flags & ~CMISSION_FLAG_BASTION);
|
||||
|
||||
@@ -1201,7 +1220,7 @@ int CFred_mission_save::save_campaign_file(char *pathname) {
|
||||
//save_campaign_sexp(Links[j].sexp, Campaign.missions[Links[j].to].name);
|
||||
if (Links[j].is_mission_loop) {
|
||||
mission_loop = true;
|
||||
} else if (Links[j].is_mission_fork) {
|
||||
} else if (Links[j].is_mission_fork && (Mission_save_format != FSO_FORMAT_RETAIL)) {
|
||||
mission_fork = true;
|
||||
} else {
|
||||
save_campaign_sexp(Links[j].sexp, Links[j].to);
|
||||
@@ -1312,7 +1331,8 @@ int CFred_mission_save::save_campaign_file(char *pathname) {
|
||||
return err;
|
||||
}
|
||||
|
||||
void CFred_mission_save::save_campaign_sexp(int node, int link_num) {
|
||||
void CFred_mission_save::save_campaign_sexp(int node, int link_num)
|
||||
{
|
||||
SCP_string sexp_out;
|
||||
Assert(node >= 0);
|
||||
|
||||
@@ -1332,7 +1352,8 @@ void CFred_mission_save::save_campaign_sexp(int node, int link_num) {
|
||||
}
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_cmd_brief() {
|
||||
int CFred_mission_save::save_cmd_brief()
|
||||
{
|
||||
int stage;
|
||||
|
||||
stage = 0;
|
||||
@@ -1370,7 +1391,8 @@ int CFred_mission_save::save_cmd_brief() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_cmd_briefs() {
|
||||
int CFred_mission_save::save_cmd_briefs()
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < Num_teams; i++) {
|
||||
@@ -1381,7 +1403,8 @@ int CFred_mission_save::save_cmd_briefs() {
|
||||
return err;
|
||||
}
|
||||
|
||||
void CFred_mission_save::fso_comment_push(char *ver) {
|
||||
void CFred_mission_save::fso_comment_push(char *ver)
|
||||
{
|
||||
if (fso_ver_comment.empty()) {
|
||||
fso_ver_comment.push_back(SCP_string(ver));
|
||||
return;
|
||||
@@ -1414,7 +1437,8 @@ void CFred_mission_save::fso_comment_push(char *ver) {
|
||||
}
|
||||
}
|
||||
|
||||
void CFred_mission_save::fso_comment_pop(bool pop_all) {
|
||||
void CFred_mission_save::fso_comment_pop(bool pop_all)
|
||||
{
|
||||
if (fso_ver_comment.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1427,7 +1451,8 @@ void CFred_mission_save::fso_comment_pop(bool pop_all) {
|
||||
fso_ver_comment.pop_back();
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_common_object_data(object *objp, ship *shipp) {
|
||||
int CFred_mission_save::save_common_object_data(object *objp, ship *shipp)
|
||||
{
|
||||
int j, z;
|
||||
ship_subsys *ptr = NULL;
|
||||
ship_info *sip = NULL;
|
||||
@@ -1446,7 +1471,7 @@ int CFred_mission_save::save_common_object_data(object *objp, ship *shipp) {
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL && (shipp->special_hitpoints)) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL && (shipp->special_hitpoints > 0)) {
|
||||
temp_max_hull_strength = (float) shipp->special_hitpoints;
|
||||
} else {
|
||||
temp_max_hull_strength = sip->max_hull_strength;
|
||||
@@ -1602,8 +1627,9 @@ int CFred_mission_save::save_common_object_data(object *objp, ship *shipp) {
|
||||
return err;
|
||||
}
|
||||
|
||||
void CFred_mission_save::save_custom_bitmap(const char *expected_string_640, const char *expected_string_1024, const char *string_field_640, const char *string_field_1024, int blank_lines) {
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
void CFred_mission_save::save_custom_bitmap(const char *expected_string_640, const char *expected_string_1024, const char *string_field_640, const char *string_field_1024, int blank_lines)
|
||||
{
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if ((*string_field_640 != '\0') || (*string_field_1024 != '\0')) {
|
||||
while (blank_lines-- > 0)
|
||||
fout("\n");
|
||||
@@ -1619,13 +1645,14 @@ void CFred_mission_save::save_custom_bitmap(const char *expected_string_640, con
|
||||
}
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_cutscenes() {
|
||||
int CFred_mission_save::save_cutscenes()
|
||||
{
|
||||
char type[NAME_LENGTH];
|
||||
SCP_string sexp_out;
|
||||
|
||||
// Let's just assume it has them for now -
|
||||
if (!(The_mission.cutscenes.empty())) {
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if (optional_string_fred("#Cutscenes")) {
|
||||
parse_comments(2);
|
||||
} else {
|
||||
@@ -1675,7 +1702,7 @@ int CFred_mission_save::save_cutscenes() {
|
||||
required_string_fred("#end");
|
||||
parse_comments();
|
||||
} else {
|
||||
MessageBox(NULL, "Warning: This mission contains cutscene data, but you are saving in the retail mission format. This information will be lost", "Incompatibility with retail mission format", MB_OK);
|
||||
MessageBox(nullptr, "Warning: This mission contains cutscene data, but you are saving in the retail mission format. This information will be lost.", "Incompatibility with retail mission format", MB_OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1683,7 +1710,8 @@ int CFred_mission_save::save_cutscenes() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_debriefing() {
|
||||
int CFred_mission_save::save_debriefing()
|
||||
{
|
||||
int j, i;
|
||||
SCP_string sexp_out;
|
||||
|
||||
@@ -1739,7 +1767,8 @@ int CFred_mission_save::save_debriefing() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_events() {
|
||||
int CFred_mission_save::save_events()
|
||||
{
|
||||
SCP_string sexp_out;
|
||||
int i, j, add_flag;
|
||||
|
||||
@@ -1778,7 +1807,7 @@ int CFred_mission_save::save_events() {
|
||||
fout(" %d", Mission_events[i].repeat_count);
|
||||
}
|
||||
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL && Mission_events[i].trigger_count != 1) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL && Mission_events[i].trigger_count != 1) {
|
||||
fso_comment_push(";;FSO 3.6.11;;");
|
||||
if (optional_string_fred("+Trigger Count:", "$Formula:")) {
|
||||
parse_comments();
|
||||
@@ -1851,7 +1880,7 @@ int CFred_mission_save::save_events() {
|
||||
|
||||
// save flags, if any
|
||||
// (the transient flags are not used in FRED, so if the flags field is nonzero they will all be meaningful flags from the flag list)
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL && Mission_events[i].flags != 0) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL && Mission_events[i].flags != 0) {
|
||||
fso_comment_push(";;FSO 20.0.0;;");
|
||||
if (optional_string_fred("+Event Flags: (", "$Formula:")) {
|
||||
parse_comments();
|
||||
@@ -1868,7 +1897,7 @@ int CFred_mission_save::save_events() {
|
||||
fout(" )");
|
||||
}
|
||||
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL && Mission_events[i].mission_log_flags != 0) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL && Mission_events[i].mission_log_flags != 0) {
|
||||
fso_comment_push(";;FSO 3.6.11;;");
|
||||
if (optional_string_fred("+Event Log Flags: (", "$Formula:")) {
|
||||
parse_comments();
|
||||
@@ -1894,9 +1923,10 @@ int CFred_mission_save::save_events() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_fiction() {
|
||||
int CFred_mission_save::save_fiction()
|
||||
{
|
||||
if (mission_has_fiction()) {
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if (optional_string_fred("#Fiction Viewer"))
|
||||
parse_comments(2);
|
||||
else
|
||||
@@ -1960,7 +1990,7 @@ int CFred_mission_save::save_fiction() {
|
||||
optional_string_fred("$Formula:");
|
||||
}
|
||||
} else {
|
||||
MessageBox(NULL, "Warning: This mission contains fiction viewer data, but you are saving in the retail mission format.", "Incompatibility with retail mission format", MB_OK);
|
||||
MessageBox(nullptr, "Warning: This mission contains fiction viewer data, but you are saving in the retail mission format.", "Incompatibility with retail mission format", MB_OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1969,7 +1999,8 @@ int CFred_mission_save::save_fiction() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_goals() {
|
||||
int CFred_mission_save::save_goals()
|
||||
{
|
||||
SCP_string sexp_out;
|
||||
int i;
|
||||
|
||||
@@ -2048,14 +2079,16 @@ int CFred_mission_save::save_goals() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_matrix(matrix &m) {
|
||||
int CFred_mission_save::save_matrix(matrix &m)
|
||||
{
|
||||
fout("\n\t%f, %f, %f,\n", m.vec.rvec.xyz.x, m.vec.rvec.xyz.y, m.vec.rvec.xyz.z);
|
||||
fout("\t%f, %f, %f,\n", m.vec.uvec.xyz.x, m.vec.uvec.xyz.y, m.vec.uvec.xyz.z);
|
||||
fout("\t%f, %f, %f", m.vec.fvec.xyz.x, m.vec.fvec.xyz.y, m.vec.fvec.xyz.z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_messages() {
|
||||
int CFred_mission_save::save_messages()
|
||||
{
|
||||
int i;
|
||||
|
||||
fred_parse_flag = 0;
|
||||
@@ -2064,7 +2097,7 @@ int CFred_mission_save::save_messages() {
|
||||
fout("\t\t;! %d total\n", Num_messages - Num_builtin_messages);
|
||||
|
||||
// Goober5000 - special Command info
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if (stricmp(The_mission.command_sender, DEFAULT_COMMAND))
|
||||
fout("\n$Command Sender: %s", The_mission.command_sender);
|
||||
|
||||
@@ -2130,7 +2163,8 @@ int CFred_mission_save::save_messages() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_mission_file(char *pathname) {
|
||||
int CFred_mission_save::save_mission_file(char *pathname)
|
||||
{
|
||||
char backup_name[256], savepath[MAX_PATH_LEN], *p;
|
||||
|
||||
strcpy_s(savepath, "");
|
||||
@@ -2160,7 +2194,8 @@ int CFred_mission_save::save_mission_file(char *pathname) {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_mission_info() {
|
||||
int CFred_mission_save::save_mission_info()
|
||||
{
|
||||
required_string_fred("#Mission Info");
|
||||
parse_comments(0);
|
||||
|
||||
@@ -2235,7 +2270,7 @@ int CFred_mission_save::save_mission_info() {
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
// write out the nebula clipping multipliers
|
||||
fout("\n+Fog Near Mult: %f\n", Neb2_fog_near_mult);
|
||||
fout("\n+Fog Far Mult: %f\n", Neb2_fog_far_mult);
|
||||
@@ -2261,7 +2296,7 @@ int CFred_mission_save::save_mission_info() {
|
||||
|
||||
fout(" %d", The_mission.num_respawns);
|
||||
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
fso_comment_push(";;FSO 3.6.11;;");
|
||||
if (optional_string_fred("+Max Respawn Time:"))
|
||||
parse_comments(2);
|
||||
@@ -2276,7 +2311,7 @@ int CFred_mission_save::save_mission_info() {
|
||||
}
|
||||
}
|
||||
|
||||
if (Format_fs2_open == FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format == FSO_FORMAT_RETAIL) {
|
||||
if (optional_string_fred("+Red Alert:"))
|
||||
parse_comments(2);
|
||||
else
|
||||
@@ -2285,7 +2320,7 @@ int CFred_mission_save::save_mission_info() {
|
||||
fout(" %d", (The_mission.flags[Mission::Mission_Flags::Red_alert]) ? 1 : 0);
|
||||
}
|
||||
|
||||
if (Format_fs2_open == FSO_FORMAT_RETAIL) //-V581
|
||||
if (Mission_save_format == FSO_FORMAT_RETAIL) //-V581
|
||||
{
|
||||
if (optional_string_fred("+Scramble:"))
|
||||
parse_comments(2);
|
||||
@@ -2304,7 +2339,7 @@ int CFred_mission_save::save_mission_info() {
|
||||
fout(" %d", (The_mission.support_ships.max_support_ships == 0) ? 1 : 0);
|
||||
|
||||
// here be WMCoolmon's hull and subsys repair stuff
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if (optional_string_fred("+Hull Repair Ceiling:")) {
|
||||
parse_comments(1);
|
||||
} else {
|
||||
@@ -2365,7 +2400,7 @@ int CFred_mission_save::save_mission_info() {
|
||||
}
|
||||
|
||||
// Goober5000 - special wing info
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
int i;
|
||||
fout("\n");
|
||||
|
||||
@@ -2499,7 +2534,8 @@ int CFred_mission_save::save_mission_info() {
|
||||
return err;
|
||||
}
|
||||
|
||||
void CFred_mission_save::save_mission_internal(const char *pathname) {
|
||||
void CFred_mission_save::save_mission_internal(const char *pathname)
|
||||
{
|
||||
CTime t;
|
||||
|
||||
t = CTime::GetCurrentTime();
|
||||
@@ -2570,7 +2606,8 @@ void CFred_mission_save::save_mission_internal(const char *pathname) {
|
||||
mprintf(("Mission saving error code #%d\n", err));
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_music() {
|
||||
int CFred_mission_save::save_music()
|
||||
{
|
||||
required_string_fred("#Music");
|
||||
parse_comments(2);
|
||||
|
||||
@@ -2628,7 +2665,7 @@ int CFred_mission_save::save_music() {
|
||||
}
|
||||
fout(" %s", Mission_music[SCORE_DEBRIEF_SUCCESS] < 0 ? "None" : Spooled_music[Mission_music[SCORE_DEBRIEF_SUCCESS]].name);
|
||||
}
|
||||
if (Mission_music[SCORE_DEBRIEF_AVERAGE] != event_music_get_spooled_music_index("Average")) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL && Mission_music[SCORE_DEBRIEF_AVERAGE] != event_music_get_spooled_music_index("Average")) {
|
||||
if (optional_string_fred("$Debriefing Average Music:")) {
|
||||
parse_comments(1);
|
||||
} else {
|
||||
@@ -2666,6 +2703,9 @@ int CFred_mission_save::save_music() {
|
||||
|
||||
int CFred_mission_save::save_warp_params(WarpDirection direction, ship *shipp)
|
||||
{
|
||||
if (Mission_save_format == FSO_FORMAT_RETAIL)
|
||||
return err;
|
||||
|
||||
// for writing to file; c.f. parse_warp_params
|
||||
const char *prefix = (direction == WarpDirection::WARP_IN) ? "$Warpin" : "$Warpout";
|
||||
|
||||
@@ -2760,7 +2800,8 @@ int CFred_mission_save::save_warp_params(WarpDirection direction, ship *shipp)
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_objects() {
|
||||
int CFred_mission_save::save_objects()
|
||||
{
|
||||
SCP_string sexp_out;
|
||||
int i, z;
|
||||
ai_info *aip;
|
||||
@@ -2791,7 +2832,7 @@ int CFred_mission_save::save_objects() {
|
||||
fout(" %s\t\t;! Object #%d\n", shipp->ship_name, i);
|
||||
|
||||
// Display name
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL && shipp->has_display_name()) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL && shipp->has_display_name()) {
|
||||
// The display name is only written if there was one at the start to avoid introducing inconsistencies
|
||||
fout("\n$Display name:");
|
||||
fout_ext(" ", "%s", shipp->display_name.c_str());
|
||||
@@ -2803,7 +2844,7 @@ int CFred_mission_save::save_objects() {
|
||||
fout(" %s", Ship_info[shipp->ship_info_index].name);
|
||||
|
||||
//alt classes stuff
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
for (SCP_vector<alt_class>::iterator ii = shipp->s_alt_classes.begin(); ii != shipp->s_alt_classes.end(); ++ii) {
|
||||
// is this a variable?
|
||||
if (ii->variable_index != -1) {
|
||||
@@ -2825,7 +2866,7 @@ int CFred_mission_save::save_objects() {
|
||||
}
|
||||
|
||||
// optional callsign
|
||||
if (strlen(Fred_callsigns[i])) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL && strlen(Fred_callsigns[i])) {
|
||||
fout("\n$Callsign: %s\n", Fred_callsigns[i]);
|
||||
}
|
||||
|
||||
@@ -2833,7 +2874,7 @@ int CFred_mission_save::save_objects() {
|
||||
parse_comments();
|
||||
fout(" %s", Iff_info[shipp->team].iff_name);
|
||||
|
||||
if (Ship_info[shipp->ship_info_index].uses_team_colors) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL && Ship_info[shipp->ship_info_index].uses_team_colors) {
|
||||
required_string_fred("$Team Color Setting:");
|
||||
parse_comments();
|
||||
fout(" %s", shipp->team_name.c_str());
|
||||
@@ -2847,7 +2888,7 @@ int CFred_mission_save::save_objects() {
|
||||
parse_comments();
|
||||
save_matrix(Objects[shipp->objnum].orient);
|
||||
|
||||
if (Format_fs2_retail) {
|
||||
if (Mission_save_format == FSO_FORMAT_RETAIL) {
|
||||
required_string_fred("$IFF:");
|
||||
parse_comments();
|
||||
fout(" %s", "IFF 1");
|
||||
@@ -2904,7 +2945,7 @@ int CFred_mission_save::save_objects() {
|
||||
if (z & SPECIAL_ARRIVAL_ANCHOR_FLAG) {
|
||||
// get name
|
||||
char tmp[NAME_LENGTH + 15];
|
||||
stuff_special_arrival_anchor_name(tmp, z, Format_fs2_retail);
|
||||
stuff_special_arrival_anchor_name(tmp, z, Mission_save_format == FSO_FORMAT_RETAIL ? 1 : 0);
|
||||
|
||||
// save it
|
||||
fout(" %s", tmp);
|
||||
@@ -2916,7 +2957,7 @@ int CFred_mission_save::save_objects() {
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if ((shipp->arrival_location == ARRIVE_FROM_DOCK_BAY) && (shipp->arrival_path_mask > 0)) {
|
||||
int anchor_shipnum;
|
||||
polymodel *pm;
|
||||
@@ -2970,7 +3011,7 @@ int CFred_mission_save::save_objects() {
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if ((shipp->departure_location == DEPART_AT_DOCK_BAY) && (shipp->departure_path_mask > 0)) {
|
||||
int anchor_shipnum;
|
||||
polymodel *pm;
|
||||
@@ -3071,12 +3112,12 @@ int CFred_mission_save::save_objects() {
|
||||
if (shipp->flags[Ship::Ship_Flags::Dont_collide_invis])
|
||||
fout(" \"don't-collide-invisible\"");
|
||||
//for compatibility reasons ship locked or weapons locked are saved as both locked in retail mode
|
||||
if ((Format_fs2_open == FSO_FORMAT_RETAIL) && ((shipp->flags[Ship::Ship_Flags::Ship_locked]) || (shipp->flags[Ship::Ship_Flags::Weapons_locked])))
|
||||
if ((Mission_save_format == FSO_FORMAT_RETAIL) && ((shipp->flags[Ship::Ship_Flags::Ship_locked]) || (shipp->flags[Ship::Ship_Flags::Weapons_locked])))
|
||||
fout(" \"locked\"");
|
||||
fout(" )");
|
||||
|
||||
// flags2 added by Goober5000 --------------------------------
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if (optional_string_fred("+Flags2:", "$Name:")) {
|
||||
parse_comments();
|
||||
fout(" (");
|
||||
@@ -3150,7 +3191,7 @@ int CFred_mission_save::save_objects() {
|
||||
}
|
||||
|
||||
// special explosions
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if (shipp->use_special_explosion) {
|
||||
fso_comment_push(";;FSO 3.6.13;;");
|
||||
if (optional_string_fred("$Special Explosion:", "$Name:")) {
|
||||
@@ -3240,7 +3281,7 @@ int CFred_mission_save::save_objects() {
|
||||
}
|
||||
|
||||
// Goober5000 ------------------------------------------------
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if (shipp->special_hitpoints) {
|
||||
fso_comment_push(";;FSO 3.6.13;;");
|
||||
if (optional_string_fred("+Special Hitpoints:", "$Name:")) {
|
||||
@@ -3295,7 +3336,7 @@ int CFred_mission_save::save_objects() {
|
||||
// Goober5000 - newer code to save off information about initially docked ships. ;)
|
||||
if (object_is_docked(&Objects[shipp->objnum])) {
|
||||
// possible incompatibility
|
||||
if (Format_fs2_open == FSO_FORMAT_RETAIL && !dock_check_docked_one_on_one(&Objects[shipp->objnum])) {
|
||||
if (Mission_save_format == FSO_FORMAT_RETAIL && !dock_check_docked_one_on_one(&Objects[shipp->objnum])) {
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
CString text = "You are saving in the retail mission format, but \"";
|
||||
@@ -3324,7 +3365,7 @@ int CFred_mission_save::save_objects() {
|
||||
}
|
||||
}
|
||||
|
||||
// check the ship flag about killing off the ship before a missino starts. Write out the appropriate
|
||||
// check the ship flag about killing off the ship before a mission starts. Write out the appropriate
|
||||
// variable if necessary
|
||||
if (shipp->flags[Ship::Ship_Flags::Kill_before_mission]) {
|
||||
if (optional_string_fred("+Destroy At:", "$Name:"))
|
||||
@@ -3377,7 +3418,7 @@ int CFred_mission_save::save_objects() {
|
||||
fout(" %d", shipp->score);
|
||||
|
||||
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL && shipp->assist_score_pct != 0) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL && shipp->assist_score_pct != 0) {
|
||||
fso_comment_push(";;FSO 3.6.10;;");
|
||||
if (optional_string_fred("+Assist Score Percentage:")) {
|
||||
parse_comments();
|
||||
@@ -3452,7 +3493,9 @@ int CFred_mission_save::save_objects() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_players() {
|
||||
int CFred_mission_save::save_players()
|
||||
{
|
||||
bool wrote_fso_data = false;
|
||||
int i, j;
|
||||
int var_idx;
|
||||
int used_pool[MAX_WEAPON_TYPES];
|
||||
@@ -3478,7 +3521,7 @@ int CFred_mission_save::save_players() {
|
||||
}
|
||||
|
||||
// write out callsign list
|
||||
if (Mission_callsign_count > 0) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL && Mission_callsign_count > 0) {
|
||||
fout("\n\n#Callsigns:\n");
|
||||
|
||||
// write them all out
|
||||
@@ -3509,6 +3552,7 @@ int CFred_mission_save::save_players() {
|
||||
if (strlen(Team_data[i].ship_list_variables[j])) {
|
||||
var_idx = get_index_sexp_variable_name(Team_data[i].ship_list_variables[j]);
|
||||
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
|
||||
wrote_fso_data = true;
|
||||
|
||||
fout("\t@%s\t", Sexp_variables[var_idx].variable_name);
|
||||
} else {
|
||||
@@ -3519,6 +3563,7 @@ int CFred_mission_save::save_players() {
|
||||
if (strlen(Team_data[i].ship_count_variables[j])) {
|
||||
var_idx = get_index_sexp_variable_name(Team_data[i].ship_count_variables[j]);
|
||||
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
|
||||
wrote_fso_data = true;
|
||||
|
||||
fout("@%s\n", Sexp_variables[var_idx].variable_name);
|
||||
} else {
|
||||
@@ -3536,11 +3581,13 @@ int CFred_mission_save::save_players() {
|
||||
|
||||
fout(" (\n");
|
||||
generate_weaponry_usage_list(i, used_pool);
|
||||
|
||||
for (j = 0; j < Team_data[i].num_weapon_choices; j++) {
|
||||
// first output the weapon name or a variable that sets it
|
||||
if (strlen(Team_data[i].weaponry_pool_variable[j])) {
|
||||
var_idx = get_index_sexp_variable_name(Team_data[i].weaponry_pool_variable[j]);
|
||||
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
|
||||
wrote_fso_data = true;
|
||||
|
||||
fout("\t@%s\t", Sexp_variables[var_idx].variable_name);
|
||||
} else {
|
||||
@@ -3553,6 +3600,7 @@ int CFred_mission_save::save_players() {
|
||||
if (strlen(Team_data[i].weaponry_amount_variable[j])) {
|
||||
var_idx = get_index_sexp_variable_name(Team_data[i].weaponry_amount_variable[j]);
|
||||
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
|
||||
wrote_fso_data = true;
|
||||
|
||||
fout("@%s\n", Sexp_variables[var_idx].variable_name);
|
||||
} else {
|
||||
@@ -3574,6 +3622,12 @@ int CFred_mission_save::save_players() {
|
||||
|
||||
fout(")");
|
||||
|
||||
// sanity check
|
||||
if (Mission_save_format == FSO_FORMAT_RETAIL && wrote_fso_data) {
|
||||
// this is such an unlikely (and hard-to-fix) case that a warning should be sufficient
|
||||
MessageBox(nullptr, "Warning: This mission contains variable-based team loadout information, but you are saving in the retail mission format. Retail FRED and FS2 will not be able to read this information.", "Incompatibility with retail mission format", MB_OK);
|
||||
}
|
||||
|
||||
// Goober5000 - mjn.mixael's required weapon feature
|
||||
bool uses_required_weapon = false;
|
||||
for (j = 0; j < weapon_info_size(); j++) {
|
||||
@@ -3582,7 +3636,7 @@ int CFred_mission_save::save_players() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (uses_required_weapon) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL && uses_required_weapon) {
|
||||
if (optional_string_fred("+Required for mission:", "$Starting Shipname:"))
|
||||
parse_comments(2);
|
||||
else
|
||||
@@ -3604,8 +3658,9 @@ int CFred_mission_save::save_players() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_plot_info() {
|
||||
if (Format_fs2_open == FSO_FORMAT_RETAIL) {
|
||||
int CFred_mission_save::save_plot_info()
|
||||
{
|
||||
if (Mission_save_format == FSO_FORMAT_RETAIL) {
|
||||
if (optional_string_fred("#Plot Info")) {
|
||||
parse_comments(2);
|
||||
|
||||
@@ -3654,7 +3709,8 @@ int CFred_mission_save::save_plot_info() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_reinforcements() {
|
||||
int CFred_mission_save::save_reinforcements()
|
||||
{
|
||||
int i, j, type;
|
||||
|
||||
fred_parse_flag = 0;
|
||||
@@ -3720,7 +3776,8 @@ int CFred_mission_save::save_reinforcements() {
|
||||
return err;
|
||||
}
|
||||
|
||||
void CFred_mission_save::save_single_dock_instance(ship *shipp, dock_instance *dock_ptr) {
|
||||
void CFred_mission_save::save_single_dock_instance(ship *shipp, dock_instance *dock_ptr)
|
||||
{
|
||||
Assert(shipp && dock_ptr);
|
||||
Assert(dock_ptr->docked_objp->type == OBJ_SHIP || dock_ptr->docked_objp->type == OBJ_START);
|
||||
|
||||
@@ -3752,7 +3809,8 @@ void CFred_mission_save::save_single_dock_instance(ship *shipp, dock_instance *d
|
||||
fso_comment_pop(true);
|
||||
}
|
||||
|
||||
void CFred_mission_save::save_turret_info(ship_subsys *ptr, int ship) {
|
||||
void CFred_mission_save::save_turret_info(ship_subsys *ptr, int ship)
|
||||
{
|
||||
int i, z;
|
||||
ship_weapon *wp = &ptr->weapons;
|
||||
|
||||
@@ -3833,7 +3891,8 @@ void CFred_mission_save::save_turret_info(ship_subsys *ptr, int ship) {
|
||||
fso_comment_pop(true);
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_variables() {
|
||||
int CFred_mission_save::save_variables()
|
||||
{
|
||||
char *type;
|
||||
char number[] = "number";
|
||||
char string[] = "string";
|
||||
@@ -3847,7 +3906,7 @@ int CFred_mission_save::save_variables() {
|
||||
// get count
|
||||
int num_variables = sexp_variable_count();
|
||||
|
||||
if (Format_fs2_open == FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format == FSO_FORMAT_RETAIL) {
|
||||
generate_special_explosion_block_variables();
|
||||
num_block_vars = num_block_variables();
|
||||
}
|
||||
@@ -3875,7 +3934,7 @@ int CFred_mission_save::save_variables() {
|
||||
fout("\n\t\t%d\t\t\"%s\"\t\t\"%s\"\t\t\"%s\"", i, Sexp_variables[i].variable_name, Sexp_variables[i].text, type);
|
||||
|
||||
// persistent and network variables
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
// Network variable - Karajorma
|
||||
if (Sexp_variables[i].type & SEXP_VARIABLE_NETWORK) {
|
||||
fout("\t\t\"%s\"", "network-variable");
|
||||
@@ -3912,12 +3971,14 @@ int CFred_mission_save::save_variables() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_vector(vec3d &v) {
|
||||
int CFred_mission_save::save_vector(vec3d &v)
|
||||
{
|
||||
fout(" %f, %f, %f", v.xyz.x, v.xyz.y, v.xyz.z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_waypoints() {
|
||||
int CFred_mission_save::save_waypoints()
|
||||
{
|
||||
//object *ptr;
|
||||
|
||||
fred_parse_flag = 0;
|
||||
@@ -3935,39 +3996,41 @@ int CFred_mission_save::save_waypoints() {
|
||||
parse_comments();
|
||||
fout(" %s", jnp->GetName());
|
||||
|
||||
if (jnp->IsSpecialModel()) {
|
||||
if (optional_string_fred("+Model File:", "$Jump Node:"))
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if (jnp->IsSpecialModel()) {
|
||||
if (optional_string_fred("+Model File:", "$Jump Node:"))
|
||||
parse_comments();
|
||||
else
|
||||
fout("\n+Model File:");
|
||||
|
||||
int model = jnp->GetModelNumber();
|
||||
polymodel *pm = model_get(model);
|
||||
fout(" %s", pm->filename);
|
||||
}
|
||||
|
||||
if (jnp->IsColored()) {
|
||||
if (optional_string_fred("+Alphacolor:", "$Jump Node:"))
|
||||
parse_comments();
|
||||
else
|
||||
fout("\n+Alphacolor:");
|
||||
|
||||
color jn_color = jnp->GetColor();
|
||||
fout(" %u %u %u %u", jn_color.red, jn_color.green, jn_color.blue, jn_color.alpha);
|
||||
}
|
||||
|
||||
int hidden_is_there = optional_string_fred("+Hidden:", "$Jump Node:");
|
||||
if (hidden_is_there)
|
||||
parse_comments();
|
||||
else
|
||||
fout("\n+Model File:");
|
||||
|
||||
int model = jnp->GetModelNumber();
|
||||
polymodel *pm = model_get(model);
|
||||
fout(" %s", pm->filename);
|
||||
}
|
||||
if (hidden_is_there || jnp->IsHidden()) {
|
||||
if (!hidden_is_there)
|
||||
fout("\n+Hidden:");
|
||||
|
||||
if (jnp->IsColored()) {
|
||||
if (optional_string_fred("+Alphacolor:", "$Jump Node:"))
|
||||
parse_comments();
|
||||
else
|
||||
fout("\n+Alphacolor:");
|
||||
|
||||
color jn_color = jnp->GetColor();
|
||||
fout(" %u %u %u %u", jn_color.red, jn_color.green, jn_color.blue, jn_color.alpha);
|
||||
}
|
||||
|
||||
int hidden_is_there = optional_string_fred("+Hidden:", "$Jump Node:");
|
||||
if (hidden_is_there)
|
||||
parse_comments();
|
||||
|
||||
if (hidden_is_there || jnp->IsHidden()) {
|
||||
if (!hidden_is_there)
|
||||
fout("\n+Hidden:");
|
||||
|
||||
if (jnp->IsHidden())
|
||||
fout(" %s", "true");
|
||||
else
|
||||
fout(" %s", "false");
|
||||
if (jnp->IsHidden())
|
||||
fout(" %s", "true");
|
||||
else
|
||||
fout(" %s", "false");
|
||||
}
|
||||
}
|
||||
|
||||
fso_comment_pop();
|
||||
@@ -3995,7 +4058,8 @@ int CFred_mission_save::save_waypoints() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_waypoint_list(waypoint_list *wp_list) {
|
||||
int CFred_mission_save::save_waypoint_list(waypoint_list *wp_list)
|
||||
{
|
||||
Assert(wp_list != NULL);
|
||||
SCP_vector<waypoint>::iterator ii;
|
||||
|
||||
@@ -4007,7 +4071,8 @@ int CFred_mission_save::save_waypoint_list(waypoint_list *wp_list) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_wings() {
|
||||
int CFred_mission_save::save_wings()
|
||||
{
|
||||
SCP_string sexp_out;
|
||||
int i, j, z, ship, count = 0;
|
||||
|
||||
@@ -4027,7 +4092,7 @@ int CFred_mission_save::save_wings() {
|
||||
fout(" %s", Wings[i].name);
|
||||
|
||||
// squad logo - Goober5000
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if (strlen(Wings[i].wing_squad_filename) > 0) //-V805
|
||||
{
|
||||
if (optional_string_fred("+Squad Logo:", "$Name:"))
|
||||
@@ -4072,7 +4137,7 @@ int CFred_mission_save::save_wings() {
|
||||
if (z & SPECIAL_ARRIVAL_ANCHOR_FLAG) {
|
||||
// get name
|
||||
char tmp[NAME_LENGTH + 15];
|
||||
stuff_special_arrival_anchor_name(tmp, z, Format_fs2_retail);
|
||||
stuff_special_arrival_anchor_name(tmp, z, Mission_save_format == FSO_FORMAT_RETAIL ? 1 : 0);
|
||||
|
||||
// save it
|
||||
fout(" %s", tmp);
|
||||
@@ -4084,7 +4149,7 @@ int CFred_mission_save::save_wings() {
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if ((Wings[i].arrival_location == ARRIVE_FROM_DOCK_BAY) && (Wings[i].arrival_path_mask > 0)) {
|
||||
int anchor_shipnum;
|
||||
polymodel *pm;
|
||||
@@ -4134,7 +4199,7 @@ int CFred_mission_save::save_wings() {
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
if (Format_fs2_open != FSO_FORMAT_RETAIL) {
|
||||
if (Mission_save_format != FSO_FORMAT_RETAIL) {
|
||||
if ((Wings[i].departure_location == DEPART_AT_DOCK_BAY) && (Wings[i].departure_path_mask > 0)) {
|
||||
int anchor_shipnum;
|
||||
polymodel *pm;
|
||||
|
||||
@@ -1195,8 +1195,7 @@ void ShipGoalsDlg::OnSelchangeObject10()
|
||||
|
||||
void ShipGoalsDlg::set_object(int item)
|
||||
{
|
||||
char *str;
|
||||
int i = 0, z, num, not_used;
|
||||
int i = 0, z, num;
|
||||
ship_subsys *subsys;
|
||||
|
||||
if (m_behavior[item] > 0) {
|
||||
@@ -1213,8 +1212,7 @@ void ShipGoalsDlg::set_object(int item)
|
||||
for (i=0; i<num; i++) {
|
||||
Assert(Docking_bay_list[i]);
|
||||
z = m_dock2_box[item] -> AddString(Docking_bay_list[i]);
|
||||
str = ai_get_goal_target_name(Docking_bay_list[i], ¬_used);
|
||||
m_dock2_box[item] -> SetItemDataPtr(z, str);
|
||||
m_dock2_box[item] -> SetItemDataPtr(z, Docking_bay_list[i]);
|
||||
}
|
||||
|
||||
} else if ((mode == AI_GOAL_DESTROY_SUBSYSTEM) && (m_data[item] >= 0)) {
|
||||
|
||||
@@ -1902,6 +1902,7 @@ void game_init()
|
||||
// Do this before the initial scripting hook runs in case that hook does something with the UI
|
||||
scpui::initialize();
|
||||
|
||||
Script_system.RunInitFunctions();
|
||||
Script_system.RunCondition(CHA_GAMEINIT);
|
||||
|
||||
game_title_screen_close();
|
||||
|
||||
@@ -95,13 +95,13 @@ initialize(const std::string& cfilepath, int argc, char* argv[], Editor* editor,
|
||||
mod_table_init();
|
||||
|
||||
listener(SubSystem::Locale);
|
||||
// initialize localization module. Make sure this is done AFTER initialzing OS.
|
||||
// NOTE : Fred should ALWAYS run in English. Otherwise it might swap in another language
|
||||
// initialize localization module. Make sure this is done AFTER initializing OS.
|
||||
// NOTE: Fred should ALWAYS run without localization. Otherwise it might swap in another language
|
||||
// when saving - which would cause inconsistencies when externalizing to tstrings.tbl via Exstr
|
||||
// trust me on this :)
|
||||
lcl_init(FS2_OPEN_DEFAULT_LANGUAGE);
|
||||
lcl_init(LCL_UNTRANSLATED);
|
||||
|
||||
// Goober5000 - force init XSTRs (so they work, but only work in English, based on above comment)
|
||||
// Goober5000 - force init XSTRs (so they work, but only work untranslated, based on above comment)
|
||||
Xstr_inited = 1;
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -254,8 +254,9 @@ initialize(const std::string& cfilepath, int argc, char* argv[], Editor* editor,
|
||||
control_config_common_init();
|
||||
|
||||
listener(SubSystem::ScriptingInitHook);
|
||||
Script_system.RunInitFunctions();
|
||||
Script_system.RunCondition(CHA_GAMEINIT);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
namespace fso {
|
||||
namespace fred {
|
||||
|
||||
int CFred_mission_save::autosave_mission_file(char* pathname) {
|
||||
int CFred_mission_save::autosave_mission_file(char* pathname)
|
||||
{
|
||||
char backup_name[256], name2[256];
|
||||
int i;
|
||||
|
||||
@@ -60,7 +61,8 @@ int CFred_mission_save::autosave_mission_file(char* pathname) {
|
||||
return err;
|
||||
}
|
||||
|
||||
void CFred_mission_save::bypass_comment(const char* comment, const char* end) {
|
||||
void CFred_mission_save::bypass_comment(const char* comment, const char* end)
|
||||
{
|
||||
char* ch = strstr(raw_ptr, comment);
|
||||
if (ch != NULL) {
|
||||
if (end != NULL) {
|
||||
@@ -84,17 +86,20 @@ void CFred_mission_save::bypass_comment(const char* comment, const char* end) {
|
||||
}
|
||||
}
|
||||
|
||||
void CFred_mission_save::convert_special_tags_to_retail(char* text, int max_len) {
|
||||
void CFred_mission_save::convert_special_tags_to_retail(char* text, int max_len)
|
||||
{
|
||||
replace_all(text, "$quote", "''", max_len);
|
||||
replace_all(text, "$semicolon", ",", max_len);
|
||||
}
|
||||
|
||||
void CFred_mission_save::convert_special_tags_to_retail(SCP_string& text) {
|
||||
void CFred_mission_save::convert_special_tags_to_retail(SCP_string& text)
|
||||
{
|
||||
replace_all(text, "$quote", "''");
|
||||
replace_all(text, "$semicolon", ",");
|
||||
}
|
||||
|
||||
void CFred_mission_save::convert_special_tags_to_retail() {
|
||||
void CFred_mission_save::convert_special_tags_to_retail()
|
||||
{
|
||||
int i, team, stage;
|
||||
|
||||
if (save_format != MissionFormat::RETAIL) {
|
||||
@@ -123,7 +128,8 @@ void CFred_mission_save::convert_special_tags_to_retail() {
|
||||
}
|
||||
}
|
||||
|
||||
int CFred_mission_save::fout(const char* format, ...) {
|
||||
int CFred_mission_save::fout(const char* format, ...)
|
||||
{
|
||||
SCP_string str;
|
||||
va_list args;
|
||||
|
||||
@@ -139,7 +145,8 @@ int CFred_mission_save::fout(const char* format, ...) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFred_mission_save::fout_ext(const char* pre_str, const char* format, ...) {
|
||||
int CFred_mission_save::fout_ext(const char* pre_str, const char* format, ...)
|
||||
{
|
||||
SCP_string str_scp;
|
||||
SCP_string str_out_scp;
|
||||
va_list args;
|
||||
@@ -241,7 +248,8 @@ int CFred_mission_save::fout_ext(const char* pre_str, const char* format, ...) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFred_mission_save::fout_version(const char* format, ...) {
|
||||
int CFred_mission_save::fout_version(const char* format, ...)
|
||||
{
|
||||
SCP_string str_scp;
|
||||
char* ch = NULL;
|
||||
va_list args;
|
||||
@@ -250,6 +258,11 @@ int CFred_mission_save::fout_version(const char* format, ...) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// if we're saving in retail format, we can completely skip this
|
||||
if (save_format == MissionFormat::RETAIL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// output the version first thing, but skip the special case where we use
|
||||
// fout_version() for multiline value strings (typically indicated by an initial space)
|
||||
if ((save_format == MissionFormat::COMPATIBILITY_MODE) && (*format != ' ') && !fso_ver_comment.empty()) {
|
||||
@@ -330,7 +343,8 @@ int CFred_mission_save::fout_version(const char* format, ...) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CFred_mission_save::parse_comments(int newlines) {
|
||||
void CFred_mission_save::parse_comments(int newlines)
|
||||
{
|
||||
char* comment_start = NULL;
|
||||
int state = 0, same_line = 0, first_comment = 1, tab = 0, flag = 0;
|
||||
bool version_added = false;
|
||||
@@ -507,7 +521,8 @@ void CFred_mission_save::parse_comments(int newlines) {
|
||||
return;
|
||||
}
|
||||
|
||||
void CFred_mission_save::save_ai_goals(ai_goal* goalp, int ship) {
|
||||
void CFred_mission_save::save_ai_goals(ai_goal* goalp, int ship)
|
||||
{
|
||||
const char* str = NULL;
|
||||
char buf[80];
|
||||
int i, valid, flag = 1;
|
||||
@@ -668,7 +683,8 @@ void CFred_mission_save::save_ai_goals(ai_goal* goalp, int ship) {
|
||||
fso_comment_pop(true);
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_asteroid_fields() {
|
||||
int CFred_mission_save::save_asteroid_fields()
|
||||
{
|
||||
int i;
|
||||
|
||||
fred_parse_flag = 0;
|
||||
@@ -763,7 +779,8 @@ int CFred_mission_save::save_asteroid_fields() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_bitmaps() {
|
||||
int CFred_mission_save::save_bitmaps()
|
||||
{
|
||||
int i;
|
||||
uint j;
|
||||
|
||||
@@ -909,7 +926,8 @@ int CFred_mission_save::save_bitmaps() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_briefing() {
|
||||
int CFred_mission_save::save_briefing()
|
||||
{
|
||||
int i, j, k, nb;
|
||||
SCP_string sexp_out;
|
||||
brief_stage* bs;
|
||||
@@ -1097,7 +1115,8 @@ int CFred_mission_save::save_briefing() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_cmd_brief() {
|
||||
int CFred_mission_save::save_cmd_brief()
|
||||
{
|
||||
int stage;
|
||||
|
||||
stage = 0;
|
||||
@@ -1140,7 +1159,8 @@ int CFred_mission_save::save_cmd_brief() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_cmd_briefs() {
|
||||
int CFred_mission_save::save_cmd_briefs()
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < Num_teams; i++) {
|
||||
@@ -1151,7 +1171,8 @@ int CFred_mission_save::save_cmd_briefs() {
|
||||
return err;
|
||||
}
|
||||
|
||||
void CFred_mission_save::fso_comment_push(const char* ver) {
|
||||
void CFred_mission_save::fso_comment_push(const char* ver)
|
||||
{
|
||||
if (fso_ver_comment.empty()) {
|
||||
fso_ver_comment.push_back(SCP_string(ver));
|
||||
return;
|
||||
@@ -1186,7 +1207,8 @@ void CFred_mission_save::fso_comment_push(const char* ver) {
|
||||
}
|
||||
}
|
||||
|
||||
void CFred_mission_save::fso_comment_pop(bool pop_all) {
|
||||
void CFred_mission_save::fso_comment_pop(bool pop_all)
|
||||
{
|
||||
if (fso_ver_comment.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1199,7 +1221,8 @@ void CFred_mission_save::fso_comment_pop(bool pop_all) {
|
||||
fso_ver_comment.pop_back();
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_common_object_data(object* objp, ship* shipp) {
|
||||
int CFred_mission_save::save_common_object_data(object* objp, ship* shipp)
|
||||
{
|
||||
int j, z;
|
||||
ship_subsys* ptr = NULL;
|
||||
ship_info* sip = NULL;
|
||||
@@ -1219,7 +1242,7 @@ int CFred_mission_save::save_common_object_data(object* objp, ship* shipp) {
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
if (save_format != MissionFormat::RETAIL && (shipp->special_hitpoints)) {
|
||||
if (save_format != MissionFormat::RETAIL && (shipp->special_hitpoints > 0)) {
|
||||
temp_max_hull_strength = (float) shipp->special_hitpoints;
|
||||
} else {
|
||||
temp_max_hull_strength = sip->max_hull_strength;
|
||||
@@ -1396,7 +1419,8 @@ void CFred_mission_save::save_custom_bitmap(const char* expected_string_640,
|
||||
const char* expected_string_1024,
|
||||
const char* string_field_640,
|
||||
const char* string_field_1024,
|
||||
int blank_lines) {
|
||||
int blank_lines)
|
||||
{
|
||||
if (save_format != MissionFormat::RETAIL) {
|
||||
if ((*string_field_640 != '\0') || (*string_field_1024 != '\0')) {
|
||||
while (blank_lines-- > 0) {
|
||||
@@ -1414,7 +1438,8 @@ void CFred_mission_save::save_custom_bitmap(const char* expected_string_640,
|
||||
}
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_cutscenes() {
|
||||
int CFred_mission_save::save_cutscenes()
|
||||
{
|
||||
char type[NAME_LENGTH];
|
||||
SCP_string sexp_out;
|
||||
|
||||
@@ -1481,7 +1506,8 @@ int CFred_mission_save::save_cutscenes() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_debriefing() {
|
||||
int CFred_mission_save::save_debriefing()
|
||||
{
|
||||
int j, i;
|
||||
SCP_string sexp_out;
|
||||
|
||||
@@ -1541,7 +1567,8 @@ int CFred_mission_save::save_debriefing() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_events() {
|
||||
int CFred_mission_save::save_events()
|
||||
{
|
||||
SCP_string sexp_out;
|
||||
int i, j, add_flag;
|
||||
|
||||
@@ -1696,7 +1723,8 @@ int CFred_mission_save::save_events() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_fiction() {
|
||||
int CFred_mission_save::save_fiction()
|
||||
{
|
||||
if (mission_has_fiction()) {
|
||||
if (save_format != MissionFormat::RETAIL) {
|
||||
if (optional_string_fred("#Fiction Viewer")) {
|
||||
@@ -1787,7 +1815,8 @@ int CFred_mission_save::save_fiction() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_goals() {
|
||||
int CFred_mission_save::save_goals()
|
||||
{
|
||||
SCP_string sexp_out;
|
||||
int i;
|
||||
|
||||
@@ -1871,14 +1900,16 @@ int CFred_mission_save::save_goals() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_matrix(matrix& m) {
|
||||
int CFred_mission_save::save_matrix(matrix& m)
|
||||
{
|
||||
fout("\n\t%f, %f, %f,\n", m.vec.rvec.xyz.x, m.vec.rvec.xyz.y, m.vec.rvec.xyz.z);
|
||||
fout("\t%f, %f, %f,\n", m.vec.uvec.xyz.x, m.vec.uvec.xyz.y, m.vec.uvec.xyz.z);
|
||||
fout("\t%f, %f, %f", m.vec.fvec.xyz.x, m.vec.fvec.xyz.y, m.vec.fvec.xyz.z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_messages() {
|
||||
int CFred_mission_save::save_messages()
|
||||
{
|
||||
int i;
|
||||
|
||||
fred_parse_flag = 0;
|
||||
@@ -1958,7 +1989,8 @@ int CFred_mission_save::save_messages() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_mission_file(const char* pathname_in) {
|
||||
int CFred_mission_save::save_mission_file(const char* pathname_in)
|
||||
{
|
||||
char backup_name[256], savepath[MAX_PATH_LEN], pathname[MAX_PATH_LEN];
|
||||
|
||||
strcpy_s(pathname, pathname_in);
|
||||
@@ -1990,7 +2022,8 @@ int CFred_mission_save::save_mission_file(const char* pathname_in) {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_mission_info() {
|
||||
int CFred_mission_save::save_mission_info()
|
||||
{
|
||||
required_string_fred("#Mission Info");
|
||||
parse_comments(0);
|
||||
|
||||
@@ -2343,7 +2376,8 @@ int CFred_mission_save::save_mission_info() {
|
||||
return err;
|
||||
}
|
||||
|
||||
void CFred_mission_save::save_mission_internal(const char* pathname) {
|
||||
void CFred_mission_save::save_mission_internal(const char* pathname)
|
||||
{
|
||||
time_t rawtime;
|
||||
|
||||
time(&rawtime);
|
||||
@@ -2416,7 +2450,8 @@ void CFred_mission_save::save_mission_internal(const char* pathname) {
|
||||
mprintf(("Mission saving error code #%d\n", err));
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_music() {
|
||||
int CFred_mission_save::save_music()
|
||||
{
|
||||
required_string_fred("#Music");
|
||||
parse_comments(2);
|
||||
|
||||
@@ -2478,7 +2513,7 @@ int CFred_mission_save::save_music() {
|
||||
Mission_music[SCORE_DEBRIEF_SUCCESS] < 0 ? "None"
|
||||
: Spooled_music[Mission_music[SCORE_DEBRIEF_SUCCESS]].name);
|
||||
}
|
||||
if (Mission_music[SCORE_DEBRIEF_AVERAGE] != event_music_get_spooled_music_index("Average")) {
|
||||
if (save_format != MissionFormat::RETAIL && Mission_music[SCORE_DEBRIEF_AVERAGE] != event_music_get_spooled_music_index("Average")) {
|
||||
if (optional_string_fred("$Debriefing Average Music:")) {
|
||||
parse_comments(1);
|
||||
} else {
|
||||
@@ -2519,6 +2554,9 @@ int CFred_mission_save::save_music() {
|
||||
|
||||
int CFred_mission_save::save_warp_params(WarpDirection direction, ship *shipp)
|
||||
{
|
||||
if (save_format == MissionFormat::RETAIL)
|
||||
return err;
|
||||
|
||||
// for writing to file; c.f. parse_warp_params
|
||||
const char *prefix = (direction == WarpDirection::WARP_IN) ? "$Warpin" : "$Warpout";
|
||||
|
||||
@@ -2613,7 +2651,8 @@ int CFred_mission_save::save_warp_params(WarpDirection direction, ship *shipp)
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_objects() {
|
||||
int CFred_mission_save::save_objects()
|
||||
{
|
||||
SCP_string sexp_out;
|
||||
int i, z;
|
||||
ai_info* aip;
|
||||
@@ -2677,7 +2716,7 @@ int CFred_mission_save::save_objects() {
|
||||
}
|
||||
|
||||
// optional callsign
|
||||
if (strlen(Fred_callsigns[i])) {
|
||||
if (save_format != MissionFormat::RETAIL && strlen(Fred_callsigns[i])) {
|
||||
fout("\n$Callsign: %s\n", Fred_callsigns[i]);
|
||||
}
|
||||
|
||||
@@ -2685,7 +2724,7 @@ int CFred_mission_save::save_objects() {
|
||||
parse_comments();
|
||||
fout(" %s", Iff_info[shipp->team].iff_name);
|
||||
|
||||
if (Ship_info[shipp->ship_info_index].uses_team_colors) {
|
||||
if (save_format != MissionFormat::RETAIL && Ship_info[shipp->ship_info_index].uses_team_colors) {
|
||||
required_string_fred("$Team Color Setting:");
|
||||
parse_comments();
|
||||
fout(" %s", shipp->team_name.c_str());
|
||||
@@ -3241,7 +3280,7 @@ int CFred_mission_save::save_objects() {
|
||||
}
|
||||
}
|
||||
|
||||
// check the ship flag about killing off the ship before a missino starts. Write out the appropriate
|
||||
// check the ship flag about killing off the ship before a mission starts. Write out the appropriate
|
||||
// variable if necessary
|
||||
if (shipp->flags[Ship::Ship_Flags::Kill_before_mission]) {
|
||||
if (optional_string_fred("+Destroy At:", "$Name:")) {
|
||||
@@ -3375,7 +3414,9 @@ int CFred_mission_save::save_objects() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_players() {
|
||||
int CFred_mission_save::save_players()
|
||||
{
|
||||
bool wrote_fso_data = false;
|
||||
int i, j;
|
||||
int var_idx;
|
||||
int used_pool[MAX_WEAPON_TYPES];
|
||||
@@ -3401,7 +3442,7 @@ int CFred_mission_save::save_players() {
|
||||
}
|
||||
|
||||
// write out callsign list
|
||||
if (Mission_callsign_count > 0) {
|
||||
if (save_format != MissionFormat::RETAIL && Mission_callsign_count > 0) {
|
||||
fout("\n\n#Callsigns:\n");
|
||||
|
||||
// write them all out
|
||||
@@ -3432,6 +3473,7 @@ int CFred_mission_save::save_players() {
|
||||
if (strlen(Team_data[i].ship_list_variables[j])) {
|
||||
var_idx = get_index_sexp_variable_name(Team_data[i].ship_list_variables[j]);
|
||||
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
|
||||
wrote_fso_data = true;
|
||||
|
||||
fout("\t@%s\t", Sexp_variables[var_idx].variable_name);
|
||||
} else {
|
||||
@@ -3442,6 +3484,7 @@ int CFred_mission_save::save_players() {
|
||||
if (strlen(Team_data[i].ship_count_variables[j])) {
|
||||
var_idx = get_index_sexp_variable_name(Team_data[i].ship_count_variables[j]);
|
||||
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
|
||||
wrote_fso_data = true;
|
||||
|
||||
fout("@%s\n", Sexp_variables[var_idx].variable_name);
|
||||
} else {
|
||||
@@ -3459,11 +3502,13 @@ int CFred_mission_save::save_players() {
|
||||
|
||||
fout(" (\n");
|
||||
generate_weaponry_usage_list_team(i, used_pool);
|
||||
|
||||
for (j = 0; j < Team_data[i].num_weapon_choices; j++) {
|
||||
// first output the weapon name or a variable that sets it
|
||||
if (strlen(Team_data[i].weaponry_pool_variable[j])) {
|
||||
var_idx = get_index_sexp_variable_name(Team_data[i].weaponry_pool_variable[j]);
|
||||
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
|
||||
wrote_fso_data = true;
|
||||
|
||||
fout("\t@%s\t", Sexp_variables[var_idx].variable_name);
|
||||
} else {
|
||||
@@ -3476,6 +3521,7 @@ int CFred_mission_save::save_players() {
|
||||
if (strlen(Team_data[i].weaponry_amount_variable[j])) {
|
||||
var_idx = get_index_sexp_variable_name(Team_data[i].weaponry_amount_variable[j]);
|
||||
Assert(var_idx > -1 && var_idx < MAX_SEXP_VARIABLES);
|
||||
wrote_fso_data = true;
|
||||
|
||||
fout("@%s\n", Sexp_variables[var_idx].variable_name);
|
||||
} else {
|
||||
@@ -3497,6 +3543,15 @@ int CFred_mission_save::save_players() {
|
||||
|
||||
fout(")");
|
||||
|
||||
// sanity check
|
||||
if (save_format == MissionFormat::RETAIL && wrote_fso_data) {
|
||||
// this is such an unlikely (and hard-to-fix) case that a warning should be sufficient
|
||||
_viewport->dialogProvider->showButtonDialog(DialogType::Warning,
|
||||
"Incompatibility with retail mission format",
|
||||
"Warning: This mission contains variable-based team loadout information, but you are saving in the retail mission format. Retail FRED and FS2 will not be able to read this information.",
|
||||
{ DialogButton::Ok });
|
||||
}
|
||||
|
||||
// Goober5000 - mjn.mixael's required weapon feature
|
||||
bool uses_required_weapon = false;
|
||||
for (j = 0; j < static_cast<int>(Weapon_info.size()); j++) {
|
||||
@@ -3505,7 +3560,7 @@ int CFred_mission_save::save_players() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (uses_required_weapon) {
|
||||
if (save_format != MissionFormat::RETAIL && uses_required_weapon) {
|
||||
if (optional_string_fred("+Required for mission:", "$Starting Shipname:")) {
|
||||
parse_comments(2);
|
||||
} else {
|
||||
@@ -3529,7 +3584,8 @@ int CFred_mission_save::save_players() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_plot_info() {
|
||||
int CFred_mission_save::save_plot_info()
|
||||
{
|
||||
if (save_format == MissionFormat::RETAIL) {
|
||||
if (optional_string_fred("#Plot Info")) {
|
||||
parse_comments(2);
|
||||
@@ -3579,7 +3635,8 @@ int CFred_mission_save::save_plot_info() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_reinforcements() {
|
||||
int CFred_mission_save::save_reinforcements()
|
||||
{
|
||||
int i, j, type;
|
||||
|
||||
fred_parse_flag = 0;
|
||||
@@ -3652,7 +3709,8 @@ int CFred_mission_save::save_reinforcements() {
|
||||
return err;
|
||||
}
|
||||
|
||||
void CFred_mission_save::save_single_dock_instance(ship* shipp, dock_instance* dock_ptr) {
|
||||
void CFred_mission_save::save_single_dock_instance(ship* shipp, dock_instance* dock_ptr)
|
||||
{
|
||||
Assert(shipp && dock_ptr);
|
||||
Assert(dock_ptr->docked_objp->type == OBJ_SHIP || dock_ptr->docked_objp->type == OBJ_START);
|
||||
|
||||
@@ -3689,7 +3747,8 @@ void CFred_mission_save::save_single_dock_instance(ship* shipp, dock_instance* d
|
||||
fso_comment_pop(true);
|
||||
}
|
||||
|
||||
void CFred_mission_save::save_turret_info(ship_subsys* ptr, int ship) {
|
||||
void CFred_mission_save::save_turret_info(ship_subsys* ptr, int ship)
|
||||
{
|
||||
int i, z;
|
||||
ship_weapon* wp = &ptr->weapons;
|
||||
|
||||
@@ -3781,7 +3840,8 @@ void CFred_mission_save::save_turret_info(ship_subsys* ptr, int ship) {
|
||||
fso_comment_pop(true);
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_variables() {
|
||||
int CFred_mission_save::save_variables()
|
||||
{
|
||||
char* type;
|
||||
char number[] = "number";
|
||||
char string[] = "string";
|
||||
@@ -3864,12 +3924,14 @@ int CFred_mission_save::save_variables() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_vector(vec3d& v) {
|
||||
int CFred_mission_save::save_vector(vec3d& v)
|
||||
{
|
||||
fout(" %f, %f, %f", v.xyz.x, v.xyz.y, v.xyz.z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_waypoints() {
|
||||
int CFred_mission_save::save_waypoints()
|
||||
{
|
||||
//object *ptr;
|
||||
|
||||
fred_parse_flag = 0;
|
||||
@@ -3887,43 +3949,48 @@ int CFred_mission_save::save_waypoints() {
|
||||
parse_comments();
|
||||
fout(" %s", jnp->GetName());
|
||||
|
||||
if (jnp->IsSpecialModel()) {
|
||||
if (optional_string_fred("+Model File:", "$Jump Node:")) {
|
||||
if (save_format != MissionFormat::RETAIL) {
|
||||
if (jnp->IsSpecialModel()) {
|
||||
if (optional_string_fred("+Model File:", "$Jump Node:")) {
|
||||
parse_comments();
|
||||
}
|
||||
else {
|
||||
fout("\n+Model File:");
|
||||
}
|
||||
|
||||
int model = jnp->GetModelNumber();
|
||||
polymodel* pm = model_get(model);
|
||||
fout(" %s", pm->filename);
|
||||
}
|
||||
|
||||
if (jnp->IsColored()) {
|
||||
if (optional_string_fred("+Alphacolor:", "$Jump Node:")) {
|
||||
parse_comments();
|
||||
}
|
||||
else {
|
||||
fout("\n+Alphacolor:");
|
||||
}
|
||||
|
||||
color jn_color = jnp->GetColor();
|
||||
fout(" %u %u %u %u", jn_color.red, jn_color.green, jn_color.blue, jn_color.alpha);
|
||||
}
|
||||
|
||||
int hidden_is_there = optional_string_fred("+Hidden:", "$Jump Node:");
|
||||
if (hidden_is_there) {
|
||||
parse_comments();
|
||||
} else {
|
||||
fout("\n+Model File:");
|
||||
}
|
||||
|
||||
int model = jnp->GetModelNumber();
|
||||
polymodel* pm = model_get(model);
|
||||
fout(" %s", pm->filename);
|
||||
}
|
||||
if (hidden_is_there || jnp->IsHidden()) {
|
||||
if (!hidden_is_there) {
|
||||
fout("\n+Hidden:");
|
||||
}
|
||||
|
||||
if (jnp->IsColored()) {
|
||||
if (optional_string_fred("+Alphacolor:", "$Jump Node:")) {
|
||||
parse_comments();
|
||||
} else {
|
||||
fout("\n+Alphacolor:");
|
||||
}
|
||||
|
||||
color jn_color = jnp->GetColor();
|
||||
fout(" %u %u %u %u", jn_color.red, jn_color.green, jn_color.blue, jn_color.alpha);
|
||||
}
|
||||
|
||||
int hidden_is_there = optional_string_fred("+Hidden:", "$Jump Node:");
|
||||
if (hidden_is_there) {
|
||||
parse_comments();
|
||||
}
|
||||
|
||||
if (hidden_is_there || jnp->IsHidden()) {
|
||||
if (!hidden_is_there) {
|
||||
fout("\n+Hidden:");
|
||||
}
|
||||
|
||||
if (jnp->IsHidden()) {
|
||||
fout(" %s", "true");
|
||||
} else {
|
||||
fout(" %s", "false");
|
||||
if (jnp->IsHidden()) {
|
||||
fout(" %s", "true");
|
||||
}
|
||||
else {
|
||||
fout(" %s", "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3952,7 +4019,8 @@ int CFred_mission_save::save_waypoints() {
|
||||
return err;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_waypoint_list(waypoint_list* wp_list) {
|
||||
int CFred_mission_save::save_waypoint_list(waypoint_list* wp_list)
|
||||
{
|
||||
Assert(wp_list != NULL);
|
||||
SCP_vector<waypoint>::iterator ii;
|
||||
|
||||
@@ -3964,7 +4032,8 @@ int CFred_mission_save::save_waypoint_list(waypoint_list* wp_list) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFred_mission_save::save_wings() {
|
||||
int CFred_mission_save::save_wings()
|
||||
{
|
||||
SCP_string sexp_out;
|
||||
int i, j, z, count = 0;
|
||||
|
||||
|
||||
@@ -29,20 +29,20 @@ class IntelParseTest : public test::FSTestFixture {
|
||||
|
||||
// Commonly used expected intel data entries.
|
||||
static intel_data expected_foo = {
|
||||
"Foo name", // XSTR id 3000
|
||||
"Foo desc", // XSTR id 3001
|
||||
"Foo name default", // XSTR id 3000
|
||||
"Foo desc default", // XSTR id 3001
|
||||
"Foo anim",
|
||||
IIF_IN_TECH_DATABASE | IIF_DEFAULT_IN_TECH_DATABASE
|
||||
};
|
||||
static intel_data expected_bar = {
|
||||
"Bar name", // XSTR id 3002
|
||||
"Bar desc", // XSTR id 3003
|
||||
"Bar name default", // XSTR id 3002
|
||||
"Bar desc default", // XSTR id 3003
|
||||
"Bar anim",
|
||||
0
|
||||
};
|
||||
static intel_data expected_baz = {
|
||||
"Baz name", // XSTR id 3004
|
||||
"Baz desc", // XSTR id 3005
|
||||
"Baz name default", // XSTR id 3004
|
||||
"Baz desc default", // XSTR id 3005
|
||||
"Baz anim",
|
||||
IIF_IN_TECH_DATABASE | IIF_DEFAULT_IN_TECH_DATABASE
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user