mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
Compare commits
90
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19ca467c41 | ||
|
|
73736280f9 | ||
|
|
d5546f95e8 | ||
|
|
29edfeedea | ||
|
|
8cf20b0175 | ||
|
|
c85040efc4 | ||
|
|
a16be863f3 | ||
|
|
de80c254ae | ||
|
|
d703fcacc3 | ||
|
|
ddef6ea377 | ||
|
|
2b2333f692 | ||
|
|
4a819d7b20 | ||
|
|
a79dd70138 | ||
|
|
d5af6b5775 | ||
|
|
e3b24ea9e2 | ||
|
|
8645210c0a | ||
|
|
8c39bf54d4 | ||
|
|
bb43411bb1 | ||
|
|
622ae3ef2c | ||
|
|
ab7a1d8fd7 | ||
|
|
a246ef21aa | ||
|
|
e8d379a91c | ||
|
|
45862e06a9 | ||
|
|
f96a27feb4 | ||
|
|
f935a4ac38 | ||
|
|
601addd5c2 | ||
|
|
7cfbdcd6cf | ||
|
|
a009dc188c | ||
|
|
bb6d7515df | ||
|
|
f33b8d987e | ||
|
|
2fd98c908b | ||
|
|
bf1a9f192a | ||
|
|
ec8cb1ba71 | ||
|
|
e9e722244b | ||
|
|
2449dd6f70 | ||
|
|
2748cc6f97 | ||
|
|
660f72dcc7 | ||
|
|
f1506987eb | ||
|
|
e0d2d76dc2 | ||
|
|
09b35edae1 | ||
|
|
390ddb3fc8 | ||
|
|
0ab523bcf4 | ||
|
|
d72c5dbe75 | ||
|
|
324ccf37cb | ||
|
|
20094df4cd | ||
|
|
8db280ea83 | ||
|
|
a12e74fe9d | ||
|
|
82e2b67ed8 | ||
|
|
3311704253 | ||
|
|
7f7eed7a48 | ||
|
|
a95938a418 | ||
|
|
557a6a989c | ||
|
|
0f168ecc5b | ||
|
|
1768f5bb1b | ||
|
|
9e6790f317 | ||
|
|
993a9e9f18 | ||
|
|
516548d6be | ||
|
|
d1800d8622 | ||
|
|
c9dd97d223 | ||
|
|
c0ea9f5ffc | ||
|
|
319c8cc2a1 | ||
|
|
07b8e8d6cb | ||
|
|
80e2908aa3 | ||
|
|
25a6bf2b87 | ||
|
|
1930943d2d | ||
|
|
ed16a81e37 | ||
|
|
0121e439aa | ||
|
|
6fb72499f9 | ||
|
|
eb69133a57 | ||
|
|
2fcc3ecbf8 | ||
|
|
30b2b6caa7 | ||
|
|
4250aab601 | ||
|
|
a9e6c328cf | ||
|
|
07b6c17db0 | ||
|
|
2ce953aee8 | ||
|
|
05f71a30fb | ||
|
|
5dfef48bf6 | ||
|
|
f280f11824 | ||
|
|
e7b73da90e | ||
|
|
8698993d44 | ||
|
|
7b04d401fe | ||
|
|
a2b05f085b | ||
|
|
6e612cdc97 | ||
|
|
9a55d90f5d | ||
|
|
cc2b9fcf94 | ||
|
|
ed8af9d197 | ||
|
|
43f8b123a8 | ||
|
|
a930301ec1 | ||
|
|
d754f7fe0d | ||
|
|
53bc7861bf |
No files matched your search
@@ -315,6 +315,6 @@ jobs:
|
||||
needs: [linux_zip, windows_zip]
|
||||
uses: ./.github/workflows/post-build-release.yaml
|
||||
with:
|
||||
linux_result: ${{ needs.build_linux.result }}
|
||||
windows_result: ${{ needs.build_windows.result }}
|
||||
linux_result: ${{ needs.linux_zip.result }}
|
||||
windows_result: ${{ needs.windows_zip.result }}
|
||||
releaseTag: ${{ github.ref_name }}
|
||||
+182
-134
@@ -2400,6 +2400,7 @@ void ai_attack_object(object* attacker, object* attacked, int ship_info_index)
|
||||
|
||||
Assert(attacker != nullptr);
|
||||
Assert(attacker->instance >= 0);
|
||||
Assert(attacker->type == OBJ_SHIP);
|
||||
Assert(Ships[attacker->instance].ai_index != -1);
|
||||
// Bogus! Who tried to get me to attack myself!
|
||||
if (attacker == attacked) {
|
||||
@@ -10015,11 +10016,25 @@ void guard_object_was_hit(object *guard_objp, object *hitter_objp)
|
||||
aip->submode_start_time = Missiontime;
|
||||
aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
|
||||
} else {
|
||||
int num_attacking_cur, num_attacking_new;
|
||||
// This section used to be a lot simpler, but some "invalid" object types were probably getting into num_ships_attacking
|
||||
// below, so we have to manually return for some object types and make sure to assert for other object types
|
||||
// that are symptomatic of a larger problem with the engine.
|
||||
|
||||
// This just checks to see that the target was a valid type. If it is not one of these, something really
|
||||
// could be drastically wrong in the engine.
|
||||
Assertion(Objects[aip->target_objnum].type == OBJ_SHIP || Objects[aip->target_objnum].type == OBJ_WEAPON || Objects[aip->target_objnum].type == OBJ_DEBRIS || Objects[aip->target_objnum].type == OBJ_ASTEROID || Objects[aip->target_objnum].type == OBJ_WAYPOINT,
|
||||
"This function just discovered that %s has an invalid target object type of %d. This is bad. Please report!",
|
||||
Ships[aip->shipnum].ship_name, Objects[aip->target_objnum].type);
|
||||
|
||||
if (!(Objects[aip->target_objnum].type == OBJ_SHIP || Objects[aip->target_objnum].type == OBJ_WEAPON || Objects[aip->target_objnum].type == OBJ_ASTEROID || Objects[aip->target_objnum].type == OBJ_DEBRIS)){
|
||||
// it's probably a valid target, but retail would not count those cases, so just return.
|
||||
return;
|
||||
}
|
||||
|
||||
int num_attacking_cur = num_ships_attacking(aip->target_objnum);
|
||||
|
||||
num_attacking_cur = num_ships_attacking(aip->target_objnum);
|
||||
if (num_attacking_cur > 1) {
|
||||
num_attacking_new = num_ships_attacking(hitter_objnum);
|
||||
int num_attacking_new = num_ships_attacking(hitter_objnum);
|
||||
|
||||
if (num_attacking_new < num_attacking_cur) {
|
||||
|
||||
@@ -14490,6 +14505,13 @@ void validate_mode_submode(ai_info *aip)
|
||||
*/
|
||||
void ai_frame(int objnum)
|
||||
{
|
||||
// Cyborg - can you believe this was added in *2022*??
|
||||
Assertion (Objects[objnum].type == OBJ_SHIP, "Ai_frame was called for a non-ship of type %d. Please report!", Objects[objnum].type);
|
||||
|
||||
if (Objects[objnum].type != OBJ_SHIP){
|
||||
return;
|
||||
}
|
||||
|
||||
ship *shipp = &Ships[Objects[objnum].instance];
|
||||
ai_info *aip = &Ai_info[shipp->ai_index];
|
||||
int target_objnum;
|
||||
@@ -14572,7 +14594,7 @@ void ai_frame(int objnum)
|
||||
} else if (aip->resume_goal_time == -1) {
|
||||
// AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
|
||||
if ( Ship_info[shipp->ship_info_index].class_type > -1 && (Ship_types[Ship_info[shipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::AI_auto_attacks]) ) {
|
||||
target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, The_mission.ai_profile->max_attackers[Game_skill_level]); // Attack up to 25K units away.
|
||||
target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, The_mission.ai_profile->max_attackers[Game_skill_level]); // Attack up to 2.5K units away.
|
||||
if (target_objnum != -1) {
|
||||
if (aip->target_objnum != target_objnum)
|
||||
aip->aspect_locked_time = 0.0f;
|
||||
@@ -14700,7 +14722,8 @@ void ai_frame(int objnum)
|
||||
}
|
||||
}
|
||||
|
||||
static void ai_control_info_check(ai_info *aip, ship_info *sip, physics_info *pi)
|
||||
// set and removes any maneuver flags for ai maneuver overrides
|
||||
static void ai_control_info_flags_upkeep(ai_info* aip, ship_info* sip, physics_info* pi)
|
||||
{
|
||||
if (aip->ai_override_flags.none_set())
|
||||
return;
|
||||
@@ -14731,6 +14754,37 @@ static void ai_control_info_check(ai_info *aip, ship_info *sip, physics_info *pi
|
||||
}
|
||||
else
|
||||
{
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Dont_bank_when_turning] || sip->flags[Ship::Info_Flags::Dont_bank_when_turning])
|
||||
AI_ci.control_flags |= CIF_DONT_BANK_WHEN_TURNING;
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Dont_clamp_max_velocity] || sip->flags[Ship::Info_Flags::Dont_clamp_max_velocity])
|
||||
AI_ci.control_flags |= CIF_DONT_CLAMP_MAX_VELOCITY;
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Instantaneous_acceleration] || sip->flags[Ship::Info_Flags::Instantaneous_acceleration])
|
||||
AI_ci.control_flags |= CIF_INSTANTANEOUS_ACCELERATION;
|
||||
}
|
||||
|
||||
// set physics flag according to whether we are instantaneously accelerating
|
||||
if (AI_ci.control_flags & CIF_INSTANTANEOUS_ACCELERATION)
|
||||
pi->flags |= PF_MANEUVER_NO_DAMP;
|
||||
else
|
||||
pi->flags &= ~PF_MANEUVER_NO_DAMP;
|
||||
}
|
||||
|
||||
// applies any rotational or lateral maneuver values to the AI's CI
|
||||
static void ai_control_info_apply(ai_info *aip)
|
||||
{
|
||||
if (aip->ai_override_flags.none_set())
|
||||
return;
|
||||
|
||||
bool lateral_maneuver = false;
|
||||
bool rotational_maneuver = false;
|
||||
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Lateral_never_expire] || !timestamp_elapsed(aip->ai_override_lat_timestamp))
|
||||
lateral_maneuver = true;
|
||||
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Rotational_never_expire] || timestamp_elapsed(aip->ai_override_rot_timestamp))
|
||||
rotational_maneuver = true;
|
||||
|
||||
if (lateral_maneuver || rotational_maneuver) {
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Full_rot])
|
||||
{
|
||||
AI_ci.pitch = aip->ai_override_ci.pitch;
|
||||
@@ -14740,17 +14794,11 @@ static void ai_control_info_check(ai_info *aip, ship_info *sip, physics_info *pi
|
||||
else
|
||||
{
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Pitch])
|
||||
{
|
||||
AI_ci.pitch = aip->ai_override_ci.pitch;
|
||||
}
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Heading])
|
||||
{
|
||||
AI_ci.heading = aip->ai_override_ci.heading;
|
||||
}
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Roll])
|
||||
{
|
||||
AI_ci.bank = aip->ai_override_ci.bank;
|
||||
}
|
||||
}
|
||||
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Full_lat])
|
||||
@@ -14762,32 +14810,13 @@ static void ai_control_info_check(ai_info *aip, ship_info *sip, physics_info *pi
|
||||
else
|
||||
{
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Up])
|
||||
{
|
||||
AI_ci.vertical = aip->ai_override_ci.vertical;
|
||||
}
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Sideways])
|
||||
{
|
||||
AI_ci.sideways = aip->ai_override_ci.sideways;
|
||||
}
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Forward])
|
||||
{
|
||||
AI_ci.forward = aip->ai_override_ci.forward;
|
||||
}
|
||||
}
|
||||
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Dont_bank_when_turning] || sip->flags[Ship::Info_Flags::Dont_bank_when_turning])
|
||||
AI_ci.control_flags |= CIF_DONT_BANK_WHEN_TURNING;
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Dont_clamp_max_velocity] || sip->flags[Ship::Info_Flags::Dont_clamp_max_velocity])
|
||||
AI_ci.control_flags |= CIF_DONT_CLAMP_MAX_VELOCITY;
|
||||
if (aip->ai_override_flags[AI::Maneuver_Override_Flags::Instantaneous_acceleration] || sip->flags[Ship::Info_Flags::Instantaneous_acceleration])
|
||||
AI_ci.control_flags |= CIF_INSTANTANEOUS_ACCELERATION;
|
||||
}
|
||||
|
||||
// set physics flag according to whether we are instantaneously accelerating
|
||||
if (AI_ci.control_flags & CIF_INSTANTANEOUS_ACCELERATION)
|
||||
pi->flags |= PF_NO_DAMP;
|
||||
else
|
||||
pi->flags &= ~PF_NO_DAMP;
|
||||
}
|
||||
|
||||
int Last_ai_obj = -1;
|
||||
@@ -14847,9 +14876,12 @@ void ai_process( object * obj, int ai_index, float frametime )
|
||||
if (shipp->flags[Ship::Ship_Flags::Dying] && sip->flags[Ship::Info_Flags::Large_ship_deathroll])
|
||||
rfc = false;
|
||||
|
||||
// regardless if we're acting on them, upkeep maneuver flags
|
||||
ai_control_info_flags_upkeep(aip, sip, &obj->phys_info);
|
||||
|
||||
if (rfc) {
|
||||
// Wanderer - sexp based override goes here - only if rfc is valid though
|
||||
ai_control_info_check(aip, sip, &obj->phys_info);
|
||||
ai_control_info_apply(aip);
|
||||
physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
|
||||
}
|
||||
|
||||
@@ -15109,9 +15141,9 @@ void init_aip_from_class_and_profile(ai_info *aip, ai_class *aicp, ai_profile_t
|
||||
|
||||
void ai_do_default_behavior(object *obj)
|
||||
{
|
||||
Assert(obj != NULL);
|
||||
Assert(obj->instance != -1);
|
||||
Assert(Ships[obj->instance].ai_index != -1);
|
||||
Assertion(obj != nullptr, "Ai_do_default_behavior() was given a bad information. Specifically obj is a nullptr. This is a coder error, please report!");
|
||||
Assertion((obj->type == OBJ_SHIP) && (obj->instance > -1) && (obj->instance < MAX_SHIPS), "This function that requires an object to be a ship was passed an invalid ship. Please report\n\nObject type: %d\nInstance #: %d", obj->type, obj->instance);
|
||||
Assertion(Ships[obj->instance].ai_index >= 0, "A ship has been found to have an invalid ai_index of %d. This should have been assigned and is coder mistake, please report!", Ships[obj->instance].ai_index);
|
||||
|
||||
ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
|
||||
ship_info* sip = &Ship_info[Ships[obj->instance].ship_info_index];
|
||||
@@ -15209,118 +15241,125 @@ void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *o
|
||||
return;
|
||||
}
|
||||
|
||||
if ((obj_team(objp_hitter) == obj_team(objp_hit)) && (objp_hitter == Player_obj)) {
|
||||
// Cyborg -- I had to switch around the logic here because it was potentially
|
||||
// sending non-ships to obj_team, which would trigger an assert
|
||||
if (objp_hitter == Player_obj){
|
||||
|
||||
// AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point. If so, bail out.
|
||||
// Cyborg - So check if this is a ship first. Probably true, but you never know, with FSO
|
||||
// (especially in multi)
|
||||
if ( objp_hitter->type != OBJ_SHIP ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assert(objp_hitter->type == OBJ_SHIP);
|
||||
// And assert that obj_hit is a ship as well.
|
||||
Assert(objp_hit->type == OBJ_SHIP);
|
||||
Assert((objp_weapon->type == OBJ_WEAPON) || (objp_weapon->type == OBJ_BEAM)); //beam added for traitor detection - FUBAR
|
||||
|
||||
ship *shipp_hitter = &Ships[objp_hitter->instance];
|
||||
ship *shipp_hit = &Ships[objp_hit->instance];
|
||||
if (obj_team(objp_hitter) == obj_team(objp_hit)) {
|
||||
|
||||
if (shipp_hitter->team != shipp_hit->team) {
|
||||
return;
|
||||
}
|
||||
Assert((objp_weapon->type == OBJ_WEAPON) || (objp_weapon->type == OBJ_BEAM)); //beam added for traitor detection - FUBAR
|
||||
|
||||
// get the player
|
||||
player *pp = &Players[Player_num];
|
||||
ship *shipp_hitter = &Ships[objp_hitter->instance];
|
||||
ship *shipp_hit = &Ships[objp_hit->instance];
|
||||
|
||||
// wacky stuff here
|
||||
if (pp->friendly_hits != 0) {
|
||||
float time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
|
||||
if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
|
||||
if (time_since_last_hit > 60.0f) {
|
||||
pp->friendly_hits = 0;
|
||||
pp->friendly_damage = 0.0f;
|
||||
} else if (time_since_last_hit > 2.0f) {
|
||||
pp->friendly_hits -= (int) time_since_last_hit/2;
|
||||
pp->friendly_damage -= time_since_last_hit;
|
||||
}
|
||||
|
||||
if (pp->friendly_damage < 0.0f) {
|
||||
pp->friendly_damage = 0.0f;
|
||||
}
|
||||
|
||||
if (pp->friendly_hits < 0) {
|
||||
pp->friendly_hits = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float damage; // Damage done by weapon. Gets scaled down based on size of ship.
|
||||
|
||||
if (objp_weapon->type == OBJ_BEAM) // added beam for traitor detection -FUBAR
|
||||
damage = beam_get_ship_damage(&Beams[objp_weapon->instance], objp_hit);
|
||||
else
|
||||
damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
|
||||
|
||||
// wacky stuff here
|
||||
ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
|
||||
if (shipp_hit->ship_max_hull_strength > 1000.0f) {
|
||||
float factor = shipp_hit->ship_max_hull_strength / 1000.0f;
|
||||
factor = MIN(100.0f, factor);
|
||||
damage /= factor;
|
||||
}
|
||||
|
||||
// Don't penalize much at all for hitting cargo
|
||||
if (sip->class_type > -1) {
|
||||
damage *= Ship_types[sip->class_type].ff_multiplier;
|
||||
}
|
||||
|
||||
// Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
|
||||
if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
|
||||
damage /= 5.0f;
|
||||
}
|
||||
|
||||
pp->friendly_last_hit_time = Missiontime;
|
||||
pp->friendly_hits++;
|
||||
|
||||
// cap damage and number of hits done this frame
|
||||
float accredited_damage = MIN(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
|
||||
pp->friendly_damage += accredited_damage;
|
||||
pp->damage_this_burst += accredited_damage;
|
||||
|
||||
// Done with adjustments to damage. Evaluate based on current friendly_damage
|
||||
nprintf(("AI", "Friendly damage: %.1f, threshold: %.1f, inc damage: %.1f, max burst: %d\n", pp->friendly_damage, FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f), pp->damage_this_burst, MAX_BURST_DAMAGE ));
|
||||
|
||||
if (is_instructor(objp_hit)) {
|
||||
// it's not nice to hit your instructor
|
||||
if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
|
||||
message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
|
||||
pp->last_warning_message_time = Missiontime;
|
||||
ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
|
||||
|
||||
training_fail();
|
||||
|
||||
// Instructor leaves.
|
||||
mission_do_departure(objp_hit);
|
||||
gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED ); // Force player to warp out.
|
||||
} else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
|
||||
// warning every 4 sec
|
||||
// use NULL as the message sender here since it is the Terran Command persona
|
||||
message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
|
||||
pp->last_warning_message_time = Missiontime;
|
||||
if (shipp_hitter->team != shipp_hit->team) {
|
||||
return;
|
||||
}
|
||||
|
||||
// not nice to hit your friends
|
||||
} else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
|
||||
process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
|
||||
mission_goal_fail_all();
|
||||
ai_abort_rearm_request( Player_obj );
|
||||
// get the player
|
||||
player *pp = &Players[Player_num];
|
||||
|
||||
Player_ship->team = Iff_traitor;
|
||||
// wacky stuff here
|
||||
if (pp->friendly_hits != 0) {
|
||||
float time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
|
||||
if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
|
||||
if (time_since_last_hit > 60.0f) {
|
||||
pp->friendly_hits = 0;
|
||||
pp->friendly_damage = 0.0f;
|
||||
} else if (time_since_last_hit > 2.0f) {
|
||||
pp->friendly_hits -= (int) time_since_last_hit/2;
|
||||
pp->friendly_damage -= time_since_last_hit;
|
||||
}
|
||||
|
||||
} else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
|
||||
// no closer than 4 sec intervals
|
||||
// Note: (damage > frand()) added on 12/9/97 by MK. Since damage is now scaled down for big ships, we could get too
|
||||
// many warnings. Kind of tedious. frand() returns a value in 0..1, so this won't affect legit hits.
|
||||
process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
|
||||
pp->last_warning_message_time = Missiontime;
|
||||
if (pp->friendly_damage < 0.0f) {
|
||||
pp->friendly_damage = 0.0f;
|
||||
}
|
||||
|
||||
if (pp->friendly_hits < 0) {
|
||||
pp->friendly_hits = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float damage; // Damage done by weapon. Gets scaled down based on size of ship.
|
||||
|
||||
if (objp_weapon->type == OBJ_BEAM) // added beam for traitor detection -FUBAR
|
||||
damage = beam_get_ship_damage(&Beams[objp_weapon->instance], objp_hit);
|
||||
else
|
||||
damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
|
||||
|
||||
// wacky stuff here
|
||||
ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
|
||||
if (shipp_hit->ship_max_hull_strength > 1000.0f) {
|
||||
float factor = shipp_hit->ship_max_hull_strength / 1000.0f;
|
||||
factor = MIN(100.0f, factor);
|
||||
damage /= factor;
|
||||
}
|
||||
|
||||
// Don't penalize much at all for hitting cargo
|
||||
if (sip->class_type > -1) {
|
||||
damage *= Ship_types[sip->class_type].ff_multiplier;
|
||||
}
|
||||
|
||||
// Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
|
||||
if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
|
||||
damage /= 5.0f;
|
||||
}
|
||||
|
||||
pp->friendly_last_hit_time = Missiontime;
|
||||
pp->friendly_hits++;
|
||||
|
||||
// cap damage and number of hits done this frame
|
||||
float accredited_damage = MIN(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
|
||||
pp->friendly_damage += accredited_damage;
|
||||
pp->damage_this_burst += accredited_damage;
|
||||
|
||||
// Done with adjustments to damage. Evaluate based on current friendly_damage
|
||||
nprintf(("AI", "Friendly damage: %.1f, threshold: %.1f, inc damage: %.1f, max burst: %d\n", pp->friendly_damage, FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f), pp->damage_this_burst, MAX_BURST_DAMAGE ));
|
||||
|
||||
if (is_instructor(objp_hit)) {
|
||||
// it's not nice to hit your instructor
|
||||
if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
|
||||
message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
|
||||
pp->last_warning_message_time = Missiontime;
|
||||
ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
|
||||
|
||||
training_fail();
|
||||
|
||||
// Instructor leaves.
|
||||
mission_do_departure(objp_hit);
|
||||
gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED ); // Force player to warp out.
|
||||
} else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
|
||||
// warning every 4 sec
|
||||
// use NULL as the message sender here since it is the Terran Command persona
|
||||
message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
|
||||
pp->last_warning_message_time = Missiontime;
|
||||
}
|
||||
|
||||
// not nice to hit your friends
|
||||
} else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
|
||||
process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
|
||||
mission_goal_fail_all();
|
||||
ai_abort_rearm_request( Player_obj );
|
||||
|
||||
Player_ship->team = Iff_traitor;
|
||||
|
||||
} else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
|
||||
// no closer than 4 sec intervals
|
||||
// Note: (damage > frand()) added on 12/9/97 by MK. Since damage is now scaled down for big ships, we could get too
|
||||
// many warnings. Kind of tedious. frand() returns a value in 0..1, so this won't affect legit hits.
|
||||
process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
|
||||
pp->last_warning_message_time = Missiontime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15545,11 +15584,14 @@ void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hit_normal)
|
||||
return;
|
||||
|
||||
hitter_objnum = hit_objp->parent;
|
||||
Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
|
||||
Assertion((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS), "hitter_objnum in this function is an invalid index of %d. This can cause random behavior, and is a coder mistake. Please report!", hitter_objnum);
|
||||
objp_hitter = &Objects[hitter_objnum];
|
||||
|
||||
Assertion((objp_hitter->type == OBJ_SHIP) || (objp_hitter->type == OBJ_WEAPON) || (objp_hitter->type == OBJ_ASTEROID) || (objp_hitter->type == OBJ_BEAM) || (objp_hitter->type == OBJ_DEBRIS),
|
||||
"The ai code is passing an invalid object type of %d to a function when trying to decide how to respond to something hitting it. This is a bug in the code, please report!", objp_hitter->type);
|
||||
|
||||
// lets not check hits by ghosts any further either
|
||||
if(objp_hitter->type == OBJ_GHOST)
|
||||
if(objp_hitter->type != OBJ_SHIP && objp_hitter->type != OBJ_WEAPON && objp_hitter->type != OBJ_ASTEROID && objp_hitter->type != OBJ_BEAM && objp_hitter->type != OBJ_DEBRIS)
|
||||
return;
|
||||
|
||||
// Hit by a protected ship, don't attack it.
|
||||
@@ -16147,6 +16189,12 @@ void maybe_cheat_fire_synaptic(object *objp)
|
||||
ship *shipp;
|
||||
int wing_index, time;
|
||||
|
||||
Assertion(objp->type == OBJ_SHIP, "This ai function requires a ship, but was called with a non-ship object type of %d. Please report!", objp->type);
|
||||
|
||||
if (objp->type != OBJ_SHIP) {
|
||||
return;
|
||||
}
|
||||
|
||||
shipp = &Ships[objp->instance];
|
||||
|
||||
if (!(strnicmp(shipp->ship_name, NOX("delta"), 5)))
|
||||
|
||||
+5
-1
@@ -1235,6 +1235,10 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
|
||||
priority = ( CDR( CDR(node) ) >= 0 ) ? atoi( CTEXT( CDR( CDR( node ) ) ) ) : -1;
|
||||
goalmode = AI_GOAL_IGNORE_NEW;
|
||||
break;
|
||||
case OP_AI_FORM_ON_WING:
|
||||
priority = 99;
|
||||
goalmode = AI_GOAL_FORM_ON_WING;
|
||||
break;
|
||||
default:
|
||||
const ai_mode_lua* luaAIMode = ai_lua_find_mode(op);
|
||||
if(luaAIMode != nullptr){
|
||||
@@ -1251,7 +1255,7 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
|
||||
priority = localnode >= 0 ? atoi( CTEXT(localnode) ) : -1;
|
||||
}
|
||||
else {
|
||||
UNREACHABLE("Invalid SEXP-OP number %d for an AI goal!", op);
|
||||
UNREACHABLE("Invalid SEXP-OP %s (number %d) for an AI goal!", Sexp_nodes[node].text, op);
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
@@ -51,6 +51,7 @@ void processEvents()
|
||||
{
|
||||
io::mouse::CursorManager::get()->showCursor(false);
|
||||
|
||||
os_remove_deferred_cutscene_key_events();
|
||||
os_poll();
|
||||
}
|
||||
|
||||
|
||||
@@ -407,10 +407,21 @@ void fireball_parse_tbl()
|
||||
{
|
||||
if (fi.lod_count > 1)
|
||||
{
|
||||
Assertion(fi.lod_count <= MAX_FIREBALL_LOD, "Fireball LOD (%d) greater than MAX_FIREBALL_LOD %d.", fi.lod_count, MAX_FIREBALL_LOD);
|
||||
static_assert(MAX_FIREBALL_LOD < 10, "The fireball LOD naming scheme needs to be changed for LODs > 9");
|
||||
|
||||
auto lod0 = fi.lod[0].filename;
|
||||
constexpr int MAX_BASENAME_LEN = MAX_FILENAME_LEN - 3; // ensure base file name + _*lod* fits in filename field
|
||||
|
||||
if (strlen(lod0) > MAX_BASENAME_LEN)
|
||||
{
|
||||
Warning(LOCATION, "A fireball base file name may not be longer than %d characters due to the LOD naming scheme.\nLODs other than LOD0 will be skipped for fireball %s", MAX_BASENAME_LEN, lod0);
|
||||
fi.lod_count = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 1; j < fi.lod_count; ++j)
|
||||
sprintf(fi.lod[j].filename, "%s_%d", lod0, j);
|
||||
sprintf(fi.lod[j].filename, "%.*s_%d", MAX_BASENAME_LEN, lod0, j % MAX_FIREBALL_LOD /*to show gcc12 format string is safe*/);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1294,7 +1294,7 @@ void parse_soundtrack()
|
||||
continue;
|
||||
|
||||
// check for file
|
||||
if (!cf_exists_full(Soundtracks[strack_idx].patterns[i].fname, CF_TYPE_MUSIC))
|
||||
if (!cf_exists_full_ext(Soundtracks[strack_idx].patterns[i].fname, CF_TYPE_MUSIC, NUM_AUDIO_EXT, audio_ext_list))
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,23 +31,23 @@
|
||||
#define GM_CAMPAIGN_MODE (1 << 10) // are we currently in a campaign.
|
||||
#define GM_LAB (1 << 11) // We are currently in the F3 lab
|
||||
|
||||
#define VM_EXTERNAL (1 << 0) // Set if not viewing from player position.
|
||||
#define VM_TRACK (1 << 1) // Set if viewer is tracking target.
|
||||
#define VM_DEAD_VIEW (1 << 2) // Set if viewer is watching from dead view.
|
||||
#define VM_CHASE (1 << 3) // Chase view.
|
||||
#define VM_OTHER_SHIP (1 << 4) // View from another ship.
|
||||
#define VM_CAMERA_LOCKED (1 << 5) // Set if player does not have control of the camera
|
||||
#define VM_WARP_CHASE (1 << 6) // View while warping out (form normal view mode)
|
||||
#define VM_PADLOCK_UP (1 << 7)
|
||||
#define VM_PADLOCK_REAR (1 << 8)
|
||||
#define VM_PADLOCK_LEFT (1 << 9)
|
||||
#define VM_PADLOCK_RIGHT (1 << 10)
|
||||
#define VM_WARPIN_ANCHOR (1 << 11) // special warpin camera mode
|
||||
#define VM_TOPDOWN (1 << 12) //Camera is looking down on ship
|
||||
#define VM_FREECAMERA (1 << 13) //Camera is not attached to any particular object, probably under SEXP control
|
||||
#define VM_CENTERING (1 << 14) // View is springing to center
|
||||
#define VM_EXTERNAL (1 << 0) // Set if not viewing from player position.
|
||||
#define VM_TRACK (1 << 1) // Set if viewer is tracking target.
|
||||
#define VM_DEAD_VIEW (1 << 2) // Set if viewer is watching from dead view.
|
||||
#define VM_CHASE (1 << 3) // Chase view.
|
||||
#define VM_OTHER_SHIP (1 << 4) // View from another ship.
|
||||
#define VM_CAMERA_LOCKED (1 << 5) // Set if player does not have control of the camera
|
||||
#define VM_WARP_CHASE (1 << 6) // View while warping out (form normal view mode)
|
||||
#define VM_PADLOCK_UP (1 << 7) // Set when player is looking up
|
||||
#define VM_PADLOCK_REAR (1 << 8) // Set when player is looking behind
|
||||
#define VM_PADLOCK_LEFT (1 << 9) // Set when player is looking left
|
||||
#define VM_PADLOCK_RIGHT (1 << 10) // Set when player is looking right
|
||||
#define VM_WARPIN_ANCHOR (1 << 11) // special warpin camera mode
|
||||
#define VM_TOPDOWN (1 << 12) // Camera is looking down on ship
|
||||
#define VM_FREECAMERA (1 << 13) // Camera is not attached to any particular object, probably under SEXP control
|
||||
#define VM_CENTERING (1 << 14) // View is springing to center
|
||||
|
||||
#define VM_PADLOCK_ANY (VM_PADLOCK_UP|VM_PADLOCK_REAR|VM_PADLOCK_LEFT|VM_PADLOCK_RIGHT)
|
||||
#define VM_PADLOCK_ANY (VM_PADLOCK_UP | VM_PADLOCK_REAR | VM_PADLOCK_LEFT | VM_PADLOCK_RIGHT)
|
||||
|
||||
//-----Cutscene stuff
|
||||
//No bars
|
||||
|
||||
@@ -1515,6 +1515,7 @@ void hud_update_frame(float /*frametime*/)
|
||||
}
|
||||
|
||||
if (Player_ai->target_objnum == -1) {
|
||||
Player->target_is_dying = -1; // according to comments elsewhere in the code, set to -1 when no target
|
||||
hud_target_change_check();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -395,7 +395,12 @@ void HudGaugeBrackets::renderObjectBrackets(object *targetp, color *clr, int w_c
|
||||
int bound_rc;
|
||||
SCP_list<CJumpNode>::iterator jnp;
|
||||
|
||||
if ( Player->target_is_dying <= 0 || (Player_ai->target_objnum >= 0 && targetp != &Objects[Player_ai->target_objnum])) {
|
||||
|
||||
bool not_player_target = (Player_ai->target_objnum < 0 || targetp != &Objects[Player_ai->target_objnum]);
|
||||
|
||||
|
||||
// target_is_dying is set to zero when the target is not dying, -1 when no target. Only 1, aka "TRUE", is invalid.
|
||||
if ( not_player_target || Player->target_is_dying <= 0) {
|
||||
int modelnum;
|
||||
|
||||
switch ( targetp->type ) {
|
||||
@@ -475,7 +480,13 @@ void HudGaugeBrackets::renderObjectBrackets(object *targetp, color *clr, int w_c
|
||||
int target_objnum = -1;
|
||||
|
||||
if(flags & TARGET_DISPLAY_DIST) {
|
||||
distance = Player_ai->current_target_distance;
|
||||
// since we can have either the target, or rogue asteroids bracketed,
|
||||
// check which case we are in, and then do the correct distance.
|
||||
if (not_player_target) {
|
||||
distance = hud_find_target_distance(targetp, &Player_obj->pos);
|
||||
} else {
|
||||
distance = Player_ai->current_target_distance;
|
||||
}
|
||||
}
|
||||
|
||||
if(flags & TARGET_DISPLAY_DOTS) {
|
||||
|
||||
@@ -194,7 +194,8 @@ void HudGaugeLock::renderOld(float frametime)
|
||||
return;
|
||||
}
|
||||
|
||||
if (Player->target_is_dying) {
|
||||
// 1 is the only value for which your target is actually dying
|
||||
if (Player->target_is_dying == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -309,12 +309,21 @@ ship_info *sip = &Ship_info[Player_ship->ship_info_index];
|
||||
}
|
||||
|
||||
void HudGaugeReticle::getFirepointStatus() {
|
||||
|
||||
// allow the firepoint status to be empty when a multiplayer observer
|
||||
// this is not a bug, the observer will simply *not* have any firepoints.
|
||||
if (Objects[Player->objnum].type == OBJ_OBSERVER) {
|
||||
// only multiplayer instances should be getting here!
|
||||
Assertion((Game_mode & GM_MULTIPLAYER), "Somehow FSO thinks its player object is an observer even though it's not in Multiplayer. Please report!");
|
||||
return;
|
||||
}
|
||||
|
||||
//First, get the player ship
|
||||
ship_info* sip;
|
||||
ship* shipp;
|
||||
polymodel* pm;
|
||||
|
||||
Assert(Objects[Player->objnum].type == OBJ_SHIP);
|
||||
Assertion(Objects[Player->objnum].type == OBJ_SHIP, "HudGaugeReticle::getFirepointStatus was passed an invalid object type of %d. Please report!", Objects[Player->objnum].type);
|
||||
|
||||
if (Objects[Player->objnum].type == OBJ_SHIP) {
|
||||
shipp = &Ships[Objects[Player->objnum].instance];
|
||||
|
||||
@@ -1530,6 +1530,8 @@ void hud_target_missile(object *source_obj, int next_flag)
|
||||
continue;
|
||||
}
|
||||
|
||||
Assertion(Objects[so->objnum].type == OBJ_SHIP, "hud_target_missile was about to call obj_team with a non-ship obejct with type %d. Please report!", Objects[so->objnum].type);
|
||||
|
||||
// only allow targeting of hostile bombs
|
||||
if (!iff_x_attacks_y(Player_ship->team, obj_team(A))) {
|
||||
continue;
|
||||
@@ -4523,6 +4525,7 @@ void HudGaugeTargetTriangle::render(float /*frametime*/)
|
||||
object *targetp = &Objects[Player_ai->target_objnum];
|
||||
|
||||
// draw the targeting triangle that orbits the outside of the outer circle of the reticle
|
||||
// checking !target_is_dying is correct here, since you do not want -1 (no target) or 1(is actually dying)
|
||||
if (!Player->target_is_dying && maybeFlashSexp() != 1) {
|
||||
|
||||
hud_set_iff_color(targetp, 1);
|
||||
|
||||
@@ -372,7 +372,7 @@ bool CFtpGet::IssuePasv()
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&dataaddr), sizeof(dataaddr)) == SOCKET_ERROR ) {
|
||||
if ( connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&dataaddr), psnet_get_sockaddr_len(&dataaddr)) == SOCKET_ERROR ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -392,7 +392,7 @@ int CFtpGet::ConnectControlSocket()
|
||||
}
|
||||
|
||||
// Now we will connect to the host
|
||||
if ( connect(m_ControlSock, reinterpret_cast<LPSOCKADDR>(&m_ServerAddr), sizeof(m_ServerAddr)) ) {
|
||||
if ( connect(m_ControlSock, reinterpret_cast<LPSOCKADDR>(&m_ServerAddr), psnet_get_sockaddr_len(&m_ServerAddr)) ) {
|
||||
m_State = FTP_STATE_CANT_CONNECT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ int ChttpGet::ConnectSocket()
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
int serr = connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&hostaddr), sizeof(hostaddr));
|
||||
int serr = connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&hostaddr), psnet_get_sockaddr_len(&hostaddr));
|
||||
int cerr = WSAGetLastError();
|
||||
if (serr) {
|
||||
// fail after 20 seconds
|
||||
@@ -360,7 +360,7 @@ int ChttpGet::ConnectSocket()
|
||||
if (m_Aborting)
|
||||
return 0;
|
||||
|
||||
serr = connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&hostaddr), sizeof(hostaddr));
|
||||
serr = connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&hostaddr), psnet_get_sockaddr_len(&hostaddr));
|
||||
|
||||
if (serr == 0)
|
||||
break;
|
||||
|
||||
@@ -1924,12 +1924,12 @@ int button_function_critical(int n, net_player *p = NULL)
|
||||
if(at_self)
|
||||
control_used(CYCLE_NUM_MISSLES);
|
||||
|
||||
if ( objp == Player_obj ) {
|
||||
if ( Player_ship->weapons.num_secondary_banks <= 0 ) {
|
||||
if ( Ships[objp->instance].weapons.num_secondary_banks <= 0 ) {
|
||||
if ( objp == Player_obj ) {
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "This ship has no secondary weapons", 33));
|
||||
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
polymodel *pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
|
||||
|
||||
@@ -642,3 +642,8 @@ fix timestamp_get_mission_time()
|
||||
|
||||
return static_cast<fix>(time / MICROSECONDS_PER_SECOND);
|
||||
}
|
||||
|
||||
uint64_t timestamp_get_mission_time_in_microseconds()
|
||||
{
|
||||
return timestamp_get_microseconds() - Timestamp_microseconds_at_mission_start;
|
||||
}
|
||||
@@ -106,6 +106,7 @@ extern int timer_get_seconds(); // seconds since program started... not accur
|
||||
constexpr int MILLISECONDS_PER_SECOND = 1000;
|
||||
constexpr uint64_t MICROSECONDS_PER_SECOND = 1000000;
|
||||
constexpr uint64_t NANOSECONDS_PER_SECOND = 1000000000;
|
||||
constexpr uint64_t NANOSECONDS_PER_MICROSECOND = 1000;
|
||||
|
||||
// use this call to get the current counter value (which represents the time at the time
|
||||
// this function is called). I.e. it doesn't return a count that would be in the future,
|
||||
@@ -225,5 +226,6 @@ void timestamp_start_mission();
|
||||
|
||||
// Calculate the current mission time using the timestamps
|
||||
fix timestamp_get_mission_time();
|
||||
uint64_t timestamp_get_mission_time_in_microseconds();
|
||||
|
||||
#endif
|
||||
@@ -2070,6 +2070,7 @@ void vm_angular_move_forward_vec(const vec3d* goal_f, const matrix* orient, cons
|
||||
|
||||
// handle bank separately, simpler, since its just a target velocity
|
||||
{
|
||||
CLAMP(bank_vel, -vel_limit->xyz.z, vel_limit->xyz.z);
|
||||
float delta_bank_vel = bank_vel - w_in->xyz.z;
|
||||
float delta_bank_accel = fl_abs(delta_bank_vel) / delta_t; // the accel required to reach the target vel this frame
|
||||
float accel = (delta_bank_accel > acc_limit->xyz.z) ? acc_limit->xyz.z : delta_bank_accel;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "menuui/optionsmenu.h"
|
||||
#include "menuui/optionsmenumulti.h"
|
||||
#include "mission/missionbriefcommon.h"
|
||||
#include "missionui/missiondebrief.h"
|
||||
#include "missionui/missionscreencommon.h"
|
||||
#include "nebula/neb.h"
|
||||
#include "network/multi.h"
|
||||
@@ -707,8 +708,13 @@ void options_button_pressed(int n)
|
||||
case ABORT_GAME_BUTTON:
|
||||
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
|
||||
choice = popup( PF_NO_NETWORKING | PF_BODY_BIG, 2, POPUP_NO, POPUP_YES, XSTR( "Exit Game?", 374));
|
||||
if ( choice == 1 )
|
||||
if ( choice == 1 ) {
|
||||
if (gameseq_get_state(1) == GS_STATE_DEBRIEF && (Game_mode & GM_CAMPAIGN_MODE)) {
|
||||
// auto-accept mission outcome before quitting
|
||||
debrief_maybe_auto_accept();
|
||||
}
|
||||
gameseq_post_event(GS_EVENT_QUIT_GAME);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONTROL_CONFIG_BUTTON:
|
||||
|
||||
@@ -951,7 +951,7 @@ void mission_campaign_store_goals_and_events()
|
||||
int cur, i;
|
||||
cmission *mission_obj;
|
||||
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE))
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.current_mission < 0))
|
||||
return;
|
||||
|
||||
cur = Campaign.current_mission;
|
||||
@@ -1030,7 +1030,7 @@ void mission_campaign_store_variables(int persistence_type, bool store_red_alert
|
||||
int cur, i, j;
|
||||
cmission *mission_obj;
|
||||
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE))
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.current_mission < 0))
|
||||
return;
|
||||
|
||||
cur = Campaign.current_mission;
|
||||
@@ -1098,7 +1098,7 @@ void mission_campaign_store_variables(int persistence_type, bool store_red_alert
|
||||
// jg18 - adapted from mission_campaign_store_variables()
|
||||
void mission_campaign_store_containers(ContainerType persistence_type, bool store_red_alert)
|
||||
{
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE))
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.current_mission < 0))
|
||||
return;
|
||||
|
||||
if (!sexp_container_has_persistent_non_eternal_containers()) {
|
||||
@@ -1901,7 +1901,7 @@ void mission_campaign_save_on_close_variables()
|
||||
int i;
|
||||
|
||||
// make sure we are actually playing a single-player campaign
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.type != CAMPAIGN_TYPE_SINGLE))
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.type != CAMPAIGN_TYPE_SINGLE) || (Campaign.current_mission < 0))
|
||||
return;
|
||||
|
||||
// now save variables
|
||||
@@ -1941,7 +1941,7 @@ void mission_campaign_save_on_close_variables()
|
||||
void mission_campaign_save_on_close_containers()
|
||||
{
|
||||
// make sure we are actually playing a single-player campaign
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.type != CAMPAIGN_TYPE_SINGLE))
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.type != CAMPAIGN_TYPE_SINGLE) || (Campaign.current_mission < 0))
|
||||
return;
|
||||
|
||||
// now save containers
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "scripting/scripting.h"
|
||||
#include "ship/ship.h"
|
||||
#include "ship/subsysdamage.h"
|
||||
#include "sound/audiostr.h"
|
||||
#include "sound/fsspeech.h"
|
||||
#include "species_defs/species_defs.h"
|
||||
#include "utils/Random.h"
|
||||
@@ -1735,7 +1736,7 @@ void message_queue_message( int message_num, int priority, int timing, const cha
|
||||
if (message_filename_has_fs1_wingman_prefix(filename)) {
|
||||
char converted[MAX_FILENAME_LEN];
|
||||
message_filename_convert_to_command(converted, filename);
|
||||
if (cf_exists_full(converted, CF_TYPE_VOICE_SPECIAL)) {
|
||||
if (cf_exists_full_ext(converted, CF_TYPE_VOICE_SPECIAL, NUM_AUDIO_EXT, audio_ext_list)) {
|
||||
convert_to_command = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6247,6 +6247,7 @@ bool post_process_mission()
|
||||
SCP_string location_str;
|
||||
SCP_string sexp_str;
|
||||
SCP_string error_msg;
|
||||
SCP_string bad_node_str;
|
||||
|
||||
// it's helpful to point out the goal/event, so do that if we can
|
||||
int index;
|
||||
@@ -6258,8 +6259,13 @@ bool post_process_mission()
|
||||
|
||||
convert_sexp_to_string(sexp_str, i, SEXP_ERROR_CHECK_MODE);
|
||||
truncate_message_lines(sexp_str, 30);
|
||||
|
||||
stuff_sexp_text_string(bad_node_str, bad_node, SEXP_ERROR_CHECK_MODE);
|
||||
if (!bad_node_str.empty()) { // the previous function adds a space at the end
|
||||
bad_node_str.pop_back();
|
||||
}
|
||||
|
||||
sprintf(error_msg, "%s%s.\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)\n", location_str.c_str(), sexp_error_message(result), sexp_str.c_str(), Sexp_nodes[bad_node].text);
|
||||
sprintf(error_msg, "%s%s.\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)\n", location_str.c_str(), sexp_error_message(result), sexp_str.c_str(), bad_node_str.c_str());
|
||||
Warning(LOCATION, "%s", error_msg.c_str());
|
||||
|
||||
// syntax errors are recoverable in Fred but not FS
|
||||
|
||||
@@ -378,6 +378,7 @@ int Debrief_should_show_popup = 1;
|
||||
static int Debrief_skip_popup_already_shown = 0;
|
||||
|
||||
void debrief_text_init();
|
||||
bool debrief_can_accept();
|
||||
void debrief_accept(int ok_to_post_start_game_event = 1);
|
||||
void debrief_kick_selected_player();
|
||||
|
||||
@@ -925,7 +926,7 @@ void debrief_choose_voice(char *voice_dest, size_t buf_size, char *voice_base, i
|
||||
if (snprintf(voice_dest, buf_size, NOX("%d_%s"), persona_index, voice_base) < static_cast<int>(buf_size))
|
||||
{
|
||||
// if it exists, we're done
|
||||
if (cf_exists_full(voice_dest, CF_TYPE_VOICE_DEBRIEFINGS))
|
||||
if (cf_exists_full_ext(voice_dest, CF_TYPE_VOICE_DEBRIEFINGS, NUM_AUDIO_EXT, audio_ext_list))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1268,12 +1269,17 @@ void debrief_assemble_optional_mission_popup_text(char *buffer, char *mission_lo
|
||||
strcat(buffer, XSTR("\n\n\nDo you want to play the optional mission?", 1491));
|
||||
}
|
||||
|
||||
bool debrief_can_accept()
|
||||
{
|
||||
return !(/*Cheats_enabled ||*/ Turned_traitor || Must_replay_mission);
|
||||
}
|
||||
|
||||
// what to do when the accept button is hit
|
||||
void debrief_accept(int ok_to_post_start_game_event)
|
||||
{
|
||||
int go_loop = 0;
|
||||
|
||||
if ( (/*Cheats_enabled ||*/ Turned_traitor || Must_replay_mission) && (Game_mode & GM_CAMPAIGN_MODE) ) {
|
||||
if ( !debrief_can_accept() && (Game_mode & GM_CAMPAIGN_MODE) ) {
|
||||
const char *str;
|
||||
int z;
|
||||
|
||||
@@ -2556,6 +2562,13 @@ void debrief_handle_player_drop()
|
||||
debrief_rebuild_player_list();
|
||||
}
|
||||
|
||||
void debrief_maybe_auto_accept()
|
||||
{
|
||||
if (debrief_can_accept()) {
|
||||
debrief_accept(0);
|
||||
}
|
||||
}
|
||||
|
||||
void debrief_disable_accept()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ void debrief_close();
|
||||
void debrief_rebuild_player_list();
|
||||
void debrief_handle_player_drop();
|
||||
|
||||
void debrief_maybe_auto_accept();
|
||||
void debrief_disable_accept();
|
||||
void debrief_assemble_optional_mission_popup_text(char *buffer, char *mission_loop_desc);
|
||||
|
||||
|
||||
@@ -885,8 +885,14 @@ void red_alert_bash_wingman_status()
|
||||
mprintf(("Invalid ship class specified in red alert data for ship %s. Using mission defaults.\n", shipp->ship_name));
|
||||
}
|
||||
|
||||
float max_hull;
|
||||
if (shipp->special_hitpoints)
|
||||
max_hull = shipp->ship_max_hull_strength;
|
||||
else
|
||||
max_hull = Ship_info[shipp->ship_info_index].max_hull_strength;
|
||||
|
||||
// restore hull (but not shields)
|
||||
if (ras->hull >= 0.0f && ras->hull <= ship_objp->hull_strength)
|
||||
if (ras->hull >= 0.0f && ras->hull <= max_hull)
|
||||
ship_objp->hull_strength = ras->hull;
|
||||
else
|
||||
mprintf(("Invalid health in red alert data for ship %s. Using mission defaults.\n", shipp->ship_name));
|
||||
@@ -979,9 +985,15 @@ void red_alert_bash_wingman_status()
|
||||
}
|
||||
}
|
||||
|
||||
float max_hull;
|
||||
if (pobjp->special_hitpoints)
|
||||
max_hull = pobjp->ship_max_hull_strength;
|
||||
else
|
||||
max_hull = Ship_info[pobjp->ship_class].max_hull_strength;
|
||||
|
||||
// restore hull (but not shields)
|
||||
if (ras->hull >= 0.0f && ras->hull <= (pobjp->initial_hull * pobjp->ship_max_hull_strength / 100.0f))
|
||||
pobjp->initial_hull = (int) (ras->hull * 100.0f / pobjp->ship_max_hull_strength);
|
||||
if (ras->hull >= 0.0f && ras->hull <= max_hull)
|
||||
pobjp->initial_hull = (int) (ras->hull * 100.0f / max_hull);
|
||||
else
|
||||
mprintf(("Invalid health in red alert data for ship %s. Using mission defaults.\n", pobjp->name));
|
||||
|
||||
|
||||
@@ -2385,26 +2385,29 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
|
||||
int n_banks = cfread_int(fp); // Number of turrets
|
||||
|
||||
for ( i = 0; i < n_banks; i++ ) {
|
||||
int parent; // The parent subobj of the turret (the gun base)
|
||||
int physical_parent; // The subobj that the firepoints are physically attached to (the gun barrel)
|
||||
int n_slots; // How many firepoints the turret has
|
||||
model_subsystem *subsystemp; // The actual turret subsystem
|
||||
|
||||
parent = cfread_int( fp );
|
||||
physical_parent = cfread_int(fp);
|
||||
int base_obj = cfread_int(fp); // The parent subobj of the turret (the gun base)
|
||||
int gun_obj = cfread_int(fp); // The subobj that the firepoints are physically attached to (the gun barrel)
|
||||
|
||||
if (base_obj != gun_obj && pm->submodel[gun_obj].parent != base_obj) {
|
||||
Warning(LOCATION, "Model %s turret %s has a gun submodel that is not an immediate child object of the base", pm->filename, pm->submodel[base_obj].name);
|
||||
gun_obj = base_obj; // fall back to singlepart handling
|
||||
}
|
||||
|
||||
int snum=-1;
|
||||
if ( subsystems ) {
|
||||
for ( snum = 0; snum < n_subsystems; snum++ ) {
|
||||
subsystemp = &subsystems[snum];
|
||||
|
||||
if ( parent == subsystemp->subobj_num ) {
|
||||
if ( base_obj == subsystemp->subobj_num ) {
|
||||
cfread_vector( &temp_vec, fp );
|
||||
vm_vec_normalize_safe(&temp_vec);
|
||||
subsystemp->turret_norm = temp_vec;
|
||||
|
||||
n_slots = cfread_int( fp );
|
||||
subsystemp->turret_gun_sobj = physical_parent;
|
||||
subsystemp->turret_gun_sobj = gun_obj;
|
||||
if(n_slots > MAX_TFP) {
|
||||
Warning(LOCATION, "Model %s has %i turret firing points on subsystem %s, maximum is %i", pm->filename, n_slots, subsystemp->name, MAX_TFP);
|
||||
}
|
||||
@@ -2423,8 +2426,8 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
|
||||
subsystemp->turret_num_firing_points = n_slots;
|
||||
|
||||
// copy the subsystem index that the gun base submodel should have at this point
|
||||
Assertion(pm->submodel[parent].subsys_num >= 0, "Turret gun base should have a subsystem index!");
|
||||
pm->submodel[physical_parent].subsys_num = pm->submodel[parent].subsys_num;
|
||||
Assertion(pm->submodel[base_obj].subsys_num >= 0, "Turret gun base should have a subsystem index!");
|
||||
pm->submodel[gun_obj].subsys_num = pm->submodel[base_obj].subsys_num;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -2434,7 +2437,7 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
|
||||
if ( (n_subsystems == 0) || (snum == n_subsystems) ) {
|
||||
vec3d bogus;
|
||||
|
||||
nprintf(("Warning", "Turret submodel %i not found for turret %i in model %s\n", parent, i, pm->filename));
|
||||
nprintf(("Warning", "Turret submodel %i not found for turret %i in model %s\n", base_obj, i, pm->filename));
|
||||
cfread_vector( &bogus, fp );
|
||||
n_slots = cfread_int( fp );
|
||||
for (j = 0; j < n_slots; j++ )
|
||||
|
||||
@@ -152,7 +152,7 @@ int ConnectToChatServer(char *serveraddr, char *nickname, char *trackerid)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SOCKET_ERROR==bind(Chatsock, reinterpret_cast<LPSOCKADDR>(&Chataddr), sizeof(Chataddr)))
|
||||
if (SOCKET_ERROR==bind(Chatsock, reinterpret_cast<LPSOCKADDR>(&Chataddr), psnet_get_sockaddr_len(&Chataddr)))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -164,7 +164,7 @@ int ConnectToChatServer(char *serveraddr, char *nickname, char *trackerid)
|
||||
return -2;
|
||||
}
|
||||
|
||||
if(SOCKET_ERROR == connect(Chatsock, reinterpret_cast<LPSOCKADDR>(&Chataddr), sizeof(Chataddr)))
|
||||
if(SOCKET_ERROR == connect(Chatsock, reinterpret_cast<LPSOCKADDR>(&Chataddr), psnet_get_sockaddr_len(&Chataddr)))
|
||||
{
|
||||
if(WSAEWOULDBLOCK == WSAGetLastError())
|
||||
{
|
||||
|
||||
@@ -1785,7 +1785,7 @@ void multi_ingame_send_ship_update(net_player *p)
|
||||
// go through the list and send all ships which are mark as OF_COULD_BE_PLAYER
|
||||
while(moveup!=END_OF_LIST(&Ship_obj_list)){
|
||||
//Make sure the object can be a player and is on the same team as this guy
|
||||
if(Objects[moveup->objnum].flags[Object::Object_Flags::Could_be_player] && obj_team(&Objects[moveup->objnum]) == p->p_info.team){
|
||||
if(Objects[moveup->objnum].flags[Object::Object_Flags::Could_be_player] && Objects[moveup->objnum].type == OBJ_SHIP && obj_team(&Objects[moveup->objnum]) == p->p_info.team){
|
||||
// send the update
|
||||
send_ingame_ship_update_packet(p,&Ships[Objects[moveup->objnum].instance]);
|
||||
}
|
||||
|
||||
@@ -1368,9 +1368,10 @@ int multi_oo_pack_data(net_player *pl, object *objp, ushort oo_flags, ubyte *dat
|
||||
|
||||
// Check that we are not sending too much data, if so, don't actually send.
|
||||
if (packet_size + ret <= OO_MAX_DATA_SIZE) {
|
||||
nprintf(("Network","Had to remove subsystems section from data packet for %s\n", shipp->ship_name));
|
||||
oo_flags |= OO_SUBSYSTEMS_NEW;
|
||||
packet_size += ret;
|
||||
} else {
|
||||
nprintf(("Network","Had to remove subsystems section from data packet for %s\n", shipp->ship_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
-5
@@ -332,8 +332,14 @@ int SENDTO(SOCKET s, char * buf, int len, int flags, SOCKADDR *to, int tolen, in
|
||||
outbuf[0] = static_cast<char>(psnet_type);
|
||||
memcpy(&outbuf[1], buf, static_cast<size_t>(len));
|
||||
|
||||
SOCKLEN_T addrlen = tolen;
|
||||
|
||||
if (addrlen == sizeof(SOCKADDR_STORAGE)) {
|
||||
addrlen = psnet_get_sockaddr_len(reinterpret_cast<SOCKADDR_STORAGE*>(to));
|
||||
}
|
||||
|
||||
// send it
|
||||
return static_cast<int>( sendto(s, outbuf, len + 1, flags, reinterpret_cast<LPSOCKADDR>(to), tolen) );
|
||||
return static_cast<int>( sendto(s, outbuf, len + 1, flags, reinterpret_cast<LPSOCKADDR>(to), addrlen) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -547,7 +553,7 @@ bool psnet_init_my_addr()
|
||||
continue;
|
||||
}
|
||||
|
||||
rval = connect(tsock, reinterpret_cast<LPSOCKADDR>(&remote_addr), sizeof(remote_addr));
|
||||
rval = connect(tsock, reinterpret_cast<LPSOCKADDR>(&remote_addr), psnet_get_sockaddr_len(&remote_addr));
|
||||
|
||||
if (rval) {
|
||||
continue;
|
||||
@@ -586,7 +592,7 @@ bool psnet_init_my_addr()
|
||||
continue;
|
||||
}
|
||||
|
||||
rval = connect(tsock, reinterpret_cast<LPSOCKADDR>(&remote_addr), sizeof(remote_addr));
|
||||
rval = connect(tsock, reinterpret_cast<LPSOCKADDR>(&remote_addr), psnet_get_sockaddr_len(&remote_addr));
|
||||
|
||||
if (rval) {
|
||||
continue;
|
||||
@@ -919,6 +925,25 @@ static void psnet_sockaddr_storage_to_in6(const SOCKADDR_STORAGE *addr, SOCKADDR
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper to get the exact length of a specific protocol struct from
|
||||
* a generic storage struct
|
||||
*
|
||||
* @param addr
|
||||
* @return SOCKLEN_T
|
||||
*/
|
||||
SOCKLEN_T psnet_get_sockaddr_len(const SOCKADDR_STORAGE *addr)
|
||||
{
|
||||
// internally we should always use IPv6, but cover the bases
|
||||
if (addr->ss_family == AF_INET6) {
|
||||
return static_cast<SOCKLEN_T>(sizeof(SOCKADDR_IN6));
|
||||
} else if (addr->ss_family == AF_INET) {
|
||||
return static_cast<SOCKLEN_T>(sizeof(SOCKADDR_IN));
|
||||
} else {
|
||||
return static_cast<SOCKLEN_T>(sizeof(SOCKADDR_STORAGE));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to map IPv4 to IPv6
|
||||
*/
|
||||
@@ -1126,6 +1151,11 @@ bool psnet_get_addr(const char *host, const char *port, SOCKADDR_STORAGE *addr,
|
||||
SOCKADDR_IN6 si4to6;
|
||||
int rval;
|
||||
|
||||
if (addr) {
|
||||
// by the spec, sockaddr_in6 must be zero'd, so just do that always
|
||||
memset(addr, 0, sizeof(*addr));
|
||||
}
|
||||
|
||||
memset(&si4to6, 0, sizeof(si4to6));
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
@@ -2317,9 +2347,12 @@ bool psnet_init_socket()
|
||||
psnet_set_socket_options();
|
||||
|
||||
// bind the socket
|
||||
psnet_get_addr(nullptr, Psnet_default_port, &sockaddr);
|
||||
if ( !psnet_get_addr(nullptr, Psnet_default_port, &sockaddr) ) {
|
||||
ml_printf("Failed to get bind addr!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( bind(Psnet_socket, reinterpret_cast<LPSOCKADDR>(&sockaddr), sizeof(sockaddr)) == SOCKET_ERROR) {
|
||||
if ( bind(Psnet_socket, reinterpret_cast<LPSOCKADDR>(&sockaddr), psnet_get_sockaddr_len(&sockaddr)) == SOCKET_ERROR) {
|
||||
Psnet_failure_code = WSAGetLastError();
|
||||
|
||||
if (Psnet_failure_code == WSAEADDRINUSE) {
|
||||
|
||||
@@ -178,6 +178,9 @@ bool psnet_get_addr(const char *host, uint16_t port, SOCKADDR_STORAGE *addr, int
|
||||
#define ADDR_FLAG_NUMERIC_SERVICE (1<<0)
|
||||
#define ADDR_FLAG_PREFER_IPV4 (1<<1)
|
||||
|
||||
// get protocol struct length from generic storage struct
|
||||
SOCKLEN_T psnet_get_sockaddr_len(const SOCKADDR_STORAGE *addr);
|
||||
|
||||
// map an IPv4 address to IPv6
|
||||
void psnet_map4to6(const in_addr *in4, in6_addr *in6);
|
||||
|
||||
|
||||
+22
-8
@@ -300,7 +300,7 @@ static char szWinTitle[128];
|
||||
static char szWinClass[128];
|
||||
static int Os_inited = 0;
|
||||
|
||||
static SCP_vector<SDL_Event> buffered_events;
|
||||
static SCP_vector<SDL_Event> deferred_events;
|
||||
|
||||
int Os_debugger_running = 0;
|
||||
|
||||
@@ -713,11 +713,11 @@ namespace os
|
||||
}
|
||||
}
|
||||
|
||||
void os_ignore_events() {
|
||||
void os_defer_events_on_load_screen() {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
// Add event to buffer
|
||||
buffered_events.push_back(event);
|
||||
// Add event to be handled later
|
||||
deferred_events.push_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -745,14 +745,28 @@ static void handle_sdl_event(const SDL_Event& event) {
|
||||
}
|
||||
}
|
||||
|
||||
void os_remove_deferred_cutscene_key_events() {
|
||||
deferred_events.erase(
|
||||
std::remove_if(deferred_events.begin(), deferred_events.end(), [](const SDL_Event &event)
|
||||
{
|
||||
return (event.type == SDL_KEYUP) && (
|
||||
event.key.keysym.sym == SDLK_KP_ENTER ||
|
||||
event.key.keysym.sym == SDLK_RETURN ||
|
||||
event.key.keysym.sym == SDLK_SPACE
|
||||
);
|
||||
}),
|
||||
deferred_events.end()
|
||||
);
|
||||
}
|
||||
|
||||
void os_poll()
|
||||
{
|
||||
// Replay the buffered events
|
||||
auto end = buffered_events.end();
|
||||
for (auto it = buffered_events.begin(); it != end; ++it) {
|
||||
// Replay the deferred events
|
||||
auto end = deferred_events.end();
|
||||
for (auto it = deferred_events.begin(); it != end; ++it) {
|
||||
handle_sdl_event(*it);
|
||||
}
|
||||
buffered_events.clear();
|
||||
deferred_events.clear();
|
||||
|
||||
SDL_Event event;
|
||||
|
||||
|
||||
+5
-2
@@ -64,9 +64,12 @@ int os_foreground();
|
||||
// process management --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Removes all pending events and ignores them
|
||||
* @brief Defers all pending events to be handled later
|
||||
*/
|
||||
void os_ignore_events();
|
||||
void os_defer_events_on_load_screen();
|
||||
|
||||
// removes key events that may unintentionally skip the cutscene
|
||||
void os_remove_deferred_cutscene_key_events();
|
||||
|
||||
// call to process windows messages. only does something in non THREADED mode
|
||||
void os_poll();
|
||||
|
||||
+37
-24
@@ -25,11 +25,20 @@
|
||||
#include "cfile/cfilesystem.h"
|
||||
#include "parse/parselo.h"
|
||||
|
||||
static const char *FILTERS_ENABLED_BY_DEFAULT[] =
|
||||
{
|
||||
"error",
|
||||
"warning",
|
||||
"general",
|
||||
"scripting"
|
||||
};
|
||||
|
||||
struct outwnd_filter_struct {
|
||||
char name[NAME_LENGTH];
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
// This technique is necessary to avoid a crash in FRED. See commit e624f6c1.
|
||||
static SCP_vector<outwnd_filter_struct>& filter_vector()
|
||||
{
|
||||
static SCP_vector<outwnd_filter_struct> vec;
|
||||
@@ -58,7 +67,16 @@ void load_filter_info()
|
||||
FILE* fp;
|
||||
char pathname[MAX_PATH_LEN];
|
||||
char inbuf[NAME_LENGTH + 4];
|
||||
outwnd_filter_struct new_filter;
|
||||
outwnd_filter_struct* filter_ptr;
|
||||
|
||||
// add default-enabled filters
|
||||
for (const char *name : FILTERS_ENABLED_BY_DEFAULT) {
|
||||
filter_vector().emplace_back();
|
||||
filter_ptr = &filter_vector().back();
|
||||
|
||||
strcpy_s(filter_ptr->name, name);
|
||||
filter_ptr->enabled = true;
|
||||
}
|
||||
|
||||
outwnd_filter_loaded = 1;
|
||||
|
||||
@@ -69,30 +87,18 @@ void load_filter_info()
|
||||
|
||||
if (!fp) {
|
||||
Outwnd_no_filter_file = 1;
|
||||
|
||||
strcpy_s( new_filter.name, "error" );
|
||||
new_filter.enabled = true;
|
||||
filter_vector().push_back( new_filter );
|
||||
|
||||
strcpy_s( new_filter.name, "general" );
|
||||
new_filter.enabled = true;
|
||||
filter_vector().push_back( new_filter );
|
||||
|
||||
strcpy_s( new_filter.name, "warning" );
|
||||
new_filter.enabled = true;
|
||||
filter_vector().push_back( new_filter );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Outwnd_no_filter_file = 0;
|
||||
|
||||
while ( fgets(inbuf, NAME_LENGTH+3, fp) ) {
|
||||
bool enabled;
|
||||
|
||||
if (*inbuf == '+')
|
||||
new_filter.enabled = true;
|
||||
enabled = true;
|
||||
else if (*inbuf == '-')
|
||||
new_filter.enabled = false;
|
||||
enabled = false;
|
||||
else
|
||||
continue; // skip everything else
|
||||
|
||||
@@ -101,17 +107,24 @@ void load_filter_info()
|
||||
inbuf[z] = 0;
|
||||
|
||||
Assert( strlen(inbuf+1) < NAME_LENGTH );
|
||||
strcpy_s(new_filter.name, inbuf + 1);
|
||||
const char *name = &inbuf[1];
|
||||
|
||||
if ( !stricmp(new_filter.name, "error") ) {
|
||||
new_filter.enabled = true;
|
||||
} else if ( !stricmp(new_filter.name, "general") ) {
|
||||
new_filter.enabled = true;
|
||||
} else if ( !stricmp(new_filter.name, "warning") ) {
|
||||
new_filter.enabled = true;
|
||||
// find it, or create it
|
||||
filter_ptr = nullptr;
|
||||
for (auto &filter : filter_vector()) {
|
||||
if (!stricmp(filter.name, name)) {
|
||||
filter_ptr = &filter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (filter_ptr == nullptr) {
|
||||
filter_vector().emplace_back();
|
||||
filter_ptr = &filter_vector().back();
|
||||
strcpy_s(filter_ptr->name, name);
|
||||
}
|
||||
|
||||
filter_vector().push_back( new_filter );
|
||||
// set it as enabled or not, depending on config
|
||||
filter_ptr->enabled = enabled;
|
||||
}
|
||||
|
||||
if ( ferror(fp) && !feof(fp) )
|
||||
|
||||
+171
-58
@@ -991,8 +991,12 @@ bool ship_class_unchanged(const ship_registry_entry *ship_entry);
|
||||
void multi_sexp_modify_variable();
|
||||
|
||||
// jg18
|
||||
// container_value_index is for using one specific value from acontainer
|
||||
// container_value_index is for using one specific value from a container
|
||||
int copy_node_to_replacement_args(int node, int container_value_index = -1);
|
||||
// checks whether the node's value can change over the course of the mission
|
||||
bool is_node_value_dynamic(int node);
|
||||
// returns 0 on success or a SEXP_CHECK_* value on failure
|
||||
int check_dynamic_value_node_type(int node, bool is_string, bool is_number);
|
||||
|
||||
#define NO_OPERATOR_INDEX_DEFINED -2
|
||||
#define NOT_A_SEXP_OPERATOR -1
|
||||
@@ -2121,6 +2125,12 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
|
||||
|
||||
} else if (Sexp_nodes[node].subtype == SEXP_ATOM_CONTAINER_DATA) {
|
||||
// this is an instance of "Replace Container Data"
|
||||
|
||||
// can't be used in special argument list
|
||||
if (type == OPF_ANYTHING || type == OPF_DATA_OR_STR_CONTAINER) {
|
||||
return SEXP_CHECK_TYPE_MISMATCH;
|
||||
}
|
||||
|
||||
const int modifier_node = Sexp_nodes[node].first;
|
||||
if (modifier_node == -1) {
|
||||
return SEXP_CHECK_MISSING_CONTAINER_MODIFIER;
|
||||
@@ -2464,9 +2474,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
|
||||
}
|
||||
Assert(ship_node >= 0);
|
||||
|
||||
// we can't check special-arg ships
|
||||
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
|
||||
break;
|
||||
if (is_node_value_dynamic(ship_node)) {
|
||||
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
|
||||
if (dyn_val_check) {
|
||||
return dyn_val_check;
|
||||
} else {
|
||||
// since the value changes at runtime
|
||||
// we can check only the type
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto shipname = CTEXT(ship_node);
|
||||
shipnum = ship_name_lookup(shipname, 1);
|
||||
@@ -2551,9 +2568,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
|
||||
|
||||
ship_node = CDR(op_node);
|
||||
|
||||
// we can't check special-arg ships
|
||||
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
|
||||
break;
|
||||
if (is_node_value_dynamic(ship_node)) {
|
||||
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
|
||||
if (dyn_val_check) {
|
||||
return dyn_val_check;
|
||||
} else {
|
||||
// since the value changes at runtime
|
||||
// we can check only the type
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto shipname = CTEXT(ship_node);
|
||||
shipnum = ship_name_lookup(shipname, 1);
|
||||
@@ -2920,9 +2944,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
|
||||
auto ship_node = Sexp_nodes[op_node].rest;
|
||||
Assert(ship_node >= 0);
|
||||
|
||||
// we can't check special-arg ships
|
||||
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
|
||||
break;
|
||||
if (is_node_value_dynamic(ship_node)) {
|
||||
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
|
||||
if (dyn_val_check) {
|
||||
return dyn_val_check;
|
||||
} else {
|
||||
// since the value changes at runtime
|
||||
// we can check only the type
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ship_num = ship_name_lookup(CTEXT(ship_node), 1); // Goober5000 - include players
|
||||
if (ship_num < 0) {
|
||||
@@ -3060,9 +3091,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
|
||||
z = Sexp_nodes[z].rest; // first argument of operator should be mission name
|
||||
Assert(z >= 0);
|
||||
|
||||
// can't check special-arg
|
||||
if (Sexp_nodes[z].flags & SNF_SPECIAL_ARG_IN_NODE)
|
||||
break;
|
||||
if (is_node_value_dynamic(z)) {
|
||||
const int dyn_val_check = check_dynamic_value_node_type(z, true, false);
|
||||
if (dyn_val_check) {
|
||||
return dyn_val_check;
|
||||
} else {
|
||||
// since the value changes at runtime
|
||||
// we can check only the type
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// look for mission
|
||||
count = count_items_with_name(CTEXT(z), Campaign.missions, Campaign.num_missions);
|
||||
@@ -3145,9 +3183,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
|
||||
}
|
||||
Assert(ship_node >= 0);
|
||||
|
||||
// can't check special-arg
|
||||
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
|
||||
break;
|
||||
if (is_node_value_dynamic(ship_node)) {
|
||||
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
|
||||
if (dyn_val_check) {
|
||||
return dyn_val_check;
|
||||
} else {
|
||||
// since the value changes at runtime
|
||||
// we can check only the type
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// look for the ship that has this dockpoint
|
||||
ship_num = ship_name_lookup(CTEXT(ship_node), 1);
|
||||
@@ -4506,36 +4551,23 @@ void stuff_sexp_text_string(SCP_string &dest, int node, int mode)
|
||||
Assertion(sexp_variables_index != -1, "Couldn't find variable: %s\n", Sexp_nodes[node].text);
|
||||
Assert((Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER) || (Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING));
|
||||
|
||||
// Error check - can be Fred or FreeSpace
|
||||
if (mode == SEXP_ERROR_CHECK_MODE)
|
||||
auto var_name = (Fred_running) ? Sexp_nodes[node].text : Sexp_variables[sexp_variables_index].variable_name;
|
||||
auto var_contents = Sexp_variables[sexp_variables_index].text;
|
||||
|
||||
// number
|
||||
if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER)
|
||||
{
|
||||
if (Fred_running)
|
||||
sprintf(dest, "@%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
|
||||
else
|
||||
sprintf(dest, "@%s[%s] ", Sexp_variables[sexp_variables_index].variable_name, Sexp_variables[sexp_variables_index].text);
|
||||
Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER);
|
||||
sprintf(dest, "@%s[%s] ", var_name, var_contents);
|
||||
}
|
||||
// string
|
||||
else if (Sexp_nodes[node].subtype == SEXP_ATOM_STRING)
|
||||
{
|
||||
Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING);
|
||||
sprintf(dest, "\"@%s[%s]\" ", var_name, var_contents);
|
||||
}
|
||||
else
|
||||
{
|
||||
// number
|
||||
if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER)
|
||||
{
|
||||
Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER);
|
||||
|
||||
// Save as string - only Fred
|
||||
Assert(mode == SEXP_SAVE_MODE);
|
||||
sprintf(dest, "@%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
|
||||
}
|
||||
// string
|
||||
else
|
||||
{
|
||||
Assert(Sexp_nodes[node].subtype == SEXP_ATOM_STRING);
|
||||
Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING);
|
||||
|
||||
// Save as string - only Fred
|
||||
Assert(mode == SEXP_SAVE_MODE);
|
||||
sprintf(dest, "\"@%s[%s]\" ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
|
||||
}
|
||||
}
|
||||
UNREACHABLE("SEXP variable nodes must be SEXP_ATOM_NUMBER or SEXP_ATOM_STRING!");
|
||||
}
|
||||
// not a variable
|
||||
else
|
||||
@@ -4917,8 +4949,8 @@ const ship_registry_entry *eval_ship(int node)
|
||||
auto ship_it = Ship_registry_map.find(CTEXT(node));
|
||||
if (ship_it != Ship_registry_map.end())
|
||||
{
|
||||
// cache the value, unless this node is a variable or argument because the value may change
|
||||
if (!(Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE))
|
||||
// cache the value if it can't change later
|
||||
if (!is_node_value_dynamic(node))
|
||||
Sexp_nodes[node].cache = new sexp_cached_data(OPF_SHIP, -1, ship_it->second);
|
||||
|
||||
return &Ship_registry[ship_it->second];
|
||||
@@ -4960,8 +4992,8 @@ wing *eval_wing(int node)
|
||||
{
|
||||
auto wingp = &Wings[wing_num];
|
||||
|
||||
// cache the value, unless this node is a variable or argument because the value may change
|
||||
if (!(Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE))
|
||||
// cache the value if it can't change later
|
||||
if (!is_node_value_dynamic(node))
|
||||
Sexp_nodes[node].cache = new sexp_cached_data(OPF_WING, wingp);
|
||||
|
||||
return wingp;
|
||||
@@ -5002,10 +5034,8 @@ int sexp_atoi(int node)
|
||||
|
||||
int num = atoi(CTEXT(node));
|
||||
|
||||
// cache the value, unless this node is a variable, argument, or container data, because the value may change
|
||||
if (!(Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) &&
|
||||
!(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) &&
|
||||
(Sexp_nodes[node].subtype != SEXP_ATOM_CONTAINER_DATA))
|
||||
// cache the value if it can't change later
|
||||
if (!is_node_value_dynamic(node))
|
||||
Sexp_nodes[node].cache = new sexp_cached_data(OPF_NUMBER, num, -1);
|
||||
|
||||
return num;
|
||||
@@ -11005,6 +11035,13 @@ void sexp_change_argument_validity(int n, bool invalidate)
|
||||
// first we must check if the arg_handler marks a selection. At the moment random-of is the only one that does this
|
||||
arg_n = CDR(arg_handler);
|
||||
while (invalidate && (arg_n != -1)) {
|
||||
Assertion(Sexp_nodes[arg_n].subtype != SEXP_ATOM_CONTAINER_NAME,
|
||||
"Attempt to use invalidate-argument with container %s. Please report!",
|
||||
Sexp_nodes[arg_n].text);
|
||||
Assertion(Sexp_nodes[arg_n].subtype != SEXP_ATOM_CONTAINER_DATA,
|
||||
"Attempt to use invalidate-argument with data from container %s. Please report!",
|
||||
Sexp_nodes[arg_n].text);
|
||||
|
||||
if (Sexp_nodes[arg_n].flags & SNF_ARGUMENT_SELECT) {
|
||||
// now check if the selected argument matches the one we want to invalidate
|
||||
if (!strcmp(CTEXT(n), CTEXT(arg_n))) {
|
||||
@@ -11023,6 +11060,13 @@ void sexp_change_argument_validity(int n, bool invalidate)
|
||||
arg_n = CDR(arg_handler);
|
||||
while (arg_n != -1)
|
||||
{
|
||||
Assertion(Sexp_nodes[arg_n].subtype != SEXP_ATOM_CONTAINER_NAME,
|
||||
"Attempt to change argument validity of container %s. Please report!",
|
||||
Sexp_nodes[arg_n].text);
|
||||
Assertion(Sexp_nodes[arg_n].subtype != SEXP_ATOM_CONTAINER_DATA,
|
||||
"Attempt to change argument validity of data from container %s. Please report!",
|
||||
Sexp_nodes[arg_n].text);
|
||||
|
||||
// match?
|
||||
if (!strcmp(CTEXT(n), CTEXT(arg_n)))
|
||||
{
|
||||
@@ -11802,7 +11846,7 @@ void sexp_clear_goals(int n)
|
||||
if (ship_entry)
|
||||
{
|
||||
if (ship_entry->status != ShipStatus::PRESENT)
|
||||
return; // ship not around anymore???? then forget it!
|
||||
continue; // ship not around anymore???? then forget it!
|
||||
|
||||
ai_clear_ship_goals(&(Ai_info[ship_entry->shipp->ai_index]));
|
||||
continue;
|
||||
@@ -11812,7 +11856,7 @@ void sexp_clear_goals(int n)
|
||||
if (wingp)
|
||||
{
|
||||
if (wingp->flags[Ship::Wing_Flags::Gone])
|
||||
return; // wing not around anymore???? then forget it!
|
||||
continue; // wing not around anymore???? then forget it!
|
||||
|
||||
ai_clear_wing_goals(wingp);
|
||||
}
|
||||
@@ -12184,6 +12228,11 @@ void multi_sexp_hud_display_gauge()
|
||||
void sexp_player_use_ai(int flag)
|
||||
{
|
||||
Player_use_ai = flag ? 1 : 0;
|
||||
|
||||
if (!flag) {
|
||||
Player_ai->ai_override_flags.reset();
|
||||
Player_obj->phys_info.flags &= ~PF_MANEUVER_NO_DAMP;
|
||||
}
|
||||
}
|
||||
|
||||
// Karajorma
|
||||
@@ -13353,7 +13402,8 @@ void sexp_send_one_message( const char *name, const char *who_from, const char *
|
||||
} else if ( !stricmp(who_from, "<none>") ) {
|
||||
source = MESSAGE_SOURCE_NONE;
|
||||
} else {
|
||||
Assertion(false, "Curious... all message sources should have been covered");
|
||||
Warning(LOCATION, "Invalid message source '%s'. Unable to send message.", who_from);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ship_index == -1 ){
|
||||
@@ -22068,8 +22118,8 @@ int sexp_string_to_int(int n)
|
||||
|
||||
int num = atoi(buf);
|
||||
|
||||
// cache the value, unless this node is a variable or argument because the value may change
|
||||
if (!(Sexp_nodes[n].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[n].flags & SNF_SPECIAL_ARG_IN_NODE))
|
||||
// cache the value if it can't change later
|
||||
if (!is_node_value_dynamic(n))
|
||||
Sexp_nodes[n].cache = new sexp_cached_data(OPF_NUMBER, num, -1);
|
||||
|
||||
return num;
|
||||
@@ -31348,6 +31398,9 @@ const char *sexp_error_message(int num)
|
||||
case SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE:
|
||||
return "Wrong container data type";
|
||||
|
||||
case SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE:
|
||||
return "Invalid special argument type";
|
||||
|
||||
default:
|
||||
Warning(LOCATION, "Unhandled sexp error code %d!", num);
|
||||
return "Unhandled sexp error code!";
|
||||
@@ -31678,6 +31731,47 @@ int copy_node_to_replacement_args(int node, int container_value_index)
|
||||
return num_args;
|
||||
}
|
||||
|
||||
bool is_node_value_dynamic(int node)
|
||||
{
|
||||
Assertion(node >= 0, "Attempt to check whether a non-existent node has a dynamic value. Please report!");
|
||||
|
||||
return (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) || (Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) ||
|
||||
(Sexp_nodes[node].subtype == SEXP_ATOM_CONTAINER_DATA);
|
||||
}
|
||||
|
||||
int check_dynamic_value_node_type(int node, bool is_string, bool is_number)
|
||||
{
|
||||
Assertion(is_node_value_dynamic(node),
|
||||
"Attempt to check dynamic value type of a node that isn't a dynamic value. Please report!");
|
||||
|
||||
if (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) {
|
||||
const int var_index = sexp_get_variable_index(node);
|
||||
Assertion(var_index != -1,
|
||||
"Attempt to get index of invalid variable %s. Please report!",
|
||||
Sexp_nodes[node].text);
|
||||
const int var_type = Sexp_variables[var_index].type;
|
||||
if (((var_type & SEXP_VARIABLE_STRING) && !is_string) || ((var_type & SEXP_VARIABLE_NUMBER) && !is_number)) {
|
||||
return SEXP_CHECK_INVALID_VARIABLE_TYPE;
|
||||
}
|
||||
} else if (Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) {
|
||||
if (((Sexp_nodes[node].subtype == SEXP_ATOM_STRING) && !is_string) ||
|
||||
((Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER) && !is_number)) {
|
||||
return SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE;
|
||||
}
|
||||
} else if (Sexp_nodes[node].subtype == SEXP_ATOM_CONTAINER_DATA) {
|
||||
const auto *p_container = get_sexp_container(Sexp_nodes[node].text);
|
||||
Assertion(p_container,
|
||||
"Attempt to check dynamic value node type of data of nonexistent container %s. Please report!",
|
||||
Sexp_nodes[node].text);
|
||||
if (!check_container_data_sexp_arg_type(p_container->type, is_string, is_number)) {
|
||||
return SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE;
|
||||
}
|
||||
} else {
|
||||
UNREACHABLE("Unhandled dynamic value node %s. Please report!", Sexp_nodes[node].text);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sexp_modify_variable(int n)
|
||||
{
|
||||
@@ -32224,6 +32318,21 @@ int get_category(int sexp_id)
|
||||
return category;
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
const char *get_category_name(int category_id)
|
||||
{
|
||||
if (category_id == OP_CATEGORY_CHANGE2)
|
||||
category_id = OP_CATEGORY_CHANGE;
|
||||
|
||||
for (auto &category : op_menu)
|
||||
{
|
||||
if (category.id == category_id)
|
||||
return category.name.c_str();
|
||||
}
|
||||
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
int category_of_subcategory(int subcategory_id)
|
||||
{
|
||||
@@ -33818,7 +33927,8 @@ SCP_vector<sexp_help_struct> Sexp_help = {
|
||||
// Goober5000
|
||||
{ OP_INVALIDATE_ARGUMENT, "Invalidate-argument (Conditional operator)\r\n"
|
||||
"\tRemoves an argument from future consideration as a " SEXP_ARGUMENT_STRING " special data item.\r\n"
|
||||
"\tFor argument-related SEXPs that accept containers as arguments, the retrieved arguments can't be individually invalidated.\r\n\r\n"
|
||||
"\tFor argument-related SEXPs that accept containers as arguments, the retrieved arguments can't be individually invalidated.\r\n"
|
||||
"\tAdditionally, you can't invalidate individual container arguments.\r\n\r\n"
|
||||
"Takes 1 or more arguments...\r\n"
|
||||
"\tAll:\tThe argument to remove from the preceding argument list." },
|
||||
|
||||
@@ -33826,19 +33936,22 @@ SCP_vector<sexp_help_struct> Sexp_help = {
|
||||
{ OP_VALIDATE_ARGUMENT, "Validate-argument (Conditional operator)\r\n"
|
||||
"\tRestores an argument for future consideration as a " SEXP_ARGUMENT_STRING " special data item.\r\n"
|
||||
"\tIf the argument hasn't been previously invalidated, it will do nothing.\r\n"
|
||||
"\tFor argument-related SEXPs that accept containers as arguments, the retrieved arguments can't be individually validated.\r\n\r\n"
|
||||
"\tFor argument-related SEXPs that accept containers as arguments, the retrieved arguments can't be individually validated.\r\n"
|
||||
"\tAdditionally, you can't validate individual container arguments.\r\n\r\n"
|
||||
"Takes 1 or more arguments...\r\n"
|
||||
"\tAll:\tThe argument to restore to the preceding argument list." },
|
||||
|
||||
// Karajorma
|
||||
{ OP_INVALIDATE_ALL_ARGUMENTS, "Invalidate-all-arguments (Conditional operator)\r\n"
|
||||
"\tRemoves all argument from future consideration as " SEXP_ARGUMENT_STRING " special data items.\r\n"
|
||||
"\tFor argument-related SEXPs that accept containers as arguments, container arguments will also be invalidated, but they can't be indvidiually validated.\r\n\r\n"
|
||||
"Takes no arguments." },
|
||||
|
||||
// Karajorma
|
||||
{ OP_VALIDATE_ALL_ARGUMENTS, "Validate-all-arguments (Conditional operator)\r\n"
|
||||
"\tRestores all arguments for future consideration as " SEXP_ARGUMENT_STRING " special data items.\r\n"
|
||||
"\tIf the argument hasn't been previously invalidated, it will do nothing.\r\n"
|
||||
"\tFor argument-related SEXPs that accept containers as arguments, container arguments will also be validated, but they can't be indvidiually invalidated.\r\n\r\n"
|
||||
"Takes no arguments." },
|
||||
|
||||
// Goober5000 - added wing capability
|
||||
|
||||
@@ -1117,6 +1117,7 @@ const char *CTEXT(int n);
|
||||
#define SEXP_CHECK_WRONG_CONTAINER_TYPE -170
|
||||
#define SEXP_CHECK_INVALID_ANIMATION -171
|
||||
#define SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE -172
|
||||
#define SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE -173
|
||||
|
||||
|
||||
#define TRAINING_CONTEXT_SPEED (1<<0)
|
||||
@@ -1296,6 +1297,7 @@ extern int query_operator_argument_type(int op, int argnum);
|
||||
extern void update_sexp_references(const char *old_name, const char *new_name);
|
||||
extern void update_sexp_references(const char *old_name, const char *new_name, int format);
|
||||
extern int query_referenced_in_sexp(int mode, const char *name, int *node);
|
||||
extern void stuff_sexp_text_string(SCP_string &dest, int node, int mode);
|
||||
extern int build_sexp_string(SCP_string &accumulator, int cur_node, int level, int mode);
|
||||
extern int sexp_query_type_match(int opf, int opr);
|
||||
extern bool sexp_recoverable_error(int num);
|
||||
@@ -1357,6 +1359,7 @@ void set_turret_secondary_ammo(ship_subsys *turret, int requested_bank, int requ
|
||||
extern int get_category(int sexp_id);
|
||||
extern int category_of_subcategory(int subcategory_id);
|
||||
extern int get_subcategory(int sexp_id);
|
||||
extern const char *get_category_name(int category_id);
|
||||
|
||||
// Goober5000
|
||||
extern void sexp_music_close();
|
||||
|
||||
@@ -125,8 +125,10 @@ void parse_sexp_table(const char* filename) {
|
||||
luaSexp->parseTable();
|
||||
|
||||
int op = add_dynamic_sexp(std::move(instance), SEXP_GOAL_OPERATOR);
|
||||
luaSexp->registerAIMode(op);
|
||||
luaSexp->maybeRegisterPlayerOrder(op);
|
||||
if (op >= 0) {
|
||||
luaSexp->registerAIMode(op);
|
||||
luaSexp->maybeRegisterPlayerOrder(op);
|
||||
}
|
||||
}
|
||||
required_string("#End");
|
||||
}
|
||||
@@ -177,8 +179,19 @@ int add_dynamic_sexp(std::unique_ptr<DynamicSEXP>&& sexp, int type)
|
||||
new_op.min = sexp->getMinimumArguments();
|
||||
new_op.max = sexp->getMaximumArguments();
|
||||
|
||||
int free_op_index = get_next_free_operator(sexp->getCategory());
|
||||
if (free_op_index >= 256) {
|
||||
Warning(LOCATION, "There are too many SEXPs in the %s category. The SEXP %s will not be added.", get_category_name(sexp->getCategory()), sexp->getName().c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Operators.size() >= FIRST_OP) {
|
||||
Warning(LOCATION, "There are too many total SEXPs. The SEXP %s will not be added.", sexp->getName().c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
// For now, all dynamic SEXPS are only valid in missions
|
||||
new_op.value = get_next_free_operator(sexp->getCategory()) | sexp->getCategory() | OP_NONCAMPAIGN_FLAG;
|
||||
new_op.value = free_op_index | sexp->getCategory() | OP_NONCAMPAIGN_FLAG;
|
||||
new_op.type = type;
|
||||
|
||||
global.operator_const_mapping.insert(std::make_pair(new_op.value, std::move(sexp)));
|
||||
|
||||
@@ -168,7 +168,7 @@ void physics_sim_rot(matrix * orient, physics_info * pi, float sim_time )
|
||||
shock_amplitude = pi->shockwave_shake_amp * shock_fraction_time_left;
|
||||
}
|
||||
}
|
||||
else if (pi->flags & PF_NO_DAMP) {
|
||||
else if (pi->flags & PF_MANEUVER_NO_DAMP) {
|
||||
rotdamp = 0.0f;
|
||||
}
|
||||
else {
|
||||
@@ -305,7 +305,7 @@ void physics_sim_vel(vec3d * position, physics_info * pi, float sim_time, matrix
|
||||
}
|
||||
}
|
||||
// no damping at all
|
||||
else if (pi->flags & PF_NO_DAMP) {
|
||||
else if (pi->flags & PF_MANEUVER_NO_DAMP) {
|
||||
damp = vmd_zero_vector;
|
||||
}
|
||||
// newtonian
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#define PF_GLIDING (1 << 14)
|
||||
#define PF_FORCE_GLIDE (1 << 15)
|
||||
#define PF_NEWTONIAN_DAMP (1 << 16) // SUSHI: Whether or not to use newtonian dampening
|
||||
#define PF_NO_DAMP (1 << 17) // Goober5000 - don't damp velocity changes in physics; used for instantaneous acceleration
|
||||
#define PF_MANEUVER_NO_DAMP (1 << 17) // Goober5000 - don't damp velocity changes in physics; used for instantaneous acceleration
|
||||
|
||||
//information for physics sim for an object
|
||||
typedef struct physics_info {
|
||||
|
||||
+35
-3
@@ -1134,11 +1134,20 @@ void pilotfile::csg_write_variables()
|
||||
|
||||
void pilotfile::csg_read_settings()
|
||||
{
|
||||
clamped_range_warnings.clear();
|
||||
// sound/voice/music
|
||||
if (!Using_in_game_options) {
|
||||
snd_set_effects_volume(cfread_float(cfp));
|
||||
event_music_set_volume(cfread_float(cfp));
|
||||
snd_set_voice_volume(cfread_float(cfp));
|
||||
float temp_volume = cfread_float(cfp);
|
||||
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Effects Volume");
|
||||
snd_set_effects_volume(temp_volume);
|
||||
|
||||
temp_volume = cfread_float(cfp);
|
||||
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Music Volume");
|
||||
event_music_set_volume(temp_volume);
|
||||
|
||||
temp_volume = cfread_float(cfp);
|
||||
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Voice Volume");
|
||||
snd_set_voice_volume(temp_volume);
|
||||
|
||||
Briefing_voice_enabled = cfread_int(cfp) != 0;
|
||||
} else {
|
||||
@@ -1154,13 +1163,20 @@ void pilotfile::csg_read_settings()
|
||||
|
||||
// skill level
|
||||
Game_skill_level = cfread_int(cfp);
|
||||
clamp_value_with_warn(&Game_skill_level, 0, 4, "Game Skill Level");
|
||||
|
||||
// input options
|
||||
if (!Using_in_game_options) {
|
||||
Use_mouse_to_fly = cfread_int(cfp) != 0;
|
||||
Mouse_sensitivity = cfread_int(cfp);
|
||||
clamp_value_with_warn(&Mouse_sensitivity, 0, 9, "Mouse Sensitivity");
|
||||
|
||||
Joy_sensitivity = cfread_int(cfp);
|
||||
clamp_value_with_warn(&Joy_sensitivity, 0, 9, "Joystick Sensitivity");
|
||||
|
||||
Joy_dead_zone_size = cfread_int(cfp);
|
||||
clamp_value_with_warn(&Joy_dead_zone_size, 0, 45, "Joystick Deadzone");
|
||||
|
||||
} else {
|
||||
// The values are set by the in-game menu but we still need to read the int from the file to maintain the correct offset
|
||||
cfread_int(cfp);
|
||||
@@ -1184,28 +1200,44 @@ void pilotfile::csg_read_settings()
|
||||
dummy = cfread_int(cfp);
|
||||
dummy = cfread_int(cfp);
|
||||
}
|
||||
if (!clamped_range_warnings.empty()) {
|
||||
ReleaseWarning(LOCATION, "The following values in the campaign save file were out of bounds and were automatically reset:\n%s\nPlease check your settings!\n", clamped_range_warnings.c_str());
|
||||
clamped_range_warnings.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void pilotfile::csg_write_settings()
|
||||
{
|
||||
startSection(Section::Settings);
|
||||
clamped_range_warnings.clear();
|
||||
|
||||
// sound/voice/music
|
||||
clamp_value_with_warn(&Master_sound_volume, 0.f, 1.f, "Effects Volume");
|
||||
cfwrite_float(Master_sound_volume, cfp);
|
||||
clamp_value_with_warn(&Master_event_music_volume, 0.f, 1.f, "Music Volume");
|
||||
cfwrite_float(Master_event_music_volume, cfp);
|
||||
clamp_value_with_warn(&Master_voice_volume, 0.f, 1.f, "Voice Volume");
|
||||
cfwrite_float(Master_voice_volume, cfp);
|
||||
|
||||
cfwrite_int(Briefing_voice_enabled ? 1 : 0, cfp);
|
||||
|
||||
// skill level
|
||||
clamp_value_with_warn(&Game_skill_level, 0, 4, "Game Skill Level");
|
||||
cfwrite_int(Game_skill_level, cfp);
|
||||
|
||||
// input options
|
||||
cfwrite_int(Use_mouse_to_fly, cfp);
|
||||
clamp_value_with_warn(&Mouse_sensitivity, 0, 9, "Mouse Sensitivity");
|
||||
cfwrite_int(Mouse_sensitivity, cfp);
|
||||
clamp_value_with_warn(&Joy_sensitivity, 0, 9, "Joystick Sensitivity");
|
||||
cfwrite_int(Joy_sensitivity, cfp);
|
||||
clamp_value_with_warn(&Joy_dead_zone_size, 0, 45, "Joystick Deadzone");
|
||||
cfwrite_int(Joy_dead_zone_size, cfp);
|
||||
|
||||
if (!clamped_range_warnings.empty()) {
|
||||
ReleaseWarning(LOCATION, "The following values were out of bounds when saving the campaign file and were automatically reset.\n%s\nThis shouldn't be possible, please contact the FreeSpace 2 Open Source Code Project!\n", clamped_range_warnings.c_str());
|
||||
clamped_range_warnings.clear();
|
||||
}
|
||||
endSection();
|
||||
}
|
||||
|
||||
|
||||
@@ -157,6 +157,17 @@ class pilotfile {
|
||||
// file offset of the size value for the current section (set with startSection())
|
||||
size_t m_size_offset;
|
||||
|
||||
SCP_string clamped_range_warnings = "";
|
||||
|
||||
template <typename T> void clamp_value_with_warn(T* value, T min, T max, const char* name) {
|
||||
if (*value < min) {
|
||||
clamped_range_warnings += SCP_string(name) + ": too low (" + std::to_string(*value) + "). Will reset to minimum (" + std::to_string(min) + ")\n";
|
||||
*value = min;
|
||||
} else if (*value > max) {
|
||||
clamped_range_warnings += SCP_string(name) + ": too high (" + std::to_string(*value) + "). Will reset to maximum (" + std::to_string(max) + ")\n";
|
||||
*value = max;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// PLR specific
|
||||
|
||||
+49
-3
@@ -795,11 +795,20 @@ void pilotfile::plr_write_controls()
|
||||
|
||||
void pilotfile::plr_read_settings()
|
||||
{
|
||||
clamped_range_warnings.clear();
|
||||
// sound/voice/music
|
||||
if (!Using_in_game_options) {
|
||||
snd_set_effects_volume(handler->readFloat("master_sound_volume"));
|
||||
event_music_set_volume(handler->readFloat("master_event_music_volume"));
|
||||
snd_set_voice_volume(handler->readFloat("aster_voice_volume"));
|
||||
float temp_volume = handler->readFloat("master_sound_volume");
|
||||
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Effects Volume");
|
||||
snd_set_effects_volume(temp_volume);
|
||||
|
||||
temp_volume = handler->readFloat("master_event_music_volume");
|
||||
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Music Volume");
|
||||
event_music_set_volume(temp_volume);
|
||||
|
||||
temp_volume = handler->readFloat("aster_voice_volume");
|
||||
clamp_value_with_warn(&temp_volume, 0.f, 1.f, "Voice Volume");
|
||||
snd_set_voice_volume(temp_volume);
|
||||
|
||||
Briefing_voice_enabled = handler->readInt("briefing_voice_enabled") != 0;
|
||||
} else {
|
||||
@@ -814,24 +823,37 @@ void pilotfile::plr_read_settings()
|
||||
|
||||
// skill level
|
||||
Game_skill_level = handler->readInt("game_skill_level");
|
||||
clamp_value_with_warn(&Game_skill_level, 0, 4, "Skill Level");
|
||||
|
||||
// input options
|
||||
if (!Using_in_game_options) {
|
||||
Use_mouse_to_fly = handler->readInt("use_mouse_to_fly") != 0;
|
||||
Mouse_sensitivity = handler->readInt("mouse_sensitivity");
|
||||
clamp_value_with_warn(&Mouse_sensitivity, 0, 9, "Mouse Sensitivity");
|
||||
Joy_sensitivity = handler->readInt("joy_sensitivity");
|
||||
clamp_value_with_warn(&Joy_sensitivity, 0, 9, "Joystick Sensitivity");
|
||||
Joy_dead_zone_size = handler->readInt("joy_dead_zone_size");
|
||||
clamp_value_with_warn(&Joy_dead_zone_size, 0, 45, "Joystick Deadzone");
|
||||
|
||||
// detail
|
||||
Detail.setting = handler->readInt("setting");
|
||||
clamp_value_with_warn(&Detail.setting, -1, NUM_DEFAULT_DETAIL_LEVELS - 1, "Detail Level Preset");
|
||||
Detail.nebula_detail = handler->readInt("nebula_detail");
|
||||
clamp_value_with_warn(&Detail.nebula_detail, 0, MAX_DETAIL_LEVEL, "Nebula Detail");
|
||||
Detail.detail_distance = handler->readInt("detail_distance");
|
||||
clamp_value_with_warn(&Detail.detail_distance, 0, MAX_DETAIL_LEVEL, "Model Detail");
|
||||
Detail.hardware_textures = handler->readInt("hardware_textures");
|
||||
clamp_value_with_warn(&Detail.hardware_textures, 0, MAX_DETAIL_LEVEL, "3D Hardware Textures");
|
||||
Detail.num_small_debris = handler->readInt("num_small_debris");
|
||||
clamp_value_with_warn(&Detail.num_small_debris, 0, MAX_DETAIL_LEVEL, "Impact Effects");
|
||||
Detail.num_particles = handler->readInt("num_particles");
|
||||
clamp_value_with_warn(&Detail.num_particles, 0, MAX_DETAIL_LEVEL, "Particles");
|
||||
Detail.num_stars = handler->readInt("num_stars");
|
||||
clamp_value_with_warn(&Detail.num_stars, 0, MAX_DETAIL_LEVEL, "Stars");
|
||||
Detail.shield_effects = handler->readInt("shield_effects");
|
||||
clamp_value_with_warn(&Detail.shield_effects, 0, MAX_DETAIL_LEVEL, "Shield Hit Effects");
|
||||
Detail.lighting = handler->readInt("lighting");
|
||||
clamp_value_with_warn(&Detail.lighting, 0, MAX_DETAIL_LEVEL, "Lighting");
|
||||
Detail.targetview_model = handler->readInt("targetview_model");
|
||||
Detail.planets_suns = handler->readInt("planets_suns");
|
||||
Detail.weapon_extras = handler->readInt("weapon_extras");
|
||||
@@ -856,6 +878,10 @@ void pilotfile::plr_read_settings()
|
||||
handler->readInt("planets_suns");
|
||||
handler->readInt("weapon_extras");
|
||||
}
|
||||
if (!clamped_range_warnings.empty()) {
|
||||
ReleaseWarning(LOCATION, "The following values in the pilot file were out of bounds and were automatically reset:\n%s\nPlease check your settings!\n", clamped_range_warnings.c_str());
|
||||
clamped_range_warnings.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void pilotfile::plr_write_settings()
|
||||
@@ -863,35 +889,55 @@ void pilotfile::plr_write_settings()
|
||||
handler->startSectionWrite(Section::Settings);
|
||||
|
||||
// sound/voice/music
|
||||
clamp_value_with_warn(&Master_sound_volume, 0.f, 1.f, "Effects Volume");
|
||||
handler->writeFloat("master_sound_volume", Master_sound_volume);
|
||||
clamp_value_with_warn(&Master_event_music_volume, 0.f, 1.f, "Music Volume");
|
||||
handler->writeFloat("master_event_music_volume", Master_event_music_volume);
|
||||
clamp_value_with_warn(&Master_voice_volume, 0.f, 1.f, "Voice Volume");
|
||||
handler->writeFloat("aster_voice_volume", Master_voice_volume);
|
||||
|
||||
handler->writeInt("briefing_voice_enabled", Briefing_voice_enabled ? 1 : 0);
|
||||
|
||||
// skill level
|
||||
clamp_value_with_warn(&Game_skill_level, 0, 4, "Skill Level");
|
||||
handler->writeInt("game_skill_level", Game_skill_level);
|
||||
|
||||
// input options
|
||||
handler->writeInt("use_mouse_to_fly", Use_mouse_to_fly);
|
||||
clamp_value_with_warn(&Mouse_sensitivity, 0, 9, "Mouse Sensitivity");
|
||||
handler->writeInt("mouse_sensitivity", Mouse_sensitivity);
|
||||
clamp_value_with_warn(&Joy_sensitivity, 0, 9, "Joystick Sensitivity");
|
||||
handler->writeInt("joy_sensitivity", Joy_sensitivity);
|
||||
clamp_value_with_warn(&Joy_dead_zone_size, 0, 45, "Joystick Deadzone");
|
||||
handler->writeInt("joy_dead_zone_size", Joy_dead_zone_size);
|
||||
|
||||
// detail
|
||||
clamp_value_with_warn(&Detail.setting, -1, NUM_DEFAULT_DETAIL_LEVELS - 1, "Detail Level Preset");
|
||||
handler->writeInt("setting", Detail.setting);
|
||||
clamp_value_with_warn(&Detail.nebula_detail, 0, MAX_DETAIL_LEVEL, "Nebula Detail");
|
||||
handler->writeInt("nebula_detail", Detail.nebula_detail);
|
||||
clamp_value_with_warn(&Detail.detail_distance, 0, MAX_DETAIL_LEVEL, "Model Detail");
|
||||
handler->writeInt("detail_distance", Detail.detail_distance);
|
||||
clamp_value_with_warn(&Detail.hardware_textures, 0, MAX_DETAIL_LEVEL, "3D Hardware Textures");
|
||||
handler->writeInt("hardware_textures", Detail.hardware_textures);
|
||||
clamp_value_with_warn(&Detail.num_small_debris, 0, MAX_DETAIL_LEVEL, "Impact Effects");
|
||||
handler->writeInt("num_small_debris", Detail.num_small_debris);
|
||||
clamp_value_with_warn(&Detail.num_particles, 0, MAX_DETAIL_LEVEL, "Particles");
|
||||
handler->writeInt("num_particles", Detail.num_particles);
|
||||
clamp_value_with_warn(&Detail.num_stars, 0, MAX_DETAIL_LEVEL, "Stars");
|
||||
handler->writeInt("num_stars", Detail.num_stars);
|
||||
clamp_value_with_warn(&Detail.shield_effects, 0, MAX_DETAIL_LEVEL, "Shield Hit Effects");
|
||||
handler->writeInt("shield_effects", Detail.shield_effects);
|
||||
clamp_value_with_warn(&Detail.lighting, 0, MAX_DETAIL_LEVEL, "Lighting");
|
||||
handler->writeInt("lighting", Detail.lighting);
|
||||
handler->writeInt("targetview_model", Detail.targetview_model);
|
||||
handler->writeInt("planets_suns", Detail.planets_suns);
|
||||
handler->writeInt("weapon_extras", Detail.weapon_extras);
|
||||
|
||||
if (!clamped_range_warnings.empty()) {
|
||||
ReleaseWarning(LOCATION, "The following values were out of bounds when saving the Pilot file and were automatically reset.\n%s\nThis shouldn't be possible, please contact the FreeSpace 2 Open Source Code Project!\n", clamped_range_warnings.c_str());
|
||||
clamped_range_warnings.clear();
|
||||
}
|
||||
handler->endSectionWrite();
|
||||
}
|
||||
|
||||
|
||||
@@ -510,16 +510,6 @@ void do_view_external()
|
||||
Viewer_external_info.angles.h = PI2 + Viewer_external_info.angles.h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Separate out the reading of thrust keys, so we can call this from external view as well as from normal view
|
||||
*/
|
||||
void do_thrust_keys(control_info *ci)
|
||||
{
|
||||
ci->forward = check_control_timef(FORWARD_THRUST) - check_control_timef(REVERSE_THRUST);
|
||||
ci->sideways = (check_control_timef(RIGHT_SLIDE_THRUST) - check_control_timef(LEFT_SLIDE_THRUST));//for slideing-Bobboau
|
||||
ci->vertical = (check_control_timef(UP_SLIDE_THRUST) - check_control_timef(DOWN_SLIDE_THRUST));//for slideing-Bobboau
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by single and multiplayer modes to reset information inside of control info structure
|
||||
*/
|
||||
@@ -552,12 +542,12 @@ void read_keyboard_controls( control_info * ci, float frame_time, physics_info *
|
||||
|
||||
// Camera & View controls
|
||||
if ( Viewer_mode & VM_EXTERNAL ) {
|
||||
// External mode
|
||||
control_used(VIEW_EXTERNAL);
|
||||
|
||||
do_view_external();
|
||||
do_thrust_keys(ci);
|
||||
|
||||
} else if ( Viewer_mode & VM_CHASE ) {
|
||||
// Chase mode
|
||||
do_view_chase();
|
||||
|
||||
} else {
|
||||
@@ -570,7 +560,6 @@ void read_keyboard_controls( control_info * ci, float frame_time, physics_info *
|
||||
// Ship controls
|
||||
if (Viewer_mode & VM_CAMERA_LOCKED) {
|
||||
// From keyboard...
|
||||
do_thrust_keys(ci);
|
||||
if ( check_control(BANK_WHEN_PRESSED) ) {
|
||||
ci->bank = check_control_timef(BANK_LEFT) + check_control_timef(YAW_LEFT) - check_control_timef(YAW_RIGHT) - check_control_timef(BANK_RIGHT);
|
||||
ci->heading = 0.0f;
|
||||
@@ -609,6 +598,12 @@ void read_keyboard_controls( control_info * ci, float frame_time, physics_info *
|
||||
}
|
||||
|
||||
if (!(Game_mode & GM_DEAD)) {
|
||||
// Thrust controls
|
||||
ci->forward = check_control_timef(FORWARD_THRUST) - check_control_timef(REVERSE_THRUST);
|
||||
ci->sideways = (check_control_timef(RIGHT_SLIDE_THRUST) - check_control_timef(LEFT_SLIDE_THRUST)); // for slideing-Bobboau
|
||||
ci->vertical = (check_control_timef(UP_SLIDE_THRUST) - check_control_timef(DOWN_SLIDE_THRUST)); // for slideing-Bobboau
|
||||
|
||||
// Throttle controls
|
||||
if ( button_info_query(&Player->bi, ONE_THIRD_THROTTLE) ) {
|
||||
control_used(ONE_THIRD_THROTTLE);
|
||||
player_clear_speed_matching();
|
||||
@@ -1373,7 +1368,7 @@ void player_level_init()
|
||||
Player->cargo_scan_loop = sound_handle::invalid();
|
||||
Player->cargo_inspect_time = 0; // time that current target's cargo has been inspected for
|
||||
|
||||
Player->target_is_dying = -1; // The player target is dying, set to -1 if no target
|
||||
Player->target_is_dying = -1; // Whether the player target is dying, -1 if no target
|
||||
Player->current_target_sx = -1; // Screen x-pos of current target (or subsystem if applicable)
|
||||
Player->current_target_sy = -1; // Screen y-pos of current target (or subsystem if applicable)
|
||||
Player->target_in_lock_cone = -1; // Is the current target in secondary weapon lock cone?
|
||||
|
||||
@@ -4,8 +4,17 @@
|
||||
#pragma push_macro("Assert")
|
||||
#undef Assert
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
|
||||
#include <Rocket/Core/XMLNodeHandler.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#pragma pop_macro("Assert")
|
||||
|
||||
namespace scpui {
|
||||
|
||||
@@ -6,9 +6,18 @@
|
||||
#pragma push_macro("Assert")
|
||||
#undef Assert
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
|
||||
#include <Rocket/Core.h>
|
||||
#include <Rocket/Core/Plugin.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#pragma pop_macro("Assert")
|
||||
|
||||
namespace scpui {
|
||||
|
||||
@@ -7,12 +7,21 @@
|
||||
#pragma push_macro("Assert")
|
||||
#undef Assert
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
|
||||
#include <Rocket/Core.h>
|
||||
#include <Rocket/Core/ElementDocument.h>
|
||||
#include <Rocket/Core/GeometryUtilities.h>
|
||||
#include <Rocket/Core/String.h>
|
||||
#include <scpui/RocketRenderingInterface.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#pragma pop_macro("Assert")
|
||||
|
||||
namespace scpui {
|
||||
|
||||
@@ -4,10 +4,19 @@
|
||||
#pragma push_macro("Assert")
|
||||
#undef Assert
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
|
||||
#include <Rocket/Core/Element.h>
|
||||
#include <Rocket/Core/Geometry.h>
|
||||
#include <Rocket/Core/Texture.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#pragma pop_macro("Assert")
|
||||
|
||||
namespace scpui {
|
||||
|
||||
@@ -10,8 +10,17 @@
|
||||
#pragma push_macro("Assert")
|
||||
#undef Assert
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
|
||||
#include <Rocket/Core.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#pragma pop_macro("Assert")
|
||||
|
||||
namespace scpui {
|
||||
|
||||
@@ -4,10 +4,19 @@
|
||||
#pragma push_macro("Assert")
|
||||
#undef Assert
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
|
||||
#include <Rocket/Core/Element.h>
|
||||
#include <Rocket/Core/Geometry.h>
|
||||
#include <Rocket/Core/Texture.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#pragma pop_macro("Assert")
|
||||
|
||||
namespace scpui {
|
||||
|
||||
@@ -92,7 +92,7 @@ LuaPromise::LuaPromise(const std::shared_ptr<resolve_context>& resolveContext) :
|
||||
{
|
||||
m_state->state = State::Pending;
|
||||
|
||||
// This executes promises eagerly since registering the callback kicks of the async operation
|
||||
// This executes promises eagerly since registering the callback kicks off the async operation
|
||||
m_state->registerResolveCallback(resolveContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ ADE_FUNC(promise,
|
||||
"function(function(any resolveVal) => void resolve, function(any errorVal) => void reject) => void body",
|
||||
"Creates a promise that resolves when the resolve function of the callback is called or errors if the reject "
|
||||
"function is called. The function will be called "
|
||||
"on it's own.",
|
||||
"on its own.",
|
||||
"promise",
|
||||
"The promise or nil on error")
|
||||
{
|
||||
|
||||
@@ -1729,7 +1729,11 @@ ADE_FUNC(waitAsync,
|
||||
|
||||
class time_resolve_context : public resolve_context, public std::enable_shared_from_this<time_resolve_context> {
|
||||
public:
|
||||
time_resolve_context(int timestamp) : m_timestamp(timestamp) {}
|
||||
time_resolve_context(int timestamp) : m_timestamp(timestamp) {
|
||||
static int unique_id_counter = 0;
|
||||
m_unique_id = unique_id_counter++;
|
||||
nprintf(("scripting", "waitAsync: Creating asynchronous context %d.\n", m_unique_id));
|
||||
}
|
||||
void setResolver(Resolver resolver) override
|
||||
{
|
||||
// Keep checking the time until the timestamp is elapsed
|
||||
@@ -1737,11 +1741,13 @@ ADE_FUNC(waitAsync,
|
||||
auto cb = [this, self, resolver](
|
||||
executor::IExecutionContext::State contextState) {
|
||||
if (contextState == executor::IExecutionContext::State::Invalid) {
|
||||
mprintf(("waitAsync: Context is invalid, possibly due to a game state change (current state is %s). Aborting asynchronous context %d.\n", GS_state_text[gameseq_get_state()], m_unique_id));
|
||||
resolver(true, luacpp::LuaValueList());
|
||||
return executor::Executor::CallbackResult::Done;
|
||||
}
|
||||
|
||||
if (timestamp_elapsed(m_timestamp)) {
|
||||
nprintf(("scripting", "waitAsync: Timestamp has elapsed for asynchronous context %d.\n", m_unique_id));
|
||||
resolver(false, luacpp::LuaValueList());
|
||||
return executor::Executor::CallbackResult::Done;
|
||||
}
|
||||
@@ -1756,6 +1762,7 @@ ADE_FUNC(waitAsync,
|
||||
|
||||
private:
|
||||
int m_timestamp = -1;
|
||||
int m_unique_id = -1;
|
||||
};
|
||||
return ade_set_args(L,
|
||||
"o",
|
||||
|
||||
@@ -10,10 +10,15 @@ namespace api {
|
||||
|
||||
ADE_LIB(l_Time, "Time", "time", "Real-Time library");
|
||||
|
||||
ADE_FUNC(getCurrentTime, l_Time, nullptr, "Gets the current real-time timestamp.", "timestamp", "The current time")
|
||||
ADE_FUNC(getCurrentTime, l_Time, nullptr, "Gets the current real-time timestamp, i.e. the actual elapsed time, regardless of whether the game has changed time compression or been paused.", "timestamp", "The current time")
|
||||
{
|
||||
return ade_set_args(L, "o", l_Timestamp.Set(timer_get_nanoseconds()));
|
||||
}
|
||||
|
||||
ADE_FUNC(getCurrentMissionTime, l_Time, nullptr, "Gets the current mission-time timestamp, which can be affected by time compression and paused.", "timestamp", "The current mission time")
|
||||
{
|
||||
return ade_set_args(L, "o", l_Timestamp.Set(timestamp_get_mission_time_in_microseconds() * NANOSECONDS_PER_MICROSECOND));
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
} // namespace scripting
|
||||
@@ -119,32 +119,33 @@ ADE_FUNC(isValid, l_ModelInstance, nullptr, "True if valid, false or nil if not"
|
||||
return ade_set_args(L, "b", mih->IsValid());
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(Orientation, l_SubmodelInstance, "orientation", "Gets or sets the submodel instance orientation (world orientation)", "orientation", "Orientation, or identity orientation if handle is not valid")
|
||||
ADE_VIRTVAR(Orientation, l_SubmodelInstance, "orientation", "Gets or sets the submodel instance orientation", "orientation", "Orientation, or identity orientation if handle is not valid")
|
||||
{
|
||||
submodelinstance_h *smih;
|
||||
matrix_h *mh = nullptr;
|
||||
if (!ade_get_args(L, "o|o", l_SubmodelInstance.GetPtr(&smih), l_Matrix.GetPtr(&mh)))
|
||||
return ade_set_error(L, "o", l_Matrix.Set(matrix_h(&vmd_identity_matrix)));
|
||||
return ade_set_error(L, "o", l_Matrix.Set(matrix_h()));
|
||||
|
||||
if (!smih->IsValid())
|
||||
return ade_set_error(L, "o", l_Matrix.Set(matrix_h(&vmd_identity_matrix)));
|
||||
return ade_set_error(L, "o", l_Matrix.Set(matrix_h()));
|
||||
|
||||
auto smi = smih->Get();
|
||||
if (smi == nullptr)
|
||||
return ade_set_error(L, "o", l_Matrix.Set(matrix_h()));
|
||||
|
||||
if (ADE_SETTING_VAR && mh != nullptr)
|
||||
{
|
||||
auto sm = smih->GetSubmodel();
|
||||
auto smi = smih->Get();
|
||||
|
||||
smi->canonical_prev_orient = smi->canonical_orient;
|
||||
smi->canonical_orient = *mh->GetMatrix();
|
||||
|
||||
float angle = 0.0f;
|
||||
vm_closest_angle_to_matrix(&smi->canonical_orient, &sm->rotation_axis, &angle);
|
||||
vm_closest_angle_to_matrix(&smi->canonical_orient, &smih->GetSubmodel()->rotation_axis, &angle);
|
||||
|
||||
smi->cur_angle = angle;
|
||||
smi->turret_idle_angle = angle;
|
||||
}
|
||||
|
||||
return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&smih->Get()->canonical_orient)));
|
||||
return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&smi->canonical_orient)));
|
||||
}
|
||||
|
||||
ADE_FUNC(findWorldPoint, l_SubmodelInstance, "vector", "Calculates the world coordinates of a point in a submodel's frame of reference", "vector", "Point, or empty vector if handle is not valid")
|
||||
|
||||
@@ -63,7 +63,7 @@ ADE_VIRTVAR(FPS, l_MoviePlayer, "number", "Determines the frames per second of t
|
||||
LuaError(L, "Tried to modify read-only member!");
|
||||
}
|
||||
|
||||
return ade_set_args(L, "f", (int)ph->player()->getMovieProperties().fps);
|
||||
return ade_set_args(L, "f", ph->player()->getMovieProperties().fps);
|
||||
}
|
||||
|
||||
ADE_FUNC(update, l_MoviePlayer, "timespan step_time",
|
||||
|
||||
@@ -101,11 +101,11 @@ ADE_VIRTVAR(AWACSRadius, l_Subsystem, "number", "Subsystem AWACS radius", "numbe
|
||||
return ade_set_args(L, "f", sso->ss->awacs_radius);
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(Orientation, l_Subsystem, "orientation", "Orientation of subobject or turret base", "orientation", "Subsystem orientation, or null orientation if handle is invalid")
|
||||
ADE_VIRTVAR(Orientation, l_Subsystem, "orientation", "Orientation of subobject or turret base", "orientation", "Subsystem orientation, or identity orientation if handle is invalid")
|
||||
{
|
||||
ship_subsys_h *sso;
|
||||
matrix_h *mh = nullptr;
|
||||
if(!ade_get_args(L, "o|o", l_Subsystem.GetPtr(&sso), l_Matrix.GetPtr(&mh)))
|
||||
if (!ade_get_args(L, "o|o", l_Subsystem.GetPtr(&sso), l_Matrix.GetPtr(&mh)))
|
||||
return ade_set_error(L, "o", l_Matrix.Set(matrix_h()));
|
||||
|
||||
if (!sso->isSubsystemValid())
|
||||
@@ -115,10 +115,19 @@ ADE_VIRTVAR(Orientation, l_Subsystem, "orientation", "Orientation of subobject o
|
||||
if (smi == nullptr)
|
||||
return ade_set_error(L, "o", l_Matrix.Set(matrix_h()));
|
||||
|
||||
if(ADE_SETTING_VAR && mh)
|
||||
if (ADE_SETTING_VAR && mh != nullptr)
|
||||
{
|
||||
auto pm = model_get(sso->ss->system_info->model_num);
|
||||
auto sm = &pm->submodel[sso->ss->system_info->subobj_num];
|
||||
|
||||
smi->canonical_prev_orient = smi->canonical_orient;
|
||||
smi->canonical_orient = *mh->GetMatrix();
|
||||
|
||||
float angle = 0.0f;
|
||||
vm_closest_angle_to_matrix(&smi->canonical_orient, &sm->rotation_axis, &angle);
|
||||
|
||||
smi->cur_angle = angle;
|
||||
smi->turret_idle_angle = angle;
|
||||
}
|
||||
|
||||
return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&smi->canonical_orient)));
|
||||
@@ -656,7 +665,7 @@ ADE_FUNC(rotateTurret, l_Subsystem, "vector Pos, boolean reset=false", "Rotates
|
||||
return ADE_RETURN_FALSE;
|
||||
}
|
||||
|
||||
ADE_FUNC(getTurretHeading, l_Subsystem, NULL, "Returns the turrets forward vector", "vector", "Returns a normalized version of the forward vector or null vector on error")
|
||||
ADE_FUNC(getTurretHeading, l_Subsystem, NULL, "Returns the turrets forward vector", "vector", "Returns a normalized version of the forward vector in the ship's reference frame or null vector on error")
|
||||
{
|
||||
ship_subsys_h *sso;
|
||||
if(!ade_get_args(L, "o", l_Subsystem.GetPtr(&sso)))
|
||||
|
||||
@@ -31,7 +31,7 @@ ADE_FUNC(getSeconds, l_TimeSpan, nullptr, "Gets the value of this timestamp in s
|
||||
}
|
||||
|
||||
// Timestamps and spans are stored in nanoseconds
|
||||
return ade_set_args(L, "f", (float)value / 1000000000.f);
|
||||
return ade_set_args(L, "f", (float)((long double)value / (long double)NANOSECONDS_PER_SECOND));
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -172,12 +172,15 @@ LuaValueList LuaFunction::call(lua_State* L, const LuaValueList& args) const {
|
||||
stackTop = lua_gettop(L);
|
||||
}
|
||||
|
||||
if(!lua_checkstack(L, (int)args.size() + 1))
|
||||
throw LuaException("Lua Stack Overflow!");
|
||||
|
||||
// Push the function onto the stack
|
||||
this->pushValue(L);
|
||||
this->pushValue(L, true);
|
||||
|
||||
// Push the arguments onto the stack
|
||||
for (const auto& arg : args) {
|
||||
arg.pushValue(L);
|
||||
arg.pushValue(L, true);
|
||||
}
|
||||
|
||||
// actually call the function now!
|
||||
|
||||
@@ -80,10 +80,14 @@ bool UniqueLuaReference::isValid() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void UniqueLuaReference::pushValue(lua_State* thread) const
|
||||
void UniqueLuaReference::pushValue(lua_State* thread, bool manualStackAllocation) const
|
||||
{
|
||||
Assertion(thread != nullptr, "Valid thread state must be specified!");
|
||||
|
||||
if (!manualStackAllocation) {
|
||||
lua_checkstack(thread, 1);
|
||||
}
|
||||
|
||||
if (this->isValid()) {
|
||||
lua_rawgeti(thread, LUA_REGISTRYINDEX, this->getReference());
|
||||
}
|
||||
|
||||
@@ -100,8 +100,9 @@ class UniqueLuaReference {
|
||||
/**
|
||||
* @brief Pushes the referenced value onto the stack.
|
||||
* @param thread A specific thread state to push the value to. nullptr for the default state of this reference
|
||||
* @param manualStackAllocation Set to true if you manually allocate sufficient stack size before calling this function. Keep false unless you know what you are doing.
|
||||
*/
|
||||
void pushValue(lua_State* thread) const;
|
||||
void pushValue(lua_State* thread, bool manualStackAllocation = false) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -88,10 +88,10 @@ bool LuaValue::isValid() const {
|
||||
return _reference && _reference->isValid();
|
||||
}
|
||||
|
||||
bool LuaValue::pushValue(lua_State* thread) const
|
||||
bool LuaValue::pushValue(lua_State* thread, bool manualStackAllocation) const
|
||||
{
|
||||
if (this->_reference->isValid()) {
|
||||
this->_reference->pushValue(thread);
|
||||
this->_reference->pushValue(thread, manualStackAllocation);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
||||
@@ -185,8 +185,9 @@ class LuaValue {
|
||||
* @brief Pushes this lua value onto the stack.
|
||||
* @param thread The thread stack onto which this value should be pushed. May be nullptr for the default state of
|
||||
* this value
|
||||
* @param manualStackAllocation Set to true if you manually allocate sufficient stack size before calling this function. Keep false unless you know what you are doing.
|
||||
*/
|
||||
bool pushValue(lua_State* thread) const;
|
||||
bool pushValue(lua_State* thread, bool manualStackAllocation = false) const;
|
||||
|
||||
lua_State* getLuaState() const;
|
||||
|
||||
|
||||
@@ -337,14 +337,20 @@ bool script_state::EvalStringWithReturn(const char* string, const char* format,
|
||||
scripting::ade_get_args(LuaState, format, rtn);
|
||||
}
|
||||
} catch (const LuaException&) {
|
||||
lua_pop(LuaState, 1);
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch (const LuaException& e) {
|
||||
LuaError(GetLuaSession(), "%s", e.what());
|
||||
|
||||
lua_pop(LuaState, 1);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_pop(LuaState, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -4616,7 +4616,7 @@ static void parse_ship_values(ship_info* sip, const bool is_template, const bool
|
||||
}
|
||||
for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
|
||||
//find the index number of the current ship info type
|
||||
if (it->name == sip->name) {
|
||||
if (&(*it) == sip) {
|
||||
Ai_tp_list[i].ship_class.push_back((int)std::distance(Ship_info.cbegin(), it));
|
||||
break;
|
||||
}
|
||||
@@ -15843,7 +15843,7 @@ SCP_string ship_return_orders(ship* sp)
|
||||
outbuf += target_name;
|
||||
outbuf += XSTR(" wing", 494);
|
||||
} else {
|
||||
outbuf += XSTR("no orders", 495);
|
||||
outbuf = XSTR("no orders", 495);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -15852,7 +15852,7 @@ SCP_string ship_return_orders(ship* sp)
|
||||
outbuf += XSTR("any ", -1);
|
||||
outbuf += target_name;
|
||||
} else {
|
||||
outbuf += XSTR("no orders", 495);
|
||||
outbuf = XSTR("no orders", 495);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -15868,7 +15868,7 @@ SCP_string ship_return_orders(ship* sp)
|
||||
if (aigp->target_name) {
|
||||
outbuf += target_name;
|
||||
} else {
|
||||
outbuf += XSTR("no orders", 495);
|
||||
outbuf = XSTR("no orders", 495);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -15879,7 +15879,7 @@ SCP_string ship_return_orders(ship* sp)
|
||||
hud_targetbox_truncate_subsys_name(subsys_name);
|
||||
sprintf(outbuf, XSTR("atk %s %s", 496), target_name, subsys_name);
|
||||
} else {
|
||||
outbuf += XSTR("no orders", 495);
|
||||
outbuf = XSTR("no orders", 495);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -20263,4 +20263,4 @@ int ship_check_visibility(const ship* viewed, ship* viewer)
|
||||
}
|
||||
|
||||
return ship_is_visible;
|
||||
}
|
||||
}
|
||||
@@ -423,7 +423,7 @@ int campaign_tree_wnd::internal_error(const char *msg, ...)
|
||||
|
||||
int campaign_tree_wnd::fred_check_sexp(int sexp, int type, char *msg, ...)
|
||||
{
|
||||
SCP_string buf, sexp_buf, error_buf;
|
||||
SCP_string buf, sexp_buf, error_buf, bad_node_str;
|
||||
int err = 0, z, faulty_node;
|
||||
va_list args;
|
||||
|
||||
@@ -440,7 +440,13 @@ int campaign_tree_wnd::fred_check_sexp(int sexp, int type, char *msg, ...)
|
||||
|
||||
convert_sexp_to_string(sexp_buf, sexp, SEXP_ERROR_CHECK_MODE);
|
||||
truncate_message_lines(sexp_buf, 30);
|
||||
sprintf(error_buf, "Error in %s: %s\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)", buf.c_str(), sexp_error_message(z), sexp_buf.c_str(), Sexp_nodes[faulty_node].text);
|
||||
|
||||
stuff_sexp_text_string(bad_node_str, faulty_node, SEXP_ERROR_CHECK_MODE);
|
||||
if (!bad_node_str.empty()) { // the previous function adds a space at the end
|
||||
bad_node_str.pop_back();
|
||||
}
|
||||
|
||||
sprintf(error_buf, "Error in %s: %s\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)", buf.c_str(), sexp_error_message(z), sexp_buf.c_str(), bad_node_str.c_str());
|
||||
|
||||
if (z < 0 && z > -100)
|
||||
err = 1;
|
||||
|
||||
+1
-1
@@ -445,7 +445,7 @@ IDR_MENU_EDIT_SEXP_TREE MENU
|
||||
BEGIN
|
||||
POPUP "Edit sexp tree"
|
||||
BEGIN
|
||||
MENUITEM "&Delete Item\tDelete", ID_DELETE
|
||||
MENUITEM "&Delete Item", ID_DELETE
|
||||
MENUITEM "&Edit Data\tSpace Bar", ID_EDIT_TEXT
|
||||
MENUITEM "Expand All", ID_EXPAND_ALL
|
||||
MENUITEM SEPARATOR
|
||||
|
||||
+8
-2
@@ -3376,7 +3376,7 @@ int CFREDView::internal_error(const char *msg, ...)
|
||||
|
||||
int CFREDView::fred_check_sexp(int sexp, int type, const char *msg, ...)
|
||||
{
|
||||
SCP_string buf, sexp_buf, error_buf;
|
||||
SCP_string buf, sexp_buf, error_buf, bad_node_str;
|
||||
int err = 0, z, faulty_node;
|
||||
va_list args;
|
||||
|
||||
@@ -3393,7 +3393,13 @@ int CFREDView::fred_check_sexp(int sexp, int type, const char *msg, ...)
|
||||
|
||||
convert_sexp_to_string(sexp_buf, sexp, SEXP_ERROR_CHECK_MODE);
|
||||
truncate_message_lines(sexp_buf, 30);
|
||||
sprintf(error_buf, "Error in %s: %s\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)", buf.c_str(), sexp_error_message(z), sexp_buf.c_str(), Sexp_nodes[faulty_node].text);
|
||||
|
||||
stuff_sexp_text_string(bad_node_str, faulty_node, SEXP_ERROR_CHECK_MODE);
|
||||
if (!bad_node_str.empty()) { // the previous function adds a space at the end
|
||||
bad_node_str.pop_back();
|
||||
}
|
||||
|
||||
sprintf(error_buf, "Error in %s: %s\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)", buf.c_str(), sexp_error_message(z), sexp_buf.c_str(), bad_node_str.c_str());
|
||||
|
||||
if (z < 0 && z > -100)
|
||||
err = 1;
|
||||
|
||||
+58
-19
@@ -21,7 +21,8 @@
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
const float input_threshold = 0.01f; // smallest increment of input box
|
||||
constexpr auto INPUT_THRESHOLD = 0.01f; // smallest increment of input box
|
||||
constexpr auto INPUT_FORMAT = "%.01f";
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// orient_editor dialog
|
||||
@@ -45,15 +46,15 @@ orient_editor::orient_editor(CWnd* pParent /*=NULL*/)
|
||||
|
||||
Assert(query_valid_object());
|
||||
auto pos = &Objects[cur_object_index].pos;
|
||||
m_position_x.Format("%.01f", pos->xyz.x);
|
||||
m_position_y.Format("%.01f", pos->xyz.y);
|
||||
m_position_z.Format("%.01f", pos->xyz.z);
|
||||
m_position_x.Format(INPUT_FORMAT, pos->xyz.x);
|
||||
m_position_y.Format(INPUT_FORMAT, pos->xyz.y);
|
||||
m_position_z.Format(INPUT_FORMAT, pos->xyz.z);
|
||||
|
||||
angles ang;
|
||||
vm_extract_angles_matrix(&ang, &Objects[cur_object_index].orient);
|
||||
m_orientation_p.Format("%.01f", to_degrees(ang.p));
|
||||
m_orientation_b.Format("%.01f", to_degrees(ang.b));
|
||||
m_orientation_h.Format("%.01f", to_degrees(ang.h));
|
||||
m_orientation_p.Format(INPUT_FORMAT, to_degrees(ang.p));
|
||||
m_orientation_b.Format(INPUT_FORMAT, to_degrees(ang.b));
|
||||
m_orientation_h.Format(INPUT_FORMAT, to_degrees(ang.h));
|
||||
}
|
||||
|
||||
void orient_editor::DoDataExchange(CDataExchange* pDX)
|
||||
@@ -182,29 +183,47 @@ BOOL orient_editor::OnInitDialog()
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool orient_editor::close(float val, const CString &str)
|
||||
/**
|
||||
* Checks whether the position value is close enough to the input string value that we can assume the input has not changed.
|
||||
*/
|
||||
bool orient_editor::is_close(float val, const CString &input_str) const
|
||||
{
|
||||
float diff = val - convert(str);
|
||||
return diff > -input_threshold && diff < input_threshold;
|
||||
val = perform_input_rounding(val);
|
||||
float input_val = convert(input_str);
|
||||
|
||||
float diff = val - input_val;
|
||||
return abs(diff) < INPUT_THRESHOLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the angle value is close enough to the input string value that we can assume the input has not changed.
|
||||
*/
|
||||
bool orient_editor::is_angle_close(float rad, const CString &input_str) const
|
||||
{
|
||||
float deg = perform_input_rounding(to_degrees(rad));
|
||||
float input_deg = normalize_degrees(convert(input_str));
|
||||
|
||||
float diff = deg - input_deg;
|
||||
return abs(diff) < INPUT_THRESHOLD;
|
||||
}
|
||||
|
||||
bool orient_editor::query_modified()
|
||||
{
|
||||
if (!close(Objects[cur_object_index].pos.xyz.x, m_position_x))
|
||||
if (!is_close(Objects[cur_object_index].pos.xyz.x, m_position_x))
|
||||
return true;
|
||||
if (!close(Objects[cur_object_index].pos.xyz.y, m_position_y))
|
||||
if (!is_close(Objects[cur_object_index].pos.xyz.y, m_position_y))
|
||||
return true;
|
||||
if (!close(Objects[cur_object_index].pos.xyz.z, m_position_z))
|
||||
if (!is_close(Objects[cur_object_index].pos.xyz.z, m_position_z))
|
||||
return true;
|
||||
|
||||
angles ang;
|
||||
vm_extract_angles_matrix(&ang, &Objects[cur_object_index].orient);
|
||||
|
||||
if (!close(to_degrees(ang.p), m_orientation_p))
|
||||
if (!is_angle_close(ang.p, m_orientation_p))
|
||||
return true;
|
||||
if (!close(to_degrees(ang.b), m_orientation_b))
|
||||
if (!is_angle_close(ang.b, m_orientation_b))
|
||||
return true;
|
||||
if (!close(to_degrees(ang.h), m_orientation_h))
|
||||
if (!is_angle_close(ang.h, m_orientation_h))
|
||||
return true;
|
||||
|
||||
if (((CButton *) GetDlgItem(IDC_POINT_TO_CHECKBOX))->GetCheck() == TRUE)
|
||||
@@ -224,7 +243,7 @@ void orient_editor::OnOK()
|
||||
|
||||
// there's enough difference in our position that we're changing it
|
||||
cur_pos = &Objects[cur_object_index].pos;
|
||||
if (!close(cur_pos->xyz.x, m_position_x) || !close(cur_pos->xyz.y, m_position_y) || !close(cur_pos->xyz.z, m_position_z))
|
||||
if (!is_close(cur_pos->xyz.x, m_position_x) || !is_close(cur_pos->xyz.y, m_position_y) || !is_close(cur_pos->xyz.z, m_position_z))
|
||||
{
|
||||
vec3d pos;
|
||||
pos.xyz.x = convert(m_position_x);
|
||||
@@ -237,7 +256,7 @@ void orient_editor::OnOK()
|
||||
|
||||
// there's enough difference in our orientation that we're changing it
|
||||
vm_extract_angles_matrix(&ang, &Objects[cur_object_index].orient);
|
||||
if (!close(to_degrees(ang.p), m_orientation_p) || !close(to_degrees(ang.b), m_orientation_b) || !close(to_degrees(ang.h), m_orientation_h))
|
||||
if (!is_angle_close(ang.p, m_orientation_p) || !is_angle_close(ang.b, m_orientation_b) || !is_angle_close(ang.h, m_orientation_h))
|
||||
{
|
||||
ang.p = fl_radians(convert(m_orientation_p));
|
||||
ang.b = fl_radians(convert(m_orientation_b));
|
||||
@@ -354,6 +373,11 @@ void orient_editor::actually_point_object(object *ptr)
|
||||
float orient_editor::to_degrees(float rad)
|
||||
{
|
||||
float deg = fl_degrees(rad);
|
||||
return normalize_degrees(deg);
|
||||
}
|
||||
|
||||
float orient_editor::normalize_degrees(float deg)
|
||||
{
|
||||
while (deg < -180.0f)
|
||||
deg += 180.0f;
|
||||
while (deg > 180.0f)
|
||||
@@ -364,7 +388,10 @@ float orient_editor::to_degrees(float rad)
|
||||
return deg;
|
||||
}
|
||||
|
||||
float orient_editor::convert(const CString &str)
|
||||
/**
|
||||
* Extract a float from the CString, being mindful of any comma separators.
|
||||
*/
|
||||
float orient_editor::convert(const CString &str) const
|
||||
{
|
||||
char buf[256];
|
||||
size_t i, j, len;
|
||||
@@ -379,6 +406,18 @@ float orient_editor::convert(const CString &str)
|
||||
return (float) atof(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform rounding of the float value by loading it into the input box format string and parsing it again.
|
||||
* This accounts for not only decimal rounding to the precision of the input box, but also floating point rounding due to inexact fractions such as 1/10.
|
||||
* See also GitHub PR #4475.
|
||||
*/
|
||||
float orient_editor::perform_input_rounding(float val) const
|
||||
{
|
||||
CString str;
|
||||
str.Format(INPUT_FORMAT, val);
|
||||
return convert(str);
|
||||
}
|
||||
|
||||
void orient_editor::OnPointTo()
|
||||
{
|
||||
UpdateData(TRUE);
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
orient_editor(CWnd* pParent = NULL); // standard constructor
|
||||
|
||||
static float to_degrees(float radians);
|
||||
static float normalize_degrees(float degrees);
|
||||
|
||||
// Dialog Data
|
||||
//{{AFX_DATA(orient_editor)
|
||||
@@ -69,8 +70,12 @@ protected:
|
||||
DECLARE_MESSAGE_MAP()
|
||||
|
||||
private:
|
||||
float convert(const CString &str);
|
||||
bool close(float val, const CString &str);
|
||||
float convert(const CString &str) const;
|
||||
float perform_input_rounding(float val) const;
|
||||
|
||||
bool is_close(float val, const CString &input_str) const;
|
||||
bool is_angle_close(float rad, const CString &input_str) const;
|
||||
|
||||
int total;
|
||||
int index[MAX_OBJECTS];
|
||||
void actually_point_object(object *ptr);
|
||||
|
||||
+4
-4
@@ -2509,13 +2509,13 @@ void sexp_tree::NodeCopy()
|
||||
|
||||
void sexp_tree::NodeReplacePaste()
|
||||
{
|
||||
if (item_index < 0)
|
||||
if (item_index < 0 || Sexp_clipboard < 0)
|
||||
return;
|
||||
|
||||
int i;
|
||||
|
||||
// the following assumptions are made..
|
||||
Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
|
||||
Assert(Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED);
|
||||
Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
|
||||
Assertion(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_CONTAINER_NAME,
|
||||
"Attempt to use container name %s from SEXP clipboard. Please report!",
|
||||
@@ -2587,13 +2587,13 @@ void sexp_tree::NodeReplacePaste()
|
||||
|
||||
void sexp_tree::NodeAddPaste()
|
||||
{
|
||||
if (item_index < 0)
|
||||
if (item_index < 0 || Sexp_clipboard < 0)
|
||||
return;
|
||||
|
||||
int i;
|
||||
|
||||
// the following assumptions are made..
|
||||
Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
|
||||
Assert(Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED);
|
||||
Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
|
||||
Assertion(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_CONTAINER_NAME,
|
||||
"Attempt to use container name %s from SEXP clipboard. Please report!",
|
||||
|
||||
+16
-17
@@ -902,6 +902,7 @@ void game_level_close()
|
||||
lock_time_compression(false);
|
||||
|
||||
audiostream_unpause_all();
|
||||
snd_aav_init();
|
||||
|
||||
gr_set_ambient_light(120, 120, 120);
|
||||
|
||||
@@ -1192,7 +1193,7 @@ void game_loading_callback(int count)
|
||||
|
||||
Script_system.RemHookVar("Progress");
|
||||
|
||||
os_ignore_events();
|
||||
os_defer_events_on_load_screen();
|
||||
|
||||
if (do_flip)
|
||||
gr_flip();
|
||||
@@ -5272,7 +5273,6 @@ void game_leave_state( int old_state, int new_state )
|
||||
if ( (Game_mode & GM_MULTIPLAYER) && (new_state == GS_STATE_MAIN_MENU) ){
|
||||
multi_quit_game(PROMPT_NONE);
|
||||
}
|
||||
snd_aav_init();
|
||||
|
||||
freespace_stop_mission();
|
||||
|
||||
@@ -6683,17 +6683,7 @@ void game_shutdown(void)
|
||||
gr_clear();
|
||||
gr_flip();
|
||||
}
|
||||
|
||||
// Free the scripting resources of the new UI first
|
||||
scpui::shutdown_scripting();
|
||||
|
||||
// Everything after this should be done without scripting so we can free those resources here
|
||||
Script_system.Clear();
|
||||
|
||||
// Deinitialize the new UI system, needs to be done after scripting shutdown to make sure the resources were
|
||||
// released properly
|
||||
scpui::shutdown();
|
||||
|
||||
|
||||
// if the player has left the "player select" screen and quit the game without actually choosing
|
||||
// a player, Player will be nullptr, in which case we shouldn't write the player file out!
|
||||
if (!(Game_mode & GM_STANDALONE_SERVER) && (Player!=nullptr) && !Is_standalone){
|
||||
@@ -6729,9 +6719,6 @@ void game_shutdown(void)
|
||||
|
||||
scoring_close();
|
||||
|
||||
// Free SEXP resources
|
||||
sexp_shutdown();
|
||||
|
||||
stars_close(); // clean out anything used by stars code
|
||||
|
||||
// the menu close functions will unload the bitmaps if they were displayed during the game
|
||||
@@ -6741,6 +6728,19 @@ void game_shutdown(void)
|
||||
// free left over memory from table parsing
|
||||
player_tips_close();
|
||||
|
||||
|
||||
// more fundamental shutdowns begin here ----------
|
||||
|
||||
sexp_shutdown(); // Free SEXP resources
|
||||
|
||||
scpui::shutdown_scripting(); // Free the scripting resources of the new UI first
|
||||
|
||||
Script_system.Clear(); // Everything after this should be done without scripting so we can free those resources here.
|
||||
// By this point, all things that hold Lua References must be destroyed (such as Lua SEXPs)
|
||||
|
||||
scpui::shutdown(); // Deinitialize the new UI system, needs to be done after scripting shutdown to make sure the resources were released properly
|
||||
|
||||
|
||||
control_config_common_close();
|
||||
io::joystick::shutdown();
|
||||
|
||||
@@ -6779,7 +6779,6 @@ void game_shutdown(void)
|
||||
}
|
||||
|
||||
lcl_xstr_close();
|
||||
|
||||
}
|
||||
|
||||
// game_stop_looped_sounds()
|
||||
|
||||
@@ -2571,7 +2571,7 @@ int Editor::internal_error(const char* msg, ...) {
|
||||
return -1;
|
||||
}
|
||||
int Editor::fred_check_sexp(int sexp, int type, const char* msg, ...) {
|
||||
SCP_string buf, sexp_buf, error_buf;
|
||||
SCP_string buf, sexp_buf, error_buf, bad_node_str;
|
||||
int err = 0, z, faulty_node;
|
||||
va_list args;
|
||||
|
||||
@@ -2588,12 +2588,18 @@ int Editor::fred_check_sexp(int sexp, int type, const char* msg, ...) {
|
||||
|
||||
convert_sexp_to_string(sexp_buf, sexp, SEXP_ERROR_CHECK_MODE);
|
||||
truncate_message_lines(sexp_buf, 30);
|
||||
|
||||
stuff_sexp_text_string(bad_node_str, faulty_node, SEXP_ERROR_CHECK_MODE);
|
||||
if (!bad_node_str.empty()) { // the previous function adds a space at the end
|
||||
bad_node_str.pop_back();
|
||||
}
|
||||
|
||||
sprintf(error_buf,
|
||||
"Error in %s: %s\n\nIn sexpression: %s\n\n(Bad node appears to be: %s)",
|
||||
buf.c_str(),
|
||||
sexp_error_message(z),
|
||||
sexp_buf.c_str(),
|
||||
Sexp_nodes[faulty_node].text);
|
||||
bad_node_str.c_str());
|
||||
|
||||
if (z < 0 && z > -100)
|
||||
err = 1;
|
||||
|
||||
Reference in new issue
Block a user