mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 17:48:46 -10:00
Compare commits
82
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 | ||
|
|
280ff2d4f0 | ||
|
|
45775a0d97 | ||
|
|
c4d405e33e | ||
|
|
86e1ebaf08 | ||
|
|
82921467a5 | ||
|
|
c2820f10c8 | ||
|
|
cd88663a92 | ||
|
|
1c10479d2f | ||
|
|
11e9fa27c6 | ||
|
|
f4f6e74456 | ||
|
|
de2e9ada72 | ||
|
|
544029f66f | ||
|
|
cc39cfe6f2 | ||
|
|
d0daa43ccf | ||
|
|
e0cd2806f8 | ||
|
|
4f026a66ab | ||
|
|
28df82a03f | ||
|
|
06f1b7d872 | ||
|
|
1dfe3a8d9f | ||
|
|
aa46ae6639 | ||
|
|
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,6 +84,7 @@ 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,
|
||||
@@ -93,6 +94,7 @@ namespace AI {
|
||||
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,
|
||||
|
||||
+24
-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,6 +371,8 @@ 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);
|
||||
@@ -395,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);
|
||||
@@ -628,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);
|
||||
}
|
||||
}
|
||||
+24
-5
@@ -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.
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -230,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", },
|
||||
@@ -274,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)
|
||||
@@ -295,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;
|
||||
@@ -1668,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
@@ -4558,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;
|
||||
|
||||
@@ -4572,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;
|
||||
@@ -4584,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
+60
-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,7 +441,6 @@ 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;
|
||||
@@ -467,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;
|
||||
@@ -1581,10 +1590,12 @@ 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>();
|
||||
@@ -1627,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)
|
||||
@@ -2074,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)
|
||||
{
|
||||
@@ -2206,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));
|
||||
|
||||
@@ -2275,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"
|
||||
@@ -2528,4 +2581,8 @@ void lab_close()
|
||||
cam_delete(Lab_cam);
|
||||
|
||||
obj_init();
|
||||
|
||||
shockwave_level_close();
|
||||
|
||||
ai_paused = 0;
|
||||
}
|
||||
@@ -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,7 @@ 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;
|
||||
|
||||
@@ -111,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
|
||||
@@ -126,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]);
|
||||
@@ -135,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;
|
||||
@@ -157,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);
|
||||
}
|
||||
@@ -252,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;
|
||||
}
|
||||
|
||||
@@ -484,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;
|
||||
}
|
||||
|
||||
@@ -544,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
|
||||
@@ -784,8 +799,8 @@ bool lcl_ext_localize_sub(const SCP_string &in, SCP_string &text_str, SCP_string
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the localization file is not open, or there's no entry, return the original string
|
||||
if ( !Xstr_inited || (str_id < 0) ) {
|
||||
// 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)
|
||||
@@ -825,7 +840,7 @@ void lcl_ext_localize(const char *in, char *out, size_t max_len, int *id)
|
||||
if (Lcl_unexpected_tstring_check)
|
||||
{
|
||||
int saved_language = Lcl_current_lang;
|
||||
Lcl_current_lang = FS2_OPEN_DEFAULT_LANGUAGE;
|
||||
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
|
||||
@@ -860,7 +875,7 @@ void lcl_ext_localize(const SCP_string &in, SCP_string &out, int *id)
|
||||
if (Lcl_unexpected_tstring_check)
|
||||
{
|
||||
int saved_language = Lcl_current_lang;
|
||||
Lcl_current_lang = FS2_OPEN_DEFAULT_LANGUAGE;
|
||||
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
|
||||
@@ -894,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;
|
||||
}
|
||||
|
||||
@@ -1151,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,7 +57,7 @@ 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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -865,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
|
||||
@@ -1451,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
|
||||
@@ -1510,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;
|
||||
@@ -1548,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) {
|
||||
@@ -1576,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
|
||||
@@ -1621,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);
|
||||
}
|
||||
|
||||
@@ -2196,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);
|
||||
@@ -2227,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;
|
||||
}
|
||||
|
||||
@@ -2372,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;
|
||||
|
||||
@@ -6001,7 +6001,7 @@ void parse_init(bool 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[])
|
||||
@@ -7103,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.
|
||||
@@ -7150,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;
|
||||
@@ -7220,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"));
|
||||
|
||||
@@ -7273,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1853,7 +1853,7 @@ 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)
|
||||
{
|
||||
@@ -1866,21 +1866,38 @@ bool is_a_weapon_slot_empty()
|
||||
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)
|
||||
{
|
||||
empty_slot = true;
|
||||
}
|
||||
// is there a full slot following a gap?
|
||||
else if (empty_slot)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1955,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, Mission::Mission_Flags::Red_alert, 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -4011,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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+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();
|
||||
|
||||
|
||||
+264
-33
@@ -6966,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,
|
||||
@@ -10645,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.
|
||||
@@ -11845,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;
|
||||
}
|
||||
}
|
||||
@@ -11902,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.
|
||||
}
|
||||
@@ -11920,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.
|
||||
}
|
||||
@@ -11934,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.
|
||||
@@ -11962,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
|
||||
@@ -12072,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
|
||||
@@ -12084,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
|
||||
@@ -13353,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;
|
||||
@@ -13361,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
|
||||
@@ -14354,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)
|
||||
@@ -14448,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
|
||||
@@ -14689,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
|
||||
@@ -15501,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);
|
||||
@@ -15596,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);
|
||||
|
||||
@@ -15606,7 +15703,6 @@ play_ask_help:
|
||||
Player->allow_scream_timestamp = timestamp(15000);
|
||||
|
||||
Player->ask_help_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -15645,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);
|
||||
@@ -15732,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]))
|
||||
@@ -15797,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.
|
||||
@@ -19165,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;
|
||||
}
|
||||
+50
-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,7 +756,7 @@ 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();
|
||||
@@ -1409,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
|
||||
@@ -1497,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);
|
||||
@@ -1538,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);
|
||||
@@ -1798,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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
+302
-4
@@ -222,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()
|
||||
@@ -985,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 );
|
||||
}
|
||||
@@ -1017,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;
|
||||
@@ -1395,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;
|
||||
@@ -3547,6 +3600,9 @@ void weapon_init()
|
||||
}
|
||||
|
||||
weapon_level_init();
|
||||
|
||||
if (Cmdline_spew_weapon_stats)
|
||||
weapon_spew_stats();
|
||||
}
|
||||
|
||||
|
||||
@@ -3593,6 +3649,7 @@ void weapon_close()
|
||||
void weapon_level_init()
|
||||
{
|
||||
int i;
|
||||
extern int ships_inited;
|
||||
|
||||
// Reset everything between levels
|
||||
Num_weapons = 0;
|
||||
@@ -3604,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
|
||||
@@ -7694,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;
|
||||
@@ -7890,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; });
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user