mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
Compare commits
139
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 | ||
|
|
112f2bb5ba | ||
|
|
5b8dce1dda | ||
|
|
b6c684644a | ||
|
|
d754f7fe0d | ||
|
|
2beaa886ca | ||
|
|
53bc7861bf | ||
|
|
9de4a49c42 | ||
|
|
6e5626722a | ||
|
|
1fa5068555 | ||
|
|
d7f0051394 | ||
|
|
6b9dacb598 | ||
|
|
eef9a340b1 | ||
|
|
4a9c6567cc | ||
|
|
ead7f10006 | ||
|
|
c7abceaf30 | ||
|
|
a057917958 | ||
|
|
010441fdbf | ||
|
|
b32698012a | ||
|
|
581fec1079 | ||
|
|
a44adde0e5 | ||
|
|
e88542580e | ||
|
|
b5043417fc | ||
|
|
049fa72df3 | ||
|
|
b8231a4ec1 | ||
|
|
1b563360ed | ||
|
|
3ee1321d9d | ||
|
|
fb9304bae0 | ||
|
|
87e6a1350d | ||
|
|
bf05cadd14 | ||
|
|
29fe214d16 | ||
|
|
e27bf0c925 | ||
|
|
83c4858f18 | ||
|
|
6104e6fcd3 | ||
|
|
113b57df29 | ||
|
|
a25e12a041 | ||
|
|
fa29b91e68 | ||
|
|
6b91a6210c | ||
|
|
6098675cc0 | ||
|
|
01331436c9 | ||
|
|
dbdc2457e0 | ||
|
|
7013981de5 | ||
|
|
8b9ac5f8a9 | ||
|
|
7cf2832113 | ||
|
|
6f5de692b9 | ||
|
|
67b3c6ce46 | ||
|
|
b3f0016d0e | ||
|
|
bd864baead | ||
|
|
e6956b1b4c | ||
|
|
e0fd189833 | ||
|
|
28f06adc12 | ||
|
|
3f830fbe78 |
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 }}
|
||||
@@ -30,13 +30,17 @@ env:
|
||||
NEBULA_PASSWORD: ${{ secrets.NEBULA_PASSWORD }}
|
||||
HLP_API: ${{ secrets.HLP_API }}
|
||||
HLP_KEY: ${{ secrets.HLP_KEY }}
|
||||
RELEASE_TAG: ${{ github.event.inputs.releaseTag }}
|
||||
RELEASE_TAG: ${{ inputs.releaseTag }}
|
||||
|
||||
jobs:
|
||||
post_builds:
|
||||
name: Post builds on Nebula and the forums
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Caller
|
||||
# Name of what called this yaml. Used to debug auto/manual step being triggered
|
||||
run: echo "github.event_name= ${{ github.event_name }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -47,10 +51,10 @@ jobs:
|
||||
run: pip install -r ci/post/requirements.txt
|
||||
|
||||
- name: Post Builds (Auto trigger)
|
||||
if: ${{ github.event_name == 'workflow_call' }}
|
||||
if: ${{ (github.event_name == 'workflow_call') || (github.event_name == 'push') }}
|
||||
env:
|
||||
LINUX_RESULT: ${{ github.event.inputs.linux_result }}
|
||||
WINDOWS_RESULT: ${{ github.event.inputs.windows_result }}
|
||||
LINUX_RESULT: ${{ inputs.linux_result }}
|
||||
WINDOWS_RESULT: ${{ inputs.windows_result }}
|
||||
run: python ci/post/main.py release
|
||||
|
||||
- name: Post Builds (Manual trigger)
|
||||
|
||||
+17
-4
@@ -1,5 +1,6 @@
|
||||
# Retrives a log of commit messages and attempts to pretty them into a human-readbile format
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
@@ -42,9 +43,16 @@ def get_last_page(response: requests.Response) -> int:
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Retrieves a log of commit messages and attempts to pretty them into a human-readable format")
|
||||
parser.add_argument("start_date",
|
||||
help="Day of the earliest commit to add to the log.")
|
||||
parser.add_argument("-u", "--user", nargs=2, metavar=("NAME", "TOKEN"), default=["", ""],
|
||||
help="Github username and token/password")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
os.chdir(os.path.dirname(__file__)) # Change working directory to this file's directory
|
||||
|
||||
start_date = datetime.strptime(sys.argv[1], "%Y-%m-%d")
|
||||
start_date = datetime.strptime(args.start_date, "%Y-%m-%d")
|
||||
|
||||
print("start_date: {}".format(start_date))
|
||||
|
||||
@@ -76,10 +84,13 @@ def main():
|
||||
|
||||
changelog = {}
|
||||
url = "https://api.github.com/repos/scp-fs2open/fs2open.github.com/pulls"
|
||||
page = 1
|
||||
last_page = MAX_PAGES
|
||||
for page in range(1, last_page + 1):
|
||||
while page <= last_page:
|
||||
# GET <url>?base=master&sort=updated&direction=desc&state=closed&page=1
|
||||
response = requests.get(url, params={
|
||||
response = requests.get(url,
|
||||
auth=(args.user[0], args.user[1]),
|
||||
params={
|
||||
"base": "master",
|
||||
"direction": "desc",
|
||||
"page": page,
|
||||
@@ -140,6 +151,8 @@ def main():
|
||||
print("Added pull #{} ({})".format(pull['number'], merge_date))
|
||||
# Else, silently ignore duplicate (for now...)
|
||||
|
||||
page += 1 # iterate to next page
|
||||
|
||||
|
||||
# Write the changelog to file
|
||||
# 2021-09-28 | (#3661) Fix homing child weapons in niche circumstances
|
||||
|
||||
@@ -106,3 +106,6 @@ set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYM
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_COPY_PHASE_STRIP[variant=Debug] "NO")
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_STRIP_INSTALLED_PRODUCT[variant=Debug] "NO")
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN[variant=Debug] "NO")
|
||||
|
||||
# Prevent GCC warnings in third-party BitOp... see GitHub #4366
|
||||
suppress_file_warnings(scripting/lua/bitop/bit.c)
|
||||
@@ -72,6 +72,7 @@ extern ai_flag_name Ai_flag_names[];
|
||||
#define AITTV_VIA_SEXP (1<<1) // Goober5000 - via sexp
|
||||
#define AITTV_IGNORE_BANK (1<<2) // Goober5000 - ignore bank when turning
|
||||
#define AITTV_SLOW_BANK_ACCEL (1<<3) // Asteroth - used by formation flying
|
||||
#define AITTV_FORCE_DELTA_BANK (1<<4) // Lafiel - Always use the provided delta bank and override calculated values
|
||||
|
||||
#define KAMIKAZE_HULL_ON_DEATH -1000.0f // Hull strength ship gets set to if it crash-dies.
|
||||
|
||||
@@ -586,6 +587,7 @@ extern void ai_do_default_behavior(object *obj);
|
||||
extern void ai_start_waypoints(object *objp, waypoint_list *wp_list, int wp_flags);
|
||||
extern void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hit_normal);
|
||||
extern void ai_ship_destroy(int shipnum);
|
||||
extern vec3d ai_get_acc_limit(vec3d* vel_limit, const object* objp);
|
||||
extern void ai_turn_towards_vector(vec3d *dest, object *objp, vec3d *slide_vec, vec3d *rel_pos, float bank_override, int flags, vec3d *rvec = nullptr, vec3d* turnrate_mod = nullptr);
|
||||
extern void init_ai_object(int objnum);
|
||||
extern void ai_init(void); // Call this one to parse ai.tbl.
|
||||
|
||||
+250
-194
@@ -1195,7 +1195,7 @@ void ai_turn_towards_vector(vec3d* dest, object* objp, vec3d* slide_vec, vec3d*
|
||||
vec3d vel_in, vel_out, desired_fvec, src;
|
||||
float delta_time;
|
||||
physics_info *pip;
|
||||
float delta_bank;
|
||||
float bank;
|
||||
|
||||
Assertion(objp->type == OBJ_SHIP || objp->type == OBJ_WEAPON, "ai_turn_towards_vector called on a non-ship non-weapon object!");
|
||||
|
||||
@@ -1261,43 +1261,7 @@ void ai_turn_towards_vector(vec3d* dest, object* objp, vec3d* slide_vec, vec3d*
|
||||
}
|
||||
}
|
||||
|
||||
// Set rate at which ship can accelerate to its rotational velocity.
|
||||
// For now, weapons just go much faster.
|
||||
acc_limit = vel_limit;
|
||||
if (objp->type == OBJ_WEAPON)
|
||||
acc_limit *= 8.0f;
|
||||
|
||||
// do _proper_ handling of acc_limit and vel_limit if this flag is set
|
||||
if (Framerate_independent_turning) {
|
||||
// handle modifications to rotdamp (that could affect acc_limit and vel_limit)
|
||||
// handled in its entirety in physics_sim_rot
|
||||
float rotdamp = pip->rotdamp;
|
||||
float shock_fraction_time_left = 0.f;
|
||||
if (pip->flags & PF_IN_SHOCKWAVE) {
|
||||
shock_fraction_time_left = timestamp_until(pip->shockwave_decay) / (float)SW_BLAST_DURATION;
|
||||
if (shock_fraction_time_left > 0)
|
||||
rotdamp = pip->rotdamp + pip->rotdamp * (SW_ROT_FACTOR - 1) * shock_fraction_time_left;
|
||||
}
|
||||
|
||||
if (Ai_respect_tabled_turntime_rotdamp) {
|
||||
// We're assuming the turn rate here is accurate so leave it as is
|
||||
// but we'll use the ship's rotdamp to modify acc_limit
|
||||
// (this is the polynomial approximation of the exponential effect rotdamp has on players)
|
||||
if (rotdamp == 0.f)
|
||||
acc_limit *= 1000.f;
|
||||
else
|
||||
acc_limit = vel_limit * (0.5f / rotdamp);
|
||||
}
|
||||
else {
|
||||
// else, calculate the retail-friendly vel and acc values
|
||||
ai_compensate_for_retail_turning(&vel_limit, &acc_limit, rotdamp, objp->type == OBJ_WEAPON);
|
||||
|
||||
// handle missile turning accel (which is bundled into rotdamp)
|
||||
if (objp->type == OBJ_WEAPON && rotdamp != 0.f) {
|
||||
acc_limit = vel_limit * (0.5f / rotdamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
acc_limit = ai_get_acc_limit(&vel_limit, objp);
|
||||
|
||||
// for formation flying
|
||||
if ((flags & AITTV_SLOW_BANK_ACCEL)) {
|
||||
@@ -1324,18 +1288,21 @@ void ai_turn_towards_vector(vec3d* dest, object* objp, vec3d* slide_vec, vec3d*
|
||||
|
||||
// Should be more general case here. Currently, anything that is not a weapon will bank when it turns.
|
||||
// Goober5000 - don't bank if sexp or ship says not to
|
||||
if ( (objp->type == OBJ_WEAPON) || (flags & AITTV_IGNORE_BANK ) )
|
||||
delta_bank = 0.0f;
|
||||
if (flags & AITTV_FORCE_DELTA_BANK)
|
||||
bank = bank_override;
|
||||
else if ( (objp->type == OBJ_WEAPON) || (flags & AITTV_IGNORE_BANK ) )
|
||||
bank = 0.0f;
|
||||
else if (objp->type == OBJ_SHIP && Ship_info[Ships[objp->instance].ship_info_index].flags[Ship::Info_Flags::Dont_bank_when_turning])
|
||||
delta_bank = 0.0f;
|
||||
bank = 0.0f;
|
||||
else if ((bank_override) && (iff_x_attacks_y(Ships[objp->instance].team, Player_ship->team))) { // Theoretically, this will only happen for Shivans.
|
||||
delta_bank = bank_override;
|
||||
bank = bank_override;
|
||||
} else {
|
||||
delta_bank = vm_vec_dot(&curr_orient.vec.rvec, &objp->last_orient.vec.rvec);
|
||||
delta_bank = 200.0f * (1.0f - delta_bank) * pip->delta_bank_const;
|
||||
bank = vm_vec_dot(&curr_orient.vec.rvec, &objp->last_orient.vec.rvec);
|
||||
bank = 200.0f * (1.0f - bank) * pip->delta_bank_const;
|
||||
if (vm_vec_dot(&objp->last_orient.vec.fvec, &objp->orient.vec.rvec) < 0.0f)
|
||||
delta_bank = -delta_bank;
|
||||
bank = -bank;
|
||||
}
|
||||
bank *= vel_limit.xyz.z;
|
||||
|
||||
|
||||
matrix out_orient;
|
||||
@@ -1347,7 +1314,7 @@ void ai_turn_towards_vector(vec3d* dest, object* objp, vec3d* slide_vec, vec3d*
|
||||
vm_angular_move_matrix(&goal_orient, &curr_orient, &vel_in, delta_time,
|
||||
&out_orient, &vel_out, &vel_limit, &acc_limit, The_mission.ai_profile->flags[AI::Profile_Flags::No_turning_directional_bias]);
|
||||
} else {
|
||||
vm_angular_move_forward_vec(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank,
|
||||
vm_angular_move_forward_vec(&desired_fvec, &curr_orient, &vel_in, delta_time, bank,
|
||||
&out_orient, &vel_out, &vel_limit, &acc_limit, The_mission.ai_profile->flags[AI::Profile_Flags::No_turning_directional_bias]);
|
||||
}
|
||||
|
||||
@@ -1364,6 +1331,52 @@ void ai_turn_towards_vector(vec3d* dest, object* objp, vec3d* slide_vec, vec3d*
|
||||
|
||||
}
|
||||
|
||||
//vel_limit can be modified if the object is a weapon and retail behaviour is enabled
|
||||
vec3d ai_get_acc_limit(vec3d* vel_limit, const object* objp) {
|
||||
// Set rate at which ship can accelerate to its rotational velocity.
|
||||
// For now, weapons just go much faster.
|
||||
vec3d acc_limit = *vel_limit;
|
||||
if (objp->type == OBJ_WEAPON)
|
||||
acc_limit *= 8.0f;
|
||||
|
||||
const physics_info* pip = &objp->phys_info;
|
||||
|
||||
// do _proper_ handling of acc_limit and vel_limit if this flag is set
|
||||
if (Framerate_independent_turning) {
|
||||
// handle modifications to rotdamp (that could affect acc_limit and vel_limit)
|
||||
// handled in its entirety in physics_sim_rot
|
||||
float rotdamp = pip->rotdamp;
|
||||
float shock_fraction_time_left = 0.f;
|
||||
if (pip->flags & PF_IN_SHOCKWAVE) {
|
||||
shock_fraction_time_left = timestamp_until(pip->shockwave_decay) / (float)SW_BLAST_DURATION;
|
||||
if (shock_fraction_time_left > 0)
|
||||
rotdamp = pip->rotdamp + pip->rotdamp * (SW_ROT_FACTOR - 1) * shock_fraction_time_left;
|
||||
}
|
||||
|
||||
if (Ai_respect_tabled_turntime_rotdamp) {
|
||||
// We're assuming the turn rate here is accurate so leave it as is
|
||||
// but we'll use the ship's rotdamp to modify acc_limit
|
||||
// (this is the polynomial approximation of the exponential effect rotdamp has on players)
|
||||
if (rotdamp == 0.f)
|
||||
acc_limit *= 1000.f;
|
||||
else
|
||||
acc_limit = *vel_limit * (0.5f / rotdamp);
|
||||
}
|
||||
else {
|
||||
// else, calculate the retail-friendly vel and acc values
|
||||
ai_compensate_for_retail_turning(vel_limit, &acc_limit, rotdamp, objp->type == OBJ_WEAPON);
|
||||
|
||||
// handle missile turning accel (which is bundled into rotdamp)
|
||||
if (objp->type == OBJ_WEAPON && rotdamp != 0.f) {
|
||||
acc_limit = *vel_limit * (0.5f / rotdamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return acc_limit;
|
||||
}
|
||||
|
||||
|
||||
// Set aip->target_objnum to objnum
|
||||
// Update aip->previous_target_objnum.
|
||||
// If new target (objnum) is different than old target, reset target_time.
|
||||
@@ -2387,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) {
|
||||
@@ -4861,12 +4875,12 @@ void avoid_ship()
|
||||
// If in front of enemy, turn away from it.
|
||||
// If behind enemy, try to get fully behind it.
|
||||
if (away_dot < 0.0f) {
|
||||
turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
|
||||
turn_away_from_point(Pl_objp, &enemy_pos, 1.0f);
|
||||
} else {
|
||||
vec3d goal_pos;
|
||||
|
||||
vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.vec.fvec, -100.0f);
|
||||
turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
|
||||
turn_towards_point(Pl_objp, &goal_pos, NULL, 1.0f);
|
||||
}
|
||||
|
||||
// Set speed.
|
||||
@@ -5303,9 +5317,9 @@ void evade_ship()
|
||||
// caused flying in an odd spiral.
|
||||
vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.vec.rvec, 1000.0f);
|
||||
if (dist < 100.0f)
|
||||
bank_override = Pl_objp->phys_info.speed;
|
||||
bank_override = 1.0f;
|
||||
} else {
|
||||
bank_override = Pl_objp->phys_info.speed; // In enemy's sights, not pointing at him, twirl away.
|
||||
bank_override = 1.0f; // In enemy's sights, not pointing at him, twirl away.
|
||||
goto evade_ship_l1;
|
||||
}
|
||||
} else {
|
||||
@@ -7652,7 +7666,7 @@ void ai_chase_attack(ai_info *aip, ship_info *sip, vec3d *predicted_enemy_pos, f
|
||||
|
||||
//SUSHI: Don't change bank while circle strafing or glide attacking
|
||||
if (dist_to_enemy < 250.0f && dot_from_enemy > 0.7f && aip->submode != AIS_CHASE_CIRCLESTRAFE && aip->submode != AIS_CHASE_GLIDEATTACK) {
|
||||
bank_override = Pl_objp->phys_info.speed;
|
||||
bank_override = 1.0f;
|
||||
}
|
||||
|
||||
// If enemy more than 500 meters away, all ships flying there will tend to match bank.
|
||||
@@ -7699,9 +7713,7 @@ void ai_chase_es(ai_info *aip)
|
||||
tvec.xyz.y += frand();
|
||||
}
|
||||
|
||||
float bank_override = Pl_objp->phys_info.speed;
|
||||
|
||||
ai_turn_towards_vector(&tvec, Pl_objp, nullptr, nullptr, bank_override, 0);
|
||||
ai_turn_towards_vector(&tvec, Pl_objp, nullptr, nullptr, 1.0f, 0);
|
||||
accelerate_ship(aip, 1.0f);
|
||||
}
|
||||
|
||||
@@ -7712,7 +7724,6 @@ void ai_chase_ga(ai_info *aip, ship_info *sip)
|
||||
{
|
||||
// If not near end of this submode, evade squiggly. If near end, just fly straight for a bit
|
||||
vec3d tvec;
|
||||
float bank_override;
|
||||
vec3d vec_from_enemy;
|
||||
|
||||
if (En_objp != NULL) {
|
||||
@@ -7725,9 +7736,7 @@ void ai_chase_ga(ai_info *aip, ship_info *sip)
|
||||
vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
|
||||
vm_vec_add2(&tvec, &Pl_objp->pos);
|
||||
|
||||
bank_override = Pl_objp->phys_info.speed;
|
||||
|
||||
ai_turn_towards_vector(&tvec, Pl_objp, nullptr, nullptr, bank_override, 0);
|
||||
ai_turn_towards_vector(&tvec, Pl_objp, nullptr, nullptr, 1.0f, 0);
|
||||
|
||||
accelerate_ship(aip, 2.0f);
|
||||
|
||||
@@ -10007,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) {
|
||||
|
||||
@@ -14482,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;
|
||||
@@ -14564,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;
|
||||
@@ -14692,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;
|
||||
@@ -14723,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;
|
||||
@@ -14732,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])
|
||||
@@ -14754,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;
|
||||
@@ -14810,12 +14847,12 @@ void ai_process( object * obj, int ai_index, float frametime )
|
||||
|
||||
memset( &AI_ci, 0, sizeof(AI_ci) );
|
||||
|
||||
ai_frame(OBJ_INDEX(obj));
|
||||
|
||||
AI_ci.pitch = 0.0f;
|
||||
AI_ci.bank = 0.0f;
|
||||
AI_ci.heading = 0.0f;
|
||||
|
||||
ai_frame(OBJ_INDEX(obj));
|
||||
|
||||
// the ships maximum velocity now depends on the energy flowing to engines
|
||||
obj->phys_info.max_vel.xyz.z = shipp->current_max_speed;
|
||||
|
||||
@@ -14839,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);
|
||||
}
|
||||
|
||||
@@ -15101,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];
|
||||
@@ -15201,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15537,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.
|
||||
@@ -16139,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;
|
||||
};
|
||||
|
||||
@@ -74,7 +74,7 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
|
||||
{ CF_TYPE_FONT, "data" DIR_SEPARATOR_STR "fonts", ".vf .ttf .otf", CF_TYPE_DATA },
|
||||
{ CF_TYPE_EFFECTS, "data" DIR_SEPARATOR_STR "effects", ".ani .eff .pcx .neb .tga .jpg .png .dds .sdr", CF_TYPE_DATA },
|
||||
{ CF_TYPE_HUD, "data" DIR_SEPARATOR_STR "hud", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
|
||||
{ CF_TYPE_PLAYERS, "data" DIR_SEPARATOR_STR "players", ".hcf", CF_TYPE_DATA },
|
||||
{ CF_TYPE_PLAYERS, "data" DIR_SEPARATOR_STR "players", ".hcf", /* DON'T add pilot files here!! */ CF_TYPE_DATA },
|
||||
{ CF_TYPE_PLAYER_IMAGES, "data" DIR_SEPARATOR_STR "players" DIR_SEPARATOR_STR "images", ".pcx .png .dds", CF_TYPE_PLAYERS },
|
||||
{ CF_TYPE_SQUAD_IMAGES, "data" DIR_SEPARATOR_STR "players" DIR_SEPARATOR_STR "squads", ".pcx .png .dds", CF_TYPE_PLAYERS },
|
||||
{ CF_TYPE_SINGLE_PLAYERS, "data" DIR_SEPARATOR_STR "players" DIR_SEPARATOR_STR "single", ".pl2 .cs2 .plr .csg .css .json", CF_TYPE_PLAYERS },
|
||||
|
||||
@@ -1551,7 +1551,7 @@ int cf_get_file_list(SCP_vector<SCP_string>& list, int pathtype, const char* fil
|
||||
}
|
||||
|
||||
bool skip_packfiles = false;
|
||||
if ( (pathtype == CF_TYPE_PLAYERS) || (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS) ) {
|
||||
if ( (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS) ) {
|
||||
skip_packfiles = true;
|
||||
} else if (Get_file_list_child != NULL) {
|
||||
skip_packfiles = true;
|
||||
@@ -1708,7 +1708,7 @@ int cf_get_file_list(int max, char** list, int pathtype, const char* filter, int
|
||||
}
|
||||
|
||||
bool skip_packfiles = false;
|
||||
if ((pathtype == CF_TYPE_PLAYERS) || (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS)) {
|
||||
if ((pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS)) {
|
||||
skip_packfiles = true;
|
||||
}
|
||||
else if (Get_file_list_child != NULL) {
|
||||
@@ -1887,7 +1887,7 @@ int cf_get_file_list_preallocated(int max, char arr[][MAX_FILENAME_LEN], char**
|
||||
}
|
||||
|
||||
bool skip_packfiles = false;
|
||||
if ((pathtype == CF_TYPE_PLAYERS) || (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS)) {
|
||||
if ((pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS)) {
|
||||
skip_packfiles = true;
|
||||
}
|
||||
else if (Get_file_list_child != NULL) {
|
||||
@@ -1981,8 +1981,10 @@ int cf_get_file_list_preallocated(int max, char arr[][MAX_FILENAME_LEN], char**
|
||||
// Output: path - Fully qualified pathname.
|
||||
//Returns 0 if the result would be too long (invalid result)
|
||||
int cf_create_default_path_string(char* path, uint path_max, int pathtype, const char* filename, bool localize,
|
||||
uint32_t location_flags)
|
||||
uint32_t _location_flags)
|
||||
{
|
||||
uint32_t location_flags = _location_flags;
|
||||
|
||||
if ( filename && strchr(filename, DIR_SEPARATOR_CHAR) ) {
|
||||
// Already has full path
|
||||
strncpy( path, filename, path_max );
|
||||
@@ -1990,6 +1992,16 @@ int cf_create_default_path_string(char* path, uint path_max, int pathtype, const
|
||||
} else {
|
||||
cf_root* root = nullptr;
|
||||
|
||||
// override location flags for path types which should always come from the root (NOT mod directories)
|
||||
// NOTE: cache directories should NOT be added here to avoid possible mod breakage
|
||||
switch(pathtype) {
|
||||
case CF_TYPE_PLAYERS:
|
||||
case CF_TYPE_MULTI_PLAYERS:
|
||||
case CF_TYPE_SINGLE_PLAYERS:
|
||||
location_flags = CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT;
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < Num_roots; ++i) {
|
||||
auto current_root = cf_get_root(i);
|
||||
|
||||
|
||||
@@ -158,8 +158,8 @@ void control_config_common_init_bindings() {
|
||||
// flight controls (flight modes)
|
||||
(BANK_WHEN_PRESSED, -1, -1, SHIP_TAB, 1, "Bank When Pressed", CC_TYPE_CONTINUOUS)
|
||||
(AFTERBURNER, KEY_TAB, 5, SHIP_TAB, 1, "Afterburner", CC_TYPE_CONTINUOUS)
|
||||
(GLIDE_WHEN_PRESSED, -1, -1, SHIP_TAB, 0, "Glide When Pressed", CC_TYPE_CONTINUOUS, true)
|
||||
(TOGGLE_GLIDING, KEY_ALTED | KEY_G, -1, SHIP_TAB, 0, "Toggle Gliding", CC_TYPE_TRIGGER, true)
|
||||
(GLIDE_WHEN_PRESSED, -1, -1, SHIP_TAB, 0, "Glide When Pressed", CC_TYPE_CONTINUOUS)
|
||||
(TOGGLE_GLIDING, KEY_ALTED | KEY_G, -1, SHIP_TAB, 0, "Toggle Gliding", CC_TYPE_TRIGGER)
|
||||
|
||||
// flight controls (axes)
|
||||
(JOY_HEADING_AXIS, JOY_X_AXIS, MOUSE_X_AXIS, SHIP_TAB, 1016, "Turn (Yaw) Axis", CC_TYPE_AXIS_REL)
|
||||
@@ -173,7 +173,7 @@ void control_config_common_init_bindings() {
|
||||
(FIRE_SECONDARY, KEY_SPACEBAR, 1, WEAPON_TAB, 1, "Fire Secondary Weapon", CC_TYPE_CONTINUOUS)
|
||||
(CYCLE_NEXT_PRIMARY, KEY_PERIOD, -1, WEAPON_TAB, 1, "Cycle Primary Weapon Forward", CC_TYPE_TRIGGER)
|
||||
(CYCLE_PREV_PRIMARY, KEY_COMMA, -1, WEAPON_TAB, 1, "Cycle Primary Weapon Backward", CC_TYPE_TRIGGER)
|
||||
(CYCLE_PRIMARY_WEAPON_SEQUENCE, KEY_O, -1, WEAPON_TAB, 0, "Cycle Primary Weapon Firing Rate", CC_TYPE_TRIGGER, true)
|
||||
(CYCLE_PRIMARY_WEAPON_SEQUENCE, KEY_O, -1, WEAPON_TAB, 0, "Cycle Primary Weapon Firing Rate", CC_TYPE_TRIGGER)
|
||||
(CYCLE_SECONDARY, KEY_DIVIDE, -1, WEAPON_TAB, 1, "Cycle Secondary Weapon Forward", CC_TYPE_TRIGGER)
|
||||
(CYCLE_NUM_MISSLES, KEY_SHIFTED | KEY_DIVIDE, -1, WEAPON_TAB, 1, "Cycle Secondary Weapon Firing Rate", CC_TYPE_TRIGGER)
|
||||
(LAUNCH_COUNTERMEASURE, KEY_X, 3, WEAPON_TAB, 1, "Launch Countermeasure", CC_TYPE_TRIGGER)
|
||||
@@ -209,8 +209,8 @@ void control_config_common_init_bindings() {
|
||||
(PADLOCK_DOWN, -1, 32, COMPUTER_TAB, 1, "View Rear", CC_TYPE_CONTINUOUS)
|
||||
(PADLOCK_LEFT, -1, 34, COMPUTER_TAB, 1, "View Left", CC_TYPE_CONTINUOUS)
|
||||
(PADLOCK_RIGHT, -1, 35, COMPUTER_TAB, 1, "View Right", CC_TYPE_CONTINUOUS)
|
||||
(VIEW_TOPDOWN, -1, -1, COMPUTER_TAB, 0, "Top-Down View", CC_TYPE_TRIGGER, true)
|
||||
(VIEW_TRACK_TARGET, -1, -1, COMPUTER_TAB, 0, "Target Padlock View", CC_TYPE_TRIGGER, true)
|
||||
(VIEW_TOPDOWN, -1, -1, COMPUTER_TAB, 0, "Top-Down View", CC_TYPE_TRIGGER)
|
||||
(VIEW_TRACK_TARGET, -1, -1, COMPUTER_TAB, 0, "Target Padlock View", CC_TYPE_TRIGGER)
|
||||
|
||||
(RADAR_RANGE_CYCLE, KEY_RAPOSTRO, -1, COMPUTER_TAB, 1, "Cycle Radar Range", CC_TYPE_TRIGGER)
|
||||
(SQUADMSG_MENU, KEY_C, -1, COMPUTER_TAB, 1, "Communications Menu", CC_TYPE_TRIGGER)
|
||||
@@ -235,8 +235,8 @@ void control_config_common_init_bindings() {
|
||||
|
||||
// Navigation and Autopilot
|
||||
(SHOW_NAVMAP, -1, -1, NO_TAB, 1, "Show Nav Map", CC_TYPE_TRIGGER, true)
|
||||
(AUTO_PILOT_TOGGLE, KEY_ALTED | KEY_A, -1, COMPUTER_TAB, 0, "Toggle Auto Pilot", CC_TYPE_TRIGGER, true)
|
||||
(NAV_CYCLE, KEY_ALTED | KEY_N, -1, COMPUTER_TAB, 0, "Cycle Nav Points", CC_TYPE_TRIGGER, true)
|
||||
(AUTO_PILOT_TOGGLE, KEY_ALTED | KEY_A, -1, COMPUTER_TAB, 0, "Toggle Auto Pilot", CC_TYPE_TRIGGER)
|
||||
(NAV_CYCLE, KEY_ALTED | KEY_N, -1, COMPUTER_TAB, 0, "Cycle Nav Points", CC_TYPE_TRIGGER)
|
||||
|
||||
// Escort
|
||||
(ADD_REMOVE_ESCORT, KEY_ALTED | KEY_E, -1, COMPUTER_TAB, 1, "Add or Remove Escort", CC_TYPE_TRIGGER)
|
||||
|
||||
@@ -51,6 +51,7 @@ void processEvents()
|
||||
{
|
||||
io::mouse::CursorManager::get()->showCursor(false);
|
||||
|
||||
os_remove_deferred_cutscene_key_events();
|
||||
os_poll();
|
||||
}
|
||||
|
||||
|
||||
@@ -668,9 +668,11 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
// ensure vel is valid
|
||||
Assert( !vm_is_vec_nan(&obj->phys_info.vel) );
|
||||
|
||||
OnDebrisCreatedHook->run(scripting::hook_param_list(
|
||||
scripting::hook_param("Debris", 'o', obj),
|
||||
scripting::hook_param("Source", 'o', source_obj)));
|
||||
if (OnDebrisCreatedHook->isActive()) {
|
||||
OnDebrisCreatedHook->run(scripting::hook_param_list(
|
||||
scripting::hook_param("Debris", 'o', obj),
|
||||
scripting::hook_param("Source", 'o', source_obj)));
|
||||
}
|
||||
|
||||
if (db->is_hull) {
|
||||
MONITOR_INC(NumHullDebris,1);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2557,7 +2557,9 @@ void gr_flip(bool execute_scripting)
|
||||
TRACE_SCOPE(tracing::LuaOnFrame);
|
||||
|
||||
// WMC - Do conditional hooks. Yippee!
|
||||
OnFrameHook->run();
|
||||
if (OnFrameHook->isActive()) {
|
||||
OnFrameHook->run();
|
||||
}
|
||||
// WMC - Do scripting reset stuff
|
||||
Script_system.EndFrame();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+12
-6
@@ -1613,6 +1613,7 @@ void hud_config_color_load(const char *name)
|
||||
int idx;
|
||||
char str[1024];
|
||||
char *fname;
|
||||
ubyte r, g, b, a;
|
||||
|
||||
fname = cf_add_ext(name, ".hcf");
|
||||
|
||||
@@ -1630,13 +1631,18 @@ void hud_config_color_load(const char *name)
|
||||
while (optional_string("+Gauge:")) {
|
||||
stuff_string(str, F_NAME, sizeof(str));
|
||||
|
||||
required_string("+RGBA:");
|
||||
stuff_ubyte(&r);
|
||||
stuff_ubyte(&g);
|
||||
stuff_ubyte(&b);
|
||||
stuff_ubyte(&a);
|
||||
|
||||
for (idx = 0; idx < NUM_HUD_GAUGES; idx++) {
|
||||
if (!stricmp(str, Hud_Gauge_Names[idx])) {
|
||||
required_string("+RGBA:");
|
||||
stuff_ubyte(&HUD_config.clr[idx].red);
|
||||
stuff_ubyte(&HUD_config.clr[idx].green);
|
||||
stuff_ubyte(&HUD_config.clr[idx].blue);
|
||||
stuff_ubyte(&HUD_config.clr[idx].alpha);
|
||||
if (!stricmp(str, HC_gauge_descriptions(idx))) {
|
||||
HUD_config.clr[idx].red = r;
|
||||
HUD_config.clr[idx].green = g;
|
||||
HUD_config.clr[idx].blue = b;
|
||||
HUD_config.clr[idx].alpha = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -603,8 +603,10 @@ void hud_sourced_print(int source, const char *msg)
|
||||
HUD_msg_buffer.push_back(new_msg);
|
||||
|
||||
// Invoke the scripting hook
|
||||
OnHudMessageReceivedHook->run(scripting::hook_param_list(scripting::hook_param("Text", 's', msg),
|
||||
scripting::hook_param("SourceType", 'i', source)));
|
||||
if (OnHudMessageReceivedHook->isActive()) {
|
||||
OnHudMessageReceivedHook->run(scripting::hook_param_list(scripting::hook_param("Text", 's', msg),
|
||||
scripting::hook_param("SourceType", 'i', source)));
|
||||
}
|
||||
}
|
||||
|
||||
int hud_query_scrollback_size()
|
||||
|
||||
+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];
|
||||
|
||||
@@ -156,6 +156,8 @@ int keys_used[] = { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_
|
||||
|
||||
SCP_string Comm_order_types[NUM_COMM_ORDER_TYPES];
|
||||
|
||||
int player_order::orderingCounter = 0;
|
||||
|
||||
std::vector<player_order> Player_orders = {
|
||||
player_order("no order", "No Order", -1, -1, NO_ORDER_ITEM), //Required to keep defines in sync with array indices
|
||||
player_order("attack ship", "Destroy my target", 299, -1, ATTACK_TARGET_ITEM),
|
||||
@@ -1924,12 +1926,17 @@ void hud_squadmsg_reinforcement_select()
|
||||
}
|
||||
}
|
||||
|
||||
bool hud_order_comparator(size_t a, size_t b) {
|
||||
return Player_orders[a].ordering < Player_orders[b].ordering;
|
||||
}
|
||||
|
||||
// function to display list of commands for a ship
|
||||
|
||||
void hud_squadmsg_ship_command()
|
||||
{
|
||||
int k;
|
||||
std::set<size_t> orders, default_orders;
|
||||
std::set<size_t> orders;
|
||||
std::set<size_t, decltype(&hud_order_comparator)> default_orders(&hud_order_comparator);
|
||||
|
||||
// when adding ship commands, we must look at the type of ship, and what messages that
|
||||
// ship allows. First, place all messages that are possible onto the menu, then
|
||||
@@ -1938,9 +1945,11 @@ void hud_squadmsg_ship_command()
|
||||
// show on comm menu.
|
||||
if ( Msg_instance != MESSAGE_ALL_FIGHTERS ) {
|
||||
orders = Ships[Msg_instance].orders_accepted;
|
||||
default_orders = ship_get_default_orders_accepted( &Ship_info[Ships[Msg_instance].ship_info_index] );
|
||||
const auto& default_orders_accepted = ship_get_default_orders_accepted(&Ship_info[Ships[Msg_instance].ship_info_index]);
|
||||
default_orders.insert(default_orders_accepted.cbegin(), default_orders_accepted.cend());
|
||||
} else {
|
||||
orders = default_orders = default_messages;
|
||||
orders = default_messages;
|
||||
default_orders.insert(default_messages.cbegin(), default_messages.cend());
|
||||
}
|
||||
|
||||
Num_menu_items = 0;
|
||||
@@ -2027,7 +2036,8 @@ void hud_squadmsg_wing_command()
|
||||
wing *wingp;
|
||||
int shipnum;
|
||||
|
||||
std::set<size_t> default_orders, orders;
|
||||
std::set<size_t> orders;
|
||||
std::set<size_t, decltype(&hud_order_comparator)> default_orders(&hud_order_comparator);
|
||||
|
||||
// when adding commands for wings, we will look at all of the ships in the wing, and create
|
||||
// the order list from that set of ships. In the future, we may want to do something else....
|
||||
|
||||
@@ -62,6 +62,8 @@ class object;
|
||||
|
||||
typedef struct player_order {
|
||||
private:
|
||||
//Needed, because legacy order-id's were not assigned in order
|
||||
static int orderingCounter;
|
||||
//Used to ensure that built-in order indexing is always correct
|
||||
int legacy_id;
|
||||
friend void hud_init_comm_orders();
|
||||
@@ -71,7 +73,8 @@ public:
|
||||
int hud_xstr;
|
||||
SCP_string localized_name;
|
||||
int lua_id;
|
||||
player_order(SCP_string parsename, SCP_string hudname, int hudxstr, int luaid = -1, int legacyid = 999999) : legacy_id(legacyid), parse_name(std::move(parsename)), hud_name(std::move(hudname)), hud_xstr(hudxstr), localized_name(""), lua_id(luaid) { }
|
||||
int ordering;
|
||||
player_order(SCP_string parsename, SCP_string hudname, int hudxstr, int luaid = -1, int legacyid = 999999) : legacy_id(legacyid), parse_name(std::move(parsename)), hud_name(std::move(hudname)), hud_xstr(hudxstr), localized_name(""), lua_id(luaid), ordering(orderingCounter++) { }
|
||||
inline void localize() { localized_name = XSTR(hud_name.c_str(), hud_xstr); }
|
||||
} player_order;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -224,7 +224,9 @@ void LabManager::onFrame(float frametime) {
|
||||
break;
|
||||
|
||||
case KEY_M:
|
||||
gr_dump_envmap(Renderer->currentMissionBackground.c_str());
|
||||
// Dumping the environment map only makes sense if we actually have a background set
|
||||
if (Renderer->currentMissionBackground != LAB_MISSION_NONE_STRING)
|
||||
gr_dump_envmap(Renderer->currentMissionBackground.c_str());
|
||||
break;
|
||||
|
||||
// bail...
|
||||
|
||||
@@ -63,7 +63,8 @@ void LabRenderer::renderModel(float frametime) {
|
||||
Ships[obj->instance].flags.set(Ship::Ship_Flags::Subsystem_movement_locked, !renderFlags[LabRenderFlag::RotateSubsystems]);
|
||||
Ships[obj->instance].flags.set(Ship::Ship_Flags::Draw_as_wireframe, renderFlags[LabRenderFlag::ShowWireframe]);
|
||||
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_full_detail, renderFlags[LabRenderFlag::ShowFullDetail]);
|
||||
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_light, renderFlags[LabRenderFlag::NoLighting] || currentMissionBackground == "None");
|
||||
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_light,
|
||||
renderFlags[LabRenderFlag::NoLighting] || currentMissionBackground == LAB_MISSION_NONE_STRING);
|
||||
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_diffuse, renderFlags[LabRenderFlag::NoDiffuseMap]);
|
||||
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_glowmap, renderFlags[LabRenderFlag::NoGlowMap]);
|
||||
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_normalmap, renderFlags[LabRenderFlag::NoNormalMap]);
|
||||
@@ -218,7 +219,7 @@ void LabRenderer::renderHud(float) {
|
||||
}
|
||||
|
||||
//Print current Team Color setting, if any
|
||||
if (currentTeamColor != "<none>") {
|
||||
if (currentTeamColor != LAB_TEAM_COLOR_NONE) {
|
||||
gr_printf_no_resize(gr_screen.center_offset_x + 2,
|
||||
gr_screen.center_offset_y + gr_screen.center_h - (gr_get_font_height() * 3) - 3,
|
||||
"Use T and Y to cycle through available Team Color settings. Current: %s",
|
||||
|
||||
@@ -67,6 +67,9 @@ enum class TextureOverride {
|
||||
Specular
|
||||
};
|
||||
|
||||
constexpr auto LAB_MISSION_NONE_STRING = "None";
|
||||
constexpr auto LAB_TEAM_COLOR_NONE = "<none>";
|
||||
|
||||
class LabRenderer {
|
||||
public:
|
||||
LabRenderer() {
|
||||
@@ -75,8 +78,8 @@ public:
|
||||
directionalFactor = static_light_factor;
|
||||
textureQuality = TextureQuality::Maximum;
|
||||
cameraDistance = 100.0f;
|
||||
currentTeamColor = "<none>";
|
||||
useBackground("None");
|
||||
currentTeamColor = LAB_TEAM_COLOR_NONE;
|
||||
useBackground(LAB_MISSION_NONE_STRING);
|
||||
|
||||
labCamera.reset(new OrbitCamera());
|
||||
|
||||
@@ -110,25 +113,28 @@ public:
|
||||
}
|
||||
|
||||
void useNextTeamColorPreset() {
|
||||
auto color_itr = Team_Colors.find(currentTeamColor);
|
||||
if (!Team_Colors.empty()) {
|
||||
auto color_itr = Team_Colors.find(currentTeamColor);
|
||||
|
||||
if (color_itr == Team_Colors.begin()) {
|
||||
color_itr = --Team_Colors.end();
|
||||
currentTeamColor = color_itr->first;
|
||||
}
|
||||
else {
|
||||
--color_itr;
|
||||
currentTeamColor = color_itr->first;
|
||||
if (color_itr == Team_Colors.begin()) {
|
||||
color_itr = --Team_Colors.end();
|
||||
currentTeamColor = color_itr->first;
|
||||
} else {
|
||||
--color_itr;
|
||||
currentTeamColor = color_itr->first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void usePreviousTeamColorPreset() {
|
||||
auto color_itr = Team_Colors.find(currentTeamColor);
|
||||
if (!Team_Colors.empty()) {
|
||||
auto color_itr = Team_Colors.find(currentTeamColor);
|
||||
|
||||
++color_itr;
|
||||
if (color_itr == Team_Colors.end())
|
||||
color_itr = Team_Colors.begin();
|
||||
currentTeamColor = color_itr->first;
|
||||
++color_itr;
|
||||
if (color_itr == Team_Colors.end())
|
||||
color_itr = Team_Colors.begin();
|
||||
currentTeamColor = color_itr->first;
|
||||
}
|
||||
}
|
||||
|
||||
void setTeamColor(SCP_string teamColor) {
|
||||
|
||||
+19
-10
@@ -1901,7 +1901,7 @@ float time_to_arrival(float goal, float vel, float vel_limit, float acc_limit) {
|
||||
// and also scales their speed to make a nice straight line
|
||||
// note that this is now treated as a movement in linear space, despite the name
|
||||
vec3d vm_angular_move(const vec3d* goal, float delta_t,
|
||||
vec3d* vel, const vec3d* vel_limit, const vec3d* acc_limit, bool aggressive_bank, bool force_no_overshoot, bool no_directional_bias)
|
||||
vec3d* vel, const vec3d* vel_limit, const vec3d* acc_limit, bool no_bank, bool force_no_overshoot, bool no_directional_bias)
|
||||
{
|
||||
vec3d ret, slow;
|
||||
vm_vec_make(&slow, 1.f, 1.f, 1.f);
|
||||
@@ -1915,12 +1915,12 @@ vec3d vm_angular_move(const vec3d* goal, float delta_t,
|
||||
// so they arrive at approximately the same time as the slowest component, so the path there is nice and straight
|
||||
float max = fmax(slow.xyz.x, fmax(slow.xyz.y, slow.xyz.z));
|
||||
if (max != 0) vm_vec_scale(&slow, 1 / max);
|
||||
if (aggressive_bank) slow.xyz.z = 1.f;
|
||||
}
|
||||
|
||||
ret.xyz.x = vm_angular_move_1dimension(goal->xyz.x, delta_t, &vel->xyz.x, vel_limit->xyz.x, acc_limit->xyz.x, slow.xyz.x, force_no_overshoot);
|
||||
ret.xyz.y = vm_angular_move_1dimension(goal->xyz.y, delta_t, &vel->xyz.y, vel_limit->xyz.y, acc_limit->xyz.y, slow.xyz.y, force_no_overshoot);
|
||||
ret.xyz.z = vm_angular_move_1dimension(goal->xyz.z, delta_t, &vel->xyz.z, vel_limit->xyz.z, acc_limit->xyz.z, slow.xyz.z, force_no_overshoot);
|
||||
if (!no_bank)
|
||||
ret.xyz.z = vm_angular_move_1dimension(goal->xyz.z, delta_t, &vel->xyz.z, vel_limit->xyz.z, acc_limit->xyz.z, slow.xyz.z, force_no_overshoot);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2012,7 +2012,7 @@ void vm_angular_move_matrix(const matrix* goal_orient, const matrix* curr_orient
|
||||
// orient => current orientation matrix (with current forward vector)
|
||||
// w_in => current input angular velocity
|
||||
// delta_t => this frametime
|
||||
// delta_bank => desired change in bank in degrees
|
||||
// bank_vel => desired bank velocity
|
||||
// next_orient => the orientation matrix at time delta_t (with current forward vector)
|
||||
// w_out => the angular velocity of the ship at delta_t
|
||||
// vel_limit => maximum rotational speed
|
||||
@@ -2027,15 +2027,14 @@ void vm_angular_move_matrix(const matrix* goal_orient, const matrix* curr_orient
|
||||
// function attempts to rotate the forward vector toward the goal forward vector taking account of anglular
|
||||
// momentum (velocity) Attempt to try to move bank by goal delta_bank.
|
||||
// called "vm_forward_interpolate" in retail
|
||||
void vm_angular_move_forward_vec(const vec3d* goal_f, const matrix* orient, const vec3d* w_in, float delta_t, float delta_bank,
|
||||
void vm_angular_move_forward_vec(const vec3d* goal_f, const matrix* orient, const vec3d* w_in, float delta_t, float bank_vel,
|
||||
matrix* next_orient, vec3d* w_out, const vec3d* vel_limit, const vec3d* acc_limit, bool no_directional_bias)
|
||||
{
|
||||
vec3d rot_axis;
|
||||
vm_vec_cross(&rot_axis, &orient->vec.fvec, goal_f); // Get the direction to rotate to the goal
|
||||
float cos_theta = vm_vec_dot(&orient->vec.fvec, goal_f); // Get cos(theta) where theta is the amount to rotate
|
||||
float sin_theta = fmin(vm_vec_mag(&rot_axis), 1.0f); // Get sin(theta) (cap at 1 for floating point errors)
|
||||
vec3d theta_goal;
|
||||
vm_vec_make(&theta_goal, 0, 0, delta_bank); // theta_goal will contain the radians to rotate (in the same direction as rot_axis but in local coords)
|
||||
vec3d theta_goal = vmd_zero_vector; // theta_goal will contain the radians to rotate (in the same direction as rot_axis but in local coords)
|
||||
|
||||
if (sin_theta <= SMALL_NUM) { // sin(theta) is small so we are either very close or very far
|
||||
if (cos_theta < 0) { // cos(theta) < 0, sin(theta) ~ 0 means we are pointed exactly the opposite way
|
||||
@@ -2063,15 +2062,25 @@ void vm_angular_move_forward_vec(const vec3d* goal_f, const matrix* orient, cons
|
||||
|
||||
// derive theta from sin(theta) for better accuracy
|
||||
vm_vec_copy_scale(&theta_goal, &local_rot_axis, (cos_theta > 0 ? asinf_safe(sin_theta) : PI - asinf_safe(sin_theta)) / sin_theta);
|
||||
|
||||
// reset z to delta_bank, because it just got cleared
|
||||
theta_goal.xyz.z = delta_bank;
|
||||
}
|
||||
|
||||
// calculate best approach in linear space (returns velocity in w_out and position difference in rot_axis)
|
||||
*w_out = *w_in;
|
||||
rot_axis = vm_angular_move(&theta_goal, delta_t, w_out, vel_limit, acc_limit, true, false, no_directional_bias);
|
||||
|
||||
// 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;
|
||||
if (delta_bank_vel < 0)
|
||||
accel = -accel;
|
||||
|
||||
rot_axis.xyz.z = w_in->xyz.z * delta_t + accel * delta_t * delta_t * 0.5f; // vt + 1/2at^2
|
||||
w_out->xyz.z = w_in->xyz.z + accel * delta_t;
|
||||
}
|
||||
|
||||
// normalize rotation axis and determine total rotation angle
|
||||
float theta = vm_vec_mag(&rot_axis);
|
||||
if (theta > SMALL_NUM)
|
||||
|
||||
@@ -1059,6 +1059,7 @@ void main_hall_do(float frametime)
|
||||
|
||||
// see if we have a missing campaign and force the player to select a new campaign if so
|
||||
extern bool Campaign_room_no_campaigns;
|
||||
bool popup_shown = false;
|
||||
if ( !(Player->flags & PLAYER_FLAGS_IS_MULTI) && Campaign_file_missing && !Campaign_room_no_campaigns ) {
|
||||
int rc = popup(0, 3, XSTR("Go to Campaign Room", 1607), XSTR("Select another pilot", 1608), XSTR("Exit Game", 1609), XSTR("The currently active campaign cannot be found. Please select another...", 1600));
|
||||
|
||||
@@ -1079,13 +1080,20 @@ void main_hall_do(float frametime)
|
||||
gameseq_post_event(GS_EVENT_CAMPAIGN_ROOM);
|
||||
break;
|
||||
}
|
||||
|
||||
// since all paths in this code block will take us to a different state, don't display any more popups
|
||||
popup_shown = true;
|
||||
}
|
||||
|
||||
// Display a popup if playermenu loaded a player file with a different version than expected
|
||||
player_tips_controls();
|
||||
if (!popup_shown) {
|
||||
popup_shown = player_tips_controls();
|
||||
}
|
||||
|
||||
// maybe run the player tips popup
|
||||
player_tips_popup();
|
||||
if (!popup_shown) {
|
||||
player_tips_popup();
|
||||
}
|
||||
|
||||
// if we were supposed to skip a frame, then stop doing it after 1 frame
|
||||
if (Main_hall_frame_skip) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1459,7 +1459,10 @@ void player_tips_popup()
|
||||
} while(ret > 0);
|
||||
}
|
||||
|
||||
void player_tips_controls() {
|
||||
/**
|
||||
* Displays a popup notification if the pilot was converted from pre-22.0 which might disrupt control settings. Returns true if the popup was shown.
|
||||
*/
|
||||
bool player_tips_controls() {
|
||||
if (Player->save_flags & PLAYER_FLAGS_PLR_VER_PRE_CONTROLS5) {
|
||||
// Special case. Since the Controls5 PR is significantly different from retail, users must be informed
|
||||
// of changes regarding their bindings
|
||||
@@ -1476,7 +1479,11 @@ void player_tips_controls() {
|
||||
Pilot.save_player(Player);
|
||||
Pilot.save_savefile();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SCP_vector<SCP_string> player_select_enumerate_pilots() {
|
||||
|
||||
@@ -41,7 +41,7 @@ int player_select_get_last_pilot();
|
||||
void player_tips_init();
|
||||
void player_tips_close();
|
||||
void player_tips_popup();
|
||||
void player_tips_controls();
|
||||
bool player_tips_controls();
|
||||
|
||||
// quick check to make sure we always load default campaign savefile values when loading from the pilot
|
||||
// select screen but let us not overwrite current values with defaults when we aren't - taylor
|
||||
|
||||
@@ -1567,7 +1567,9 @@ void campaign_reset(const SCP_string& campaign_file)
|
||||
Pilot.save_savefile();
|
||||
}
|
||||
|
||||
OnCampaignBeginHook->run(scripting::hook_param_list(scripting::hook_param("Campaign", 's', Campaign.filename)));
|
||||
if (OnCampaignBeginHook->isActive()) {
|
||||
OnCampaignBeginHook->run(scripting::hook_param_list(scripting::hook_param("Campaign", 's', Campaign.filename)));
|
||||
}
|
||||
}
|
||||
|
||||
mission_campaign_next_mission();
|
||||
@@ -1686,7 +1688,9 @@ void campaign_select_campaign(const SCP_string& campaign_file)
|
||||
Pilot.save_savefile();
|
||||
}
|
||||
|
||||
OnCampaignBeginHook->run(scripting::hook_param_list(scripting::hook_param("Campaign", 's', Campaign.filename)));
|
||||
if (OnCampaignBeginHook->isActive()) {
|
||||
OnCampaignBeginHook->run(scripting::hook_param_list(scripting::hook_param("Campaign", 's', Campaign.filename)));
|
||||
}
|
||||
}
|
||||
|
||||
// that's all we need to do for now; the campaign loading status will be checked again when we try to load the
|
||||
|
||||
@@ -760,7 +760,6 @@ void techroom_change_tab(int num)
|
||||
switch (Tab) {
|
||||
case SHIPS_DATA_TAB:
|
||||
si_mask.set(multi ? Ship::Info_Flags::In_tech_database_m : Ship::Info_Flags::In_tech_database);
|
||||
si_mask.set(multi ? Ship::Info_Flags::Default_in_tech_database_m : Ship::Info_Flags::Default_in_tech_database);
|
||||
|
||||
// load ship info if necessary
|
||||
if ( !Ships_loaded ) {
|
||||
@@ -809,7 +808,6 @@ void techroom_change_tab(int num)
|
||||
Weapon_list.reserve(Weapon_info.size());
|
||||
|
||||
wi_mask.set(multi ? Weapon::Info_Flags::Player_allowed : Weapon::Info_Flags::In_tech_database);
|
||||
wi_mask.set(Weapon::Info_Flags::Default_in_tech_database);
|
||||
|
||||
int i = 0;
|
||||
tech_list_entry temp_entry;
|
||||
|
||||
@@ -25,7 +25,7 @@ typedef struct {
|
||||
// flags by Goober5000
|
||||
#define IIF_DEFAULT_VALUE 0
|
||||
#define IIF_IN_TECH_DATABASE (1 << 0) // in tech database? - Goober5000
|
||||
#define IIF_DEFAULT_IN_TECH_DATABASE (1 << 1) // in tech database by default? - Goober5000
|
||||
#define IIF_DEFAULT_IN_TECH_DATABASE (1 << 1) // this entry's default tech database status, as specified in species.tbl; used when the tech db is "reset to default" - Goober5000
|
||||
|
||||
extern int Techroom_overlay_id;
|
||||
|
||||
|
||||
@@ -951,6 +951,9 @@ void mission_campaign_store_goals_and_events()
|
||||
int cur, i;
|
||||
cmission *mission_obj;
|
||||
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.current_mission < 0))
|
||||
return;
|
||||
|
||||
cur = Campaign.current_mission;
|
||||
name = Campaign.missions[cur].name;
|
||||
|
||||
@@ -1027,6 +1030,9 @@ 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) || (Campaign.current_mission < 0))
|
||||
return;
|
||||
|
||||
cur = Campaign.current_mission;
|
||||
mission_obj = &Campaign.missions[cur];
|
||||
|
||||
@@ -1092,6 +1098,9 @@ 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) || (Campaign.current_mission < 0))
|
||||
return;
|
||||
|
||||
if (!sexp_container_has_persistent_non_eternal_containers()) {
|
||||
// nothing to do
|
||||
return;
|
||||
@@ -1822,7 +1831,12 @@ void mission_campaign_exit_loop()
|
||||
|
||||
// set things up for next mission
|
||||
mission_campaign_next_mission();
|
||||
gameseq_post_event(GS_EVENT_START_GAME);
|
||||
|
||||
if ( Campaign.next_mission == -1 || (The_mission.flags[Mission::Mission_Flags::End_to_mainhall]) ) {
|
||||
gameseq_post_event( GS_EVENT_MAIN_MENU );
|
||||
} else {
|
||||
gameseq_post_event( GS_EVENT_START_GAME );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1886,12 +1900,8 @@ void mission_campaign_save_on_close_variables()
|
||||
{
|
||||
int i;
|
||||
|
||||
// make sure we are actually playing a campaign
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE))
|
||||
return;
|
||||
|
||||
// make sure this is a single-player campaign
|
||||
if (!(Campaign.type == CAMPAIGN_TYPE_SINGLE))
|
||||
// make sure we are actually playing a single-player campaign
|
||||
if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.type != CAMPAIGN_TYPE_SINGLE) || (Campaign.current_mission < 0))
|
||||
return;
|
||||
|
||||
// now save variables
|
||||
@@ -1931,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"
|
||||
@@ -1633,13 +1634,15 @@ void message_queue_process()
|
||||
if (Message_shipnum >= 0) {
|
||||
sender = &Objects[Ships[Message_shipnum].objnum];
|
||||
}
|
||||
OnMessageReceivedHook->run(scripting::hook_param_list(
|
||||
scripting::hook_param("Name", 's', m->name),
|
||||
scripting::hook_param("MessageHandle", 'o', scripting::api::l_Message.Set(q->message_num)),
|
||||
scripting::hook_param("Message", 's', buf),
|
||||
scripting::hook_param("SenderString", 's', who_from),
|
||||
scripting::hook_param("Builtin", 'b', builtinMessage),
|
||||
scripting::hook_param("Sender", 'o', sender)));
|
||||
if (OnMessageReceivedHook->isActive()) {
|
||||
OnMessageReceivedHook->run(scripting::hook_param_list(
|
||||
scripting::hook_param("Name", 's', m->name),
|
||||
scripting::hook_param("MessageHandle", 'o', scripting::api::l_Message.Set(q->message_num)),
|
||||
scripting::hook_param("Message", 's', buf),
|
||||
scripting::hook_param("SenderString", 's', who_from),
|
||||
scripting::hook_param("Builtin", 'b', builtinMessage),
|
||||
scripting::hook_param("Sender", 'o', sender)));
|
||||
}
|
||||
|
||||
Num_messages_playing++; // this has to be done at the end because some sound functions use it to index into the array
|
||||
all_done:
|
||||
@@ -1733,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1798,7 +1798,7 @@ void parse_dock_one_docked_object(p_object *pobjp, p_object *parent_pobjp)
|
||||
ai_dock_with_object(objp, dockpoint, parent_objp, parent_dockpoint, AIDO_DOCK_NOW);
|
||||
}
|
||||
|
||||
int parse_create_object_sub(p_object *objp);
|
||||
int parse_create_object_sub(p_object *objp, bool standalone_ship = false);
|
||||
|
||||
// Goober5000
|
||||
void parse_create_docked_object_helper(p_object *pobjp, p_dock_function_info *infop)
|
||||
@@ -1825,7 +1825,7 @@ void parse_create_docked_object_helper(p_object *pobjp, p_dock_function_info *in
|
||||
* This is a bit tricky because of the way initial docking is now handled.
|
||||
* Docking groups require special treatment.
|
||||
*/
|
||||
int parse_create_object(p_object *pobjp)
|
||||
int parse_create_object(p_object *pobjp, bool standalone_ship)
|
||||
{
|
||||
object *objp;
|
||||
|
||||
@@ -1861,7 +1861,7 @@ int parse_create_object(p_object *pobjp)
|
||||
// create normally
|
||||
else
|
||||
{
|
||||
parse_create_object_sub(pobjp);
|
||||
parse_create_object_sub(pobjp, standalone_ship);
|
||||
}
|
||||
|
||||
// get the main object
|
||||
@@ -1894,7 +1894,7 @@ void parse_bring_in_docked_wing(p_object *p_objp, int wingnum, int shipnum);
|
||||
* Given a stuffed p_object struct, create an object and fill in the necessary fields.
|
||||
* @return object number.
|
||||
*/
|
||||
int parse_create_object_sub(p_object *p_objp)
|
||||
int parse_create_object_sub(p_object *p_objp, bool standalone_ship)
|
||||
{
|
||||
int i, j, k, objnum, shipnum;
|
||||
int anchor_objnum = -1;
|
||||
@@ -1912,7 +1912,7 @@ int parse_create_object_sub(p_object *p_objp)
|
||||
MONITOR_INC(NumShipArrivals, 1);
|
||||
|
||||
// base level creation - need ship name in case of duplicate textures
|
||||
objnum = ship_create(&p_objp->orient, &p_objp->pos, p_objp->ship_class, p_objp->name);
|
||||
objnum = ship_create(&p_objp->orient, &p_objp->pos, p_objp->ship_class, p_objp->name, standalone_ship);
|
||||
Assert(objnum != -1);
|
||||
shipnum = Objects[objnum].instance;
|
||||
|
||||
@@ -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
|
||||
@@ -7545,10 +7551,13 @@ int mission_do_departure(object *objp, bool goal_is_to_warp)
|
||||
|
||||
mprintf(("Entered mission_do_departure() for %s\n", shipp->ship_name));
|
||||
|
||||
// add scripting hook for 'On Departure Started' --wookieejedi
|
||||
// hook is placed at the beginning of this function to allow the scripter to
|
||||
// actually have access to the ship's departure decisions before they are all executed
|
||||
OnDepartureStartedHook->run(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp), scripting::hook_param("Ship", 'o', objp)));
|
||||
if (OnDepartureStartedHook->isActive())
|
||||
{
|
||||
// add scripting hook for 'On Departure Started' --wookieejedi
|
||||
// hook is placed at the beginning of this function to allow the scripter to
|
||||
// actually have access to the ship's departure decisions before they are all executed
|
||||
OnDepartureStartedHook->run(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp), scripting::hook_param("Ship", 'o', objp)));
|
||||
}
|
||||
|
||||
// abort rearm, because if we entered this function we're either going to depart via hyperspace, depart via bay,
|
||||
// or revert to our default behavior
|
||||
|
||||
@@ -495,7 +495,7 @@ p_object *mission_parse_get_arrival_ship(const char *name);
|
||||
bool mission_check_ship_yet_to_arrive(const char *name);
|
||||
p_object *mission_parse_get_parse_object(ushort net_signature);
|
||||
p_object *mission_parse_get_parse_object(const char *name);
|
||||
int parse_create_object(p_object *objp);
|
||||
int parse_create_object(p_object *objp, bool standalone_ship = false);
|
||||
void resolve_parse_flags(object *objp, flagset<Mission::Parse_Object_Flags> &parse_flags);
|
||||
|
||||
void mission_parse_close();
|
||||
|
||||
@@ -351,7 +351,7 @@ void brief_skip_training_pressed()
|
||||
mission_campaign_eval_next_mission();
|
||||
mission_campaign_mission_over();
|
||||
|
||||
if ( The_mission.flags[Mission::Mission_Flags::End_to_mainhall] ) {
|
||||
if ( Campaign.next_mission == -1 || (The_mission.flags[Mission::Mission_Flags::End_to_mainhall]) ) {
|
||||
gameseq_post_event( GS_EVENT_MAIN_MENU );
|
||||
} else {
|
||||
gameseq_post_event( GS_EVENT_START_GAME );
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -2100,7 +2100,7 @@ void pick_from_wing(int wb_num, int ws_num)
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( ws->status ) {
|
||||
switch ( ws->status & ~WING_SLOT_WEAPONS_DISABLED ) {
|
||||
case WING_SLOT_EMPTY:
|
||||
case WING_SLOT_EMPTY|WING_SLOT_IS_PLAYER:
|
||||
// TODO: add fail sound
|
||||
@@ -2499,7 +2499,7 @@ int create_wings()
|
||||
|
||||
for ( j = 0; j < MAX_WING_SLOTS; j++ ) {
|
||||
ws = &wb->ss_slots[j];
|
||||
switch( ws->status ) {
|
||||
switch( ws->status & ~WING_SLOT_WEAPONS_DISABLED ) {
|
||||
case WING_SLOT_EMPTY:
|
||||
// delete ship that is not going to be used by the wing
|
||||
if ( wb->is_late ) {
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
+20
-15
@@ -69,7 +69,8 @@ static int model_initted = 0;
|
||||
CFILE *ss_fp = NULL; // file pointer used to dump subsystem information
|
||||
char model_filename[_MAX_PATH]; // temp used to store filename
|
||||
char debug_name[_MAX_PATH];
|
||||
int ss_warning_shown = 0; // have we shown the warning dialog concerning the subsystems?
|
||||
static bool ss_warning_shown_null = false; // have we shown the warning dialog concerning the subsystems?
|
||||
static bool ss_warning_shown_mismatch = false; // ditto but for a different warning
|
||||
#endif
|
||||
|
||||
static uint Global_checksum = 0;
|
||||
@@ -882,9 +883,9 @@ void do_new_subsystem( int n_subsystems, model_subsystem *slist, int subobj_num,
|
||||
|
||||
if ( slist==NULL ) {
|
||||
#ifndef NDEBUG
|
||||
if (!ss_warning_shown) {
|
||||
if (!ss_warning_shown_null) {
|
||||
mprintf(("No subsystems found for model \"%s\".\n", model_get(model_num)->filename));
|
||||
ss_warning_shown = 1;
|
||||
ss_warning_shown_null = true;
|
||||
}
|
||||
#endif
|
||||
return; // For TestCode, POFView, etc don't bother
|
||||
@@ -924,12 +925,12 @@ void do_new_subsystem( int n_subsystems, model_subsystem *slist, int subobj_num,
|
||||
#ifndef NDEBUG
|
||||
char bname[_MAX_FNAME];
|
||||
|
||||
if ( !ss_warning_shown) {
|
||||
if ( !ss_warning_shown_mismatch) {
|
||||
_splitpath(model_filename, NULL, NULL, bname, NULL);
|
||||
// Lets still give a comment about it and not just erase it
|
||||
Warning(LOCATION,"Not all subsystems in model \"%s\" have a record in ships.tbl.\nThis can cause game to crash.\n\nList of subsystems not found from table is in log file.\n", model_get(model_num)->filename );
|
||||
mprintf(("Subsystem %s in model %s was not found in ships.tbl!\n", subobj_name, model_get(model_num)->filename));
|
||||
ss_warning_shown = 1;
|
||||
ss_warning_shown_mismatch = true;
|
||||
} else
|
||||
#endif
|
||||
mprintf(("Subsystem %s in model %s was not found in ships.tbl!\n", subobj_name, model_get(model_num)->filename));
|
||||
@@ -1432,7 +1433,8 @@ int read_model_file(polymodel * pm, const char *filename, int n_subsystems, mode
|
||||
mprintf(( "Can't open debug file for writing subsystems for %s\n", filename));
|
||||
} else {
|
||||
strcpy_s(model_filename, filename);
|
||||
ss_warning_shown = 0;
|
||||
ss_warning_shown_null = false;
|
||||
ss_warning_shown_mismatch = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -2383,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);
|
||||
}
|
||||
@@ -2421,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;
|
||||
}
|
||||
@@ -2432,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,6 +207,7 @@ void multi_msg_eval_text_msg()
|
||||
int multi_msg_text_process(int k)
|
||||
{
|
||||
char str[2];
|
||||
int ascii;
|
||||
|
||||
// keep eating keys for a short period of time
|
||||
if ( Multi_msg_eat_stamp.isValid() && !ui_timestamp_elapsed(Multi_msg_eat_stamp) ) {
|
||||
@@ -248,9 +249,16 @@ int multi_msg_text_process(int k)
|
||||
|
||||
// stick other printable characters onto the text
|
||||
default :
|
||||
ascii = key_to_ascii(k);
|
||||
|
||||
// make sure it's actually a printable character
|
||||
if (ascii == 255) {
|
||||
break;
|
||||
}
|
||||
|
||||
// if we're not already at the maximum length
|
||||
if(strlen(Multi_msg_text) < MULTI_MSG_MAX_LEN){
|
||||
str[0] = (char)key_to_ascii(k);
|
||||
str[0] = static_cast<char>(ascii);
|
||||
str[1] = '\0';
|
||||
strcat_s(Multi_msg_text,str);
|
||||
}
|
||||
|
||||
@@ -1564,7 +1564,7 @@ void multi_create_standalone_object()
|
||||
Net_player->m_player->objnum = objnum;
|
||||
|
||||
// create the default player ship object and use that as my default virtual "ship", and make it "invisible"
|
||||
pobj_num = parse_create_object(Player_start_pobject);
|
||||
pobj_num = parse_create_object(Player_start_pobject, true);
|
||||
Assert(pobj_num != -1);
|
||||
flagset<Object::Object_Flags> tmp_flags;
|
||||
obj_set_flags(&Objects[pobj_num], tmp_flags + Object::Object_Flags::Player_ship);
|
||||
|
||||
+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) )
|
||||
|
||||
+182
-69
@@ -871,7 +871,10 @@ int Training_context_goal_waypoint;
|
||||
int Training_context_at_waypoint;
|
||||
float Training_context_distance;
|
||||
|
||||
// If you edit this, make sure this is greater than zero,
|
||||
// so that we don't have to write pointless asserts. :)
|
||||
#define SEXP_NODE_INCREMENT 250
|
||||
|
||||
int Num_sexp_nodes = 0;
|
||||
sexp_node *Sexp_nodes = nullptr;
|
||||
|
||||
@@ -988,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
|
||||
@@ -1247,8 +1254,6 @@ int alloc_sexp(const char *text, int type, int subtype, int first, int rest)
|
||||
{
|
||||
int old_size = Num_sexp_nodes;
|
||||
|
||||
Assert(SEXP_NODE_INCREMENT > 0);
|
||||
|
||||
// allocate in blocks of SEXP_NODE_INCREMENT
|
||||
Num_sexp_nodes += SEXP_NODE_INCREMENT;
|
||||
Sexp_nodes = (sexp_node *) vm_realloc(Sexp_nodes, sizeof(sexp_node) * Num_sexp_nodes);
|
||||
@@ -2120,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;
|
||||
@@ -2463,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);
|
||||
@@ -2550,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);
|
||||
@@ -2919,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) {
|
||||
@@ -3059,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);
|
||||
@@ -3144,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);
|
||||
@@ -3507,7 +3553,7 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
|
||||
if ( type2 != SEXP_ATOM_STRING )
|
||||
return SEXP_CHECK_TYPE_MISMATCH;
|
||||
|
||||
if ( stricmp(CTEXT(node), NOX("default")) != 0 && !strstr(CTEXT(node), NOX(".pof")) )
|
||||
if ( stricmp(CTEXT(node), NOX("default")) != 0 && stricmp(CTEXT(node), NOX("none")) != 0 && !strstr(CTEXT(node), NOX(".pof")) )
|
||||
return SEXP_CHECK_INVALID_SKYBOX_NAME;
|
||||
|
||||
break;
|
||||
@@ -4505,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
|
||||
@@ -4916,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];
|
||||
@@ -4959,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;
|
||||
@@ -5001,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;
|
||||
@@ -11004,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))) {
|
||||
@@ -11022,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)))
|
||||
{
|
||||
@@ -11801,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;
|
||||
@@ -11811,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);
|
||||
}
|
||||
@@ -12183,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
|
||||
@@ -13352,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 ){
|
||||
@@ -19140,13 +19191,12 @@ void sexp_set_skybox_model_preload(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( !stricmp("default", name) ) {
|
||||
// if there isn't a mission skybox model then don't load one
|
||||
if ( strlen(The_mission.skybox_model) ) {
|
||||
i = model_load( The_mission.skybox_model, 0, nullptr );
|
||||
model_page_in_textures( i );
|
||||
}
|
||||
} else {
|
||||
if (!stricmp("default", name)) {
|
||||
name = The_mission.skybox_model;
|
||||
}
|
||||
|
||||
// if there isn't a skybox model then don't load one
|
||||
if ( strlen(name) && stricmp(name, "none") != 0 ) {
|
||||
i = model_load( name, 0, nullptr );
|
||||
model_page_in_textures( i );
|
||||
}
|
||||
@@ -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
|
||||
@@ -36734,7 +36847,7 @@ SCP_vector<sexp_help_struct> Sexp_help = {
|
||||
"\t3-8:\tSet or unset the following skyboxes flags (optional)\r\n"
|
||||
"\t\t\tadd-lighting, no-transparency, add-zbuffer\r\n"
|
||||
"\t\t\tadd-culling, no-glowmaps, force-clamp\r\n\r\n"
|
||||
"Note: If the model filename is set to \"default\" with no extension then it will switch to the mission supplied default skybox."
|
||||
"Note: If the model filename is set to \"default\" with no extension then it will switch to the mission supplied default skybox. If it is an empty string or \"none\" then it will remove any current skybox."
|
||||
},
|
||||
|
||||
// Goober5000
|
||||
|
||||
@@ -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)));
|
||||
|
||||
+131
-109
@@ -66,10 +66,10 @@ namespace {
|
||||
report_problematic_container(sexp_name, "non-map", container_name);
|
||||
}
|
||||
|
||||
void report_container_used_in_special_arg(const SCP_string &sexp_name, const char *container_name)
|
||||
void report_container_used_in_special_arg(const SCP_string &op_name, const char *container_name)
|
||||
{
|
||||
const SCP_string msg =
|
||||
sexp_name + " called on container " + container_name + " while it is being used in a special argument SEXP";
|
||||
op_name + " called on container " + container_name + " while it is being used in a special argument SEXP";
|
||||
Warning(LOCATION, "%s", msg.c_str());
|
||||
log_printf(LOGFILE_EVENT_LOG, "%s", msg.c_str());
|
||||
}
|
||||
@@ -92,6 +92,30 @@ namespace {
|
||||
#endif
|
||||
map_data.emplace(key, data);
|
||||
}
|
||||
|
||||
// Containers should not be modified if the game is simply checking the syntax.
|
||||
bool are_containers_modifiable()
|
||||
{
|
||||
// can always modify containers during the mission itself
|
||||
if (Game_mode & GM_IN_MISSION) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// can also modify if we are calling the sexp from a script or if we are briefing / debriefing, etc.
|
||||
switch (gameseq_get_state()) {
|
||||
case GS_STATE_BRIEFING:
|
||||
case GS_STATE_DEBRIEF:
|
||||
case GS_STATE_CMD_BRIEF:
|
||||
case GS_STATE_FICTION_VIEWER:
|
||||
case GS_STATE_SCRIPTING:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const SCP_string Remove_op_prefix = "Remove_";
|
||||
} // namespace
|
||||
|
||||
// map_container_hash impl
|
||||
@@ -186,6 +210,88 @@ const SCP_string &sexp_container::get_value_at_index(int index) const
|
||||
}
|
||||
}
|
||||
|
||||
SCP_string sexp_container::apply_list_modifier(ListModifier modifier, int data_index)
|
||||
{
|
||||
Assertion(is_list(),
|
||||
"Attempt to call list modifier (%d) on non-list container %s (type %d). Please report!",
|
||||
(int)modifier,
|
||||
container_name.c_str(),
|
||||
(int)type);
|
||||
Assertion(!list_data.empty(),
|
||||
"Attempt to call list modifier (%d) on empty list container %s. Please report!",
|
||||
(int)modifier,
|
||||
container_name.c_str());
|
||||
|
||||
switch (modifier) {
|
||||
case ListModifier::GET_FIRST:
|
||||
case ListModifier::REMOVE_FIRST:
|
||||
return list_get_first(modifier == ListModifier::REMOVE_FIRST);
|
||||
|
||||
case ListModifier::GET_LAST:
|
||||
case ListModifier::REMOVE_LAST:
|
||||
return list_get_last(modifier == ListModifier::REMOVE_LAST);
|
||||
|
||||
case ListModifier::GET_RANDOM:
|
||||
case ListModifier::REMOVE_RANDOM:
|
||||
return list_get_random(modifier == ListModifier::REMOVE_RANDOM);
|
||||
|
||||
case ListModifier::AT_INDEX:
|
||||
return list_get_at(data_index);
|
||||
|
||||
default:
|
||||
UNREACHABLE("Unhandled list modifier %d. Please report!", (int)modifier);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
SCP_string sexp_container::list_get_first(bool remove)
|
||||
{
|
||||
return list_apply_iterator(list_data.begin(), "First", remove);
|
||||
}
|
||||
|
||||
SCP_string sexp_container::list_get_last(bool remove)
|
||||
{
|
||||
return list_apply_iterator(--list_data.end(), "Last", remove);
|
||||
}
|
||||
|
||||
SCP_string sexp_container::list_get_random(bool remove)
|
||||
{
|
||||
const int rand_index = util::Random::next((int)list_data.size());
|
||||
auto list_it = std::next(list_data.begin(), rand_index);
|
||||
return list_apply_iterator(list_it, "Random", remove);
|
||||
}
|
||||
|
||||
SCP_string sexp_container::list_get_at(int index)
|
||||
{
|
||||
Assertion(index >= 0 && index < (int)list_data.size(),
|
||||
"Attempt to use At list modifier for container %s with invalid index %d. Please report!",
|
||||
container_name.c_str(),
|
||||
index);
|
||||
|
||||
auto list_it = std::next(list_data.begin(), index);
|
||||
return list_apply_iterator(list_it, "At", false);
|
||||
}
|
||||
|
||||
SCP_string sexp_container::list_apply_iterator(SCP_list<SCP_string>::iterator list_it, const char *location, bool remove)
|
||||
{
|
||||
Assertion(list_it != list_data.end(),
|
||||
"Attempt to access invalid position in list container %s at location %s. Please report!",
|
||||
container_name.c_str(),
|
||||
location);
|
||||
|
||||
SCP_string result = *list_it;
|
||||
|
||||
if (remove && are_containers_modifiable()) {
|
||||
if (is_being_used_in_special_arg()) {
|
||||
report_container_used_in_special_arg(Remove_op_prefix + location, container_name.c_str());
|
||||
} else {
|
||||
list_data.erase(list_it);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ListModifier-related functions
|
||||
|
||||
ListModifier get_list_modifier(const char *text, bool accept_prefix)
|
||||
@@ -495,70 +601,34 @@ bool get_replace_text_for_modifier(const SCP_string &text,
|
||||
}
|
||||
|
||||
const size_t modifier_length = strlen(get_list_modifier_name(modifier));
|
||||
int data_index = 0;
|
||||
int data_index = -1;
|
||||
size_t number_length = 0;
|
||||
SCP_string number_string;
|
||||
auto &list_data = container.list_data;
|
||||
auto list_it = list_data.begin();
|
||||
|
||||
switch (modifier) {
|
||||
case ListModifier::GET_FIRST:
|
||||
replacement_text = list_data.front();
|
||||
break;
|
||||
|
||||
case ListModifier::GET_LAST:
|
||||
replacement_text = list_data.back();
|
||||
break;
|
||||
|
||||
case ListModifier::REMOVE_FIRST:
|
||||
replacement_text = list_data.front();
|
||||
list_data.pop_front();
|
||||
break;
|
||||
|
||||
case ListModifier::REMOVE_LAST:
|
||||
replacement_text = list_data.back();
|
||||
list_data.pop_back();
|
||||
break;
|
||||
|
||||
case ListModifier::GET_RANDOM:
|
||||
data_index = util::Random::next((int)list_data.size());
|
||||
replacement_text = *std::next(list_it, data_index);
|
||||
break;
|
||||
|
||||
case ListModifier::REMOVE_RANDOM:
|
||||
data_index = util::Random::next((int)list_data.size());
|
||||
std::advance(list_it, data_index);
|
||||
replacement_text = *list_it;
|
||||
list_data.erase(list_it);
|
||||
break;
|
||||
|
||||
case ListModifier::AT_INDEX:
|
||||
number_string = text.substr(lookHere + modifier_length);
|
||||
if (modifier == ListModifier::AT_INDEX) {
|
||||
SCP_string number_string = text.substr(lookHere + modifier_length);
|
||||
if (number_string.empty()) {
|
||||
Warning(LOCATION,
|
||||
"Container text replacement found empty index into list container %s",
|
||||
container.container_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
data_index = atoi(number_string.c_str());
|
||||
if (data_index < 0 || data_index >= (int)list_data.size()) {
|
||||
|
||||
if (data_index < 0 || data_index >= (int)container.list_data.size()) {
|
||||
Warning(LOCATION,
|
||||
"Container text replacement found invalid index '%s' into list container %s",
|
||||
number_string.c_str(),
|
||||
container.container_name.c_str());
|
||||
return false;
|
||||
}
|
||||
replacement_text = *std::next(list_it, data_index);
|
||||
|
||||
// include spaces, in case the FREDder put one between "At" and the index
|
||||
number_length = strspn(number_string.c_str(), " 0123456789");
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE("Unhandled list modifier %d. Please report!", (int)modifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
replacement_text = container.apply_list_modifier(modifier, data_index);
|
||||
|
||||
num_chars_to_replace += modifier_length;
|
||||
|
||||
// for the "At" modifier, we need to include the chars used by the index
|
||||
@@ -668,28 +738,6 @@ bool sexp_container_replace_refs_with_values(char *text, size_t max_len)
|
||||
return replaced_anything;
|
||||
}
|
||||
|
||||
// Containers should not be modified if the game is simply checking the syntax.
|
||||
bool are_containers_modifiable()
|
||||
{
|
||||
// can always modify containers during the mission itself
|
||||
if (Game_mode & GM_IN_MISSION) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// can also modify if we are calling the sexp from a script or if we are briefing / debriefing, etc.
|
||||
switch (gameseq_get_state()) {
|
||||
case GS_STATE_BRIEFING:
|
||||
case GS_STATE_DEBRIEF:
|
||||
case GS_STATE_CMD_BRIEF:
|
||||
case GS_STATE_FICTION_VIEWER:
|
||||
case GS_STATE_SCRIPTING:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool sexp_container_CTEXT_helper(int &node, sexp_container &container, SCP_string &result)
|
||||
{
|
||||
if (container.is_map()) {
|
||||
@@ -722,7 +770,6 @@ bool sexp_container_CTEXT_helper(int &node, sexp_container &container, SCP_strin
|
||||
const char *modifier_name = CTEXT(node);
|
||||
const auto modifier = get_list_modifier(modifier_name);
|
||||
|
||||
|
||||
if (modifier == ListModifier::INVALID) {
|
||||
const SCP_string &container_name = container.container_name;
|
||||
Warning(LOCATION,
|
||||
@@ -737,60 +784,32 @@ bool sexp_container_CTEXT_helper(int &node, sexp_container &container, SCP_strin
|
||||
}
|
||||
|
||||
int data_index = -1;
|
||||
auto &list_data = container.list_data;
|
||||
auto list_it = list_data.begin();
|
||||
|
||||
switch (modifier) {
|
||||
case ListModifier::GET_FIRST:
|
||||
case ListModifier::REMOVE_FIRST:
|
||||
result = list_data.front();
|
||||
if (modifier == ListModifier::REMOVE_FIRST && are_containers_modifiable()) {
|
||||
list_data.pop_front();
|
||||
}
|
||||
return true;
|
||||
|
||||
case ListModifier::GET_LAST:
|
||||
case ListModifier::REMOVE_LAST:
|
||||
result = list_data.back();
|
||||
if (modifier == ListModifier::REMOVE_LAST && are_containers_modifiable()) {
|
||||
list_data.pop_back();
|
||||
}
|
||||
return true;
|
||||
|
||||
case ListModifier::GET_RANDOM:
|
||||
case ListModifier::REMOVE_RANDOM:
|
||||
data_index = util::Random::next((int)list_data.size());
|
||||
std::advance(list_it, data_index);
|
||||
result = *list_it;
|
||||
if (modifier == ListModifier::REMOVE_RANDOM && are_containers_modifiable()) {
|
||||
list_data.erase(list_it);
|
||||
}
|
||||
return true;
|
||||
|
||||
case ListModifier::AT_INDEX:
|
||||
if (modifier == ListModifier::AT_INDEX) {
|
||||
node = CDR(node);
|
||||
if (node == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_nan, is_nan_forever;
|
||||
data_index = eval_num(node, is_nan, is_nan_forever);
|
||||
if (is_nan || is_nan_forever) {
|
||||
return false;
|
||||
}
|
||||
if (data_index < 0 || data_index >= (int)list_data.size()) {
|
||||
|
||||
if (data_index < 0 || data_index >= (int)container.list_data.size()) {
|
||||
return false;
|
||||
}
|
||||
result = *std::next(list_it, data_index);
|
||||
return true;
|
||||
|
||||
default:
|
||||
UNREACHABLE("Unhandled list modifier %d found for container %s",
|
||||
(int)modifier,
|
||||
container.container_name.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
result = container.apply_list_modifier(modifier, data_index);
|
||||
return true;
|
||||
} else {
|
||||
UNREACHABLE("Container %s has invalid type (%d). Please report!",
|
||||
container.container_name.c_str(),
|
||||
(int)container.type);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// inspired by sexp_campaign_file_variable_count()
|
||||
@@ -1750,6 +1769,9 @@ void sexp_container_set_special_arg_status(int arg_handler_node, bool status)
|
||||
"Attempt to set special argument status on invalid argument handler. Please report!");
|
||||
|
||||
for (int arg_node = CDR(arg_handler_node); arg_node != -1; arg_node = CDR(arg_node)) {
|
||||
Assertion(Sexp_nodes[arg_node].subtype != SEXP_ATOM_CONTAINER_DATA,
|
||||
"Attempt to use Replace Container Data as special argument option, which isn't supported. Please report!");
|
||||
|
||||
if (Sexp_nodes[arg_node].subtype == SEXP_ATOM_CONTAINER_NAME) {
|
||||
const char *container_name = Sexp_nodes[arg_node].text;
|
||||
auto *p_container = get_sexp_container(container_name);
|
||||
|
||||
+32
-22
@@ -87,6 +87,28 @@ struct map_container_hash
|
||||
uint32_t operator()(const SCP_string &str) const;
|
||||
};
|
||||
|
||||
struct list_modifier {
|
||||
enum class Modifier
|
||||
{
|
||||
INVALID = 0,
|
||||
GET_FIRST,
|
||||
GET_LAST,
|
||||
REMOVE_FIRST,
|
||||
REMOVE_LAST,
|
||||
GET_RANDOM,
|
||||
REMOVE_RANDOM,
|
||||
AT_INDEX
|
||||
};
|
||||
|
||||
const char *name;
|
||||
const Modifier modifier;
|
||||
};
|
||||
|
||||
using ListModifier = list_modifier::Modifier;
|
||||
|
||||
ListModifier get_list_modifier(const char *text, bool accept_prefix = false);
|
||||
const char *get_list_modifier_name(ListModifier modifier);
|
||||
|
||||
struct sexp_container
|
||||
{
|
||||
// meta-character for containers in text replacement, etc.
|
||||
@@ -150,30 +172,18 @@ struct sexp_container
|
||||
bool is_of_string_type() const;
|
||||
// returns data for list container or key for map container
|
||||
const SCP_string &get_value_at_index(int index) const;
|
||||
|
||||
// list modifier operations
|
||||
SCP_string apply_list_modifier(ListModifier modifier, int data_index = -1);
|
||||
|
||||
private:
|
||||
SCP_string list_get_first(bool remove);
|
||||
SCP_string list_get_last(bool remove);
|
||||
SCP_string list_get_random(bool remove);
|
||||
SCP_string list_get_at(int index);
|
||||
SCP_string list_apply_iterator(SCP_list<SCP_string>::iterator list_it, const char *location, bool remove);
|
||||
};
|
||||
|
||||
struct list_modifier {
|
||||
enum class Modifier
|
||||
{
|
||||
INVALID = 0,
|
||||
GET_FIRST,
|
||||
GET_LAST,
|
||||
REMOVE_FIRST,
|
||||
REMOVE_LAST,
|
||||
GET_RANDOM,
|
||||
REMOVE_RANDOM,
|
||||
AT_INDEX
|
||||
};
|
||||
|
||||
const char *name;
|
||||
const Modifier modifier;
|
||||
};
|
||||
|
||||
using ListModifier = list_modifier::Modifier;
|
||||
|
||||
ListModifier get_list_modifier(const char *text, bool accept_prefix = false);
|
||||
const char *get_list_modifier_name(ListModifier modifier);
|
||||
|
||||
// management functions
|
||||
void init_sexp_containers();
|
||||
void update_sexp_containers(SCP_vector<sexp_container> &containers,
|
||||
|
||||
@@ -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
|
||||
|
||||
+52
-4
@@ -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();
|
||||
}
|
||||
|
||||
@@ -1124,7 +1170,9 @@ bool pilotfile::load_player(const char* callsign, player* _p, bool force_binary)
|
||||
}
|
||||
|
||||
mprintf(("PLR => Loading complete!\n"));
|
||||
OnPlayerLoadedHook->run(scripting::hook_param_list(scripting::hook_param("Player", 'o', scripting::api::l_Player.Set(scripting::api::player_h(p)))));
|
||||
if (OnPlayerLoadedHook->isActive()) {
|
||||
OnPlayerLoadedHook->run(scripting::hook_param_list(scripting::hook_param("Player", 'o', scripting::api::l_Player.Set(scripting::api::player_h(p)))));
|
||||
}
|
||||
|
||||
// cleanup and return
|
||||
plr_close();
|
||||
|
||||
@@ -165,7 +165,9 @@ void init_new_pilot(player *p, int reset)
|
||||
|
||||
pilot_set_start_campaign(p);
|
||||
|
||||
OnCampaignBeginHook->run(scripting::hook_param_list(scripting::hook_param("Campaign", 's', p->current_campaign)));
|
||||
if (OnCampaignBeginHook->isActive()) {
|
||||
OnCampaignBeginHook->run(scripting::hook_param_list(scripting::hook_param("Campaign", 's', p->current_campaign)));
|
||||
}
|
||||
}
|
||||
|
||||
int local_num_campaigns = 0;
|
||||
|
||||
@@ -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")
|
||||
{
|
||||
|
||||
@@ -334,6 +334,8 @@ ADE_VIRTVAR(MouseControlStatus, l_Mouse, "boolean", "Gets and sets the retail mo
|
||||
{
|
||||
Use_mouse_to_fly = false;
|
||||
}
|
||||
|
||||
mouse_flush();
|
||||
}
|
||||
|
||||
if (Use_mouse_to_fly)
|
||||
|
||||
@@ -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
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "vecmath.h"
|
||||
|
||||
#include "ai/ai.h"
|
||||
#include "mission/missionparse.h"
|
||||
#include "ship/ship.h"
|
||||
|
||||
namespace scripting {
|
||||
namespace api {
|
||||
@@ -39,17 +41,17 @@ inline int aici_getset_helper(lua_State* L, float control_info::* value) {
|
||||
return ade_set_args(L, "f", AI_ci.*value);
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(Pitch, l_AI_Helper, "number", "The pitch rate for the ship this frame, -1 to 1", "number", "The pitch rate, or 0 if the handle is invalid")
|
||||
ADE_VIRTVAR(Pitch, l_AI_Helper, "number", "The pitch thrust rate for the ship this frame, -1 to 1", "number", "The pitch rate, or 0 if the handle is invalid")
|
||||
{
|
||||
return aici_getset_helper(L, &control_info::pitch);
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(Bank, l_AI_Helper, "number", "The bank rate for the ship this frame, -1 to 1", "number", "The bank rate, or 0 if the handle is invalid")
|
||||
ADE_VIRTVAR(Bank, l_AI_Helper, "number", "The bank thrust rate for the ship this frame, -1 to 1", "number", "The bank rate, or 0 if the handle is invalid")
|
||||
{
|
||||
return aici_getset_helper(L, &control_info::bank);
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(Heading, l_AI_Helper, "number", "The heading rate for the ship this frame, -1 to 1", "number", "The heading rate, or 0 if the handle is invalid")
|
||||
ADE_VIRTVAR(Heading, l_AI_Helper, "number", "The heading thrust rate for the ship this frame, -1 to 1", "number", "The heading rate, or 0 if the handle is invalid")
|
||||
{
|
||||
return aici_getset_helper(L, &control_info::heading);
|
||||
}
|
||||
@@ -71,7 +73,7 @@ ADE_VIRTVAR(SidewaysThrust, l_AI_Helper, "number", "The sideways thrust rate for
|
||||
|
||||
ADE_FUNC(turnTowardsPoint,
|
||||
l_AI_Helper,
|
||||
"vector target, [boolean respectDifficulty = true, vector turnrateModifier /* 100% of tabled values in all rotation axes by default */]",
|
||||
"vector target, [boolean respectDifficulty = true, vector turnrateModifier /* 100% of tabled values in all rotation axes by default */, number bank /* native bank-on-heading by default */ ]",
|
||||
"turns the ship towards the specified point during this frame",
|
||||
nullptr,
|
||||
nullptr)
|
||||
@@ -80,11 +82,14 @@ ADE_FUNC(turnTowardsPoint,
|
||||
vec3d* target;
|
||||
bool diffTurn = true;
|
||||
vec3d* modifier = nullptr;
|
||||
if (!ade_get_args(L, "oo|bo", l_AI_Helper.Get(&ship), l_Vector.GetPtr(&target), &diffTurn, l_Vector.GetPtr(&modifier))) {
|
||||
float bank = 0.0f;
|
||||
|
||||
int argnum = ade_get_args(L, "oo|bof", l_AI_Helper.Get(&ship), l_Vector.GetPtr(&target), &diffTurn, l_Vector.GetPtr(&modifier), &bank);
|
||||
if (argnum == 0) {
|
||||
return ADE_RETURN_NIL;
|
||||
}
|
||||
|
||||
ai_turn_towards_vector(target, ship.objp, nullptr, nullptr, 0.0f, diffTurn ? 0 : AITTV_FAST, nullptr, modifier);
|
||||
ai_turn_towards_vector(target, ship.objp, nullptr, nullptr, bank, (diffTurn ? 0 : AITTV_FAST) | (argnum >= 5 ? AITTV_FORCE_DELTA_BANK : 0), nullptr, modifier);
|
||||
return ADE_RETURN_NIL;
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -327,7 +327,9 @@ ADE_FUNC(loadCampaign, l_Player, "string campaign", "Loads the specified campaig
|
||||
Pilot.save_savefile();
|
||||
}
|
||||
|
||||
OnCampaignBeginHook->run(scripting::hook_param_list(scripting::hook_param("Campaign", 's', Campaign.filename)));
|
||||
if (OnCampaignBeginHook->isActive()) {
|
||||
OnCampaignBeginHook->run(scripting::hook_param_list(scripting::hook_param("Campaign", 's', Campaign.filename)));
|
||||
}
|
||||
}
|
||||
|
||||
// that's all we need to do for now; the campaign loading status will be checked again when we try to load the
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+37
-21
@@ -196,7 +196,7 @@ int Player_ship_class; // needs to be player specific, move to player structure
|
||||
#define SHIP_OBJ_USED (1<<0) // flag used in ship_obj struct
|
||||
#define MAX_SHIP_OBJS MAX_SHIPS // max number of ships tracked in ship list
|
||||
ship_obj Ship_objs[MAX_SHIP_OBJS]; // array used to store ship object indexes
|
||||
ship_obj Ship_obj_list; // head of linked list of ship_obj structs
|
||||
ship_obj Ship_obj_list; // head of linked list of ship_obj structs, Standalone ship cannot be in this list or it will cause bugs.
|
||||
|
||||
SCP_vector<ship_info> Ship_info;
|
||||
reinforcements Reinforcements[MAX_REINFORCEMENTS];
|
||||
@@ -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;
|
||||
}
|
||||
@@ -7419,10 +7419,20 @@ void ship_render_show_ship_cockpit(object *objp)
|
||||
model_render_params render_info;
|
||||
|
||||
render_info.set_object_number(OBJ_INDEX(objp));
|
||||
render_info.set_replacement_textures(Ships[objp->instance].ship_replacement_textures);
|
||||
|
||||
// update any replacement and/or team color textures (wookieejedi), then render
|
||||
ship* shipp = &Ships[objp->instance];
|
||||
ship_info* sip = &Ship_info[Ships[objp->instance].ship_info_index];
|
||||
|
||||
render_info.set_replacement_textures(shipp->ship_replacement_textures);
|
||||
|
||||
if (sip->uses_team_colors) {
|
||||
render_info.set_team_color(shipp->team_name, shipp->secondary_team_name, 0, 0);
|
||||
}
|
||||
|
||||
render_info.set_detail_level_lock(0);
|
||||
|
||||
model_render_immediate(&render_info, Ship_info[Ships[objp->instance].ship_info_index].model_num, &objp->orient,
|
||||
model_render_immediate(&render_info, sip->model_num, &objp->orient,
|
||||
&objp->pos); // Render ship model with fixed detail level 0 so its not switching LOD when
|
||||
// moving away from origin
|
||||
Glowpoint_override = false;
|
||||
@@ -7864,10 +7874,12 @@ void ship_destroy_instantly(object *ship_objp, bool with_debris)
|
||||
Assert(ship_objp->type == OBJ_SHIP);
|
||||
Assert(!(ship_objp == Player_obj));
|
||||
|
||||
// add scripting hook for 'On Ship Death Started' -- Goober5000
|
||||
// hook is placed at the beginning of this function to allow the scripter to
|
||||
// actually have access to the ship before any death routines (such as mission logging) are executed
|
||||
OnShipDeathStartedHook->run(scripting::hook_param_list(scripting::hook_param("Ship", 'o', ship_objp)));
|
||||
if (OnShipDeathStartedHook->isActive()) {
|
||||
// add scripting hook for 'On Ship Death Started' -- Goober5000
|
||||
// hook is placed at the beginning of this function to allow the scripter to
|
||||
// actually have access to the ship before any death routines (such as mission logging) are executed
|
||||
OnShipDeathStartedHook->run(scripting::hook_param_list(scripting::hook_param("Ship", 'o', ship_objp)));
|
||||
}
|
||||
|
||||
// undocking and death preparation
|
||||
ship_stop_fire_primary(ship_objp);
|
||||
@@ -9882,7 +9894,7 @@ static void ship_init_afterburners(ship *shipp)
|
||||
* Returns object index of ship.
|
||||
* @return -1 means failed.
|
||||
*/
|
||||
int ship_create(matrix* orient, vec3d* pos, int ship_type, const char* ship_name)
|
||||
int ship_create(matrix* orient, vec3d* pos, int ship_type, const char* ship_name, bool standalone_ship)
|
||||
{
|
||||
int i, n, objnum, j, k, t;
|
||||
ship_info *sip;
|
||||
@@ -10074,8 +10086,10 @@ int ship_create(matrix* orient, vec3d* pos, int ship_type, const char* ship_name
|
||||
|
||||
animation::anim_set_initial_states(shipp);
|
||||
|
||||
// Add this ship to Ship_obj_list
|
||||
shipp->ship_list_index = ship_obj_list_add(objnum);
|
||||
// Add this ship to Ship_obj_list, if it is *not* the standalone ship. That can cause big time bugs.
|
||||
if (!standalone_ship){
|
||||
shipp->ship_list_index = ship_obj_list_add(objnum);
|
||||
}
|
||||
|
||||
// Goober5000 - update the ship registry
|
||||
// (since scripts and sexps can create ships, the entry may not yet exist)
|
||||
@@ -10878,7 +10892,7 @@ void change_ship_type(int n, int ship_type, int by_sexp)
|
||||
if (sip->uses_team_colors)
|
||||
{
|
||||
// wookieejedi - maintain team color setting if possible
|
||||
if (!p_objp->team_color_setting.empty()) {
|
||||
if (!Fred_running && !p_objp->team_color_setting.empty()) {
|
||||
sp->team_name = p_objp->team_color_setting;
|
||||
} else {
|
||||
sp->team_name = sip->default_team_name;
|
||||
@@ -10988,10 +11002,12 @@ int ship_launch_countermeasure(object *objp, int rand_val)
|
||||
send_NEW_countermeasure_fired_packet(objp, cmeasure_count, Objects[cobjnum].net_signature);
|
||||
}
|
||||
|
||||
// add scripting hook for 'On Countermeasure Fire' --wookieejedi
|
||||
OnCountermeasureFireHook->run(scripting::hook_param_list(scripting::hook_param("Ship", 'o', objp),
|
||||
scripting::hook_param("CountermeasuresLeft", 'i', shipp->cmeasure_count),
|
||||
scripting::hook_param("Countermeasure", 'o', &Objects[cobjnum])));
|
||||
if (OnCountermeasureFireHook->isActive()) {
|
||||
// add scripting hook for 'On Countermeasure Fire' --wookieejedi
|
||||
OnCountermeasureFireHook->run(scripting::hook_param_list(scripting::hook_param("Ship", 'o', objp),
|
||||
scripting::hook_param("CountermeasuresLeft", 'i', shipp->cmeasure_count),
|
||||
scripting::hook_param("Countermeasure", 'o', &Objects[cobjnum])));
|
||||
}
|
||||
}
|
||||
|
||||
return (cobjnum >= 0); // return 0 if not fired, 1 otherwise
|
||||
@@ -15827,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;
|
||||
|
||||
@@ -15836,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;
|
||||
|
||||
@@ -15852,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;
|
||||
|
||||
@@ -15863,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;
|
||||
}
|
||||
@@ -20247,4 +20263,4 @@ int ship_check_visibility(const ship* viewed, ship* viewer)
|
||||
}
|
||||
|
||||
return ship_is_visible;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1584,7 +1584,7 @@ extern void ship_init(); // called once at game start
|
||||
extern void ship_level_init(); // called before the start of each level
|
||||
|
||||
//returns -1 if failed
|
||||
extern int ship_create(matrix* orient, vec3d* pos, int ship_type, const char* ship_name = nullptr);
|
||||
extern int ship_create(matrix* orient, vec3d* pos, int ship_type, const char* ship_name = nullptr, bool standalone_ship = false);
|
||||
extern void change_ship_type(int n, int ship_type, int by_sexp = 0);
|
||||
extern void ship_process_pre( object * objp, float frametime );
|
||||
extern void ship_process_post( object * objp, float frametime );
|
||||
|
||||
@@ -182,8 +182,8 @@ namespace Ship {
|
||||
Awacs, // ditto
|
||||
Knossos_device, // this is the knossos device
|
||||
No_fred, // not available in fred
|
||||
Default_in_tech_database, // default in tech database - Goober5000
|
||||
Default_in_tech_database_m, // ditto - Goober5000
|
||||
Default_in_tech_database, // this entry's default tech database status, as specified in ships.tbl; used when the tech db is "reset to default" - Goober5000
|
||||
Default_in_tech_database_m, // ditto for multiplayer - Goober5000
|
||||
Flash, // makes a flash when it explodes
|
||||
Show_ship_model, // Show ship model even in first person view
|
||||
Surface_shields, // _argv[-1], 16 Jan 2005: Enable surface shields for this ship.
|
||||
|
||||
+18
-8
@@ -537,10 +537,13 @@ void shipfx_warpin_start( object *objp )
|
||||
}
|
||||
|
||||
//WMC - Check if scripting handles this.
|
||||
if(OnWarpInHook->isOverride(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp))
|
||||
if (OnWarpInHook->isActive())
|
||||
{
|
||||
OnWarpInHook->run(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp);
|
||||
return;
|
||||
if (OnWarpInHook->isOverride(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp))
|
||||
{
|
||||
OnWarpInHook->run(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if there is no arrival warp, set the flag but don't create the effect
|
||||
@@ -554,7 +557,10 @@ void shipfx_warpin_start( object *objp )
|
||||
Assertion(shipp->warpin_effect != nullptr, "shipfx_warpin_start() was fed a ship with an uninitialized warpin_effect.");
|
||||
shipp->warpin_effect->warpStart();
|
||||
|
||||
OnWarpInHook->run(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp);
|
||||
if (OnWarpInHook->isActive())
|
||||
{
|
||||
OnWarpInHook->run(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp);
|
||||
}
|
||||
}
|
||||
|
||||
void shipfx_warpin_frame( object *objp, float frametime )
|
||||
@@ -673,9 +679,11 @@ void shipfx_warpout_start( object *objp )
|
||||
return;
|
||||
}
|
||||
|
||||
if (OnWarpOutHook->isOverride(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp)) {
|
||||
OnWarpOutHook->run(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp);
|
||||
return;
|
||||
if (OnWarpOutHook->isActive()) {
|
||||
if (OnWarpOutHook->isOverride(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp)) {
|
||||
OnWarpOutHook->run(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we're dying return
|
||||
@@ -712,7 +720,9 @@ void shipfx_warpout_start( object *objp )
|
||||
"shipfx_warpout_start() was fed a ship with an uninitialized warpout_effect.");
|
||||
shipp->warpout_effect->warpStart();
|
||||
|
||||
OnWarpOutHook->run(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp);
|
||||
if (OnWarpOutHook->isActive()) {
|
||||
OnWarpOutHook->run(scripting::hook_param_list(scripting::hook_param("Self", 'o', objp)), objp);
|
||||
}
|
||||
}
|
||||
|
||||
void shipfx_warpout_frame( object *objp, float frametime )
|
||||
|
||||
Loaded 100 of 115 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user