mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 17:48:46 -10:00
Compare commits
602
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19952f6c6c | ||
|
|
6618d3d2f8 | ||
|
|
60ddce0ad7 | ||
|
|
173cd2517f | ||
|
|
6006ef5208 | ||
|
|
afc198a82c | ||
|
|
2796eda3cb | ||
|
|
a5b9216fc6 | ||
|
|
5fbd0997a4 | ||
|
|
8bf3b01d16 | ||
|
|
ff2bb8ee95 | ||
|
|
690d600df8 | ||
|
|
e7a897aa4d | ||
|
|
050c76b721 | ||
|
|
7bb0c77bb5 | ||
|
|
e81001f6a6 | ||
|
|
945427a849 | ||
|
|
9d1c93041e | ||
|
|
5fb9a6f0c6 | ||
|
|
e2a359b546 | ||
|
|
e48011d175 | ||
|
|
8766c642fe | ||
|
|
565f4d469c | ||
|
|
8e7476c1b8 | ||
|
|
d41020222e | ||
|
|
9c1cf58f8d | ||
|
|
183db641d7 | ||
|
|
10a5a0d29a | ||
|
|
830ecf2ea2 | ||
|
|
472fd6f20f | ||
|
|
262e4d4489 | ||
|
|
c9d71ce1ab | ||
|
|
e742bd108a | ||
|
|
990d0b7784 | ||
|
|
7c5371af1a | ||
|
|
99972a17c6 | ||
|
|
2e743d9cd6 | ||
|
|
b7681cfee9 | ||
|
|
5eb924cedc | ||
|
|
d561cd87c4 | ||
|
|
562522e098 | ||
|
|
f7bfd637d8 | ||
|
|
235857ca10 | ||
|
|
f529fbc633 | ||
|
|
22b16c32d4 | ||
|
|
b1298c2b68 | ||
|
|
2ec18afe02 | ||
|
|
7f742a2145 | ||
|
|
b96d9f6e62 | ||
|
|
eaa3fe0979 | ||
|
|
1cd16b7281 | ||
|
|
44ce5e9b00 | ||
|
|
13b922207b | ||
|
|
efc686db2d | ||
|
|
2738919cc9 | ||
|
|
7e9121ed8e | ||
|
|
f0712cfb41 | ||
|
|
af8422cf7b | ||
|
|
39a16019e3 | ||
|
|
bbca518524 | ||
|
|
bb28c37829 | ||
|
|
ad2335cfbf | ||
|
|
7bf7bf5c1d | ||
|
|
99b7c89883 | ||
|
|
370419b1a2 | ||
|
|
17d1924897 | ||
|
|
a351e72895 | ||
|
|
cd4c6e483a | ||
|
|
82c36b1c59 | ||
|
|
47f84846e3 | ||
|
|
2fe789f87b | ||
|
|
f850725c14 | ||
|
|
344eb5a7ed | ||
|
|
10b357172a | ||
|
|
4892ac7b01 | ||
|
|
4474b0087d | ||
|
|
ee1b6263ba | ||
|
|
73ced78633 | ||
|
|
50266c5065 | ||
|
|
00f4797b45 | ||
|
|
2377b5dff2 | ||
|
|
f8c41ac377 | ||
|
|
aee9dc6910 | ||
|
|
2e5a6871c5 | ||
|
|
535b0857b6 | ||
|
|
2fa7ddc552 | ||
|
|
a6f821daee | ||
|
|
360198b99f | ||
|
|
7fe62aabcd | ||
|
|
d2859f4979 | ||
|
|
643a87a7f1 | ||
|
|
d1d78c980e | ||
|
|
148df94e50 | ||
|
|
d443e9e7d0 | ||
|
|
e1bbbf9f9a | ||
|
|
fdafe21684 | ||
|
|
7ccd13bbce | ||
|
|
9f45f49ec1 | ||
|
|
6b5124a8ad | ||
|
|
1187ebbc2c | ||
|
|
d92fab80ef | ||
|
|
1b9dd78233 | ||
|
|
ebb5ed9ba6 | ||
|
|
dabdc871a6 | ||
|
|
77a255af63 | ||
|
|
45c904c25e | ||
|
|
3d0bd54980 | ||
|
|
7228244f0c | ||
|
|
f0f5679731 | ||
|
|
1306a23fd4 | ||
|
|
f067a19583 | ||
|
|
8ad52cd8e0 | ||
|
|
a27976bf9a | ||
|
|
deefbf0860 | ||
|
|
c0f9ad7b3c | ||
|
|
482cc7d7a6 | ||
|
|
18754fafc1 | ||
|
|
d05b3fa637 | ||
|
|
a26367db5f | ||
|
|
e3617169ca | ||
|
|
3c487f81f3 | ||
|
|
878dd4b313 | ||
|
|
74d95f01bc | ||
|
|
226d51c214 | ||
|
|
9855062732 | ||
|
|
5709e24e9a | ||
|
|
13c410e864 | ||
|
|
5f4729bddb | ||
|
|
65fa916cba | ||
|
|
473acced54 | ||
|
|
b58b60b555 | ||
|
|
9c3b90f403 | ||
|
|
3b0e02545e | ||
|
|
9e6e1ef123 | ||
|
|
2e4e9855bc | ||
|
|
9adc89a5a2 | ||
|
|
8faa521df9 | ||
|
|
a3513c38b6 | ||
|
|
15901e430f | ||
|
|
37584f576c | ||
|
|
3863d9b5a9 | ||
|
|
5318fba077 | ||
|
|
d187a6ae99 | ||
|
|
30b35b483b | ||
|
|
f7f0c2ba21 | ||
|
|
f1362a694d | ||
|
|
db55349f70 | ||
|
|
2d84fdf5c6 | ||
|
|
5e7a6ece52 | ||
|
|
ff62ea010e | ||
|
|
a14a746a4f | ||
|
|
d4c0c45595 | ||
|
|
06eaeca9d7 | ||
|
|
dd5c92a910 | ||
|
|
2de1776308 | ||
|
|
833b51e423 | ||
|
|
726d66d0ad | ||
|
|
c7e2b3c78f | ||
|
|
97a93162f8 | ||
|
|
1fa270ad66 | ||
|
|
5b316a562d | ||
|
|
9c14717b81 | ||
|
|
0b8bbb3f47 | ||
|
|
314833ae61 | ||
|
|
4a4d07872a | ||
|
|
fdc33d8ab5 | ||
|
|
c0430f0b55 | ||
|
|
0d2d94a49b | ||
|
|
4f46fff48c | ||
|
|
1b1d864c59 | ||
|
|
f1ddfafdf3 | ||
|
|
3481032ae5 | ||
|
|
3a4e5d75a3 | ||
|
|
84b25b52d7 | ||
|
|
f2d3aaaf20 | ||
|
|
ffbcdab9da | ||
|
|
8db2f374f2 | ||
|
|
46959276f5 | ||
|
|
d5afd8d79b | ||
|
|
2a8064a3e9 | ||
|
|
9741b36442 | ||
|
|
0fe59e10ad | ||
|
|
e287c575ce | ||
|
|
b14a27cdff | ||
|
|
63b0f0a373 | ||
|
|
c464a1cd4c | ||
|
|
6889ee856f | ||
|
|
292f1547c4 | ||
|
|
a687fe0aef | ||
|
|
31fa21bbf1 | ||
|
|
be6a7f552a | ||
|
|
93da6616f7 | ||
|
|
4aeaabf337 | ||
|
|
589071ac46 | ||
|
|
ccf0e7eb04 | ||
|
|
adb7d9d195 | ||
|
|
7a2a4b6640 | ||
|
|
e4f9ca05c5 | ||
|
|
0160fac2ff | ||
|
|
cc2d06d37c | ||
|
|
c5b29c62c6 | ||
|
|
8e2d93211c | ||
|
|
eca6647dbf | ||
|
|
b6e214a4d4 | ||
|
|
841a4b8cea | ||
|
|
19bc18cb32 | ||
|
|
823c76ec22 | ||
|
|
a75ad91518 | ||
|
|
bdefb55ec9 | ||
|
|
00f7f13371 | ||
|
|
1441cc8e85 | ||
|
|
a8a9127d81 | ||
|
|
028168d395 | ||
|
|
7dae8940d7 | ||
|
|
814d44c986 | ||
|
|
99fb2c00ab | ||
|
|
f7c3ab85d3 | ||
|
|
d03a1007df | ||
|
|
b6fe676072 | ||
|
|
07f4f4716b | ||
|
|
30e48c1b03 | ||
|
|
352fd3cdfc | ||
|
|
1becca7cbe | ||
|
|
07312ff4c0 | ||
|
|
f9c73e81f2 | ||
|
|
e2585a9a0e | ||
|
|
d6c65d5139 | ||
|
|
c3551f4361 | ||
|
|
d787bd7c6f | ||
|
|
7dd454f563 | ||
|
|
f1deee8566 | ||
|
|
b24289e918 | ||
|
|
94b1d11f47 | ||
|
|
c541ac91db | ||
|
|
eb6f7fb013 | ||
|
|
d2c24efeff | ||
|
|
318fed546f | ||
|
|
474f225672 | ||
|
|
14337c22b0 | ||
|
|
647334f4ff | ||
|
|
03d51ba400 | ||
|
|
3767758ae5 | ||
|
|
def814fa4e | ||
|
|
11c5af39d8 | ||
|
|
c6b75d3561 | ||
|
|
866a7c40df | ||
|
|
5076066627 | ||
|
|
70546b68a5 | ||
|
|
a6084ba707 | ||
|
|
27650ef45d | ||
|
|
4f5fb48f79 | ||
|
|
c1d2920526 | ||
|
|
90558a5858 | ||
|
|
390f979b7c | ||
|
|
7258861791 | ||
|
|
76b944e01d | ||
|
|
8006baec65 | ||
|
|
443e148869 | ||
|
|
f9265301af | ||
|
|
bd6dae97a7 | ||
|
|
74c6b0c3cb | ||
|
|
3685a78ea9 | ||
|
|
d0948e67e3 | ||
|
|
3660761d7c | ||
|
|
e6579fca09 | ||
|
|
da0459feeb | ||
|
|
b428899d5b | ||
|
|
00ec3431b7 | ||
|
|
353d66c17d | ||
|
|
84ceae724c | ||
|
|
c47518ec6e | ||
|
|
76c9583e45 | ||
|
|
131fadfab3 | ||
|
|
74233c029c | ||
|
|
c1d8834d81 | ||
|
|
7b69c10e56 | ||
|
|
c0bb5bc8eb | ||
|
|
2e56b3ca61 | ||
|
|
3c3f79bc8f | ||
|
|
462a234e81 | ||
|
|
eaf14760c4 | ||
|
|
a9e5505a7c | ||
|
|
1525b520d7 | ||
|
|
1843f76d04 | ||
|
|
41cf0b3d52 | ||
|
|
990b5a3908 | ||
|
|
e4661b00b1 | ||
|
|
4457471ec5 | ||
|
|
128107ea43 | ||
|
|
c840d3e032 | ||
|
|
35a09527e5 | ||
|
|
20e5f35fb4 | ||
|
|
d217873281 | ||
|
|
37c5041c03 | ||
|
|
fb657c081c | ||
|
|
7945baa751 | ||
|
|
e9a2ff0ebf | ||
|
|
4328b66a28 | ||
|
|
11c5bcda74 | ||
|
|
43f9a48cb9 | ||
|
|
c6e79a61ea | ||
|
|
5d58d9ca7a | ||
|
|
384b2e20c7 | ||
|
|
20e742ed61 | ||
|
|
29846e2101 | ||
|
|
a1635cdd76 | ||
|
|
c80144ce44 | ||
|
|
6e11f356ea | ||
|
|
7e4190b97e | ||
|
|
65e65b01b6 | ||
|
|
f3773388b9 | ||
|
|
b05132a64f | ||
|
|
6c399ccace | ||
|
|
373a8b940a | ||
|
|
b4d0db8846 | ||
|
|
9c0575f173 | ||
|
|
64f5ad925b | ||
|
|
81b06fec41 | ||
|
|
3c553665aa | ||
|
|
f252473b84 | ||
|
|
1caf54e909 | ||
|
|
6aecc4660d | ||
|
|
c915bfd474 | ||
|
|
bd2356600f | ||
|
|
9c6cc422ac | ||
|
|
7bd74b2a7b | ||
|
|
9d5eeb6e08 | ||
|
|
5ffc05fa85 | ||
|
|
8ae77e0356 | ||
|
|
a113897c88 | ||
|
|
4ee04f2492 | ||
|
|
093a471602 | ||
|
|
22be402de5 | ||
|
|
84133c42a8 | ||
|
|
df1c518c3b | ||
|
|
8ac4c54d43 | ||
|
|
1445e778f0 | ||
|
|
2b6041f766 | ||
|
|
59d30157d8 | ||
|
|
552201884b | ||
|
|
e053d06a5f | ||
|
|
f677767846 | ||
|
|
9b35a4e653 | ||
|
|
6c0bfd4df5 | ||
|
|
102bc28424 | ||
|
|
081e34815c | ||
|
|
6cf5553147 | ||
|
|
80f4f1abde | ||
|
|
2e30674823 | ||
|
|
448d0457c8 | ||
|
|
9d7acbcc9d | ||
|
|
c8cee4cdb1 | ||
|
|
21aa608eb6 | ||
|
|
d6a0d9fb02 | ||
|
|
edcdbd02b6 | ||
|
|
d49e24fa0c | ||
|
|
e74ebe6b84 | ||
|
|
c197755c0e | ||
|
|
ec4de81a79 | ||
|
|
c10d6ececf | ||
|
|
6c2ebab01c | ||
|
|
9a682ea32f | ||
|
|
a5f501b58a | ||
|
|
5f8cdb37bd | ||
|
|
f998215b07 | ||
|
|
106b16d5eb | ||
|
|
61faa3fa9a | ||
|
|
3a960db633 | ||
|
|
b89d395578 | ||
|
|
f33b42085e | ||
|
|
87f449415f | ||
|
|
a59bb99fc6 | ||
|
|
ca6cc21be8 | ||
|
|
b52ab8bd4e | ||
|
|
a5a2f9e3df | ||
|
|
40840ee3f9 | ||
|
|
76af0129f3 | ||
|
|
f91d3dbe17 | ||
|
|
5ad373502c | ||
|
|
37c9ecd0e3 | ||
|
|
b92cbaff64 | ||
|
|
ed69a1584b | ||
|
|
789243ea06 | ||
|
|
2701b557b8 | ||
|
|
b4fe5c9cbe | ||
|
|
f1ee621624 | ||
|
|
bcf87e8a02 | ||
|
|
3d6fe919da | ||
|
|
2e272cce2d | ||
|
|
309bfd7825 | ||
|
|
308794801c | ||
|
|
0b26662755 | ||
|
|
8fe55fa659 | ||
|
|
9f1e556031 | ||
|
|
d77554767d | ||
|
|
52e2cda4a0 | ||
|
|
3a888906c5 | ||
|
|
638a25ea0d | ||
|
|
6d60647a2a | ||
|
|
cd2e69eb3b | ||
|
|
e92ba1849b | ||
|
|
ce9a0d5e08 | ||
|
|
d971f34ee2 | ||
|
|
47f6d2b368 | ||
|
|
144d484c85 | ||
|
|
3c809a9bb5 | ||
|
|
1c1ee910fb | ||
|
|
6f2e707f5a | ||
|
|
3a427d923b | ||
|
|
a663a7d916 | ||
|
|
42d96d967e | ||
|
|
a3c5186635 | ||
|
|
cf7fa02e6d | ||
|
|
72aa1eebc9 | ||
|
|
28f60df7fa | ||
|
|
bed18012d6 | ||
|
|
d43f30053d | ||
|
|
76dccbe07d | ||
|
|
3eafcde138 | ||
|
|
ca7065df46 | ||
|
|
cedf66cb31 | ||
|
|
03a3fbdc1c | ||
|
|
9c6661e0e8 | ||
|
|
6bea921fbe | ||
|
|
79f5eda0d6 | ||
|
|
4e4c8449fd | ||
|
|
7980891b48 | ||
|
|
3aa61dfccb | ||
|
|
a6a7482467 | ||
|
|
0febfb3545 | ||
|
|
e1c8f5d2ed | ||
|
|
6ce2a445fe | ||
|
|
96ae629ec6 | ||
|
|
1b9577eb85 | ||
|
|
beeb18b46c | ||
|
|
44343f8d1e | ||
|
|
424c69d1e5 | ||
|
|
6d1354f1f6 | ||
|
|
d5c4f3e5c7 | ||
|
|
89bd64f4ab | ||
|
|
417739665c | ||
|
|
11f2920b4f | ||
|
|
9e4ab292ac | ||
|
|
51addab005 | ||
|
|
e236a79773 | ||
|
|
d523f0aef3 | ||
|
|
697c009fb3 | ||
|
|
1f918fd4c6 | ||
|
|
3ea7a6d1ba | ||
|
|
743989525b | ||
|
|
5bca1417c2 | ||
|
|
633cd88e83 | ||
|
|
7431aba995 | ||
|
|
328bebe83a | ||
|
|
94e4f3d4cd | ||
|
|
b57573c439 | ||
|
|
0b1d1eed56 | ||
|
|
123feba557 | ||
|
|
8c64d73a18 | ||
|
|
e156f15561 | ||
|
|
3e1d89ba53 | ||
|
|
4f7bad2a5c | ||
|
|
dd621e8904 | ||
|
|
396d2c55a9 | ||
|
|
8f5064b353 | ||
|
|
782f6f100d | ||
|
|
6ec1f45997 | ||
|
|
4f76c441d6 | ||
|
|
a2bd895b7b | ||
|
|
a4cf53694d | ||
|
|
6bd7a43e3d | ||
|
|
fffc0ebf8b | ||
|
|
f2426de80b | ||
|
|
27f6088117 | ||
|
|
30c76f36ce | ||
|
|
35094851f2 | ||
|
|
b562cf822e | ||
|
|
62942557d5 | ||
|
|
4d68bed436 | ||
|
|
19d5b8e68e | ||
|
|
9dc50706a7 | ||
|
|
f2a0e35959 | ||
|
|
e637eea2fb | ||
|
|
f3e3769056 | ||
|
|
e218f687a9 | ||
|
|
5e10fccdbf | ||
|
|
12672d0c2b | ||
|
|
de7081fbb3 | ||
|
|
d73e4d4dec | ||
|
|
aeefcb4657 | ||
|
|
023dcfca76 | ||
|
|
c6e9f954f0 | ||
|
|
961d5eb171 | ||
|
|
1a66c97c2e | ||
|
|
00e2c2ad52 | ||
|
|
8991fc67cd | ||
|
|
7641abf41c | ||
|
|
871f7a822e | ||
|
|
ca66664eac | ||
|
|
4c1b3551b4 | ||
|
|
07d47cb984 | ||
|
|
ce9ee8c264 | ||
|
|
8039650248 | ||
|
|
cd2b578a1b | ||
|
|
052879b3e4 | ||
|
|
85ec8251e3 | ||
|
|
7e51a13c3a | ||
|
|
538b2c6c95 | ||
|
|
d3a70e8ed6 | ||
|
|
9dc68849d6 | ||
|
|
b6bab87601 | ||
|
|
b8d868f828 | ||
|
|
01e6a57aaa | ||
|
|
e0fe9227aa | ||
|
|
803751959a | ||
|
|
9597b5962e | ||
|
|
792f74b33e | ||
|
|
5181e71dab | ||
|
|
3d0e0f80ae | ||
|
|
251125913a | ||
|
|
89d2d6ed46 | ||
|
|
06f198afc1 | ||
|
|
19a289539a | ||
|
|
c14653b391 | ||
|
|
2527bf31c7 | ||
|
|
184107283a | ||
|
|
42c19cdfee | ||
|
|
3830534978 | ||
|
|
48eef6566d | ||
|
|
b253442545 | ||
|
|
26314c9b97 | ||
|
|
5ebb12e47c | ||
|
|
1d5e9794d6 | ||
|
|
b7a9f545fd | ||
|
|
9541e244d1 | ||
|
|
f12d202afe | ||
|
|
507a5f1de8 | ||
|
|
3810b039f5 | ||
|
|
827754d6de | ||
|
|
d455e17e34 | ||
|
|
7a7999ca56 | ||
|
|
db360820a9 | ||
|
|
804a222041 | ||
|
|
6dab1e464f | ||
|
|
86c45309e8 | ||
|
|
4f4690a482 | ||
|
|
1ca60e1d2f | ||
|
|
011f7e4d6c | ||
|
|
eb538ffba8 | ||
|
|
2a73fb340f | ||
|
|
edee63491d | ||
|
|
5bd351d42a | ||
|
|
ad60240eb6 | ||
|
|
29b03cd49f | ||
|
|
f8df97b489 | ||
|
|
d5feddfb8a | ||
|
|
f5e32fa1a2 | ||
|
|
9fa3baa458 | ||
|
|
4b3760dfb4 | ||
|
|
142d586a1c | ||
|
|
95103dfcb8 | ||
|
|
f50ea26051 | ||
|
|
983dd0b6ca | ||
|
|
3bdc5e0c27 | ||
|
|
ba6a471054 | ||
|
|
ce44e3522b | ||
|
|
c1b87bc2b6 | ||
|
|
1f164dbb64 | ||
|
|
1275cbfd83 | ||
|
|
20cccdbf20 | ||
|
|
077338086a | ||
|
|
0b2ee5f76b | ||
|
|
3f9218e671 | ||
|
|
3003d0589d | ||
|
|
4bd15d43da | ||
|
|
8df62e99f5 | ||
|
|
d93c9c4220 | ||
|
|
1b5fd3c5fd | ||
|
|
8292d8c44a | ||
|
|
78c79ac88d | ||
|
|
6e54ed3dfc | ||
|
|
3c53b74e5e | ||
|
|
829e0b8b7c | ||
|
|
4b60ca467c | ||
|
|
b45cb19f3c | ||
|
|
e30c919286 | ||
|
|
5004f6ab32 | ||
|
|
2ee128cd88 | ||
|
|
a2b349855e | ||
|
|
4b3a1a8d84 | ||
|
|
824c537bd2 | ||
|
|
bc8e16b37b | ||
|
|
79e4c9b4e3 | ||
|
|
70969c4475 | ||
|
|
ecbb6a12f8 | ||
|
|
218059ffbb | ||
|
|
95de0c6833 | ||
|
|
ab2c08392e | ||
|
|
1239ec47e3 | ||
|
|
eb1814e303 | ||
|
|
6ce29b5c6a | ||
|
|
bc99331d47 |
No files matched your search
@@ -15,7 +15,7 @@ jobs:
|
||||
configuration: [FastDebug, Release]
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-e29f882
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
@@ -210,3 +210,28 @@ jobs:
|
||||
DATACORDER_USER: ${{ secrets.DATACORDER_USER }}
|
||||
DATACORDER_SSHPASS: ${{ secrets.DATACORDER_PASSWORD }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh nightly
|
||||
|
||||
post_builds:
|
||||
# Post nightlies if at least one job was successful
|
||||
if: ${{ needs.linux_zip == "success" || needs.windows_zip == "success" }}
|
||||
name: Post builds on Nebula and the forums
|
||||
needs: [linux_zip, windows_zip]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
- name: Install Python dependencies
|
||||
run: pip install -r ci/post/requirements.txt
|
||||
- name: Post builds
|
||||
env:
|
||||
INDIEGAMES_USER: ${{ secrets.INDIEGAMES_USER }}
|
||||
INDIEGAMES_SSHPASS: ${{ secrets.INDIEGAMES_PASSWORD }}
|
||||
NEBULA_USER: ${{ secrets.NEBULA_USER }}
|
||||
NEBULA_PASSWORD: ${{ secrets.NEBULA_PASSWORD }}
|
||||
HLP_API: ${{ secrets.HLP_API }}
|
||||
HLP_KEY: ${{ secrets.HLP_KEY }}
|
||||
LINUX_RESULT: ${{ needs.linux_zip.result }}
|
||||
WINDOWS_RESULT: ${{ needs.windows_zip.result }}
|
||||
run: python ci/post/main.py nightly
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
name: Linux
|
||||
needs: create_release
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-e29f882
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
@@ -263,3 +263,26 @@ jobs:
|
||||
asset_path: ${{ steps.generate_package.outputs.debug_path }}
|
||||
asset_name: ${{ steps.generate_package.outputs.debug_name }}
|
||||
asset_content_type: ${{ steps.generate_package.outputs.debug_mime }}
|
||||
|
||||
post_builds:
|
||||
name: Post builds on Nebula and the forums
|
||||
needs: [linux_zip, windows_zip]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
- name: Install Python dependencies
|
||||
run: pip install -r ci/post/requirements.txt
|
||||
- name: Post builds
|
||||
env:
|
||||
INDIEGAMES_USER: ${{ secrets.INDIEGAMES_USER }}
|
||||
INDIEGAMES_SSHPASS: ${{ secrets.INDIEGAMES_PASSWORD }}
|
||||
NEBULA_USER: ${{ secrets.NEBULA_USER }}
|
||||
NEBULA_PASSWORD: ${{ secrets.NEBULA_PASSWORD }}
|
||||
HLP_API: ${{ secrets.HLP_API }}
|
||||
HLP_KEY: ${{ secrets.HLP_KEY }}
|
||||
LINUX_RESULT: ${{ needs.linux_zip.result }}
|
||||
WINDOWS_RESULT: ${{ needs.windows_zip.result }}
|
||||
run: python ci/post/main.py release
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
configuration: [FastDebug, Release]
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-e29f882
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
name: Build caches for master
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
QT_VERSION: 5.12.9
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
compiler: [gcc-5, gcc-10]
|
||||
cmake_options: [""]
|
||||
include:
|
||||
# Also include configurations that check if the code compiles without the graphics backends
|
||||
- configuration: Debug
|
||||
compiler: gcc-10
|
||||
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
|
||||
- configuration: Release
|
||||
compiler: gcc-10
|
||||
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ github.workspace }}/../Qt
|
||||
key: ${{ runner.os }}-QtCache-${{ env.QT_VERSION }}
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
version: ${{ env.QT_VERSION }}
|
||||
dir: ${{ github.workspace }}/..
|
||||
cached: ${{ steps.cache-qt-lin.outputs.cache-hit }}
|
||||
setup-python: 'false'
|
||||
aqtversion: ==1.1.3
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ runner.os }}-${{ matrix.configuration }}-${{ matrix.compiler }}
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
JOB_CMAKE_OPTIONS: ${{ matrix.cmake_options }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
run: LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH ninja -k 20 all
|
||||
@@ -0,0 +1,21 @@
|
||||
name: Push a new nightly tag
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6 * * *'
|
||||
|
||||
jobs:
|
||||
tag:
|
||||
if: github.repository == 'scp-fs2open/fs2open.github.com'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Prepare environment
|
||||
run: |
|
||||
git config --global user.email '[email protected]'
|
||||
git config --global user.name SirKnightly
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
- name: Create tag
|
||||
run: git tag "nightly_$(date '+%Y%m%d')_$(git rev-parse --short HEAD)"
|
||||
- name: Push tag
|
||||
run: git push --tags
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
cmake_options: -DFSO_BUILD_WITH_OPENGL=OFF -DFSO_BUILD_WITH_VULKAN=OFF
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-e29f882
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
@@ -44,6 +44,11 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ runner.os }}-${{ matrix.configuration }}-${{ matrix.compiler }}
|
||||
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import re
|
||||
from ftplib import FTP, error_perm
|
||||
from typing import List, Tuple, Dict
|
||||
|
||||
import requests
|
||||
|
||||
from util import retry_multi, GLOBAL_TIMEOUT
|
||||
|
||||
class ReleaseFile:
|
||||
def __init__(self, name, url, group, subgroup=None, mirrors=None):
|
||||
if mirrors is None:
|
||||
mirrors = []
|
||||
self.mirrors = mirrors
|
||||
self.subgroup = subgroup
|
||||
self.group = group
|
||||
self.url = url
|
||||
self.name = name
|
||||
|
||||
self.base_url = "/".join(url.split('/')[0:-1]) + "/"
|
||||
self.filename = url.split('/')[-1]
|
||||
|
||||
# A list of tuples of (filename, hash)
|
||||
self.content_hashes = None
|
||||
|
||||
self.hash = None
|
||||
self.size = 0
|
||||
|
||||
|
||||
class SourceFile:
|
||||
def __init__(self, name, url, group):
|
||||
self.group = group
|
||||
self.url = url
|
||||
self.name = name
|
||||
|
||||
|
||||
def get_release_files(tag_name, config) -> Tuple[List[ReleaseFile], Dict[str, SourceFile]]:
|
||||
@retry_multi(5)
|
||||
def execute_request(path):
|
||||
headers = {
|
||||
"Accept": "application/vnd.github.v3+json"
|
||||
}
|
||||
url = "https://api.github.com" + path
|
||||
|
||||
response = requests.get(url, headers=headers, timeout=GLOBAL_TIMEOUT)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
build_group_regex = re.compile("fs2_open_.*-builds-([^.-]*)(-([^.]*))?.*")
|
||||
source_file_regex = re.compile("fs2_open_.*-source-([^.]*)?.*")
|
||||
|
||||
response = execute_request(
|
||||
"/repos/{}/{}/releases/tags/{}".format(config["github"]["user"], config["github"]["repo"], tag_name))
|
||||
|
||||
binary_files = []
|
||||
source_files = {}
|
||||
for asset in response["assets"]:
|
||||
url = asset["browser_download_url"]
|
||||
name = asset["name"]
|
||||
|
||||
group_match = build_group_regex.match(name)
|
||||
|
||||
if group_match is not None:
|
||||
platform = group_match.group(1)
|
||||
# x64 is the Visual Studio name but for consistency we need Win64
|
||||
if platform == "x64":
|
||||
platform = "Win64"
|
||||
|
||||
binary_files.append(ReleaseFile(name, url, platform, group_match.group(3)))
|
||||
else:
|
||||
group_match = source_file_regex.match(name)
|
||||
|
||||
if group_match is None:
|
||||
continue
|
||||
|
||||
group = group_match.group(1)
|
||||
|
||||
source_files[group] = SourceFile(name, url, group)
|
||||
|
||||
return binary_files, source_files
|
||||
|
||||
|
||||
def get_ftp_files(build_type, tag_name, config):
|
||||
tag_regex = re.compile("nightly_(.*)")
|
||||
build_group_regex = re.compile("nightly_.*-builds-([^.]+).*")
|
||||
|
||||
files = []
|
||||
try:
|
||||
with FTP(config["ftp"]["host"], config["ftp"]["user"], config["ftp"]["pass"]) as ftp:
|
||||
version_str = tag_regex.match(tag_name).group(1)
|
||||
|
||||
path_template = config["ftp"]["path"]
|
||||
path = path_template.format(type=build_type, version=version_str)
|
||||
file_entries = list(ftp.mlsd(path, ["type"]))
|
||||
|
||||
for entry in file_entries:
|
||||
if entry[1]["type"] == "file":
|
||||
files.append(entry[0])
|
||||
except error_perm:
|
||||
print("Received permanent FTP error!")
|
||||
return []
|
||||
|
||||
out_data = []
|
||||
for file in files:
|
||||
file_match = build_group_regex.match(file)
|
||||
if file_match is None:
|
||||
print("Ignoring non nightly file '{}'".format(file))
|
||||
continue
|
||||
|
||||
group_match = file_match.group(1)
|
||||
primary_url = None
|
||||
mirrors = []
|
||||
|
||||
# x64 is the name Visual Studio uses but Win64 works better for us since that gets displayed in the nightly post
|
||||
if "x64" in group_match:
|
||||
group_match = group_match.replace("x64", "Win64")
|
||||
|
||||
for mirror in config["ftp"]["mirrors"]:
|
||||
download_url = mirror.format(type=build_type, version=version_str, file=file)
|
||||
if primary_url is None:
|
||||
primary_url = download_url
|
||||
else:
|
||||
mirrors.append(download_url)
|
||||
|
||||
out_data.append(ReleaseFile(file, primary_url, group_match, None, mirrors))
|
||||
|
||||
return out_data
|
||||
@@ -0,0 +1,82 @@
|
||||
from itertools import groupby
|
||||
from typing import List
|
||||
|
||||
import requests
|
||||
import semantic_version
|
||||
from mako.template import Template
|
||||
|
||||
from file_list import ReleaseFile
|
||||
|
||||
|
||||
class FileGroup:
|
||||
def __init__(self, name, files: List[ReleaseFile]):
|
||||
self.files = files
|
||||
self.name = name
|
||||
|
||||
if len(files) == 1:
|
||||
self.mainFile = files[0]
|
||||
self.subFiles = {}
|
||||
else:
|
||||
self.mainFile = None
|
||||
subFiles = []
|
||||
for file in files:
|
||||
# We only have subcategories for Windows where SSE2 is the main group
|
||||
if file.subgroup == "SSE2":
|
||||
self.mainFile = file
|
||||
else:
|
||||
subFiles.append(file)
|
||||
|
||||
self.subFiles = dict(((x[0], next(x[1])) for x in groupby(subFiles, lambda f: f.subgroup)))
|
||||
|
||||
|
||||
|
||||
class ForumAPI:
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
def create_post(self, title, content, board):
|
||||
resp = requests.post(self.config["hlp"]["api"], data={
|
||||
"api_key": self.config["hlp"]["key"],
|
||||
"board": str(board),
|
||||
"subject": title,
|
||||
"body": content
|
||||
})
|
||||
|
||||
if resp.text != "OK":
|
||||
print("Post failed!")
|
||||
|
||||
def post_nightly(self, date, revision, files, log, success):
|
||||
print("Posting nightly thread...")
|
||||
|
||||
title = "Nightly: {} - Revision {}".format(date, revision)
|
||||
|
||||
template = Template(filename=self.config["templates"]["nightly"])
|
||||
rendered = template.render_unicode(**{
|
||||
"date": date,
|
||||
"revision": revision,
|
||||
"files": files,
|
||||
"log": log,
|
||||
"success": success
|
||||
})
|
||||
|
||||
print("Creating post...")
|
||||
self.create_post(title, rendered, self.config["nightly"]["hlp_board"])
|
||||
|
||||
def post_release(self, date, version: semantic_version.Version, groups, sources):
|
||||
print("Posting release thread...")
|
||||
|
||||
title = "Release: {}".format(version)
|
||||
|
||||
template = Template(
|
||||
filename=self.config["templates"]["release"].format(major=version.major, minor=version.minor),
|
||||
module_directory='/tmp/mako_modules')
|
||||
rendered = template.render_unicode(**{
|
||||
"date": date,
|
||||
"version": version,
|
||||
"groups": groups,
|
||||
"sources": sources
|
||||
}).strip("\n")
|
||||
|
||||
print("Creating post...")
|
||||
|
||||
self.create_post(title, rendered, self.config["release"]["hlp_board"])
|
||||
@@ -0,0 +1,88 @@
|
||||
import os
|
||||
import hashlib
|
||||
import tarfile
|
||||
import traceback
|
||||
import zipfile
|
||||
from tempfile import NamedTemporaryFile
|
||||
from zipfile import ZipFile
|
||||
|
||||
import requests
|
||||
import sys
|
||||
from mako.template import Template
|
||||
|
||||
from file_list import ReleaseFile
|
||||
from util import GLOBAL_TIMEOUT
|
||||
|
||||
|
||||
def _download_file(url, dest_file, session):
|
||||
print("Downloading " + url)
|
||||
# NOTE the stream=True parameter
|
||||
r = session.get(url, stream=True, timeout=GLOBAL_TIMEOUT)
|
||||
for chunk in r.iter_content(chunk_size=1024):
|
||||
if chunk: # filter out keep-alive new chunks
|
||||
dest_file.write(chunk)
|
||||
dest_file.flush()
|
||||
|
||||
|
||||
def _gen_hash(fileobj, hash_alg):
|
||||
h = hashlib.new(hash_alg)
|
||||
while True:
|
||||
data = fileobj.read(4096)
|
||||
|
||||
if not data:
|
||||
break
|
||||
|
||||
h.update(data)
|
||||
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def get_hashed_file_list(file: ReleaseFile, session: requests.Session = None, hash_alg: str = "sha256"):
|
||||
if session is None:
|
||||
session = requests.Session()
|
||||
|
||||
with NamedTemporaryFile('w+b', suffix=file.filename) as local_file:
|
||||
_download_file(file.url, local_file, session)
|
||||
|
||||
filename = local_file.name
|
||||
hash_list = []
|
||||
|
||||
local_file.seek(0)
|
||||
file.hash = _gen_hash(local_file, hash_alg)
|
||||
file.size = os.stat(filename).st_size
|
||||
|
||||
try:
|
||||
if tarfile.is_tarfile(filename):
|
||||
with tarfile.open(filename) as archive:
|
||||
for entry in archive:
|
||||
if not entry.isfile():
|
||||
continue
|
||||
|
||||
print("Computing hash for " + entry.name)
|
||||
fileobj = archive.extractfile(entry)
|
||||
hash_list.append((entry.path, _gen_hash(fileobj, hash_alg)))
|
||||
elif zipfile.is_zipfile(filename):
|
||||
with ZipFile(filename) as archive:
|
||||
for entry in archive.infolist():
|
||||
# Python 3.6 has is_dir but that version is relatively new so we'll use the "safe" version here
|
||||
if entry.filename.endswith('/'):
|
||||
continue
|
||||
|
||||
print("Computing hash for " + entry.filename)
|
||||
with archive.open(entry) as fileobj:
|
||||
hash_list.append((entry.filename, _gen_hash(fileobj, hash_alg)))
|
||||
else:
|
||||
raise NotImplementedError("Unsupported archive type!")
|
||||
except:
|
||||
traceback.print_exception(*sys.exc_info())
|
||||
return
|
||||
|
||||
file.content_hashes = hash_list
|
||||
|
||||
|
||||
def render_installer_config(version, groups, config):
|
||||
template = Template(filename=config["templates"]["installer"], module_directory='/tmp/mako_modules')
|
||||
return template.render(**{
|
||||
"version": version,
|
||||
"groups": groups,
|
||||
}).strip("\n")
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from subprocess import check_output
|
||||
from datetime import datetime
|
||||
from itertools import groupby
|
||||
|
||||
import semantic_version
|
||||
|
||||
import file_list
|
||||
import installer
|
||||
import nebula
|
||||
import forum
|
||||
|
||||
|
||||
MAJOR_VERSION_PATTERN = re.compile("set_if_not_defined\(FSO_VERSION_MAJOR (\d+)\)")
|
||||
MINOR_VERSION_PATTERN = re.compile("set_if_not_defined\(FSO_VERSION_MINOR (\d+)\)")
|
||||
BUILD_VERSION_PATTERN = re.compile("set_if_not_defined\(FSO_VERSION_BUILD (\d+)\)")
|
||||
LOG_FORMAT = """
|
||||
------------------------------------------------------------------------%n
|
||||
commit %h%nAuthor: %an <%ad>%n
|
||||
Commit: %cn <%cd>%n%n %s
|
||||
"""
|
||||
DATEFORMAT_VERSION = "%Y%m%d"
|
||||
DATEFORMAT_FORUM = "%d %B %Y"
|
||||
|
||||
|
||||
def _match_version_number(text, regex):
|
||||
match = regex.search(text)
|
||||
return int(match.group(1))
|
||||
|
||||
|
||||
def get_source_version(date_version):
|
||||
with open(os.path.join("..", "..", "cmake", "version.cmake"), "r") as f:
|
||||
filetext = f.read()
|
||||
|
||||
major = _match_version_number(filetext, MAJOR_VERSION_PATTERN)
|
||||
minor = _match_version_number(filetext, MINOR_VERSION_PATTERN)
|
||||
build = _match_version_number(filetext, BUILD_VERSION_PATTERN)
|
||||
|
||||
return semantic_version.Version("{}.{}.{}-{}".format(major, minor, build, date_version))
|
||||
|
||||
|
||||
def main():
|
||||
os.chdir(os.path.dirname(__file__))
|
||||
|
||||
config = {
|
||||
"github": {
|
||||
"user": "scp-fs2open",
|
||||
"repo": "fs2open.github.com",
|
||||
},
|
||||
"ftp": {
|
||||
"host": "scp.indiegames.us",
|
||||
"user": os.environ["INDIEGAMES_USER"],
|
||||
"pass": os.environ["INDIEGAMES_SSHPASS"],
|
||||
"path": "public_html/builds/{type}/{version}/",
|
||||
"mirrors": (
|
||||
"https://porphyrion.feralhosting.com/datacorder/builds/{type}/{version}/{file}",
|
||||
"http://scp.indiegames.us/builds/{type}/{version}/{file}",
|
||||
),
|
||||
},
|
||||
"nebula": {
|
||||
"user": os.environ["NEBULA_USER"],
|
||||
"password": os.environ["NEBULA_PASSWORD"],
|
||||
},
|
||||
"hlp": {
|
||||
"api": os.environ["HLP_API"],
|
||||
"key": os.environ["HLP_KEY"],
|
||||
},
|
||||
"templates": {
|
||||
"nightly": "nightly.mako",
|
||||
"release": "release.mako",
|
||||
},
|
||||
"nightly": {
|
||||
"hlp_board": 173,
|
||||
},
|
||||
"release": {
|
||||
"hlp_board": 50,
|
||||
},
|
||||
}
|
||||
|
||||
if sys.argv[1] not in ("release", "nightly"):
|
||||
print("ERROR: Invalid release mode %s passed. Expected release or nightly!" % sys.argv[1])
|
||||
sys.exit(1)
|
||||
|
||||
tag_name = os.path.basename(os.environ["GITHUB_REF"])
|
||||
date = datetime.now()
|
||||
version = get_source_version(date.strftime(DATEFORMAT_VERSION))
|
||||
success = os.environ["LINUX_RESULT"] == "success" and os.environ["WINDOWS_RESULT"] == "success"
|
||||
|
||||
tags = check_output(("git", "for-each-ref", "--sort=-taggerdate", "--format", "%(tag)", "refs/tags")).splitlines()
|
||||
previous_tag = None
|
||||
found = False
|
||||
|
||||
for tag in tags:
|
||||
if found:
|
||||
if tag.startswith(sys.argv[1] + "_"):
|
||||
previous_tag = tag
|
||||
break
|
||||
elif tag == tag_name:
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
print("ERROR: Couldn't find tag %s in repo!" % tag_name)
|
||||
sys.exit(1)
|
||||
|
||||
if not previous_tag:
|
||||
print("ERROR: Could not find a previous tag for %s!" % tag_name)
|
||||
sys.exit(1)
|
||||
|
||||
if sys.argv[1] == "release":
|
||||
if not tag_name.startswith("release_"):
|
||||
print("ERROR: Invalid tag name detected. Expected release_ prefix for release builds but found %s (full ref = %s)" % tag_name, os.environ["GITHUB_REF"])
|
||||
sys.exit(1)
|
||||
|
||||
files, sources = file_list.get_release_files(tag_name, config)
|
||||
|
||||
print("Hashing files")
|
||||
for file in files:
|
||||
installer.get_hashed_file_list(file)
|
||||
|
||||
# Construct the file groups
|
||||
groups = dict(((x[0], file_list.FileGroup(x[0], list(x[1]))) for x in groupby(files, lambda g: g.group)))
|
||||
|
||||
print(installer.render_installer_config(version, groups, config))
|
||||
|
||||
nebula.submit_release(
|
||||
nebula.render_nebula_release(version, "rc" if version.prerelease else "stable", "files", config),
|
||||
config)
|
||||
|
||||
fapi = forum.ForumAPI(config)
|
||||
fapi.post_release(date.strftime(DATEFORMAT_FORUM), version, groups, sources)
|
||||
else:
|
||||
if not tag_name.startswith("nightly_"):
|
||||
print("ERROR: Invalid tag name detected. Expected nightly_ prefix for nightly builds but found %s (full ref = %s)" % tag_name, os.environ["GITHUB_REF"])
|
||||
sys.exit(1)
|
||||
|
||||
print("Retrieving file list")
|
||||
files = file_list.get_ftp_files("nightly", tag_name, config)
|
||||
|
||||
print("Hashing files")
|
||||
for file in files:
|
||||
installer.get_hashed_file_list(file)
|
||||
|
||||
nebula.submit_release(nebula.render_nebula_release(version, "nightly", files, config), config)
|
||||
|
||||
log = check_output(("git", "log", "%s^..%s^" % (previous_tag, tag_name), "--no-merges", "--stat", "--pretty=format:\"%s\"" % LOG_FORMAT.strip()))
|
||||
|
||||
fapi = forum.ForumAPI(config)
|
||||
fapi.post_nightly(date.strftime(DATEFORMAT_FORUM), os.environ["GITHUB_SHA"], files, log, success)
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,240 @@
|
||||
import os.path
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import requests
|
||||
|
||||
from util import retry_multi, GLOBAL_TIMEOUT
|
||||
|
||||
metadata = {
|
||||
'type': 'engine',
|
||||
'title': 'FSO',
|
||||
'notes': '',
|
||||
'banner': 'https://fsnebula.org/storage/0d/e7/bf64bcdea9a9c115969cfb784e1ca457d24a7c2da4fc6f213521c3bb6abb.png',
|
||||
'screenshots': [],
|
||||
'videos': [],
|
||||
'first_release': '2017-09-24',
|
||||
'cmdline': '',
|
||||
'mod_flag': ['FSO'],
|
||||
'tile': 'https://fsnebula.org/storage/ec/cc/0bf23e028c26d5175ff52d003bff85b0a17b0ddfc1130d65bdf6d36f6324.png',
|
||||
'logo': None,
|
||||
'release_thread': None,
|
||||
'description': '',
|
||||
'id': 'FSO',
|
||||
'packages': [],
|
||||
}
|
||||
|
||||
platforms = {
|
||||
'Linux': 'linux',
|
||||
'MacOSX': 'macosx',
|
||||
'Win32-SSE2': 'windows',
|
||||
'Win64-SSE2': 'windows',
|
||||
'Win32-AVX': 'windows',
|
||||
'Win64-AVX': 'windows'
|
||||
}
|
||||
|
||||
envs = {
|
||||
'Linux': 'linux && x86_64', # Linux only has 64bit builds
|
||||
'MacOSX': 'macosx',
|
||||
'Win32-SSE2': 'windows',
|
||||
'Win64-SSE2': 'windows && x86_64',
|
||||
'Win32-AVX': 'windows && avx',
|
||||
'Win64-AVX': 'windows && avx && x86_64'
|
||||
}
|
||||
|
||||
subdirs = {
|
||||
'Win32-SSE2': 'x86',
|
||||
'Win64-SSE2': 'x64',
|
||||
'Win32-AVX': 'x86_avx',
|
||||
'Win64-AVX': 'x64_avx'
|
||||
}
|
||||
|
||||
|
||||
def render_nebula_release(version, stability, files, config):
|
||||
meta = metadata.copy()
|
||||
meta['version'] = str(version)
|
||||
meta['stability'] = stability # This can be one of ('stable', 'rc', 'nightly')
|
||||
|
||||
for file in files:
|
||||
if file.content_hashes is None:
|
||||
# The extraction probably failed which is why we don't have any hashes
|
||||
continue
|
||||
|
||||
group = file.group
|
||||
|
||||
# release.py sets subgroup but nightly.py doesn't which means we have to normalize group here.
|
||||
if file.subgroup:
|
||||
group += '-' + file.subgroup
|
||||
|
||||
pkg = {
|
||||
'name': group,
|
||||
'notes': '',
|
||||
'is_vp': False,
|
||||
'files': [{
|
||||
'dest': platforms[group] + '/' + subdirs.get(group, ''),
|
||||
'filesize': file.size,
|
||||
'checksum': ['sha256', file.hash],
|
||||
'urls': [file.url] + file.mirrors,
|
||||
'filename': file.name
|
||||
}],
|
||||
'environment': envs.get(group),
|
||||
'filelist': [],
|
||||
'status': 'required',
|
||||
'folder': None,
|
||||
'dependencies': [],
|
||||
'executables': []
|
||||
}
|
||||
|
||||
for fn, checksum in file.content_hashes:
|
||||
if group in subdirs:
|
||||
dest_fn = subdirs[group] + '/' + fn
|
||||
else:
|
||||
dest_fn = fn
|
||||
|
||||
# If the group is a known platform then we put the binaries into a separate subfolder for each platform
|
||||
if group in platforms:
|
||||
dest_fn = platforms[group] + '/' + dest_fn
|
||||
|
||||
pkg['filelist'].append({
|
||||
'orig_name': fn,
|
||||
'checksum': ('sha256', checksum),
|
||||
'archive': file.name,
|
||||
'filename': dest_fn
|
||||
})
|
||||
|
||||
if group == 'Linux' and fn.endswith('.AppImage'):
|
||||
if 'qtfred' in fn:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'QtFRED Debug'
|
||||
else:
|
||||
label = 'QtFRED'
|
||||
|
||||
else:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'Fast Debug'
|
||||
else:
|
||||
label = None
|
||||
|
||||
props = {
|
||||
"x64": True, # All Linux builds are 64-bit
|
||||
"sse2": True, # Linux builds are forced to compile with SSE2 but not AVX
|
||||
"avx": False,
|
||||
"avx2": False,
|
||||
}
|
||||
|
||||
pkg['executables'].append({
|
||||
'file': dest_fn,
|
||||
'label': label,
|
||||
'properties': props,
|
||||
})
|
||||
elif group == 'MacOSX' and fn.startswith(os.path.basename(fn) + '.app/'):
|
||||
if 'qtfred' in fn:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'QtFRED Debug'
|
||||
else:
|
||||
label = 'QtFRED'
|
||||
else:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'Fast Debug'
|
||||
else:
|
||||
label = None
|
||||
|
||||
props = {
|
||||
"x64": True, # All Mac builds are 64-bit
|
||||
"sse2": True, # There are no forced options on mac but 64-bit always implies SSE2 so we set that here
|
||||
"avx": False,
|
||||
"avx2": False,
|
||||
}
|
||||
|
||||
pkg['executables'].append({
|
||||
'file': dest_fn,
|
||||
'label': label,
|
||||
'properties': props,
|
||||
})
|
||||
elif group.startswith('Win') and fn.endswith('.exe'):
|
||||
if 'fred2' in fn:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'FRED2 Debug'
|
||||
else:
|
||||
label = 'FRED2'
|
||||
|
||||
elif 'qtfred' in fn:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'QtFRED Debug'
|
||||
else:
|
||||
label = 'QtFRED'
|
||||
|
||||
else:
|
||||
if '-FASTDBG' in fn:
|
||||
label = 'Fast Debug'
|
||||
else:
|
||||
label = None
|
||||
|
||||
props = {
|
||||
"x64": "x64" in fn,
|
||||
"sse2": "SSE2" in fn or "AVX" in fn, # AVX implies SSE2
|
||||
"avx": "AVX" in fn, # This conveniently also covers the AVX2 case since AVX2 implies AVX
|
||||
"avx2": "AVX2" in fn,
|
||||
}
|
||||
|
||||
pkg['executables'].append({
|
||||
'file': dest_fn,
|
||||
'label': label,
|
||||
'properties': props,
|
||||
})
|
||||
|
||||
meta["packages"].append(pkg)
|
||||
|
||||
return meta
|
||||
|
||||
|
||||
@retry_multi(5)
|
||||
def nebula_request(session, kind, path, **kwargs):
|
||||
uri = "https://fsnebula.org/api/1/{}".format(path)
|
||||
|
||||
request_args = {
|
||||
"timeout": GLOBAL_TIMEOUT
|
||||
}
|
||||
request_args.update(kwargs)
|
||||
|
||||
return session.request(kind, uri, **request_args)
|
||||
|
||||
|
||||
def submit_release(meta, config):
|
||||
try:
|
||||
with requests.Session() as session:
|
||||
print('Logging into Nebula...')
|
||||
result = nebula_request(session, 'post', 'login', data={
|
||||
'user': config['nebula']['user'],
|
||||
'password': config['nebula']['password']
|
||||
})
|
||||
|
||||
if result.status_code != 200:
|
||||
print('Login failed!')
|
||||
return False
|
||||
|
||||
data = result.json()
|
||||
if not data['result']:
|
||||
print('Login failed!')
|
||||
return False
|
||||
|
||||
print('Submitting release...')
|
||||
result = nebula_request(session, 'post', 'mod/release', headers={
|
||||
'X-KN-TOKEN': data['token']
|
||||
}, json=meta)
|
||||
|
||||
if result.status_code != 200:
|
||||
print('Request failed!')
|
||||
return False
|
||||
|
||||
data = result.json()
|
||||
if not data['result']:
|
||||
print('ERROR: ' + data['reason'])
|
||||
return False
|
||||
|
||||
print('Success!')
|
||||
return True
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
traceback.print_exception(exc_type, exc_value, exc_traceback)
|
||||
return False
|
||||
@@ -0,0 +1,27 @@
|
||||
Here is the nightly for ${date} - Revision ${revision}
|
||||
|
||||
% if not success:
|
||||
[b][color=red]At least one of the nightly builds failed![/color][/b]
|
||||
% endif
|
||||
% for file in files:
|
||||
|
||||
|
||||
Group: ${file.group}
|
||||
[url=${file.url}]${file.name}[/url] \
|
||||
%for mirror in file.mirrors:
|
||||
%if loop.first:
|
||||
(\
|
||||
%endif
|
||||
[url=${mirror}]Mirror[/url]\
|
||||
%if loop.last:
|
||||
)\
|
||||
%else:
|
||||
, \
|
||||
%endif
|
||||
%endfor
|
||||
|
||||
% endfor
|
||||
|
||||
[code]
|
||||
${log}
|
||||
[/code]
|
||||
@@ -0,0 +1,80 @@
|
||||
|
||||
<%def name="build(file)">[url=${file.url}]${file.filename}[/url]</%def>
|
||||
|
||||
[b]Change log:[/b] (chronologically ordered)
|
||||
[b]Bugfixes and behavior changes:[/b] Check these first if something breaks (most of these changes can be reverted / disabled).
|
||||
[list]
|
||||
[li]Wrap Docking getTime in a timestamp ([gh]#3701[/gh])[/li]
|
||||
[li]expand some sexp documentation ([gh]#3671[/gh])[/li]
|
||||
[li]Fix warnings concerning ballistic weapons ([gh]#3683[/gh])[/li]
|
||||
[li]Initialize team visibility before using it ([gh]#3695[/gh])[/li]
|
||||
[li]Actually fix the goal's target being sent ([gh]#3665[/gh])[/li]
|
||||
[li]Safety for replace-texture ([gh]#3694[/gh])[/li]
|
||||
[li]Fix issue 3685 ([gh]#3688[/gh])[/li]
|
||||
[li]Exclude dead subsys from targeting ([gh]#3614[/gh])[/li]
|
||||
[li]Fix scripted kills ([gh]#3679[/gh])[/li]
|
||||
[li]Fix homing child weapons in niche circumstances ([gh]#3661[/gh])[/li]
|
||||
[/list]
|
||||
|
||||
[b]Deprecations:[/b]
|
||||
[list][li]None[/li][/list]
|
||||
|
||||
Deprecations are a mechanism in FSO where a certain feature or aspect of the engine has changed or is no longer supported. Since this would normally break existing mods we have the mod table feature "[tt]$Target Version:[/tt]" with which a mod can specify what version of FSO it was developed with. The features listed above will be removed or changed when the target version of a mod is at least the version released in this post.
|
||||
|
||||
[size=5pt]Previous [url=https://www.hard-light.net/forums/index.php?topic=97293.0]21.0 Release Thread[/url][/size]
|
||||
|
||||
Launchers, if you don't have one already:
|
||||
[b]All platforms: [/b] For every day use, we recommend [url=https://www.hard-light.net/forums/index.php?topic=94068.0]Knossos[/url], an integrated solution for downloading and launching mods.
|
||||
|
||||
[hidden=Alternative Launchers]
|
||||
Cross-platform: [url=http://www.hard-light.net/forums/index.php?topic=89162]wxLauncher 0.12.x Test Build[/url] (ongoing project for a unified launcher, you should upgrade to the latest RC/test build if you have not yet)
|
||||
[b]Important:[/b] For best compatibility with FSO 3.8 and later you should use at least wxLauncher 0.12.
|
||||
|
||||
Windows: [url=http://scp.fsmods.net/files/Launcher55g.zip]Launcher 5.5g[/url] ([url=http://scp.indiegames.us/builds/Launcher55g.zip]Mirror[/url]) ([url=http://www.mediafire.com/?wdvzn7hhhzh418m]Mirror[/url]) Not compatible with Windows 8+, use wxLauncher above
|
||||
OS X: Soulstorm's [url=http://www.hard-light.net/forums/index.php/topic,51391.0.html]OS X Launcher 3.0[/url]
|
||||
Linux: [url=http://www.hard-light.net/forums/index.php/topic,53206.0.html]YAL[/url] or [url=http://www.hard-light.net/wiki/index.php/Fs2_open_on_Linux/Graphics_Settings]by hand[/url] or whatever you can figure out.[/hidden]
|
||||
|
||||
[img]http://scp.indiegames.us/img/windows-icon.png[/img] [color=green][size=12pt]Windows (32/64-bit)[/size][/color]
|
||||
[size=8pt]Compiled using GitHub Actions on Windows Server 2019 (10.0.17763), Visual Studio Enterprise 2019[/size]
|
||||
|
||||
[b]64-bit:[/b] ${build(groups["Win64"].mainFile)}
|
||||
|
||||
[b]32-bit:[/b] ${build(groups["Win32"].mainFile)}
|
||||
[size=8pt]This one is based on the SSE2 Optimizations from the MSVC Compiler.[/size]
|
||||
|
||||
[hidden=Alternative builds]
|
||||
|
||||
[b]64-bit AVX:[/b] ${build(groups["Win64"].subFiles["AVX"])}
|
||||
[size=8pt]This one is based on the AVX Optimizations from the MSVC Compiler (fastest build if your CPU supports AVX instructions).[/size]
|
||||
|
||||
|
||||
[b]32-bit AVX:[/b] ${build(groups["Win32"].subFiles["AVX"])}
|
||||
[size=8pt]This one is based on the AVX Optimizations from the MSVC Compiler.[/size]
|
||||
|
||||
[b]What are those SSE, SSE2 and AVX builds I keep seeing everywhere?[/b]
|
||||
[url=http://www.hard-light.net/forums/index.php?topic=65628.0]Your answer is in this topic.[/url]
|
||||
Don't want to deal with that? Use [url=https://www.hard-light.net/forums/index.php?topic=94068.0]Knossos[/url] and it will download the best build specifically for your PC!
|
||||
[/hidden]
|
||||
|
||||
[img]http://scp.indiegames.us/img/linux-icon.png[/img] [color=green][size=12pt]Linux 64-bit[/size][/color]
|
||||
[size=8pt]Compiled with Ubuntu 16.04 LTS 64-bit, GCC 5[/size]
|
||||
${build(groups["Linux"].mainFile)}
|
||||
|
||||
These builds use a mechanism called [url=http://appimage.org/]AppImage[/url] which should allow these builds to run on most Linux distributions. However, we recommend that you compile your own builds which will result in less issues.
|
||||
Alternatively, if there is a package in your software repository then you should use that. If you are the maintainer of such a package for a distribution then let us know and we will include that here.
|
||||
|
||||
|
||||
[img]http://scp.indiegames.us/img/mac-icon.png[/img] [color=green][size=12pt]OS X[/size][/color]
|
||||
[b][color=red]Not available[/color][/b] We recently lost access to our Mac CI environment which we usually used for compiling these builds so for the time being, there will be no builds for this OS.
|
||||
|
||||
[hidden=TrackIR Users]
|
||||
[size=12pt]Important!![/size]
|
||||
An external DLL is required for FSO to use TrackIR functions. The following DLL is simply unpacked in to your main FreeSpace2 root dir.
|
||||
TrackIR is only supported on Windows.
|
||||
[url=http://www.mediafire.com/download.php?4zw024zrh44etse]TrackIR SCP DLL[/url] ([url=http://scp.fsmods.net/builds/scptrackir.zip]Mirror[/url]) ([url=http://scp.indiegames.us/builds/scptrackir.zip]Mirror[/url])[/hidden]
|
||||
|
||||
Known issues:
|
||||
[list]
|
||||
[li][url=https://github.com/scp-fs2open/fs2open.github.com/issues]Github issues[/url] and [url=https://github.com/scp-fs2open/fs2open.github.com/pulls]pending pull requests[/url][/li]
|
||||
[li]See the list of [url=http://scp.indiegames.us/mantis/search.php?project_id=1&status_id%5B%5D=10&status_id%5B%5D=20&status_id%5B%5D=30&status_id%5B%5D=40&status_id%5B%5D=50&priority_id%5B%5D=40&priority_id%5B%5D=50&priority_id%5B%5D=60&sticky_issues=on&sortby=last_updated&dir=DESC&per_page=200&hide_status_id=-2]Fix for next release[/url] bugs - mark a bug as an elevated priority (high, urgent, immediate) to get it included in that filter.[/li]
|
||||
[/list]
|
||||
@@ -0,0 +1,3 @@
|
||||
Mako==1.1.5
|
||||
requests==2.26.0
|
||||
semantic-version==2.8.5
|
||||
@@ -0,0 +1,37 @@
|
||||
import os
|
||||
import time
|
||||
from functools import wraps
|
||||
|
||||
from requests import RequestException
|
||||
|
||||
GLOBAL_TIMEOUT = 30
|
||||
|
||||
|
||||
def retry_multi(max_retries):
|
||||
"""
|
||||
Retry a function `max_retries` times.
|
||||
This has been copied from https://stackoverflow.com/a/23892489
|
||||
"""
|
||||
|
||||
def retry(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
num_retries = 0
|
||||
while num_retries <= max_retries:
|
||||
try:
|
||||
ret = func(*args, **kwargs)
|
||||
break
|
||||
except RequestException:
|
||||
if num_retries == max_retries:
|
||||
raise
|
||||
num_retries += 1
|
||||
time.sleep(5)
|
||||
return ret
|
||||
|
||||
return wrapper
|
||||
|
||||
return retry
|
||||
|
||||
|
||||
def expand_config_vars(config):
|
||||
config["git"]["repo"] = os.path.expandvars(config["git"]["repo"])
|
||||
@@ -1,15 +1,3 @@
|
||||
|
||||
include(WriteCompilerDetectionHeader)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED 11)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
get_property(CMAKE_CXX_KNOWN_FEATURES GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
|
||||
|
||||
write_compiler_detection_header(
|
||||
FILE "${GENERATED_SOURCE_DIR}/scp_compiler_detection.h"
|
||||
PREFIX SCP
|
||||
COMPILERS AppleClang Clang GNU MSVC
|
||||
FEATURES ${CMAKE_CXX_KNOWN_FEATURES}
|
||||
VERSION 3.2
|
||||
)
|
||||
@@ -21,8 +21,6 @@ endif()
|
||||
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES "cstddef")
|
||||
CHECK_TYPE_SIZE("std::max_align_t" STD_MAX_ALIGN_T LANGUAGE CXX)
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES "type_traits")
|
||||
CHECK_TYPE_SIZE("std::is_trivially_copyable<int>" STD_IS_TRIVIALLY_COPYABLE LANGUAGE CXX)
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES)
|
||||
|
||||
CHECK_TYPE_SIZE("char32_t" CHAR32_T LANGUAGE CXX)
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#cmakedefine SCP_HAVE_MAX_ALIGN_T
|
||||
#cmakedefine SCP_HAVE_STD_MAX_ALIGN_T
|
||||
|
||||
#cmakedefine01 HAVE_STD_IS_TRIVIALLY_COPYABLE
|
||||
|
||||
#cmakedefine01 IS_64BIT
|
||||
|
||||
#cmakedefine01 SCP_HAVE_STRCASECMP
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@ if (EXISTS "${PROJECT_SOURCE_DIR}/version_override.cmake")
|
||||
endif()
|
||||
|
||||
set_if_not_defined(FSO_VERSION_MAJOR 21)
|
||||
set_if_not_defined(FSO_VERSION_MINOR 1)
|
||||
set_if_not_defined(FSO_VERSION_BUILD 0)
|
||||
set_if_not_defined(FSO_VERSION_MINOR 4)
|
||||
set_if_not_defined(FSO_VERSION_BUILD 1)
|
||||
|
||||
# A numeric revision e.g. the CI build number
|
||||
set_if_not_defined(FSO_VERSION_REVISION 0)
|
||||
|
||||
@@ -26,6 +26,7 @@ TARGET_LINK_LIBRARIES(code PUBLIC openal)
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${LUA_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${PNG_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC ${JPEG_LIBS})
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC lz4)
|
||||
|
||||
TARGET_LINK_LIBRARIES(code PUBLIC sdl2)
|
||||
|
||||
|
||||
+3
-1
@@ -405,7 +405,6 @@ typedef struct ai_info {
|
||||
|
||||
|
||||
union {
|
||||
float lead_scale; // Amount to lead current opponent by.
|
||||
float stay_near_distance; // Distance to stay within for AIM_STAY_NEAR mode.
|
||||
};
|
||||
|
||||
@@ -491,6 +490,9 @@ typedef struct ai_info {
|
||||
int ai_override_rot_timestamp; // mark for when to end the current rotational maneuver override
|
||||
|
||||
int form_obj_slotnum; // for flying in formation object mode, the position in the formation
|
||||
|
||||
int multilock_check_timestamp; // when to check for multilock next
|
||||
SCP_vector<std::pair<int, ship_subsys*>> ai_missile_locks_firing; // a list of missile locks (locked objnum, locked subsys) the ai is currently firing
|
||||
} ai_info;
|
||||
|
||||
// Goober5000
|
||||
|
||||
+4
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace AI {
|
||||
FLAG_LIST(AI_Flags) {
|
||||
Formation_wing, // Fly in formation as part of wing.
|
||||
Formation_wing, // Fly in formation as part of wing. Also used when flying waypoints.
|
||||
Awaiting_repair, // Awaiting a repair ship.
|
||||
Being_repaired, // Currently docked with repair ship.
|
||||
Repairing, // Repairing a ship (or going to repair a ship)
|
||||
@@ -137,6 +137,9 @@ namespace AI {
|
||||
Improved_missile_avoidance,
|
||||
Friendlies_use_countermeasure_firechance,
|
||||
Improved_subsystem_attack_pathing,
|
||||
Fixed_ship_weapon_collision,
|
||||
No_shield_damage_from_ship_collisions,
|
||||
Reset_last_hit_target_time_for_player_hits,
|
||||
|
||||
NUM_VALUES
|
||||
};
|
||||
|
||||
+52
-7
@@ -58,6 +58,26 @@ int ai_path_type_match(char *p)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
const char* AI_secondary_range_aware_select_modes[] = {
|
||||
"retail",
|
||||
"aware",
|
||||
};
|
||||
|
||||
int Num_ai_secondary_range_aware_select_modes = sizeof(AI_secondary_range_aware_select_modes) / sizeof(char*);
|
||||
|
||||
int ai_secondary_range_select_mode_match(char* p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < Num_ai_secondary_range_aware_select_modes; i++)
|
||||
{
|
||||
if (!stricmp(AI_secondary_range_aware_select_modes[i], p))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void parse_ai_profiles_tbl(const char *filename)
|
||||
{
|
||||
int i;
|
||||
@@ -448,7 +468,6 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
|
||||
set_flag(profile, "$firing requires exact los:", AI::Profile_Flags::Require_exact_los);
|
||||
|
||||
profile->ai_path_mode = AI_PATH_MODE_NORMAL;
|
||||
if (optional_string("$ai path mode:"))
|
||||
{
|
||||
stuff_string(buf, F_NAME, NAME_LENGTH);
|
||||
@@ -481,14 +500,12 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
if (path_radii >= Minimum_subsystem_path_pt_dist) {
|
||||
profile->subsystem_path_radii = path_radii;
|
||||
} else {
|
||||
mprintf(("Warning: \"$override radius for subsystem path points:\" should be >= %i (read %i). Value will not be used. ", Minimum_subsystem_path_pt_dist, path_radii));
|
||||
mprintf(("Warning: \"$override radius for subsystem path points:\" should be >= %i (read %i). Value will not be used.\n", Minimum_subsystem_path_pt_dist, path_radii));
|
||||
}
|
||||
}
|
||||
|
||||
set_flag(profile, "$use POF radius for subsystem path points:", AI::Profile_Flags::Use_subsystem_path_point_radii);
|
||||
|
||||
profile->bay_arrive_speed_mult = 1.0f;
|
||||
profile->bay_depart_speed_mult = 1.0f;
|
||||
if (optional_string("$bay arrive speed multiplier:")) {
|
||||
stuff_float(&profile->bay_arrive_speed_mult);
|
||||
}
|
||||
@@ -544,6 +561,26 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
|
||||
set_flag(profile, "$improved subsystem attack pathing:", AI::Profile_Flags::Improved_subsystem_attack_pathing);
|
||||
|
||||
set_flag(profile, "$fixed ship-weapon collisions:", AI::Profile_Flags::Fixed_ship_weapon_collision);
|
||||
|
||||
//Intention is to expand this feature to include a preference for close or long range weapons
|
||||
//hence using something besides a simple flag.
|
||||
if (optional_string("$AI secondary range awareness:"))
|
||||
{
|
||||
stuff_string(buf, F_NAME, NAME_LENGTH);
|
||||
int j = ai_secondary_range_select_mode_match(buf);
|
||||
if (j >= 0) {
|
||||
profile->ai_range_aware_secondary_select_mode = j;
|
||||
}
|
||||
else {
|
||||
Warning(LOCATION, "Invalid ai secondary range awareness mode '%s' specified", buf);
|
||||
}
|
||||
}
|
||||
|
||||
set_flag(profile, "$no shield damage from ship collisions:", AI::Profile_Flags::No_shield_damage_from_ship_collisions);
|
||||
|
||||
set_flag(profile, "$reset last_hit_target_time for player hits:", AI::Profile_Flags::Reset_last_hit_target_time_for_player_hits);
|
||||
|
||||
// if we've been through once already and are at the same place, force a move
|
||||
if (saved_Mp && (saved_Mp == Mp))
|
||||
{
|
||||
@@ -615,11 +652,12 @@ void ai_profile_t::reset()
|
||||
|
||||
flags.reset();
|
||||
|
||||
ai_path_mode = 0;
|
||||
ai_path_mode = AI_PATH_MODE_NORMAL;
|
||||
subsystem_path_radii = 0;
|
||||
bay_arrive_speed_mult = 0;
|
||||
bay_depart_speed_mult = 0;
|
||||
bay_arrive_speed_mult = 1.0f;
|
||||
bay_depart_speed_mult = 1.0f;
|
||||
second_order_lead_predict_factor = 0;
|
||||
ai_range_aware_secondary_select_mode = AI_RANGE_AWARE_SEC_SEL_MODE_RETAIL;
|
||||
|
||||
for (int i = 0; i < NUM_SKILL_LEVELS; ++i) {
|
||||
max_incoming_asteroids[i] = 0;
|
||||
@@ -697,4 +735,11 @@ void ai_profile_t::reset()
|
||||
if (mod_supports_version(3, 7, 2)) {
|
||||
flags.set(AI::Profile_Flags::Fix_ramming_stationary_targets_bug);
|
||||
}
|
||||
// and this flag has been enabled ever since 3.6.10
|
||||
if (mod_supports_version(3, 6, 10)) {
|
||||
flags.set(AI::Profile_Flags::Reset_last_hit_target_time_for_player_hits);
|
||||
}
|
||||
if (mod_supports_version(21, 4, 0)) {
|
||||
flags.set(AI::Profile_Flags::Fixed_ship_weapon_collision);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,9 @@
|
||||
// AI Path types
|
||||
#define AI_PATH_MODE_NORMAL 0
|
||||
#define AI_PATH_MODE_ALT1 1
|
||||
|
||||
#define AI_RANGE_AWARE_SEC_SEL_MODE_RETAIL 0
|
||||
#define AI_RANGE_AWARE_SEC_SEL_MODE_AWARE 1
|
||||
|
||||
#define MAX_AI_PROFILES 5
|
||||
|
||||
@@ -107,6 +110,9 @@ public:
|
||||
|
||||
// How much 0-1 of a second-order lead prediction factor to add to lead indicators. Affects only the HUD indicator, and autoaim.
|
||||
float second_order_lead_predict_factor;
|
||||
|
||||
//Controls if the AI is dumb enough to keep trying to use out-of-range secondaries.
|
||||
int ai_range_aware_secondary_select_mode;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
+26
-12
@@ -17,12 +17,12 @@
|
||||
#include "io/timer.h"
|
||||
#include "math/staticrand.h"
|
||||
#include "mission/missionparse.h"
|
||||
#include "object/objcollide.h"
|
||||
#include "object/object.h"
|
||||
#include "ship/afterburner.h"
|
||||
#include "ship/ship.h"
|
||||
#include "utils/Random.h"
|
||||
#include "weapon/weapon.h"
|
||||
#include "object/objcollide.h"
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma optimize("", off)
|
||||
@@ -777,11 +777,25 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
|
||||
|
||||
ai_turn_towards_vector(enemy_pos, Pl_objp, nullptr, rel_pos, 0.0f, 0, rvec);
|
||||
|
||||
// set an optimal range if applicable
|
||||
float optimal_range = 0.0f;
|
||||
ship_weapon* weapons = &Ships[Pl_objp->instance].weapons;
|
||||
weapon_info* wip = nullptr;
|
||||
|
||||
if (weapons->num_primary_banks >= 1 && weapons->current_primary_bank >= 0) {
|
||||
wip = &Weapon_info[weapons->primary_bank_weapons[weapons->current_primary_bank]];
|
||||
} else if (weapons->num_secondary_banks >= 1 && weapons->current_secondary_bank >= 0) {
|
||||
wip = &Weapon_info[weapons->secondary_bank_weapons[weapons->current_secondary_bank]];
|
||||
}
|
||||
|
||||
if (wip != nullptr && wip->optimum_range > 0)
|
||||
optimal_range = wip->optimum_range;
|
||||
|
||||
// calc range of primary weapon
|
||||
weapon_travel_dist = ai_get_weapon_dist(&Ships[Pl_objp->instance].weapons);
|
||||
|
||||
if ( aip->targeted_subsys != NULL ) {
|
||||
if (dist_to_enemy > (weapon_travel_dist-20)) {
|
||||
if (dist_to_enemy > (optimal_range > 0.0f ? optimal_range : (weapon_travel_dist-20))) {
|
||||
accelerate_ship(aip, 1.0f);
|
||||
|
||||
ship *shipp = &Ships[Pl_objp->instance];
|
||||
@@ -809,7 +823,7 @@ void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float d
|
||||
} else if (dot_to_enemy < 0.75f) {
|
||||
accel = 0.75f;
|
||||
} else {
|
||||
if (dist_to_enemy > weapon_travel_dist/2.0f) {
|
||||
if (dist_to_enemy > (optimal_range > 0.0f ? optimal_range : (weapon_travel_dist/2.0f))) {
|
||||
accel = 1.0f;
|
||||
} else {
|
||||
accel = 1.0f - dot_to_enemy;
|
||||
@@ -914,7 +928,7 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
}
|
||||
}
|
||||
|
||||
if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
|
||||
if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank]) && weapon_target_satisfies_lock_restrictions(swip, En_objp)) {
|
||||
float firing_range;
|
||||
if (swip->wi_flags[Weapon::Info_Flags::Local_ssm])
|
||||
firing_range=swip->lssm_lock_range;
|
||||
@@ -944,11 +958,11 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
} else {
|
||||
t = swip->fire_wait;
|
||||
if ((swip->burst_shots > 0) && (swip->burst_flags[Weapon::Burst_Flags::Random_length])) {
|
||||
swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
|
||||
swp->burst_counter[current_bank_adjusted] = Random::next(swip->burst_shots);
|
||||
} else {
|
||||
swp->burst_counter[current_bank_adjusted] = 0;
|
||||
}
|
||||
swp->burst_seed[current_bank_adjusted] = rand32();
|
||||
swp->burst_seed[current_bank_adjusted] = Random::next();
|
||||
}
|
||||
} else {
|
||||
if (swip->burst_shots > swp->burst_counter[current_bank_adjusted]) {
|
||||
@@ -957,11 +971,11 @@ static void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy)
|
||||
} else {
|
||||
t = set_secondary_fire_delay(aip, temp_shipp, swip, false);
|
||||
if ((swip->burst_shots > 0) && (swip->burst_flags[Weapon::Burst_Flags::Random_length])) {
|
||||
swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
|
||||
swp->burst_counter[current_bank_adjusted] = Random::next(swip->burst_shots);
|
||||
} else {
|
||||
swp->burst_counter[current_bank_adjusted] = 0;
|
||||
}
|
||||
swp->burst_seed[current_bank_adjusted] = rand32();
|
||||
swp->burst_seed[current_bank_adjusted] = Random::next();
|
||||
}
|
||||
}
|
||||
swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
|
||||
@@ -1448,7 +1462,7 @@ void ai_big_strafe_attack()
|
||||
if (ai_new_maybe_reposition_attack_subsys())
|
||||
return;
|
||||
} else if (ai_big_maybe_follow_subsys_path(0))
|
||||
return
|
||||
return;
|
||||
|
||||
ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
|
||||
if ( ai_big_strafe_maybe_retreat(&target_pos) )
|
||||
@@ -1471,7 +1485,7 @@ void ai_big_strafe_attack()
|
||||
if ( t > 0.0f && t < 1.5f ) {
|
||||
// set up goal_point for avoid path to turn towards
|
||||
aip->goal_point = Pl_objp->pos;
|
||||
switch(rand()%4) {
|
||||
switch(Random::next(4)) {
|
||||
case 0:
|
||||
vm_vec_scale_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.uvec, 2.0f);
|
||||
break;
|
||||
@@ -1703,7 +1717,7 @@ void ai_big_strafe_retreat1()
|
||||
if (Missiontime - aip->submode_start_time > fl2f(1.50f)) {
|
||||
// set up goal_point for avoid path to turn towards
|
||||
aip->prev_goal_point = Pl_objp->pos;
|
||||
switch(rand()%4) {
|
||||
switch(Random::next(4)) {
|
||||
case 0:
|
||||
vm_vec_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.uvec);
|
||||
break;
|
||||
|
||||
+250
-79
@@ -68,6 +68,7 @@
|
||||
#include "ship/shipfx.h"
|
||||
#include "ship/shiphit.h"
|
||||
#include "ship/subsysdamage.h"
|
||||
#include "utils/Random.h"
|
||||
#include "weapon/beam.h"
|
||||
#include "weapon/flak.h"
|
||||
#include "weapon/swarm.h"
|
||||
@@ -1077,7 +1078,8 @@ void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
|
||||
float old_dot, new_dot;
|
||||
object *old_weapon_objp = NULL;
|
||||
|
||||
if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
|
||||
// any object number less than 0 is invalid
|
||||
if ((attacked_objnum < 0) || (weapon_objnum < 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2287,7 +2289,7 @@ int num_turrets_attacking(object *turret_parent, int target_objnum)
|
||||
*/
|
||||
int get_enemy_timestamp()
|
||||
{
|
||||
return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
|
||||
return (NUM_SKILL_LEVELS - Game_skill_level) * Random::next(500, 999);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2348,8 +2350,9 @@ void ai_set_goal_abort_support_call(object *objp, ai_info *aip)
|
||||
object *repair_obj;
|
||||
|
||||
if (aip->support_ship_objnum == -1) {
|
||||
repair_obj = NULL;
|
||||
repair_obj = nullptr;
|
||||
} else {
|
||||
Assertion(aip->support_ship_objnum >= 0, "%s has a nonsense support_ship_objnum of %d, please report!", Ships[aip->shipnum].ship_name, aip->support_ship_objnum);
|
||||
repair_obj = &Objects[aip->support_ship_objnum];
|
||||
}
|
||||
ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
|
||||
@@ -2376,7 +2379,7 @@ void ai_attack_object(object* attacker, object* attacked, int ship_info_index)
|
||||
ai_info* aip;
|
||||
|
||||
Assert(attacker != nullptr);
|
||||
Assert(attacker->instance != -1);
|
||||
Assert(attacker->instance >= 0);
|
||||
Assert(Ships[attacker->instance].ai_index != -1);
|
||||
// Bogus! Who tried to get me to attack myself!
|
||||
if (attacker == attacked) {
|
||||
@@ -4417,6 +4420,13 @@ void ai_fly_to_target_position(vec3d* target_pos, bool* pl_done_p=NULL, bool* pl
|
||||
bool carry_flag = ((shipp->flags[Ship::Ship_Flags::Navpoint_carry]) || ((shipp->wingnum >= 0) && (Wings[shipp->wingnum].flags[Ship::Wing_Flags::Nav_carry])));
|
||||
|
||||
float waypoint_turnrate = 1 / (3.0f * scale);
|
||||
|
||||
// for retail compatability reasons, have to take into account rotdamp so that ships with less than 1 rotdamp have increased waypoint turnrate
|
||||
// please see PR #2740 and #3494 for more information
|
||||
if (Pl_objp->phys_info.rotdamp < 1.0f )
|
||||
// start increasing the turnrate to 2x at 0.5 rotdamp and 3x at 0
|
||||
waypoint_turnrate *= (3 - 2 * Pl_objp->phys_info.rotdamp);
|
||||
|
||||
vec3d turnrate_mod;
|
||||
vm_vec_make(&turnrate_mod, waypoint_turnrate, waypoint_turnrate, waypoint_turnrate);
|
||||
|
||||
@@ -4697,11 +4707,13 @@ void ai_waypoints()
|
||||
mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, aip->wp_list->get_name(), -1 );
|
||||
}
|
||||
// adds scripting hook for 'On Waypoints Done' --wookieejedi
|
||||
Script_system.SetHookObject("Ship", &Objects[Ships[Pl_objp->instance].objnum]);
|
||||
Script_system.SetHookVar("Wing", 'o', scripting::api::l_Wing.Set(Ships[Pl_objp->instance].wingnum));
|
||||
Script_system.SetHookVar("Waypointlist", 'o', scripting::api::l_WaypointList.Set(scripting::api::waypointlist_h(aip->wp_list)));
|
||||
Script_system.RunCondition(CHA_ONWAYPOINTSDONE);
|
||||
Script_system.RemHookVars({"Ship", "Wing", "Waypointlist"});
|
||||
if (Script_system.IsActiveAction(CHA_ONWAYPOINTSDONE)) {
|
||||
Script_system.SetHookObject("Ship", &Objects[Ships[Pl_objp->instance].objnum]);
|
||||
Script_system.SetHookVar("Wing", 'o', scripting::api::l_Wing.Set(Ships[Pl_objp->instance].wingnum));
|
||||
Script_system.SetHookVar("Waypointlist", 'o', scripting::api::l_WaypointList.Set(scripting::api::waypointlist_h(aip->wp_list)));
|
||||
Script_system.RunCondition(CHA_ONWAYPOINTSDONE);
|
||||
Script_system.RemHookVars({"Ship", "Wing", "Waypointlist"});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5285,7 +5297,7 @@ void evade_ship()
|
||||
}
|
||||
} else {
|
||||
evade_ship_l1: ;
|
||||
if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
|
||||
if (aip->ai_evasion > Random::next()*Random::INV_F_MAX_VALUE*100.0f) {
|
||||
int temp;
|
||||
float scale;
|
||||
float psrandval; // some value close to zero to choose whether to turn right or left.
|
||||
@@ -5755,6 +5767,140 @@ void set_primary_weapon_linkage(object *objp)
|
||||
}
|
||||
}
|
||||
|
||||
const int MULTILOCK_CHECK_INTERVAL = 250; // every quarter of a second
|
||||
|
||||
// This function returns if the ship could theoretically have fully locked all available targets
|
||||
// given the current time it's been locking on its primary target
|
||||
// If so, it cheats a little bit and assumes that's what it has done, and fills
|
||||
// the ai's missile_locks_firing vector, and to be fired at those targets
|
||||
bool ai_do_multilock(ai_info* aip, weapon_info* wip) {
|
||||
|
||||
if (!timestamp_elapsed(aip->multilock_check_timestamp))
|
||||
return false;
|
||||
|
||||
object* primary_target = &Objects[aip->target_objnum];
|
||||
|
||||
struct multilock_target {
|
||||
object* objp;
|
||||
ship_subsys* subsys;
|
||||
float dot;
|
||||
};
|
||||
|
||||
// First we accrue all available targets into this vector which we will later sort by dot
|
||||
SCP_vector<multilock_target> multilock_targets;
|
||||
|
||||
if (wip->target_restrict == LR_CURRENT_TARGET)
|
||||
multilock_targets.push_back(multilock_target{ primary_target, aip->targeted_subsys, 1.0f });
|
||||
else if (wip->target_restrict == LR_CURRENT_TARGET_SUBSYS) {
|
||||
|
||||
if (primary_target->type != OBJ_SHIP)
|
||||
multilock_targets.push_back(multilock_target{ primary_target, nullptr, 1.0f });
|
||||
else {
|
||||
ship* sp = &Ships[primary_target->instance];
|
||||
ship_subsys* ss;
|
||||
|
||||
if (Ship_info[sp->ship_info_index].is_big_or_huge()) {
|
||||
for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss)) {
|
||||
float dot;
|
||||
|
||||
if (!weapon_multilock_can_lock_on_subsys(Pl_objp, primary_target, ss, wip, &dot))
|
||||
continue;
|
||||
|
||||
// this will guarantee the ship will send always send some missiles at its actual target
|
||||
if (aip->targeted_subsys == ss)
|
||||
dot = 1.0f;
|
||||
|
||||
multilock_targets.push_back(multilock_target{ primary_target, ss, dot });
|
||||
}
|
||||
} else
|
||||
multilock_targets.push_back(multilock_target{ primary_target, nullptr, 1.0f });
|
||||
}
|
||||
} else { // any target
|
||||
for (ship_obj* so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so)) {
|
||||
object* target_objp = &Objects[so->objnum];
|
||||
ship* target_ship = &Ships[target_objp->instance];
|
||||
float dot;
|
||||
|
||||
if (!weapon_multilock_can_lock_on_target(Pl_objp, target_objp, wip, &dot))
|
||||
continue;
|
||||
|
||||
if (Ship_info[target_ship->ship_info_index].is_big_or_huge()) {
|
||||
// add ALL the valid subsystems as possible targets
|
||||
// dont check range or lock fov, the sub function will do that for subsystems
|
||||
ship_subsys* ss;
|
||||
for (ss = GET_FIRST(&target_ship->subsys_list); ss != END_OF_LIST(&target_ship->subsys_list); ss = GET_NEXT(ss)) {
|
||||
|
||||
if (!weapon_multilock_can_lock_on_subsys(Pl_objp, target_objp, ss, wip, &dot))
|
||||
continue;
|
||||
|
||||
// this will guarantee the ship will send always send some missiles at its actual target
|
||||
if (aip->targeted_subsys == ss)
|
||||
dot = 1.0f;
|
||||
|
||||
multilock_targets.push_back(multilock_target{ target_objp, ss, dot });
|
||||
}
|
||||
} else {
|
||||
// just a small target, now we check range and fov
|
||||
if (!weapon_secondary_world_pos_in_range(Player_obj, wip, &target_objp->pos))
|
||||
continue;
|
||||
|
||||
if (dot < wip->lock_fov)
|
||||
continue;
|
||||
|
||||
// this will guarantee the ship will send always send some missiles at its actual target
|
||||
if (primary_target == target_objp)
|
||||
dot = 1.0f;
|
||||
|
||||
multilock_targets.push_back(multilock_target{ target_objp, nullptr, dot });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// before any sorting let's first determine if we're maximally locked
|
||||
int allocatable_missiles = MIN(weapon_get_max_missile_seekers(wip), (int)multilock_targets.size() * wip->max_seekers_per_target);
|
||||
float missiles_allocated = wip->max_seeking * (aip->aspect_locked_time / wip->min_lock_time);
|
||||
|
||||
// we could still lock more missiles! wait for it (or couldn't find any targets??)
|
||||
if (missiles_allocated < allocatable_missiles || allocatable_missiles == 0) {
|
||||
aip->multilock_check_timestamp = timestamp(MULTILOCK_CHECK_INTERVAL);
|
||||
return false;
|
||||
}
|
||||
|
||||
// we're fully locked! sort by dot and let them fly!
|
||||
std::sort(multilock_targets.begin(), multilock_targets.end(), [](multilock_target &a, multilock_target &b)
|
||||
{ return a.dot < b.dot; });
|
||||
|
||||
aip->ai_missile_locks_firing.clear();
|
||||
int missiles = weapon_get_max_missile_seekers(wip);
|
||||
int remaining_seekers_per_target = wip->max_seekers_per_target;
|
||||
for (size_t i = multilock_targets.size() - 1;; i--) {
|
||||
std::pair<int, ship_subsys*> lock;
|
||||
|
||||
lock.first = OBJ_INDEX(multilock_targets.at(i).objp);
|
||||
lock.second = multilock_targets.at(i).subsys;
|
||||
aip->ai_missile_locks_firing.push_back(lock);
|
||||
missiles--;
|
||||
|
||||
// out of missiles, we're done
|
||||
if (missiles == 0)
|
||||
break;
|
||||
|
||||
// still more missiles, but exhausted our targets, time to loop over them all again
|
||||
// and start double-locking triple-locking etc
|
||||
if (i == 0) {
|
||||
remaining_seekers_per_target--;
|
||||
// exhausted our seekers per target, we're done
|
||||
if (remaining_seekers_per_target == 0)
|
||||
break;
|
||||
|
||||
// reset i and go through the whole list again
|
||||
i = multilock_targets.size();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Fire the current primary weapon.
|
||||
// *objp is the object to fire from.
|
||||
@@ -6396,9 +6542,18 @@ int ai_fire_secondary_weapon(object *objp)
|
||||
// Note, maybe don't fire if firing at player and any homers yet fired.
|
||||
// Decreasing chance to fire the more homers are incoming on player.
|
||||
if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip, current_bank, nullptr)) {
|
||||
|
||||
if (wip->multi_lock && !ai_do_multilock(aip, wip))
|
||||
return rval;
|
||||
|
||||
if (ship_fire_secondary(objp)) {
|
||||
rval = 1;
|
||||
swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
|
||||
|
||||
if (wip->multi_lock && wip->launch_reset_locks) {
|
||||
aip->aspect_locked_time = 0.0f;
|
||||
aip->current_target_is_locked = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -6606,6 +6761,8 @@ float ai_get_weapon_dist(ship_weapon *swp)
|
||||
return 1000.0f;
|
||||
}
|
||||
|
||||
Assertion(weapon_num >= 0, "A ship's weapon has a nonsense index of %d, but the ship's name is not accessible from this function. Please report!", weapon_num);
|
||||
|
||||
return MIN((Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime), Weapon_info[weapon_num].weapon_range);
|
||||
}
|
||||
|
||||
@@ -6623,6 +6780,8 @@ float ai_get_weapon_speed(ship_weapon *swp)
|
||||
return 100.0f;
|
||||
}
|
||||
|
||||
Assertion(weapon_num >= 0, "A ship's weapon has a nonsense index of %d, but the ship's name is not accessible from this function. Please report!", weapon_num);
|
||||
|
||||
return Weapon_info[weapon_num].max_speed;
|
||||
}
|
||||
|
||||
@@ -6632,14 +6791,16 @@ weapon_info* ai_get_weapon(ship_weapon *swp)
|
||||
|
||||
bank_num = swp->current_primary_bank;
|
||||
if (bank_num < 0)
|
||||
return NULL;
|
||||
return nullptr;
|
||||
|
||||
weapon_num = swp->primary_bank_weapons[bank_num];
|
||||
|
||||
if (weapon_num == -1) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Assertion(weapon_num >= 0, "A ship's weapon has a nonsense index of %d, but the ship's name is not accessible from this function. Please report!", weapon_num);
|
||||
|
||||
return &Weapon_info[weapon_num];
|
||||
}
|
||||
|
||||
@@ -6894,6 +7055,8 @@ float ai_endangered_by_weapon(ai_info *aip)
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
Assertion(aip->danger_weapon_objnum >= 0, "%s has a nonsense danger_weapon_objnum of %d. Please report!", Ships[aip->shipnum].ship_name, aip->danger_weapon_objnum);\
|
||||
|
||||
weapon_objp = &Objects[aip->danger_weapon_objnum];
|
||||
|
||||
if (weapon_objp->signature != aip->danger_weapon_signature) {
|
||||
@@ -6935,6 +7098,8 @@ void do_random_sidethrust(ai_info *aip, ship_info *sip)
|
||||
AI_ci.sideways = side_vec.x;
|
||||
AI_ci.vertical = side_vec.y;
|
||||
}
|
||||
|
||||
const float AI_DEFAULT_ATTACK_APPROACH_DIST = 200.0f;
|
||||
|
||||
/**
|
||||
* Set acceleration while in attack mode.
|
||||
@@ -6987,10 +7152,24 @@ void attack_set_accel(ai_info *aip, ship_info *sip, float dist_to_enemy, float d
|
||||
return;
|
||||
}
|
||||
|
||||
// assume the original retail value of a 200m range
|
||||
float optimal_range = AI_DEFAULT_ATTACK_APPROACH_DIST;
|
||||
ship_weapon* weapons = &Ships[Pl_objp->instance].weapons;
|
||||
weapon_info* wip = nullptr;
|
||||
|
||||
if (dist_to_enemy > 200.0f + vm_vec_mag_quick(&En_objp->phys_info.vel) * dot_from_enemy + Pl_objp->phys_info.speed * speed_ratio) {
|
||||
// see if we can get a better one
|
||||
if (weapons->num_primary_banks >= 1 && weapons->current_primary_bank >= 0) {
|
||||
wip = &Weapon_info[weapons->primary_bank_weapons[weapons->current_primary_bank]];
|
||||
} else if (weapons->num_secondary_banks >= 1 && weapons->current_secondary_bank >= 0) {
|
||||
wip = &Weapon_info[weapons->secondary_bank_weapons[weapons->current_secondary_bank]];
|
||||
}
|
||||
|
||||
if (wip != nullptr && wip->optimum_range > 0)
|
||||
optimal_range = wip->optimum_range;
|
||||
|
||||
if (dist_to_enemy > optimal_range + vm_vec_mag_quick(&En_objp->phys_info.vel) * dot_from_enemy + Pl_objp->phys_info.speed * speed_ratio) {
|
||||
if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
|
||||
if (dist_to_enemy > 800.0f) {
|
||||
if (dist_to_enemy > optimal_range + 600.0f) {
|
||||
if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
|
||||
float percent_left;
|
||||
ship *shipp;
|
||||
@@ -7543,7 +7722,7 @@ void ai_chase_attack(ai_info *aip, ship_info *sip, vec3d *predicted_enemy_pos, f
|
||||
vec3d new_pos;
|
||||
|
||||
start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
|
||||
if (po->n_guns && start_bank != -1 ) {
|
||||
if (po->n_guns && start_bank != -1 && !(The_mission.ai_profile->flags[AI::Profile_Flags::Ai_aims_from_ship_center])) {
|
||||
rel_pos = &po->gun_banks[start_bank].pnt[0];
|
||||
} else
|
||||
rel_pos = NULL;
|
||||
@@ -7677,6 +7856,8 @@ int ai_set_attack_subsystem(object *objp, int subnum)
|
||||
if ( aip->target_objnum == -1 )
|
||||
return 0;
|
||||
|
||||
Assertion(aip->target_objnum >= 0, "The target_objnum for %s has become a nonsense value of %d. Please report!",attacker_shipp->ship_name ,aip->target_objnum);
|
||||
|
||||
attacked_objp = &Objects[aip->target_objnum];
|
||||
shipp = &Ships[attacked_objp->instance]; // need to get our target's ship pointer!!!
|
||||
|
||||
@@ -7891,11 +8072,13 @@ void update_aspect_lock_information(ai_info *aip, vec3d *vec_to_enemy, float dis
|
||||
(tshpp == NULL ||
|
||||
ship_get_closest_subsys_in_sight(tshpp, SUBSYSTEM_ENGINE, &aiobjp->pos))))) {
|
||||
aip->aspect_locked_time += flFrametime;
|
||||
if (aip->aspect_locked_time >= wip->min_lock_time) {
|
||||
aip->aspect_locked_time = wip->min_lock_time;
|
||||
if (aip->aspect_locked_time >= wip->min_lock_time) {
|
||||
aip->current_target_is_locked = 1;
|
||||
}
|
||||
} else {
|
||||
if (aip->aspect_locked_time > wip->min_lock_time)
|
||||
aip->aspect_locked_time = wip->min_lock_time;
|
||||
|
||||
aip->aspect_locked_time -= flFrametime*2;
|
||||
if (aip->aspect_locked_time < 0.0f)
|
||||
aip->aspect_locked_time = 0.0f;
|
||||
@@ -7932,6 +8115,8 @@ void ai_chase_fly_away(object *objp, ai_info *aip)
|
||||
} else {
|
||||
vec3d v2e;
|
||||
float dot;
|
||||
|
||||
Assertion(aip->target_objnum >= 0, "This ship's target objnum is nonsense. It is %d instead of a positive number or -1.", aip->target_objnum);
|
||||
|
||||
vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
|
||||
|
||||
@@ -8776,7 +8961,7 @@ void ai_chase()
|
||||
if (frand() > 0.5f) {
|
||||
aip->submode = SM_CONTINUOUS_TURN;
|
||||
aip->submode_start_time = Missiontime;
|
||||
aip->submode_parm0 = myrand() & 0x0f;
|
||||
aip->submode_parm0 = Random::next() & 0x0f;
|
||||
} else {
|
||||
aip->submode = SM_EVADE;
|
||||
aip->submode_start_time = Missiontime;
|
||||
@@ -8859,7 +9044,7 @@ void ai_chase()
|
||||
? (float)(aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)
|
||||
: aip->ai_get_away_chance;
|
||||
|
||||
switch (myrand() % 5) {
|
||||
switch (Random::next(5)) {
|
||||
case 0:
|
||||
aip->submode = SM_CONTINUOUS_TURN;
|
||||
aip->submode_start_time = Missiontime;
|
||||
@@ -9064,7 +9249,7 @@ void ai_chase()
|
||||
float range_max = pwip->max_speed * (1.0f + scale);
|
||||
if (aip->ai_profile_flags[AI::Profile_Flags::Use_actual_primary_range]) {
|
||||
range_max = std::min({range_max, pwip->max_speed * pwip->lifetime, pwip->weapon_range});
|
||||
range_min = pwip->WeaponMinRange;
|
||||
range_min = pwip->weapon_min_range;
|
||||
}
|
||||
if ((dist_to_enemy < range_max) && (dist_to_enemy >= range_min)) {
|
||||
if(ai_fire_primary_weapon(Pl_objp) == 1){
|
||||
@@ -9148,11 +9333,11 @@ void ai_chase()
|
||||
} else {
|
||||
t = swip->fire_wait;
|
||||
if ((swip->burst_shots > 0) && (swip->burst_flags[Weapon::Burst_Flags::Random_length])) {
|
||||
swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
|
||||
swp->burst_counter[current_bank_adjusted] = Random::next(swip->burst_shots);
|
||||
} else {
|
||||
swp->burst_counter[current_bank_adjusted] = 0;
|
||||
}
|
||||
swp->burst_seed[current_bank_adjusted] = rand32();
|
||||
swp->burst_seed[current_bank_adjusted] = Random::next();
|
||||
}
|
||||
} else {
|
||||
if (swip->burst_shots > swp->burst_counter[current_bank_adjusted]) {
|
||||
@@ -9161,11 +9346,11 @@ void ai_chase()
|
||||
} else {
|
||||
t = set_secondary_fire_delay(aip, temp_shipp, swip, false);
|
||||
if ((swip->burst_shots > 0) && (swip->burst_flags[Weapon::Burst_Flags::Random_length])) {
|
||||
swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
|
||||
swp->burst_counter[current_bank_adjusted] = Random::next(swip->burst_shots);
|
||||
} else {
|
||||
swp->burst_counter[current_bank_adjusted] = 0;
|
||||
}
|
||||
swp->burst_seed[current_bank_adjusted] = rand32();
|
||||
swp->burst_seed[current_bank_adjusted] = Random::next();
|
||||
}
|
||||
}
|
||||
swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
|
||||
@@ -9756,7 +9941,7 @@ void remove_farthest_attacker(int objnum)
|
||||
aip->ignore_objnum = aip->target_objnum;
|
||||
aip->ignore_signature = Objects[aip->target_objnum].signature;
|
||||
aip->ai_flags.set(AI::AI_Flags::Temporary_ignore);
|
||||
aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000); // OK to attack again in 20 to 24 seconds.
|
||||
aip->ignore_expire_timestamp = timestamp(Random::next(20, 29) * 1000); // OK to attack again in 20 to 29 seconds.
|
||||
}
|
||||
aip->target_objnum = -1;
|
||||
ai_do_default_behavior(farthest_objp);
|
||||
@@ -11253,7 +11438,7 @@ void ai_dock()
|
||||
// calculate time until animations elapse
|
||||
int time1 = model_anim_get_time_type(shipp, AnimationTriggerType::Docked, docker_index);
|
||||
int time2 = model_anim_get_time_type(goal_shipp, AnimationTriggerType::Docked, dockee_index);
|
||||
aip->mode_time = MAX(time1, time2);
|
||||
aip->mode_time = timestamp(MAX(time1, time2));
|
||||
}
|
||||
|
||||
// if not enough time has passed, just wait
|
||||
@@ -11463,9 +11648,6 @@ void process_subobjects(int objnum)
|
||||
ai_info *aip = &Ai_info[shipp->ai_index];
|
||||
ship_info *sip = &Ship_info[shipp->ship_info_index];
|
||||
|
||||
//Look for enemies. If none are present, we don't have to move turrets
|
||||
int enemies_present = -1;
|
||||
|
||||
model_subsystem *psub;
|
||||
for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
|
||||
psub = pss->system_info;
|
||||
@@ -11485,34 +11667,7 @@ void process_subobjects(int objnum)
|
||||
|
||||
if ( psub->turret_num_firing_points > 0 )
|
||||
{
|
||||
if(enemies_present == -1)
|
||||
{
|
||||
enemies_present = 0;
|
||||
for(unsigned int i = 0; i < MAX_OBJECTS; i++)
|
||||
{
|
||||
objp = &Objects[i];
|
||||
switch(objp->type)
|
||||
{
|
||||
case OBJ_SHIP:
|
||||
case OBJ_DEBRIS:
|
||||
case OBJ_WEAPON:
|
||||
if(obj_team(objp) != shipp->team)
|
||||
enemies_present = 1;
|
||||
break;
|
||||
case OBJ_ASTEROID:
|
||||
enemies_present = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if(enemies_present==1)
|
||||
break;
|
||||
}
|
||||
//Reset objp
|
||||
objp = &Objects[objnum];
|
||||
}
|
||||
//Only move turrets if enemies are present
|
||||
if(enemies_present == 1 || pss->turret_enemy_objnum >= 0)
|
||||
ai_fire_from_turret(shipp, pss);
|
||||
ai_turret_execute_behavior(shipp, pss);
|
||||
} else {
|
||||
Warning( LOCATION, "Turret %s on ship %s has no firing points assigned to it.\nThis needs to be fixed in the model.\n", psub->name, shipp->ship_name );
|
||||
}
|
||||
@@ -12294,6 +12449,8 @@ void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
|
||||
if (support_objnum == -1)
|
||||
return;
|
||||
|
||||
Assertion(support_objnum >= 0, "The support ship objnum is nonsense. It is %d instead of a positive number or -1.", support_objnum);
|
||||
|
||||
// Curious -- object numbers match, but signatures do not.
|
||||
// Must mean original repair ship died and was replaced by current ship.
|
||||
Assert(Objects[support_objnum].signature == aip->support_ship_signature);
|
||||
@@ -12893,7 +13050,9 @@ int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, int allowed_path_
|
||||
pnode *pnp;
|
||||
vec3d *next_point;
|
||||
|
||||
Assertion(pl_objp->instance >= 0, "Arriving ship does not have a valid ship number. Has %d instead. Please report!", pl_objp->instance);
|
||||
ship *shipp = &Ships[pl_objp->instance];
|
||||
Assertion(shipp->ai_index >= 0, "Arriving ship does not have a valid ai index. Has %d instead. Please report!", shipp->ai_index);
|
||||
ai_info *aip = &Ai_info[shipp->ai_index];
|
||||
|
||||
if ( parent_objnum == -1 ) {
|
||||
@@ -12901,6 +13060,7 @@ int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, int allowed_path_
|
||||
return -1;
|
||||
}
|
||||
|
||||
Assertion(parent_objnum >= 0, "Arriving ship does not have a parent object number. Has %d instead. Please report!", parent_objnum);
|
||||
object *parent_objp = &Objects[parent_objnum];
|
||||
ship *parent_shipp = &Ships[parent_objp->instance];
|
||||
|
||||
@@ -13609,7 +13769,7 @@ void ai_maybe_depart(object *objp)
|
||||
if (sip->is_fighter_bomber()) {
|
||||
if (aip->warp_out_timestamp == 0) {
|
||||
//if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
|
||||
// aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
|
||||
// aip->warp_out_timestamp = timestamp(Random::next(10, 19) * 1000);
|
||||
//}
|
||||
} else if (timestamp_elapsed(aip->warp_out_timestamp)) {
|
||||
mission_do_departure(objp);
|
||||
@@ -14096,7 +14256,7 @@ void ai_maybe_self_destruct(object *objp, ai_info *aip)
|
||||
}
|
||||
|
||||
if (timestamp_elapsed(aip->self_destruct_timestamp)) {
|
||||
ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
|
||||
ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, -1, MISS_SHIELDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14705,7 +14865,6 @@ void init_ai_object(int objnum)
|
||||
aip->danger_weapon_objnum = -1;
|
||||
aip->danger_weapon_signature = -1;
|
||||
|
||||
aip->lead_scale = 0.0f;
|
||||
aip->last_hit_target_time = Missiontime;
|
||||
aip->last_hit_time = Missiontime;
|
||||
|
||||
@@ -14765,6 +14924,9 @@ void init_ai_object(int objnum)
|
||||
memset(&aip->ai_override_ci,0,sizeof(control_info));
|
||||
|
||||
aip->form_obj_slotnum = -1;
|
||||
|
||||
aip->multilock_check_timestamp = timestamp(1);
|
||||
aip->ai_missile_locks_firing.clear();
|
||||
}
|
||||
|
||||
void init_ai_system()
|
||||
@@ -15225,7 +15387,7 @@ void ai_update_lethality(object *pship_obj, object *other_obj, float damage)
|
||||
void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hit_normal)
|
||||
{
|
||||
int hitter_objnum = -2;
|
||||
object *objp_hitter = NULL;
|
||||
object *objp_hitter = nullptr;
|
||||
ship *shipp;
|
||||
ai_info *aip, *hitter_aip;
|
||||
|
||||
@@ -15233,25 +15395,29 @@ void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hit_normal)
|
||||
aip = &Ai_info[shipp->ai_index];
|
||||
|
||||
if (objp_ship->flags[Object::Object_Flags::Player_ship]) {
|
||||
//SUSHI: So that hitting a player ship actually resets the last_hit_target_time counter for whoever hit the player.
|
||||
//This is all copypasted from code below
|
||||
// Added OBJ_BEAM for traitor detection - FUBAR
|
||||
if ((hit_objp->type == OBJ_WEAPON) || (hit_objp->type == OBJ_BEAM)) {
|
||||
hitter_objnum = hit_objp->parent;
|
||||
Assert((hitter_objnum < MAX_OBJECTS));
|
||||
if (hitter_objnum == -1) {
|
||||
return; // Possible SSM, bail while we still can.
|
||||
if (The_mission.ai_profile->flags[AI::Profile_Flags::Reset_last_hit_target_time_for_player_hits]) {
|
||||
//SUSHI: So that hitting a player ship actually resets the last_hit_target_time counter for whoever hit the player.
|
||||
//This is all copypasted from code below
|
||||
// Added OBJ_BEAM for traitor detection - FUBAR
|
||||
if ((hit_objp->type == OBJ_WEAPON) || (hit_objp->type == OBJ_BEAM)) {
|
||||
hitter_objnum = hit_objp->parent;
|
||||
Assert((hitter_objnum < MAX_OBJECTS));
|
||||
if (hitter_objnum == -1) {
|
||||
return; // Possible SSM, bail while we still can.
|
||||
}
|
||||
objp_hitter = &Objects[hitter_objnum];
|
||||
} else if (hit_objp->type == OBJ_SHIP) {
|
||||
objp_hitter = hit_objp;
|
||||
} else {
|
||||
UNREACHABLE("Should never happen.");
|
||||
return;
|
||||
}
|
||||
objp_hitter = &Objects[hitter_objnum];
|
||||
} else if (hit_objp->type == OBJ_SHIP) {
|
||||
objp_hitter = hit_objp;
|
||||
} else {
|
||||
Int3(); // Should never happen.
|
||||
return;
|
||||
Assert(objp_hitter != nullptr);
|
||||
hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
|
||||
hitter_aip->last_hit_target_time = Missiontime;
|
||||
|
||||
aip->last_hit_time = Missiontime;
|
||||
}
|
||||
Assert(objp_hitter != NULL);
|
||||
hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
|
||||
hitter_aip->last_hit_target_time = Missiontime;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15277,6 +15443,11 @@ void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hit_normal)
|
||||
if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
|
||||
return;
|
||||
}
|
||||
|
||||
weapon_info* wip = hit_objp->type == OBJ_WEAPON ? &Weapon_info[Weapons[hit_objp->instance].weapon_info_index] :
|
||||
&Weapon_info[Beams[hit_objp->instance].weapon_info_index];
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Heals])
|
||||
return;
|
||||
|
||||
hitter_objnum = hit_objp->parent;
|
||||
Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
|
||||
@@ -15332,7 +15503,7 @@ void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hit_normal)
|
||||
if (hit_objp->flags[Object::Object_Flags::Protected])
|
||||
return;
|
||||
|
||||
Assert(objp_hitter != NULL);
|
||||
Assert(objp_hitter != nullptr);
|
||||
hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
|
||||
hitter_aip->last_hit_target_time = Missiontime;
|
||||
|
||||
|
||||
+9
-3
@@ -23,6 +23,7 @@
|
||||
#include "playerman/player.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "ship/ship.h"
|
||||
#include "ship/awacs.h"
|
||||
#include "weapon/weapon.h"
|
||||
|
||||
// Just a reference to this struct being for looking up a ship's team in TVT
|
||||
@@ -187,6 +188,9 @@ void ai_post_process_mission()
|
||||
object *objp;
|
||||
int i;
|
||||
|
||||
// make sure team visibility is updated first
|
||||
awacs_process();
|
||||
|
||||
// Check ships in player starting wings. Those ships should follow these rules:
|
||||
// (1) if they have no orders, they should get a form on my wing order
|
||||
// (2) if they have an order, they are free to act on it.
|
||||
@@ -293,9 +297,11 @@ void ai_clear_ship_goals( ai_info *aip )
|
||||
}
|
||||
|
||||
// add scripting hook for 'On Goals Cleared' --wookieejedi
|
||||
Script_system.SetHookObject("Ship", &Objects[Ships[aip->shipnum].objnum]);
|
||||
Script_system.RunCondition(CHA_ONGOALSCLEARED);
|
||||
Script_system.RemHookVars({"Ship"});
|
||||
if (Script_system.IsActiveAction(CHA_ONGOALSCLEARED)) {
|
||||
Script_system.SetHookObject("Ship", &Objects[Ships[aip->shipnum].objnum]);
|
||||
Script_system.RunCondition(CHA_ONGOALSCLEARED);
|
||||
Script_system.RemHookVars({"Ship"});
|
||||
}
|
||||
}
|
||||
|
||||
void ai_clear_wing_goals( wing *wingp )
|
||||
|
||||
@@ -32,6 +32,6 @@ int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip, int second
|
||||
bool check_los(int objnum, int target_objnum, float threshold, int primary_bank, int secondary_bank, ship_subsys* turret);
|
||||
|
||||
//Does all the stuff needed to aim and fire a turret.
|
||||
void ai_fire_from_turret(ship *shipp, ship_subsys *ss);
|
||||
void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss);
|
||||
|
||||
#endif
|
||||
+117
-163
@@ -18,6 +18,7 @@
|
||||
#include "render/3d.h"
|
||||
#include "ship/ship.h"
|
||||
#include "ship/shipfx.h"
|
||||
#include "utils/Random.h"
|
||||
#include "weapon/beam.h"
|
||||
#include "weapon/flak.h"
|
||||
#include "weapon/muzzleflash.h"
|
||||
@@ -87,6 +88,18 @@ typedef struct eval_enemy_obj_struct {
|
||||
int nearest_objnum;
|
||||
} eval_enemy_obj_struct;
|
||||
|
||||
// the current world orientation of the turret matrix, corresponding to its fvec and uvec defined in the model
|
||||
// is NOT affected by the turret's current aiming
|
||||
void turret_instance_find_world_orient(matrix* out_mat, int model_instance_num, int submodel_num, const matrix* objorient)
|
||||
{
|
||||
auto pmi = model_get_instance(model_instance_num);
|
||||
auto pm = model_get(pmi->model_num);
|
||||
vec3d fvec, uvec;
|
||||
model_instance_find_world_dir(&fvec, &pm->submodel[submodel_num].frame_of_reference.vec.fvec, pm, pmi, pm->submodel[submodel_num].parent, objorient);
|
||||
model_instance_find_world_dir(&uvec, &pm->submodel[submodel_num].frame_of_reference.vec.uvec, pm, pmi, pm->submodel[submodel_num].parent, objorient);
|
||||
vm_vector_2_matrix_norm(out_mat, &fvec, &uvec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is object in turret field of view?
|
||||
*
|
||||
@@ -98,124 +111,53 @@ typedef struct eval_enemy_obj_struct {
|
||||
*
|
||||
* @return 1 if objp is in fov of the specified turret. Otherwise return 0.
|
||||
*/
|
||||
int object_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, float dist)
|
||||
bool object_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, float dist)
|
||||
{
|
||||
vec3d v2e;
|
||||
float size_mod;
|
||||
bool in_fov;
|
||||
bool in_fov = false;
|
||||
|
||||
if (ss->flags[Ship::Subsystem_Flags::FOV_edge_check]) {
|
||||
int model_num;
|
||||
switch (objp->type) {
|
||||
case OBJ_SHIP:
|
||||
model_num = Ship_info[Ships[objp->instance].ship_info_index].model_num;
|
||||
break;
|
||||
case OBJ_ASTEROID:
|
||||
model_num = Asteroid_info[Asteroids[objp->instance].asteroid_type].model_num[Asteroids[objp->instance].asteroid_subtype];
|
||||
break;
|
||||
default:
|
||||
vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
|
||||
size_mod = objp->radius / (dist + objp->radius);
|
||||
|
||||
in_fov = turret_fov_test(ss, tvec, &v2e, size_mod);
|
||||
|
||||
return in_fov;
|
||||
}
|
||||
|
||||
auto pm = model_get(model_num);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
vec3d bbox_point;
|
||||
vm_vec_unrotate(&bbox_point, &pm->bounding_box[i], &objp->orient);
|
||||
bbox_point += objp->pos;
|
||||
|
||||
vm_vec_normalized_dir(&v2e, &bbox_point, tpos);
|
||||
in_fov = turret_fov_test(ss, tvec, &v2e, -0.2f);
|
||||
|
||||
if (in_fov)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if the bbox points method didn't work (or fov_edge_checks isn't on)
|
||||
// try the normal method
|
||||
|
||||
vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
|
||||
size_mod = objp->radius / (dist + objp->radius);
|
||||
|
||||
in_fov = turret_fov_test(ss, tvec, &v2e, size_mod);
|
||||
|
||||
if ( in_fov ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_object_radius_in_turret_fov(object *objp, ship_subsys *ss, vec3d *tvec, vec3d *tpos, vec3d *v2e, vec3d *predicted_pos, float distance)
|
||||
{
|
||||
float target_dist = distance;
|
||||
if (distance == 0.0f)
|
||||
target_dist = vm_vec_dist(predicted_pos,tpos);
|
||||
|
||||
if (object_in_turret_fov(objp, ss, tvec, tpos, target_dist + objp->radius)) {
|
||||
// so the targeted spot in not in fov but the enemy + radius is
|
||||
// lets align the darn gun and try shooting there
|
||||
vec3d temp_vec;
|
||||
float multiplier = 0;
|
||||
model_subsystem *tp = ss->system_info;
|
||||
|
||||
// project v2e_from_turret to turret normal
|
||||
// substract resultant vector from the temp_vec (v2e_from_turret)
|
||||
// adjust z component as necessary
|
||||
// calculate multiplier for the resultant vector
|
||||
// use multiplier and the z component and compose a new vector
|
||||
float dot = vm_vec_dot(v2e, tvec);
|
||||
|
||||
vm_vec_scale_add(&temp_vec, v2e, tvec, -dot);
|
||||
|
||||
if (IS_VEC_NULL_SQ_SAFE(&temp_vec)) {
|
||||
// return false, target is perfectly aligned over or below the turret
|
||||
// safe bet is to allow turret to reacquire better target
|
||||
return false;
|
||||
}
|
||||
|
||||
// normalize the vec, it needs to be done regardless
|
||||
vm_vec_normalize(&temp_vec);
|
||||
bool fix_elevation = false;
|
||||
bool fix_base_rot = false;
|
||||
|
||||
if (dot < tp->turret_fov) {
|
||||
dot = tp->turret_fov;
|
||||
fix_elevation = true;
|
||||
}
|
||||
if (dot > tp->turret_max_fov) {
|
||||
dot = tp->turret_max_fov;
|
||||
fix_elevation = true;
|
||||
}
|
||||
|
||||
if (tp->flags[Model::Subsystem_Flags::Turret_alt_math]) {
|
||||
// Since we no longer maintain world_to_turret_matrix, regenerate it here.
|
||||
vec3d turret_norm;
|
||||
matrix turret_matrix, world_to_turret_matrix;
|
||||
model_instance_find_world_dir(&turret_norm, &tp->turret_norm, Ships[objp->instance].model_instance_num, tp->subobj_num, &vmd_identity_matrix);
|
||||
vm_vector_2_matrix(&turret_matrix, &turret_norm, nullptr, nullptr);
|
||||
vm_matrix_x_matrix(&world_to_turret_matrix, &objp->orient, &turret_matrix);
|
||||
|
||||
vec3d temp_vec2;
|
||||
vm_vec_rotate(&temp_vec2, &temp_vec, &world_to_turret_matrix);
|
||||
|
||||
// now in turrets frame of reference
|
||||
// check if math is actually possible
|
||||
if (!((temp_vec2.xyz.x == 0) && (temp_vec2.xyz.y == 0))) {
|
||||
float temp_z = temp_vec2.xyz.z;
|
||||
temp_vec2.xyz.z = 0.0f;
|
||||
// make sure null vecs wont happen
|
||||
if (!IS_VEC_NULL_SQ_SAFE(&temp_vec2)) {
|
||||
vm_vec_normalize(&temp_vec2);
|
||||
// only do this if it actually is required
|
||||
if (-temp_vec2.xyz.y < tp->turret_y_fov) {
|
||||
float check_pos = 1;
|
||||
|
||||
fix_base_rot = true;
|
||||
temp_vec2.xyz.y = -tp->turret_y_fov;
|
||||
if (temp_vec2.xyz.x < 0)
|
||||
check_pos = -1;
|
||||
temp_vec2.xyz.x = check_pos * sqrtf(1 - (temp_vec2.xyz.y*temp_vec2.xyz.y));
|
||||
|
||||
// restore z component
|
||||
float scaler = sqrtf(1 - (temp_z*temp_z));
|
||||
vm_vec_scale(&temp_vec2,scaler);
|
||||
temp_vec2.xyz.z = temp_z;
|
||||
// back to world frame
|
||||
vm_vec_unrotate(&temp_vec, &temp_vec2, &world_to_turret_matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fix_elevation || fix_base_rot) {
|
||||
if (fix_elevation) {
|
||||
multiplier = sqrtf(1 - (dot*dot));
|
||||
// keep the temp_vec scaled with the tweaked vector
|
||||
vm_vec_scale(&temp_vec, multiplier);
|
||||
}
|
||||
vm_vec_scale_add(v2e, &temp_vec, tvec, dot);
|
||||
// and we are done with v2e...
|
||||
vm_vec_scale_add(predicted_pos, tpos, v2e, target_dist);
|
||||
// and we are done with predicted position
|
||||
return true;
|
||||
} else {
|
||||
mprintf(("Warning: Function 'is_object_radius_in_turret_fov' was called\nwithout need to fix turret alignments\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// outside of the expanded radii, unable to align, return false
|
||||
return false;
|
||||
return in_fov;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1196,17 +1138,11 @@ int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vec3d *tpos, vec3d
|
||||
if ( tagged_only_flag && ship_is_tagged(&Objects[aip->target_objnum]) ) {
|
||||
// select new target if aip->target_objnum is out of field of view
|
||||
vec3d v2e;
|
||||
float dist;
|
||||
bool in_fov;
|
||||
dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
|
||||
vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
|
||||
|
||||
in_fov = turret_fov_test(turret_subsys, tvec, &v2e);
|
||||
|
||||
if (turret_subsys->flags[Ship::Subsystem_Flags::FOV_edge_check]) {
|
||||
if (in_fov == false)
|
||||
if (object_in_turret_fov(&Objects[aip->target_objnum], turret_subsys, tvec, tpos, dist + Objects[aip->target_objnum].radius))
|
||||
in_fov = true;
|
||||
}
|
||||
// MODIFY FOR ATTACKING BIG SHIP
|
||||
// dot += (0.5f * Objects[aip->target_objnum].radius / dist);
|
||||
if (in_fov) {
|
||||
@@ -1256,7 +1192,7 @@ void ship_get_global_turret_info(const object *objp, const model_subsystem *tp,
|
||||
if (gpos)
|
||||
model_instance_find_world_point(gpos, &vmd_zero_vector, model_instance_num, tp->subobj_num, &objp->orient, &objp->pos);
|
||||
if (gvec)
|
||||
model_instance_find_world_dir(gvec, &tp->turret_norm, model_instance_num, tp->subobj_num, &objp->orient);
|
||||
model_instance_find_world_dir(gvec, &tp->turret_norm, model_instance_num, tp->subobj_num, &objp->orient, true);
|
||||
}
|
||||
|
||||
void turret_ai_update_aim(ai_info *aip, object *En_Objp, ship_subsys *ss);
|
||||
@@ -1317,7 +1253,7 @@ void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vec3d *gpos
|
||||
} else {
|
||||
if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].is_big_or_huge())) {
|
||||
// the turret norm here is from the perspective of the base submodel, not the gun submodel
|
||||
model_instance_find_world_dir(&turret_norm, &tp->turret_norm, pm, pmi, tp->subobj_num, &objp->orient);
|
||||
model_instance_find_world_dir(&turret_norm, &tp->turret_norm, pm, pmi, pm->submodel[tp->subobj_num].parent, &objp->orient);
|
||||
ai_big_pick_attack_point_turret(lep, ssp, &tmp_pos, &turret_norm, &enemy_point, MIN(wip->max_speed * wip->lifetime, wip->weapon_range), tp->turret_fov);
|
||||
}
|
||||
else {
|
||||
@@ -1448,18 +1384,13 @@ int aifft_rotate_turret(object *objp, ship *shipp, ship_subsys *ss, object *lep,
|
||||
|
||||
in_fov = turret_fov_test(ss, gvec, &v2e);
|
||||
|
||||
if (ss->flags[Ship::Subsystem_Flags::FOV_edge_check]) {
|
||||
if (in_fov == false)
|
||||
in_fov = is_object_radius_in_turret_fov(&Objects[ss->turret_enemy_objnum], ss, gvec, &gun_pos, &v2e, predicted_enemy_pos, 0.0f);
|
||||
}
|
||||
|
||||
if (in_fov) {
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, tp, predicted_enemy_pos);
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos);
|
||||
} else if ((tp->flags[Model::Subsystem_Flags::Turret_reset_idle]) &&(timestamp_elapsed(ss->rotation_timestamp))) {
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, tp, predicted_enemy_pos, true);
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos, true);
|
||||
}
|
||||
} else if ((ss->system_info->flags[Model::Subsystem_Flags::Turret_reset_idle]) && (timestamp_elapsed(ss->rotation_timestamp))) {
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, ss->system_info, predicted_enemy_pos, true);
|
||||
ret_val = model_rotate_gun(objp, pm, pmi, ss, predicted_enemy_pos, true);
|
||||
}
|
||||
|
||||
// by default "ret_val" should be set to 1 for multi-part turrets, and 0 for single-part turrets
|
||||
@@ -1491,7 +1422,7 @@ float aifft_compute_turret_dot(object *objp, object *enemy_objp, vec3d *abs_gunp
|
||||
if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
|
||||
vec3d turret_norm;
|
||||
|
||||
model_instance_find_world_dir(&turret_norm, &turret_subsysp->system_info->turret_norm, Ships[objp->instance].model_instance_num, turret_subsysp->system_info->subobj_num, &objp->orient);
|
||||
model_instance_find_world_dir(&turret_norm, &turret_subsysp->system_info->turret_norm, Ships[objp->instance].model_instance_num, turret_subsysp->system_info->subobj_num, &objp->orient, true);
|
||||
float dot_return = vm_vec_dot(&turret_norm, &vector_out);
|
||||
|
||||
if (Ai_info[Ships[objp->instance].ai_index].ai_profile_flags[AI::Profile_Flags::Smart_subsystem_targeting_for_turrets]) {
|
||||
@@ -1674,8 +1605,10 @@ void turret_set_next_fire_timestamp(int weapon_num, weapon_info *wip, ship_subsy
|
||||
{
|
||||
Assert(weapon_num < MAX_SHIP_WEAPONS);
|
||||
float wait = 1000.0f;
|
||||
// should subtract 1 from firepoints to match behavior of wip->burst_shots
|
||||
int burst_shots = (wip->burst_flags[Weapon::Burst_Flags::Num_firepoints_burst_shots] ? turret->system_info->turret_num_firing_points - 1 : wip->burst_shots);
|
||||
|
||||
if (wip->burst_shots > turret->weapons.burst_counter[weapon_num]) {
|
||||
if (burst_shots > turret->weapons.burst_counter[weapon_num]) {
|
||||
wait *= wip->burst_delay;
|
||||
turret->weapons.burst_counter[weapon_num]++;
|
||||
} else {
|
||||
@@ -1685,12 +1618,12 @@ void turret_set_next_fire_timestamp(int weapon_num, weapon_info *wip, ship_subsy
|
||||
} else {
|
||||
wait *= wip->fire_wait;
|
||||
}
|
||||
if ((wip->burst_shots > 0) && (wip->burst_flags[Weapon::Burst_Flags::Random_length])) {
|
||||
turret->weapons.burst_counter[weapon_num] = (myrand() % wip->burst_shots);
|
||||
turret->weapons.burst_seed[weapon_num] = rand32();
|
||||
if ((burst_shots > 0) && (wip->burst_flags[Weapon::Burst_Flags::Random_length])) {
|
||||
turret->weapons.burst_counter[weapon_num] = Random::next(burst_shots);
|
||||
turret->weapons.burst_seed[weapon_num] = Random::next();
|
||||
} else {
|
||||
turret->weapons.burst_counter[weapon_num] = 0;
|
||||
turret->weapons.burst_seed[weapon_num] = rand32();
|
||||
turret->weapons.burst_seed[weapon_num] = Random::next();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1701,7 +1634,7 @@ void turret_set_next_fire_timestamp(int weapon_num, weapon_info *wip, ship_subsy
|
||||
fs_dest = &turret->weapons.next_secondary_fire_stamp[weapon_num - MAX_SHIP_PRIMARY_BANKS];
|
||||
|
||||
//Check for the new cooldown flag
|
||||
if(!((wip->wi_flags[Weapon::Info_Flags::Same_turret_cooldown]) || ((wip->burst_shots > 0) && (wip->burst_flags[Weapon::Burst_Flags::Fast_firing]))))
|
||||
if(!((wip->wi_flags[Weapon::Info_Flags::Same_turret_cooldown]) || ((burst_shots > 0) && (wip->burst_flags[Weapon::Burst_Flags::Fast_firing]))))
|
||||
{
|
||||
|
||||
// make side even for team vs. team
|
||||
@@ -1857,8 +1790,19 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
|
||||
vm_vector_2_matrix(&turret_orient, firing_vec, nullptr, nullptr);
|
||||
|
||||
// set next fire timestamp for the turret
|
||||
// grab and set some burst data before turret_set_next_fire_timestamp wipes it
|
||||
int old_burst_seed = swp->burst_seed[weapon_num];
|
||||
int old_burst_counter = swp->burst_counter[weapon_num];
|
||||
// only used by type 5 beams
|
||||
if (turret->weapons.burst_counter[weapon_num] == 0) {
|
||||
swp->per_burst_rot += wip->b_info.t5info.per_burst_rot;
|
||||
if (swp->per_burst_rot > PI2)
|
||||
swp->per_burst_rot -= PI2;
|
||||
else if (swp->per_burst_rot < -PI2)
|
||||
swp->per_burst_rot += PI2;
|
||||
}
|
||||
|
||||
// set next fire timestamp for the turret
|
||||
if (last_shot_in_salvo)
|
||||
turret_set_next_fire_timestamp(weapon_num, wip, turret, parent_aip);
|
||||
|
||||
@@ -1884,6 +1828,8 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
fire_info.turret = turret;
|
||||
fire_info.burst_seed = old_burst_seed;
|
||||
fire_info.fire_method = BFM_TURRET_FIRED;
|
||||
fire_info.per_burst_rotation = swp->per_burst_rot;
|
||||
fire_info.burst_index = old_burst_counter;
|
||||
|
||||
// fire a beam weapon
|
||||
weapon_objnum = beam_fire(&fire_info);
|
||||
@@ -1894,9 +1840,11 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
parent_ship->last_fired_turret = turret;
|
||||
turret->last_fired_weapon_info_index = turret_weapon_class;
|
||||
|
||||
Script_system.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", nullptr, "Beam", objp, "Target", &Objects[turret->turret_enemy_objnum]);
|
||||
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[parent_objnum]);
|
||||
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
|
||||
if (Script_system.IsActiveAction(CHA_ONTURRETFIRED)) {
|
||||
Script_system.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", nullptr, "Beam", objp, "Target", &Objects[turret->turret_enemy_objnum]);
|
||||
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[parent_objnum]);
|
||||
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
|
||||
}
|
||||
}
|
||||
|
||||
turret->flags.set(Ship::Subsystem_Flags::Has_fired); //set fired flag for scripting -nike
|
||||
@@ -2026,9 +1974,11 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
// AL 1-6-97: Store pointer to turret subsystem
|
||||
wp->turret_subsys = turret;
|
||||
|
||||
Script_system.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", objp, "Beam", nullptr, "Target", &Objects[turret->turret_enemy_objnum]);
|
||||
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[parent_objnum]);
|
||||
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
|
||||
if (Script_system.IsActiveAction(CHA_ONTURRETFIRED)) {
|
||||
Script_system.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", objp, "Beam", nullptr, "Target", &Objects[turret->turret_enemy_objnum]);
|
||||
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[parent_objnum]);
|
||||
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
|
||||
}
|
||||
|
||||
// if the gun is a flak gun
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Flak]) {
|
||||
@@ -2142,9 +2092,11 @@ void turret_swarm_fire_from_turret(turret_swarm_info *tsi)
|
||||
Ships[Objects[tsi->parent_objnum].instance].last_fired_turret = tsi->turret;
|
||||
tsi->turret->last_fired_weapon_info_index = tsi->weapon_class;
|
||||
|
||||
Script_system.SetHookObjects(4, "Ship", &Objects[tsi->parent_objnum], "Weapon", &Objects[weapon_objnum], "Beam", nullptr, "Target", &Objects[tsi->turret->turret_enemy_objnum]);
|
||||
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[tsi->parent_objnum]);
|
||||
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
|
||||
if (Script_system.IsActiveAction(CHA_ONTURRETFIRED)) {
|
||||
Script_system.SetHookObjects(4, "Ship", &Objects[tsi->parent_objnum], "Weapon", &Objects[weapon_objnum], "Beam", nullptr, "Target", &Objects[tsi->turret->turret_enemy_objnum]);
|
||||
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[tsi->parent_objnum]);
|
||||
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
|
||||
}
|
||||
|
||||
// muzzle flash?
|
||||
if (Weapon_info[tsi->weapon_class].muzzle_flash >= 0) {
|
||||
@@ -2175,13 +2127,15 @@ int Num_find_turret_enemy = 0;
|
||||
int Num_turrets_fired = 0;
|
||||
|
||||
/**
|
||||
* Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
|
||||
* Previously called ai_fire_from_turret()
|
||||
* Given a ship and a turret subsystem, handle all its behavior, mostly targeting and shooting but also
|
||||
* all turret movement and resetting when idle
|
||||
*/
|
||||
extern int Nebula_sec_range;
|
||||
void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
|
||||
{
|
||||
float weapon_firing_range;
|
||||
float WeaponMinRange; // *Weapon minimum firing range -Et1
|
||||
float weapon_min_range; // *Weapon minimum firing range -Et1
|
||||
vec3d v2e;
|
||||
object *lep; // Last enemy pointer
|
||||
model_subsystem *tp = ss->system_info;
|
||||
@@ -2356,12 +2310,12 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Local_ssm])
|
||||
{
|
||||
weapon_firing_range = wip->lssm_lock_range;
|
||||
WeaponMinRange = 0.0f;
|
||||
weapon_min_range = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
weapon_firing_range = MIN(wip->lifetime * wip->max_speed, wip->weapon_range);
|
||||
WeaponMinRange = wip->WeaponMinRange;
|
||||
weapon_min_range = wip->weapon_min_range;
|
||||
}
|
||||
|
||||
// if beam weapon in nebula and target not tagged, decrease firing range
|
||||
@@ -2374,7 +2328,7 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
}
|
||||
|
||||
// Don't try to fire beyond weapon_limit_range (or within min range)
|
||||
if (dist_to_enemy < WeaponMinRange || dist_to_enemy > weapon_firing_range) {
|
||||
if (dist_to_enemy < weapon_min_range || dist_to_enemy > weapon_firing_range) {
|
||||
// it's possible another weapon is in range, but if not,
|
||||
// we will end up selecting a new target
|
||||
continue;
|
||||
@@ -2441,6 +2395,11 @@ void ai_fire_from_turret(ship *shipp, ship_subsys *ss)
|
||||
tentative_return = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!weapon_target_satisfies_lock_restrictions(wip, lep)) {
|
||||
tentative_return = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// check tagged-only for bombs only if the flag is set; see Mantis #3114
|
||||
@@ -2812,25 +2771,20 @@ bool turret_adv_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mo
|
||||
model_subsystem *tp = ss->system_info;
|
||||
float dot = vm_vec_dot(v2e, gvec);
|
||||
if (((dot + size_mod) >= tp->turret_fov) && ((dot - size_mod) <= tp->turret_max_fov)) {
|
||||
|
||||
// Since we no longer maintain world_to_turret_matrix, regenerate it here.
|
||||
object *objp = &Objects[ss->parent_objnum];
|
||||
vec3d turret_norm;
|
||||
matrix turret_matrix, world_to_turret_matrix;
|
||||
model_instance_find_world_dir(&turret_norm, &tp->turret_norm, Ships[objp->instance].model_instance_num, tp->subobj_num, &vmd_identity_matrix);
|
||||
vm_vector_2_matrix(&turret_matrix, &turret_norm, nullptr, nullptr);
|
||||
vm_matrix_x_matrix(&world_to_turret_matrix, &objp->orient, &turret_matrix);
|
||||
object* objp = &Objects[ss->parent_objnum];
|
||||
matrix turret_matrix;
|
||||
turret_instance_find_world_orient(&turret_matrix, Ships[objp->instance].model_instance_num, tp->subobj_num, &objp->orient);
|
||||
|
||||
vec3d of_dst;
|
||||
vm_vec_rotate( &of_dst, v2e, &world_to_turret_matrix );
|
||||
if ((of_dst.xyz.x == 0) && (of_dst.xyz.y == 0)) {
|
||||
return true;
|
||||
} else {
|
||||
of_dst.xyz.z = 0;
|
||||
vm_vec_rotate(&of_dst, v2e, &turret_matrix);
|
||||
if ((of_dst.xyz.x == 0) && (of_dst.xyz.z == 0))
|
||||
return true;
|
||||
else {
|
||||
of_dst.xyz.y = 0;
|
||||
if (!IS_VEC_NULL_SQ_SAFE(&of_dst)) {
|
||||
vm_vec_normalize(&of_dst);
|
||||
// now we have 2d vector with lenght of 1 that points at the targets direction after being rotated to turrets FOR
|
||||
if ((-of_dst.xyz.y + size_mod) >= tp->turret_y_fov)
|
||||
if ((of_dst.xyz.z + size_mod) >= tp->turret_base_fov)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2841,7 +2795,7 @@ bool turret_adv_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mo
|
||||
bool turret_fov_test(ship_subsys *ss, vec3d *gvec, vec3d *v2e, float size_mod)
|
||||
{
|
||||
bool in_fov = false;
|
||||
if (ss->system_info->flags[Model::Subsystem_Flags::Turret_alt_math])
|
||||
if (ss->system_info->flags[Model::Subsystem_Flags::Turret_restricted_fov])
|
||||
in_fov = turret_adv_fov_test(ss, gvec, v2e, size_mod);
|
||||
else
|
||||
in_fov = turret_std_fov_test(ss, gvec, v2e, size_mod);
|
||||
|
||||
+17
-10
@@ -324,7 +324,7 @@ object *asteroid_create(asteroid_field *asfieldp, int asteroid_type, int asteroi
|
||||
asteroid_default_flagset += Object::Object_Flags::Physics;
|
||||
asteroid_default_flagset += Object::Object_Flags::Collides;
|
||||
|
||||
objnum = obj_create(OBJ_ASTEROID, -1, n, &orient, &pos, radius, asteroid_default_flagset);
|
||||
objnum = obj_create(OBJ_ASTEROID, -1, n, &orient, &pos, radius, asteroid_default_flagset, false);
|
||||
|
||||
if ( (objnum == -1) || (objnum >= MAX_OBJECTS) ) {
|
||||
mprintf(("Couldn't create asteroid -- out of object slots\n"));
|
||||
@@ -496,7 +496,7 @@ void asteroid_create_all()
|
||||
|
||||
// ship_debris_odds_table keeps track of debris type of the next debris piece
|
||||
// each different type (size) of debris piece has a diffenent weight, smaller weighted more heavily than larger.
|
||||
// choose next type from table ship_debris_odds_table by rand()%max_weighted_range,
|
||||
// choose next type from table ship_debris_odds_table by Random::next()%max_weighted_range,
|
||||
// the threshold *below* which the debris type is selected.
|
||||
struct {
|
||||
float random_threshold;
|
||||
@@ -564,7 +564,7 @@ void asteroid_create_all()
|
||||
// For asteroid, load only large asteroids
|
||||
|
||||
// get a valid subtype
|
||||
int subtype = rand() % NUM_DEBRIS_POFS;
|
||||
int subtype = Random::next(NUM_DEBRIS_POFS);
|
||||
while (Asteroid_field.field_debris_type[subtype] == -1) {
|
||||
subtype = (subtype + 1) % NUM_DEBRIS_POFS;
|
||||
}
|
||||
@@ -758,7 +758,7 @@ static void maybe_throw_asteroid(int count)
|
||||
for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
|
||||
object *A = &Objects[so->objnum];
|
||||
if (so->objnum == Asteroid_throw_objnum) {
|
||||
int subtype = rand() % NUM_DEBRIS_POFS;
|
||||
int subtype = Random::next(NUM_DEBRIS_POFS);
|
||||
while (Asteroid_field.field_debris_type[subtype] == -1) {
|
||||
subtype = (subtype + 1) % NUM_DEBRIS_POFS;
|
||||
}
|
||||
@@ -1324,7 +1324,7 @@ static void asteroid_do_area_effect(object *asteroid_objp)
|
||||
if ( weapon_area_calc_damage(ship_objp, &asteroid_objp->pos, asip->inner_rad, asip->outer_rad, asip->blast, asip->damage, &blast, &damage, asip->outer_rad) == -1 )
|
||||
continue;
|
||||
|
||||
ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage );
|
||||
ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage, asip->damage_type_idx);
|
||||
weapon_area_apply_blast(NULL, ship_objp, &asteroid_objp->pos, blast, 0);
|
||||
} // end for
|
||||
}
|
||||
@@ -1432,9 +1432,14 @@ static void asteroid_maybe_break_up(object *pasteroid_obj)
|
||||
|
||||
if ( timestamp_elapsed(asp->final_death_time) ) {
|
||||
vec3d relvec, vfh, tvec;
|
||||
bool skip = false;
|
||||
bool hooked = Script_system.IsActiveAction(CHA_DEATH);
|
||||
|
||||
Script_system.SetHookObject("Self", pasteroid_obj);
|
||||
if(!Script_system.IsConditionOverride(CHA_DEATH, pasteroid_obj))
|
||||
if (hooked) {
|
||||
Script_system.SetHookObject("Self", pasteroid_obj);
|
||||
skip = Script_system.IsConditionOverride(CHA_DEATH, pasteroid_obj);
|
||||
}
|
||||
if (!skip)
|
||||
{
|
||||
pasteroid_obj->flags.set(Object::Object_Flags::Should_be_dead);
|
||||
|
||||
@@ -1484,7 +1489,7 @@ static void asteroid_maybe_break_up(object *pasteroid_obj)
|
||||
int num_roids_var = split->max - split->min;
|
||||
|
||||
if (num_roids_var > 0)
|
||||
num_roids += rand() % num_roids_var;
|
||||
num_roids += Random::next(num_roids_var);
|
||||
|
||||
if (num_roids > 0)
|
||||
for (int i=0; i<num_roids; i++)
|
||||
@@ -1539,8 +1544,10 @@ static void asteroid_maybe_break_up(object *pasteroid_obj)
|
||||
}
|
||||
asp->final_death_time = timestamp(-1);
|
||||
}
|
||||
Script_system.RunCondition(CHA_DEATH, pasteroid_obj);
|
||||
Script_system.RemHookVar("Self");
|
||||
if (hooked) {
|
||||
Script_system.RunCondition(CHA_DEATH, pasteroid_obj);
|
||||
Script_system.RemHookVar("Self");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -716,7 +716,7 @@ bool StartAutopilot()
|
||||
vm_vec_scale(&pos, 5*speed_cap*tc_factor); // pos is now scaled by 5 times the speed (5 seconds ahead)
|
||||
vm_vec_add(&pos, &pos, &Autopilot_flight_leader->pos); // pos is now 5*speed cap in front of player ship
|
||||
|
||||
switch (myrand()%24)
|
||||
switch (Random::next(24))
|
||||
// 8 different ways of getting perp points
|
||||
// 4 of which will not be used when capships are present (anything below, or straight above)
|
||||
{
|
||||
@@ -795,7 +795,8 @@ bool StartAutopilot()
|
||||
vm_vec_scale(&perp, Autopilot_flight_leader->radius+radius);
|
||||
|
||||
// randomly scale up/down by up to 20%
|
||||
j = 20-myrand()%40; // [-20,20]
|
||||
// jg18 - original comment said [-20,20] but preserving existing behavior
|
||||
j = Random::next(-19, 20);
|
||||
|
||||
vm_vec_scale(&perp, 1.0f+(float(j)/100.0f));
|
||||
vm_vec_add(&cameraPos, &pos, &perp);
|
||||
|
||||
@@ -866,6 +866,7 @@ static int cfget_cfile_block()
|
||||
cfile->data = nullptr;
|
||||
cfile->fp = nullptr;
|
||||
cfile->type = CFILE_BLOCK_USED;
|
||||
cf_clear_compression_info(cfile);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -921,7 +922,7 @@ int cfclose( CFILE * cfile )
|
||||
} else {
|
||||
// VP do nothing
|
||||
}
|
||||
|
||||
cf_clear_compression_info(cfile);
|
||||
cfile->type = CFILE_BLOCK_UNUSED;
|
||||
return result;
|
||||
}
|
||||
@@ -973,7 +974,7 @@ static CFILE *cf_open_fill_cfblock(const char* source, int line, const char* ori
|
||||
if(pos == -1L)
|
||||
pos = 0;
|
||||
cf_init_lowlevel_read_code(cfp,0,filelength(fileno(fp)), 0 );
|
||||
|
||||
cf_check_compression(cfp);
|
||||
return cfp;
|
||||
}
|
||||
}
|
||||
@@ -1008,7 +1009,7 @@ static CFILE *cf_open_packed_cfblock(const char* source, int line, const char* o
|
||||
cfp->line_num = line;
|
||||
|
||||
cf_init_lowlevel_read_code(cfp,offset, size, 0 );
|
||||
|
||||
cf_check_compression(cfp);
|
||||
return cfp;
|
||||
}
|
||||
|
||||
@@ -1052,6 +1053,7 @@ static CFILE *cf_open_mapped_fill_cfblock(const char* source, int line, const ch
|
||||
cfp->line_num = line;
|
||||
|
||||
cf_init_lowlevel_read_code(cfp, 0, 0, 0 );
|
||||
|
||||
#if defined _WIN32
|
||||
cfp->hMapFile = CreateFileMapping(cfp->hInFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (cfp->hMapFile == NULL) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "cfile/cfile.h"
|
||||
#include "cfile/cfilearchive.h"
|
||||
#include "cfile/cfilecompression.h"
|
||||
#include "luaconf.h"
|
||||
|
||||
#include <sstream>
|
||||
@@ -33,7 +34,6 @@
|
||||
#define CHECK_POSITION
|
||||
|
||||
// Called once to setup the low-level reading code.
|
||||
|
||||
void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size, size_t pos )
|
||||
{
|
||||
Assert(cfile != nullptr);
|
||||
@@ -54,6 +54,33 @@ void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size,
|
||||
}
|
||||
}
|
||||
|
||||
//This function checks the file header to see if the file is compressed, if so it fills the correct compression info.
|
||||
void cf_check_compression(CFILE* cfile)
|
||||
{
|
||||
if (cfile->size <= 16)
|
||||
return;
|
||||
int header=cfread_int(cfile);
|
||||
cfseek(cfile, 0, SEEK_SET);
|
||||
if (comp_check_header(header) == COMP_HEADER_MATCH)
|
||||
comp_create_ci(cfile, header);
|
||||
}
|
||||
|
||||
//This function is called to cleanup compression info when the cfile handle is reused.
|
||||
void cf_clear_compression_info(CFILE* cfile)
|
||||
{
|
||||
if (cfile->compression_info.header != 0)
|
||||
{
|
||||
free(cfile->compression_info.offsets);
|
||||
free(cfile->compression_info.decoder_buffer);
|
||||
cfile->compression_info.offsets = nullptr;
|
||||
cfile->compression_info.decoder_buffer = nullptr;
|
||||
cfile->compression_info.header = 0;
|
||||
cfile->compression_info.block_size = 0;
|
||||
cfile->compression_info.last_decoded_block_pos = 0;
|
||||
cfile->compression_info.last_decoded_block_bytes = 0;
|
||||
cfile->compression_info.num_offsets = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// cfeof() Tests for end-of-file on a stream
|
||||
@@ -61,11 +88,11 @@ void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size,
|
||||
// returns a nonzero value after the first read operation that attempts to read
|
||||
// past the end of the file. It returns 0 if the current position is not end of file.
|
||||
// There is no error return.
|
||||
|
||||
int cfeof(CFILE *cfile)
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
|
||||
if (cfile->compression_info.header != 0)
|
||||
return comp_feof(cfile);
|
||||
int result = 0;
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
@@ -92,6 +119,8 @@ int cfeof(CFILE *cfile)
|
||||
int cftell( CFILE * cfile )
|
||||
{
|
||||
Assert(cfile != NULL);
|
||||
if (cfile->compression_info.header != 0)
|
||||
return (int)comp_ftell(cfile);
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
if (cfile->fp) {
|
||||
@@ -117,6 +146,9 @@ int cfseek( CFILE *cfile, int offset, int where )
|
||||
|
||||
Assert(cfile != NULL);
|
||||
|
||||
if (cfile->compression_info.header != 0)
|
||||
return comp_fseek(cfile, offset, where);
|
||||
|
||||
// TODO: seek to offset in memory mapped file
|
||||
Assert( !cfile->mem_mapped );
|
||||
|
||||
@@ -202,23 +234,31 @@ int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
// This is a file from memory
|
||||
bytes_read = size;
|
||||
memcpy(buf, reinterpret_cast<const char*>(cfile->data) + cfile->raw_position, size);
|
||||
} else if (cfile->compression_info.header != 0) {
|
||||
bytes_read = comp_fread(cfile, reinterpret_cast<char*>(buf),size);
|
||||
if (bytes_read != size)
|
||||
{
|
||||
mprintf(("\nFile: %s decompression error. Result was: %d expected: %d\n", cfile->original_filename.c_str(), (int)bytes_read, (int)size));
|
||||
Assertion(bytes_read == size, "File decompression error!");
|
||||
}
|
||||
} else {
|
||||
bytes_read = fread( buf, 1, size, cfile->fp );
|
||||
bytes_read = fread(buf, 1, size, cfile->fp);
|
||||
}
|
||||
|
||||
if ( bytes_read > 0 ) {
|
||||
cfile->raw_position += bytes_read;
|
||||
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
|
||||
}
|
||||
|
||||
#if defined(CHECK_POSITION) && !defined(NDEBUG)
|
||||
if (cfile->fp) {
|
||||
//Raw position is not the fp position with compressed files
|
||||
if (cfile->fp && cfile->compression_info.header == 0) {
|
||||
auto tmp_offset = ftell(cfile->fp) - cfile->lib_offset;
|
||||
Assert(tmp_offset==cfile->raw_position);
|
||||
Assert(tmp_offset == cfile->raw_position);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (int)(bytes_read / elsize);
|
||||
|
||||
}
|
||||
|
||||
int cfread_lua_number(double *buf, CFILE *cfile)
|
||||
@@ -266,4 +306,3 @@ int cfread_lua_number(double *buf, CFILE *cfile)
|
||||
return items_read;
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _CFILEARCHIVE_H
|
||||
#define _CFILEARCHIVE_H
|
||||
|
||||
@@ -23,6 +22,17 @@
|
||||
#define CFILE_BLOCK_UNUSED 0
|
||||
#define CFILE_BLOCK_USED 1
|
||||
|
||||
struct COMPRESSION_INFO {
|
||||
int header = 0;
|
||||
size_t compressed_size = 0;
|
||||
int block_size = 0;
|
||||
int num_offsets = 0;
|
||||
int* offsets = nullptr;
|
||||
char* decoder_buffer = nullptr;
|
||||
int last_decoded_block_pos = 0;
|
||||
int last_decoded_block_bytes = 0;
|
||||
};
|
||||
|
||||
struct CFILE {
|
||||
int type = CFILE_BLOCK_UNUSED; // CFILE_BLOCK_UNUSED, CFILE_BLOCK_USED
|
||||
int dir_type; // directory location
|
||||
@@ -44,12 +54,19 @@ struct CFILE {
|
||||
SCP_string original_filename;
|
||||
const char* source_file;
|
||||
int line_num;
|
||||
COMPRESSION_INFO compression_info;
|
||||
};
|
||||
|
||||
#define MAX_CFILE_BLOCKS 64
|
||||
extern std::array<CFILE, MAX_CFILE_BLOCKS> Cfile_block_list;
|
||||
|
||||
// Called once to setup the low-level reading code.
|
||||
void cf_init_lowlevel_read_code( CFILE * cfile, size_t lib_offset, size_t size, size_t pos );
|
||||
void cf_init_lowlevel_read_code(CFILE* cfile, size_t lib_offset, size_t size, size_t pos);
|
||||
|
||||
// This checks if the file is compressed or not, and creates the proper compression info if so.
|
||||
void cf_check_compression(CFILE* cfile);
|
||||
|
||||
// Used to clear compression info data and free dynamic memory used by compression
|
||||
void cf_clear_compression_info(CFILE* cfile);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,206 @@
|
||||
#define _CFILE_INTERNAL
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
#endif
|
||||
|
||||
#include "lz4.h"
|
||||
#include "cfilecompression.h"
|
||||
#include "cfilearchive.h"
|
||||
|
||||
/*INTERNAL FUNCTIONS*/
|
||||
/*LZ41*/
|
||||
void lz41_load_offsets(CFILE* cf);
|
||||
size_t lz41_stream_random_access(CFILE* cf, char* bytes_out, size_t offset, size_t length);
|
||||
void lz41_create_ci(CFILE* cf, int header);
|
||||
/*MISC*/
|
||||
int fso_fseek(CFILE* cfile, int offset, int where);
|
||||
/*END OF INTERNAL FUNCTIONS*/
|
||||
|
||||
int comp_check_header(int header)
|
||||
{
|
||||
if (LZ41_FILE_HEADER == header)
|
||||
return COMP_HEADER_MATCH;
|
||||
|
||||
return COMP_HEADER_MISMATCH;
|
||||
}
|
||||
|
||||
void comp_create_ci(CFILE* cf, int header)
|
||||
{
|
||||
//mprintf(("(CI)Compressed File Opened: %s \n", cf->original_filename.c_str()));
|
||||
|
||||
if (LZ41_FILE_HEADER == header)
|
||||
lz41_create_ci(cf, header);
|
||||
|
||||
//mprintf(("(CI)Uncompressed FileSize: %d \n", cf->size));
|
||||
//mprintf(("(CI)Compressed FileSize: %d \n", cf->compression_info.compressed_size));
|
||||
//mprintf(("(CI)Block Size: %d \n", cf->compression_info.block_size));
|
||||
}
|
||||
|
||||
size_t comp_fread(CFILE* cf, char* buffer, size_t length)
|
||||
{
|
||||
/* Check the request to be at least 1 byte */
|
||||
Assertion(length > 0, "Invalid length requested.");
|
||||
|
||||
/* Check that we are not requesting to read beyond end of file */
|
||||
Assertion(cf->raw_position + length <= cf->size, "Invalid length requested.");
|
||||
|
||||
if (LZ41_FILE_HEADER == cf->compression_info.header)
|
||||
return lz41_stream_random_access(cf, buffer, cf->raw_position, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t comp_ftell(CFILE* cf)
|
||||
{
|
||||
return cf->raw_position;
|
||||
}
|
||||
|
||||
int comp_feof(CFILE* cf)
|
||||
{
|
||||
if (cf->raw_position >= cf->size)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int comp_fseek(CFILE* cf, int offset, int where)
|
||||
{
|
||||
size_t goal_position;
|
||||
switch (where) {
|
||||
case SEEK_SET: goal_position = offset; break;
|
||||
case SEEK_CUR: goal_position = cf->raw_position + offset; break;
|
||||
case SEEK_END: goal_position = cf->size + offset; break;
|
||||
default: return 1;
|
||||
}
|
||||
|
||||
// Make sure we don't seek beyond the end of the file
|
||||
CAP(goal_position,(size_t)0, cf->size);
|
||||
|
||||
cf->raw_position = goal_position;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Special fseek for compressed files handled by FSO, only SEEK_SET and SEEK_END is supported.
|
||||
int fso_fseek(CFILE* cfile, int offset, int where)
|
||||
{
|
||||
size_t goal_position;
|
||||
|
||||
switch (where) {
|
||||
case SEEK_SET: goal_position = offset + cfile->lib_offset; break;
|
||||
case SEEK_END: goal_position = cfile->compression_info.compressed_size + offset + cfile->lib_offset; break;
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
// Make sure we don't seek beyond the end of the file
|
||||
CAP(goal_position, cfile->lib_offset, cfile->lib_offset + cfile->compression_info.compressed_size);
|
||||
|
||||
int result = 0;
|
||||
if (cfile->fp)
|
||||
{
|
||||
result = fseek(cfile->fp, (long)goal_position, SEEK_SET);
|
||||
Assertion(goal_position >= cfile->lib_offset, "Invalid offset values detected while seeking! Goal was " SIZE_T_ARG ", lib_offset is " SIZE_T_ARG ".", goal_position, cfile->lib_offset);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void lz41_create_ci(CFILE* cf, int header)
|
||||
{
|
||||
cf->compression_info.header = header;
|
||||
cf->compression_info.compressed_size = cf->size;
|
||||
fso_fseek(cf, (int)sizeof(int)*-3, SEEK_END);
|
||||
auto fNumoffsets = fread(&cf->compression_info.num_offsets, sizeof(int), 1, cf->fp);
|
||||
auto fSize = fread(&cf->size, sizeof(int), 1, cf->fp);
|
||||
auto fBsize = fread(&cf->compression_info.block_size, sizeof(int), 1, cf->fp);
|
||||
|
||||
Assertion(cf->compression_info.num_offsets > 0, "Invalid number of offsets, compressed file is possibly in the wrong format or corrupted.");
|
||||
#if !defined(NDEBUG)
|
||||
Assertion(cf->size > 4, "Invalid filesize, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(cf->compression_info.block_size > 16, "Invalid block size, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(fNumoffsets == 1, "Error while reading the number of offsets, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(fSize == 1, "Error while reading original filesize, compressed file is possibly in the wrong format or corrupted.");
|
||||
Assertion(fBsize == 1, "Error while reading block size, compressed file is possibly in the wrong format or corrupted.");
|
||||
#endif
|
||||
|
||||
cf->compression_info.decoder_buffer = (char*)malloc(LZ4_compressBound(cf->compression_info.block_size));
|
||||
cf->compression_info.last_decoded_block_pos = 0;
|
||||
cf->compression_info.last_decoded_block_bytes = 0;
|
||||
lz41_load_offsets(cf);
|
||||
}
|
||||
|
||||
void lz41_load_offsets(CFILE* cf)
|
||||
{
|
||||
cf->compression_info.offsets = (int*)malloc(cf->compression_info.num_offsets * sizeof(int));
|
||||
int block;
|
||||
int* offsets_ptr = cf->compression_info.offsets;
|
||||
|
||||
/* Seek to the first offset position, remember to consider the trailing ints */
|
||||
fso_fseek(cf, ( ( sizeof(int) * cf->compression_info.num_offsets ) * -1 ) - (sizeof(int)*3 ), SEEK_END);
|
||||
for (block = 0; block < cf->compression_info.num_offsets; ++block)
|
||||
{
|
||||
auto bytes_read = fread(offsets_ptr++, sizeof(int), 1, cf->fp);
|
||||
Assertion(bytes_read == 1, "Error reading offset list.");
|
||||
}
|
||||
}
|
||||
|
||||
size_t lz41_stream_random_access(CFILE* cf, char* bytes_out, size_t offset, size_t length)
|
||||
{
|
||||
LZ4_streamDecode_t lz4_stream_decode_body;
|
||||
LZ4_streamDecode_t* lz4_stream_decode = &lz4_stream_decode_body;
|
||||
/* The blocks (current_block to end_block) contain the data we want */
|
||||
size_t current_block = offset / cf->compression_info.block_size;
|
||||
size_t end_block = ((offset + length - 1) / cf->compression_info.block_size) + 1;
|
||||
size_t written_bytes = 0;
|
||||
|
||||
if (cf->compression_info.num_offsets <= (int)end_block)
|
||||
return (size_t)LZ41_OFFSETS_MISMATCH;
|
||||
|
||||
/* Seek to the first block to read, if it matches the cached block, search the next one instead */
|
||||
if (cf->compression_info.last_decoded_block_pos == cf->compression_info.offsets[current_block] && current_block + 1 <= end_block)
|
||||
fso_fseek(cf, cf->compression_info.offsets[current_block+1], SEEK_SET);
|
||||
else
|
||||
fso_fseek(cf, cf->compression_info.offsets[current_block], SEEK_SET);
|
||||
|
||||
offset = offset % cf->compression_info.block_size;
|
||||
char* cmp_buf = (char*)malloc(LZ4_compressBound(cf->compression_info.block_size));
|
||||
|
||||
/* Start decoding */
|
||||
for (; current_block < end_block; ++current_block)
|
||||
{
|
||||
/* Only read and decode if the requested block is not the cached one */
|
||||
if (cf->compression_info.last_decoded_block_pos != cf->compression_info.offsets[current_block])
|
||||
{
|
||||
cf->compression_info.last_decoded_block_pos = cf->compression_info.offsets[current_block];
|
||||
|
||||
/* The difference in offsets is the size of the block */
|
||||
int cmp_bytes = cf->compression_info.offsets[current_block + 1] - cf->compression_info.offsets[current_block];
|
||||
auto bytes_read = fread(cmp_buf, cmp_bytes, 1, cf->fp);
|
||||
Assertion(bytes_read == 1, "Error reading from compressed file.");
|
||||
|
||||
cf->compression_info.last_decoded_block_bytes = LZ4_decompress_safe_continue(lz4_stream_decode, cmp_buf, cf->compression_info.decoder_buffer, cmp_bytes, cf->compression_info.block_size);
|
||||
if (cf->compression_info.last_decoded_block_bytes <= 0)
|
||||
{
|
||||
free(cmp_buf);
|
||||
return (size_t)LZ41_DECOMPRESSION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write out the part of the data we care about from buffer */
|
||||
size_t block_length = (length < (cf->compression_info.last_decoded_block_bytes - offset) ? length : (cf->compression_info.last_decoded_block_bytes - offset));
|
||||
memcpy(bytes_out + written_bytes, cf->compression_info.decoder_buffer + offset, (size_t)block_length);
|
||||
written_bytes += block_length;
|
||||
offset = 0;
|
||||
length -= block_length;
|
||||
}
|
||||
|
||||
free(cmp_buf);
|
||||
return written_bytes;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
ShivanSpS - Compressed files support for FSO. Many thanks to ngld, taylor and everyone elsewho helped me in getting this done.
|
||||
|
||||
-The file header is a version, this is used to tell FSO how to decompress that file, always use 4 chars to mantain alignment.
|
||||
-The header ID can be used to add diferent revisions to LZ41 decompression system or to add other compression format supports whiout breaking compatibility.
|
||||
-The system uses a offset list to record the position of every block in file, this list, along with the number of offsets, original filesize,
|
||||
and block size, must be written by the compressor app.
|
||||
-A decoder cache is used to store the last decoded block, this ensures each block is read and decoded only once in sequential reads. This cache
|
||||
is a little bit bigger than the block size, a higher block size means less overhead added to the file, but it also means a little more ram will
|
||||
be used during decompression.
|
||||
-All this dynamic memory is assigned at cfopen() and it is cleared on cfclose().
|
||||
|
||||
................................char[4]..........(n ints)...(int)..........(int)..........(int)
|
||||
-COMPRESSED FILE DATA STUCTURE: HEADER|N BLOCKS|N OFFSETS|NUM_OFFSETS|ORIGINAL_FILESIZE|BLOCK_SIZE
|
||||
*/
|
||||
|
||||
#ifndef _CFILECOMPRESSION_H
|
||||
#define _CFILECOMPRESSION_H
|
||||
struct CFILE;
|
||||
|
||||
/*LZ41*/
|
||||
#define LZ41_FILE_HEADER 0x31345A4C // "14ZL" -> "LZ41", inverted
|
||||
#define LZ41_DECOMPRESSION_ERROR -1
|
||||
#define LZ41_MAX_BLOCKS_OVERFLOW -2
|
||||
#define LZ41_HEADER_MISMATCH -3
|
||||
#define LZ41_OFFSETS_MISMATCH -4
|
||||
/******/
|
||||
|
||||
#define COMP_HEADER_MATCH 1
|
||||
#define COMP_HEADER_MISMATCH 0
|
||||
|
||||
/*
|
||||
Returns COMP_HEADER_MATCH if header is a valid compressed file header,
|
||||
returns COMP_HEADER_MISMATCH if it dosent.
|
||||
*/
|
||||
int comp_check_header(int header);
|
||||
|
||||
/*
|
||||
This is called to generate the correct compression_info data
|
||||
after the file has been indentified as a compressed file.
|
||||
This must be done before calling any other function.
|
||||
*/
|
||||
void comp_create_ci(CFILE* cf, int header);
|
||||
|
||||
/*
|
||||
Read X bytes from the uncompressed file starting from X offset.
|
||||
Returns the amount of bytes read, and 0 or lower to indicate errors.
|
||||
*/
|
||||
size_t comp_fread(CFILE* cf, char* buffer, size_t length);
|
||||
|
||||
/*
|
||||
Returns the current uncompressed file position.
|
||||
*/
|
||||
size_t comp_ftell(CFILE* cf);
|
||||
|
||||
/*
|
||||
Returns 1 of the uncompressed file has been completely read, otherwise it returns a 0.
|
||||
*/
|
||||
int comp_feof(CFILE* cf);
|
||||
|
||||
/*
|
||||
Used to move the uncompressed file current position.
|
||||
*/
|
||||
int comp_fseek(CFILE* cf, int offset, int where);
|
||||
|
||||
#endif
|
||||
@@ -246,7 +246,7 @@ Flag exe_params[] =
|
||||
{ "-noninteractive", "Disables interactive dialogs", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noninteractive", },
|
||||
{ "-no_unfocused_pause","Don't pause if the window isn't focused", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_unfocused_pause", },
|
||||
{ "-benchmark_mode", "Puts the game into benchmark mode", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-benchmark_mode", },
|
||||
{ "-profile_frame_time","Profile frame time", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_frame_time", },
|
||||
{ "-profile_frame_time","Profile frame time", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_frame_time", },
|
||||
{ "-profile_write_file", "Write profiling information to file", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-profile_write_file", },
|
||||
{ "-json_profiling", "Generate JSON profiling output", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-json_profiling", },
|
||||
{ "-debug_window", "Enable the debug window", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-debug_window", },
|
||||
@@ -479,6 +479,7 @@ cmdline_parm reparse_mainhall_arg("-reparse_mainhall", NULL, AT_NONE); //Cmdline
|
||||
cmdline_parm frame_profile_write_file("-profile_write_file", NULL, AT_NONE); // Cmdline_profile_write_file
|
||||
cmdline_parm no_unfocused_pause_arg("-no_unfocused_pause", NULL, AT_NONE); //Cmdline_no_unfocus_pause
|
||||
cmdline_parm benchmark_mode_arg("-benchmark_mode", NULL, AT_NONE); //Cmdline_benchmark_mode
|
||||
cmdline_parm pilot_arg("-pilot", nullptr, AT_STRING); //Cmdline_pilot
|
||||
cmdline_parm noninteractive_arg("-noninteractive", NULL, AT_NONE); //Cmdline_noninteractive
|
||||
cmdline_parm json_profiling("-json_profiling", NULL, AT_NONE); //Cmdline_json_profiling
|
||||
cmdline_parm show_video_info("-show_video_info", NULL, AT_NONE); //Cmdline_show_video_info
|
||||
@@ -510,6 +511,7 @@ int Cmdline_reparse_mainhall = 0;
|
||||
bool Cmdline_profile_write_file = false;
|
||||
bool Cmdline_no_unfocus_pause = false;
|
||||
bool Cmdline_benchmark_mode = false;
|
||||
const char *Cmdline_pilot = nullptr;
|
||||
bool Cmdline_noninteractive = false;
|
||||
bool Cmdline_json_profiling = false;
|
||||
bool Cmdline_frame_profile = false;
|
||||
@@ -1617,7 +1619,7 @@ bool SetCmdlineParams()
|
||||
Cmdline_game_name = gamename_arg.str();
|
||||
|
||||
// be sure that this string fits in our limits
|
||||
if ( strlen(Cmdline_game_name) > MAX_GAMENAME_LEN ) {
|
||||
if ( strlen(Cmdline_game_name) >= MAX_GAMENAME_LEN ) {
|
||||
Cmdline_game_name[MAX_GAMENAME_LEN-1] = '\0';
|
||||
}
|
||||
}
|
||||
@@ -1627,8 +1629,9 @@ bool SetCmdlineParams()
|
||||
Cmdline_game_password = gamepassword_arg.str();
|
||||
|
||||
// be sure that this string fits in our limits
|
||||
if ( strlen(Cmdline_game_name) > MAX_PASSWD_LEN ) {
|
||||
Cmdline_game_name[MAX_PASSWD_LEN-1] = '\0';
|
||||
if ( strlen(Cmdline_game_password) >= MAX_PASSWD_LEN ) {
|
||||
ReleaseWarning(LOCATION, "Multi game password is longer than max of %d charaters and will be trimmed to fit!", MAX_PASSWD_LEN-1);
|
||||
Cmdline_game_password[MAX_PASSWD_LEN-1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1713,7 +1716,7 @@ bool SetCmdlineParams()
|
||||
sprintf(override, "{\"width\":%d,\"height\":%d}", width, height);
|
||||
options::OptionsManager::instance()->setOverride("Graphics.Resolution", override);
|
||||
} else {
|
||||
mprintf(("Failed to parse -res parameter \"%s\". Must be in format \"<width>x<height>\".", Cmdline_res));
|
||||
mprintf(("Failed to parse -res parameter \"%s\". Must be in format \"<width>x<height>\".\n", Cmdline_res));
|
||||
}
|
||||
}
|
||||
if(center_res_arg.found()){
|
||||
@@ -2123,6 +2126,11 @@ bool SetCmdlineParams()
|
||||
Cmdline_benchmark_mode = true;
|
||||
}
|
||||
|
||||
if (pilot_arg.found())
|
||||
{
|
||||
Cmdline_pilot = pilot_arg.str();
|
||||
}
|
||||
|
||||
if (noninteractive_arg.found())
|
||||
{
|
||||
Cmdline_noninteractive = true;
|
||||
|
||||
@@ -143,6 +143,7 @@ extern int Cmdline_reparse_mainhall;
|
||||
extern bool Cmdline_profile_write_file;
|
||||
extern bool Cmdline_no_unfocus_pause;
|
||||
extern bool Cmdline_benchmark_mode;
|
||||
extern const char *Cmdline_pilot;
|
||||
extern bool Cmdline_noninteractive;
|
||||
extern bool Cmdline_json_profiling;
|
||||
extern bool Cmdline_frame_profile;
|
||||
|
||||
@@ -115,10 +115,10 @@ int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
|
||||
// conflict warning anim coords
|
||||
int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2] = {
|
||||
{
|
||||
-1, 420 // GR_640
|
||||
320, 420 // GR_640
|
||||
},
|
||||
{
|
||||
-1, 669 // GR_1024
|
||||
512, 669 // GR_1024
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1288,7 +1288,7 @@ void control_config_cancel_exit()
|
||||
{
|
||||
// Restore all bindings with the backup
|
||||
std::move(Control_config_backup.begin(), Control_config_backup.end(), Control_config.begin());
|
||||
std::move(Axis_map_to_backup, Axis_map_to_backup + JOY_NUM_AXES, Axis_map_to);
|
||||
std::move(Axis_map_to_backup, Axis_map_to_backup + NUM_JOY_AXIS_ACTIONS, Axis_map_to);
|
||||
std::move(Invert_axis_backup, Invert_axis_backup + JOY_NUM_AXES, Invert_axis);
|
||||
|
||||
gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
|
||||
@@ -1934,7 +1934,7 @@ void control_config_do_frame(float frametime)
|
||||
for (i=0; i<MOUSE_NUM_BUTTONS; i++) {
|
||||
if (mouse_down(1 << i)) {
|
||||
Assert(!(z & JOY_AXIS));
|
||||
control_config_bind_btn(z, CC_bind(CID_MOUSE, static_cast<short>(i)), 1);
|
||||
control_config_bind_btn(z, CC_bind(CID_JOY0, static_cast<short>(i)), 1);
|
||||
|
||||
strcpy_s(bound_string, Joy_button_text[i]);
|
||||
done = true;
|
||||
@@ -2295,7 +2295,9 @@ void control_config_do_frame(float frametime)
|
||||
int sw, sh;
|
||||
gr_get_string_size(&sw, &sh, conflict_str);
|
||||
|
||||
gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str, GR_RESIZE_MENU);
|
||||
x = Conflict_warning_coords[gr_screen.res][CONTROL_X_COORD] - (sw / 2);
|
||||
y = Conflict_warning_coords[gr_screen.res][CONTROL_Y_COORD];
|
||||
gr_printf_menu(x, y, "%s", conflict_str);
|
||||
|
||||
font::set_font(font::FONT1);
|
||||
} else {
|
||||
@@ -2550,9 +2552,11 @@ int check_control(int id, int key)
|
||||
// If we reach this point, then it means this is a continuous control
|
||||
// which has just been released
|
||||
|
||||
Script_system.SetHookVar("Action", 's', Control_config[id].text);
|
||||
Script_system.RunCondition(CHA_ONACTIONSTOPPED, nullptr, id);
|
||||
Script_system.RemHookVar("Action");
|
||||
if (Script_system.IsActiveAction(CHA_ONACTIONSTOPPED)) {
|
||||
Script_system.SetHookVar("Action", 's', Control_config[id].text);
|
||||
Script_system.RunCondition(CHA_ONACTIONSTOPPED, nullptr, id);
|
||||
Script_system.RemHookVar("Action");
|
||||
}
|
||||
|
||||
Control_config[id].continuous_ongoing = false;
|
||||
}
|
||||
@@ -2647,4 +2651,4 @@ void control_config_clear()
|
||||
for (auto &item : Control_config) {
|
||||
item.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1516,9 +1516,9 @@ void control_config_common_read_section(int s, bool first_override) {
|
||||
|
||||
// Assign the various attributes to this control
|
||||
int iTemp;
|
||||
short key = 0;
|
||||
auto item = &Control_config[item_id];
|
||||
auto& new_binding = new_preset.bindings[item_id];
|
||||
short key = new_binding.get_btn(CID_KEYBOARD);
|
||||
|
||||
// Key assignment and modifiers
|
||||
if (optional_string("$Key Default:")) {
|
||||
@@ -1987,7 +1987,7 @@ CCI_builder& CCI_builder::operator()(IoActionId action_id, short key_default, sh
|
||||
item.type = type;
|
||||
|
||||
if (tab == NO_TAB) {
|
||||
mprintf(("Control item defined without a valid tab. Disabling: %s", item.text.c_str()));
|
||||
mprintf(("Control item defined without a valid tab. Disabling: %s\n", item.text.c_str()));
|
||||
}
|
||||
|
||||
// Assign disabled state
|
||||
|
||||
@@ -93,13 +93,12 @@ class Decoder {
|
||||
bool m_decoding;
|
||||
size_t m_queueSize = 0;
|
||||
|
||||
Decoder(const Decoder&) SCP_DELETED_FUNCTION;
|
||||
|
||||
Decoder& operator=(const Decoder&) SCP_DELETED_FUNCTION;
|
||||
protected:
|
||||
Decoder();
|
||||
|
||||
public:
|
||||
Decoder(const Decoder&) = delete;
|
||||
Decoder& operator=(const Decoder&) = delete;
|
||||
|
||||
virtual ~Decoder();
|
||||
|
||||
|
||||
@@ -613,7 +613,7 @@ void cutscenes_screen_do_frame()
|
||||
if (src)
|
||||
{
|
||||
Text_size = split_str(src, Cutscene_desc_coords[gr_screen.res][2], Text_line_size, Text_lines,
|
||||
Cutscene_max_text_lines[gr_screen.res]);
|
||||
Cutscene_max_text_lines[gr_screen.res], MAX_TEXT_LINE_LEN);
|
||||
Assert(Text_size >= 0 && Text_size < Cutscene_max_text_lines[gr_screen.res]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,10 +45,8 @@ struct DecoderStatus {
|
||||
|
||||
~DecoderStatus();
|
||||
|
||||
private:
|
||||
DecoderStatus(const DecoderStatus&) SCP_DELETED_FUNCTION;
|
||||
|
||||
DecoderStatus& operator=(const DecoderStatus&) SCP_DELETED_FUNCTION;
|
||||
DecoderStatus(const DecoderStatus&) = delete;
|
||||
DecoderStatus& operator=(const DecoderStatus&) = delete;
|
||||
};
|
||||
|
||||
template<typename Frame>
|
||||
@@ -67,8 +65,8 @@ class FFMPEGStreamDecoder {
|
||||
explicit FFMPEGStreamDecoder(DecoderStatus* status);
|
||||
virtual ~FFMPEGStreamDecoder();
|
||||
|
||||
FFMPEGStreamDecoder(const FFMPEGStreamDecoder&) SCP_DELETED_FUNCTION;
|
||||
FFMPEGStreamDecoder& operator=(const FFMPEGStreamDecoder&) SCP_DELETED_FUNCTION;
|
||||
FFMPEGStreamDecoder(const FFMPEGStreamDecoder&) = delete;
|
||||
FFMPEGStreamDecoder& operator=(const FFMPEGStreamDecoder&) = delete;
|
||||
|
||||
virtual void decodePacket(AVPacket* packet) = 0;
|
||||
|
||||
|
||||
+30
-67
@@ -30,8 +30,9 @@
|
||||
#include "ship/ship.h"
|
||||
#include "ship/shipfx.h"
|
||||
#include "species_defs/species_defs.h"
|
||||
#include "weapon/weapon.h"
|
||||
#include "tracing/Monitor.h"
|
||||
#include "utils/Random.h"
|
||||
#include "weapon/weapon.h"
|
||||
|
||||
#define MIN_RADIUS_FOR_PERSISTANT_DEBRIS 50 // ship radius at which debris from it becomes persistant
|
||||
#define DEBRIS_SOUND_DELAY 2000 // time to start debris sound after created
|
||||
@@ -51,8 +52,6 @@ int Debris_model = -1;
|
||||
int Debris_vaporize_model = -1;
|
||||
int Debris_num_submodels = 0;
|
||||
|
||||
#define MAX_DEBRIS_DIST 10000.0f // Debris goes away if it's this far away.
|
||||
#define DEBRIS_DISTANCE_CHECK_TIME (10*1000) // Check every 10 seconds.
|
||||
#define DEBRIS_INDEX(dp) (int)(dp-Debris.data())
|
||||
|
||||
const auto OnDebrisCreatedHook = scripting::Hook::Factory(
|
||||
@@ -75,7 +74,7 @@ static void debris_start_death_roll(object *debris_obj, debris *debris_p)
|
||||
|
||||
int fireball_type = fireball_ship_explosion_type(&Ship_info[debris_p->ship_info_index]);
|
||||
if(fireball_type < 0) {
|
||||
fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS;
|
||||
fireball_type = FIREBALL_EXPLOSION_LARGE1 + Random::next(FIREBALL_NUM_LARGE_EXPLOSIONS);
|
||||
}
|
||||
fireball_create( &debris_obj->pos, fireball_type, FIREBALL_LARGE_EXPLOSION, OBJ_INDEX(debris_obj), debris_obj->radius*1.75f);
|
||||
|
||||
@@ -196,34 +195,6 @@ void debris_delete( object * obj )
|
||||
db->objnum = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* If debris piece *db is far away from all players, make it go away very soon.
|
||||
* In single player game, delete if MAX_DEBRIS_DIST from player.
|
||||
* In multiplayer game, delete if MAX_DEBRIS_DIST from all players.
|
||||
*/
|
||||
void maybe_delete_debris(debris *db)
|
||||
{
|
||||
object *objp;
|
||||
|
||||
if (Player_obj != nullptr && timestamp_elapsed(db->next_distance_check) && timestamp_elapsed(db->must_survive_until)) {
|
||||
if (!(Game_mode & GM_MULTIPLAYER)) { // In single player game, just check against player.
|
||||
if (vm_vec_dist_quick(&Player_obj->pos, &Objects[db->objnum].pos) > MAX_DEBRIS_DIST)
|
||||
db->lifeleft = 0.1f;
|
||||
else
|
||||
db->next_distance_check = timestamp(DEBRIS_DISTANCE_CHECK_TIME);
|
||||
} else {
|
||||
for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
|
||||
if (objp->flags[Object::Object_Flags::Player_ship]) {
|
||||
if (vm_vec_dist_quick(&objp->pos, &Objects[db->objnum].pos) < MAX_DEBRIS_DIST) {
|
||||
db->next_distance_check = timestamp(DEBRIS_DISTANCE_CHECK_TIME);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
db->lifeleft = 0.1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do various updates to debris: check if time to die, start fireballs
|
||||
@@ -246,7 +217,7 @@ void debris_process_post(object * obj, float frame_time)
|
||||
radar_plot_object( obj );
|
||||
|
||||
if ( timestamp_elapsed(db->sound_delay) ) {
|
||||
obj_snd_assign(objnum, db->ambient_sound, &vmd_zero_vector, 0);
|
||||
obj_snd_assign(objnum, db->ambient_sound, &vmd_zero_vector);
|
||||
db->sound_delay = 0;
|
||||
}
|
||||
}
|
||||
@@ -260,8 +231,6 @@ void debris_process_post(object * obj, float frame_time)
|
||||
}
|
||||
}
|
||||
|
||||
maybe_delete_debris(db); // Make this debris go away if it's very far away.
|
||||
|
||||
// ================== DO THE ELECTRIC ARCING STUFF =====================
|
||||
if ( db->arc_frequency <= 0 ) {
|
||||
return; // If arc_frequency <= 0, this piece has no arcs on it
|
||||
@@ -274,7 +243,7 @@ void debris_process_post(object * obj, float frame_time)
|
||||
|
||||
if (db->is_hull) {
|
||||
|
||||
int n, n_arcs = ((rand()>>5) % 3)+1; // Create 1-3 sparks
|
||||
int n, n_arcs = Random::next(1, 3); // Create 1-3 sparks
|
||||
|
||||
vec3d v1, v2, v3, v4;
|
||||
|
||||
@@ -283,8 +252,7 @@ void debris_process_post(object * obj, float frame_time)
|
||||
|
||||
n = 0;
|
||||
|
||||
int a = 100, b = 1000;
|
||||
int lifetime = (myrand()%((b)-(a)+1))+(a);
|
||||
int lifetime = Random::next(100, 1000);
|
||||
|
||||
// Create the spark effects
|
||||
for (int i=0; i<MAX_DEBRIS_ARCS; ++i) {
|
||||
@@ -351,8 +319,8 @@ void debris_process_post(object * obj, float frame_time)
|
||||
db->arc_timestamp[i] = timestamp(-1);
|
||||
} else {
|
||||
// Maybe move a vertex.... 20% of the time maybe?
|
||||
int mr = myrand();
|
||||
if ( mr < RAND_MAX/5 ) {
|
||||
int mr = Random::next();
|
||||
if ( mr < Random::MAX_VALUE/5 ) {
|
||||
vec3d v1, v2;
|
||||
|
||||
submodel_get_two_random_points_better(db->model_num, db->submodel_num, &v1, &v2);
|
||||
@@ -424,17 +392,6 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
sip = &Ship_info[shipp->ship_info_index];
|
||||
vaporize = (shipp->flags[Ship::Ship_Flags::Vaporize]);
|
||||
|
||||
if ( !hull_flag ) {
|
||||
// Make vaporize debris seen from farther away
|
||||
float dist = vm_vec_dist_quick( pos, &Eye_position );
|
||||
if (vaporize) {
|
||||
dist /= 2.0f;
|
||||
}
|
||||
if ( dist > source_obj->radius * 20.0f ) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// try to maintain our soft limit
|
||||
if (hull_flag && (Num_hull_pieces >= SOFT_LIMIT_DEBRIS_PIECES)) {
|
||||
// cause oldest hull debris chunk to blow up
|
||||
@@ -460,15 +417,15 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
if (!hull_flag) {
|
||||
if (model_num >= 0) {
|
||||
db->model_num = model_num;
|
||||
db->submodel_num = (myrand() >> 4) % sip->generic_debris_num_submodels;
|
||||
db->submodel_num = Random::next(sip->generic_debris_num_submodels);
|
||||
}
|
||||
else if (vaporize) {
|
||||
db->model_num = Debris_vaporize_model;
|
||||
db->submodel_num = (myrand() >> 4) % Debris_num_submodels;
|
||||
db->submodel_num = Random::next(Debris_num_submodels);
|
||||
}
|
||||
else {
|
||||
db->model_num = Debris_model;
|
||||
db->submodel_num = (myrand() >> 4) % Debris_num_submodels;
|
||||
db->submodel_num = Random::next(Debris_num_submodels);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -480,8 +437,17 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
|
||||
float radius = submodel_get_radius(db->model_num, db->submodel_num);
|
||||
|
||||
//WMC - We must survive until now, at least.
|
||||
db->must_survive_until = timestamp();
|
||||
// if its generic, maybe cull it if its too small and far
|
||||
if (!hull_flag) {
|
||||
float dist = vm_vec_dist_quick(pos, &Eye_position);
|
||||
// Make vaporize debris seen from farther away
|
||||
if (vaporize) {
|
||||
dist /= 2.0f;
|
||||
}
|
||||
if (dist > radius * 200.0f) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(hull_flag && sip->debris_min_lifetime >= 0.0f && sip->debris_max_lifetime >= 0.0f)
|
||||
{
|
||||
@@ -491,7 +457,7 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
{
|
||||
// Create Debris piece n!
|
||||
if ( hull_flag ) {
|
||||
if (rand() < RAND_MAX/6) // Make some pieces blow up shortly after explosion.
|
||||
if (Random::next() < (Random::MAX_VALUE/6)) // Make some pieces blow up shortly after explosion.
|
||||
db->lifeleft = 2.0f * (frand()) + 0.5f;
|
||||
else {
|
||||
db->flags.set(Debris_Flags::DoNotExpire);
|
||||
@@ -509,12 +475,10 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
{
|
||||
if(sip->debris_min_lifetime >= 0.0f && sip->debris_max_lifetime >= 0.0f)
|
||||
{
|
||||
db->must_survive_until = timestamp(fl2i(sip->debris_min_lifetime * 1000.0f));
|
||||
db->lifeleft = (( sip->debris_max_lifetime - sip->debris_min_lifetime ) * frand()) + sip->debris_min_lifetime;
|
||||
}
|
||||
else if(sip->debris_min_lifetime >= 0.0f)
|
||||
{
|
||||
db->must_survive_until = timestamp(fl2i(sip->debris_min_lifetime * 1000.0f));
|
||||
if(db->lifeleft < sip->debris_min_lifetime)
|
||||
db->lifeleft = sip->debris_min_lifetime;
|
||||
}
|
||||
@@ -539,7 +503,6 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
db->fire_timeout = 0; // if not changed, timestamp_elapsed() will return false
|
||||
db->time_started = Missiontime;
|
||||
db->species = Ship_info[shipp->ship_info_index].species;
|
||||
db->next_distance_check = (myrand() % 2000) + 4*DEBRIS_DISTANCE_CHECK_TIME;
|
||||
db->parent_alt_name = shipp->alt_type_index;
|
||||
db->damage_mult = 1.0f;
|
||||
|
||||
@@ -560,7 +523,7 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
|
||||
db->next_fireball = timestamp_rand(500,2000); //start one 1/2 - 2 secs later
|
||||
|
||||
if ( pos == NULL )
|
||||
if ( pos == nullptr )
|
||||
pos = &source_obj->pos;
|
||||
|
||||
flagset<Object::Object_Flags> default_flags;
|
||||
@@ -576,10 +539,10 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
vm_vector_2_matrix(&orient, &rand);
|
||||
}
|
||||
|
||||
objnum = obj_create( OBJ_DEBRIS, parent_objnum, n, &orient, pos, radius, default_flags);
|
||||
objnum = obj_create(OBJ_DEBRIS, parent_objnum, n, &orient, pos, radius, default_flags, false);
|
||||
if ( objnum == -1 ) {
|
||||
mprintf(("Couldn't create debris object -- out of object slots\n"));
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
db->objnum = objnum;
|
||||
@@ -629,7 +592,7 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
|
||||
if ( hull_flag ) {
|
||||
float t;
|
||||
scale = exp_force * i2fl((myrand()%20) + 10); // for radial_vel away from location of blast center
|
||||
scale = exp_force * i2fl(Random::next(10, 29)); // for radial_vel away from location of blast center
|
||||
db->sound_delay = timestamp(DEBRIS_SOUND_DELAY);
|
||||
|
||||
// set up physics mass and I_inv for hull debris pieces
|
||||
@@ -641,7 +604,7 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
|
||||
// limit the amount of time that fireballs appear
|
||||
// let fireball length be linked to radius of ship. Range is .33 radius => 3.33 radius seconds.
|
||||
t = 1000*Objects[db->source_objnum].radius/3 + myrand()%(fl2i(1000*3*Objects[db->source_objnum].radius));
|
||||
t = 1000*Objects[db->source_objnum].radius/3 + Random::next(fl2i(1000*3*Objects[db->source_objnum].radius));
|
||||
db->fire_timeout = timestamp(fl2i(t)); // fireballs last from 5 - 30 seconds
|
||||
|
||||
if ( Objects[db->source_objnum].radius < MIN_RADIUS_FOR_PERSISTANT_DEBRIS ) {
|
||||
@@ -652,7 +615,7 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
}
|
||||
}
|
||||
else {
|
||||
scale = exp_force * i2fl((myrand()%20) + 10); // for radial_vel away from blast center (non-hull)
|
||||
scale = exp_force * i2fl(Random::next(10, 29)); // for radial_vel away from blast center (non-hull)
|
||||
}
|
||||
|
||||
if ( vm_vec_mag_squared( &to_center ) < 0.1f ) {
|
||||
@@ -679,7 +642,7 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
radius = 1.0f;
|
||||
}
|
||||
|
||||
scale = ( 6.0f + i2fl(myrand()%4) ) / radius;
|
||||
scale = (6.0f + i2fl(Random::next(4))) / radius;
|
||||
|
||||
vm_vec_rand_vec_quick(&rotvel);
|
||||
vm_vec_scale(&rotvel, scale);
|
||||
|
||||
@@ -40,7 +40,6 @@ typedef struct debris {
|
||||
gamesnd_id ambient_sound; // Ambient looping sound
|
||||
int objnum = -1; // What object this is linked to
|
||||
float lifeleft; // When 0 or less object dies
|
||||
int must_survive_until; //WMC - timestamp of earliest point that it can be murthered.
|
||||
int model_num; // What model this uses
|
||||
int model_instance_num; // What model instance this uses - needed for arcs
|
||||
int submodel_num; // What submodel this uses
|
||||
@@ -50,7 +49,6 @@ typedef struct debris {
|
||||
int fire_timeout; // timestamp that holds time for fireballs to stop appearing
|
||||
int sound_delay; // timestamp to signal when sound should start
|
||||
fix time_started; // time when debris was created
|
||||
int next_distance_check; // timestamp to determine whether to delete this piece of debris.
|
||||
|
||||
vec3d arc_pts[MAX_DEBRIS_ARCS][2]; // The endpoints of each arc
|
||||
int arc_timestamp[MAX_DEBRIS_ARCS]; // When this times out, the spark goes away. -1 is not used
|
||||
|
||||
@@ -12,7 +12,7 @@ layout (std140) uniform genericData {
|
||||
vec3 fog_color;
|
||||
float fog_start;
|
||||
|
||||
float fog_scale;
|
||||
float fog_density;
|
||||
float zNear;
|
||||
float zFar;
|
||||
};
|
||||
@@ -31,9 +31,7 @@ void main()
|
||||
if (isinf(view_depth)) {
|
||||
fragOut0.rgb = color_in.rgb;
|
||||
} else {
|
||||
// This is the same formula that is also used by the main shader
|
||||
float fog_dist = clamp((view_depth - fog_start) * 0.75 * fog_scale, 0.0, 1.0);
|
||||
|
||||
float fog_dist = clamp(1 - pow(fog_density, view_depth - fog_start) , 0.0, 1.0);
|
||||
vec3 finalFogColor = srgb_to_linear(fog_color);
|
||||
|
||||
fragOut0.rgb = mix(color_in.rgb, finalFogColor, fog_dist);
|
||||
|
||||
@@ -429,7 +429,15 @@ void main()
|
||||
fragOut0 = baseColor;
|
||||
#ifdef FLAG_DEFERRED
|
||||
fragOut1 = vec4(vertIn.position.xyz, 1.0);
|
||||
fragOut2 = vec4(normal, glossData);
|
||||
|
||||
// Anti-glint "trick" based on Valve's "Advanced VR Rendering" talk at GDC2015:
|
||||
// http://media.steampowered.com/apps/valve/2015/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf Page 43
|
||||
// basically make surfaces rougher if local normals change too fast in screenspace
|
||||
vec2 normDx = dFdx(normal.xy);
|
||||
vec2 normDy = dFdy(normal.xy);
|
||||
float glossGeo = 1.0f - pow(clamp(max(dot(normDx,normDx), dot(normDy,normDy)),0.0,1.0),0.33);
|
||||
fragOut2 = vec4(normal, min(glossData, glossGeo));
|
||||
|
||||
fragOut3 = vec4(specColor.rgb, fresnelFactor);
|
||||
fragOut4 = emissiveColor;
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "graphics/tmapper.h"
|
||||
#include "localization/localize.h"
|
||||
#include "model/model.h"
|
||||
#include "nebula/neb.h"
|
||||
#include "object/object.h"
|
||||
#include "options/Option.h"
|
||||
#include "parse/parselo.h"
|
||||
@@ -286,11 +287,13 @@ static void parse_fireball_tbl(const char *table_filename)
|
||||
}
|
||||
}
|
||||
|
||||
bool first_time;
|
||||
// now select our entry accordingly...
|
||||
// are we using a previous entry?
|
||||
if (existing_idx >= 0)
|
||||
{
|
||||
fi = &Fireball_info[existing_idx];
|
||||
first_time = false;
|
||||
}
|
||||
// we are creating a new entry, so set some defaults
|
||||
else
|
||||
@@ -314,6 +317,7 @@ static void parse_fireball_tbl(const char *table_filename)
|
||||
fireball_set_default_warp_attributes(Num_fireball_types);
|
||||
|
||||
Num_fireball_types++;
|
||||
first_time = true;
|
||||
}
|
||||
|
||||
// copy over what we already parsed
|
||||
@@ -321,6 +325,7 @@ static void parse_fireball_tbl(const char *table_filename)
|
||||
strcpy_s(fi->unique_id, unique_id);
|
||||
strcpy_s(fi->lod[0].filename, fireball_filename);
|
||||
|
||||
|
||||
// Do we have a LOD num?
|
||||
if (optional_string("$LOD:"))
|
||||
{
|
||||
@@ -328,6 +333,9 @@ static void parse_fireball_tbl(const char *table_filename)
|
||||
|
||||
if (fi->lod_count > MAX_FIREBALL_LOD)
|
||||
fi->lod_count = MAX_FIREBALL_LOD;
|
||||
} else if (first_time) {
|
||||
//assume a LOD of at least 1
|
||||
fi->lod_count = 1;
|
||||
}
|
||||
|
||||
// check for particular lighting color
|
||||
@@ -830,7 +838,7 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
|
||||
|
||||
flagset<Object::Object_Flags> default_flags;
|
||||
default_flags.set(Object::Object_Flags::Renders);
|
||||
objnum = obj_create(OBJ_FIREBALL, parent_obj, n, &orient, pos, size, default_flags);
|
||||
objnum = obj_create(OBJ_FIREBALL, parent_obj, n, &orient, pos, size, default_flags, false);
|
||||
|
||||
obj = &Objects[objnum];
|
||||
|
||||
@@ -843,11 +851,11 @@ int fireball_create(vec3d *pos, int fireball_type, int render_type, int parent_o
|
||||
switch( new_fireball->fireball_render_type ) {
|
||||
|
||||
case FIREBALL_MEDIUM_EXPLOSION:
|
||||
new_fireball->orient = (myrand()>>8) & 7; // 0 - 7
|
||||
new_fireball->orient = Random::next() & 7; // 0 - 7
|
||||
break;
|
||||
|
||||
case FIREBALL_LARGE_EXPLOSION:
|
||||
new_fireball->orient = (myrand()>>8) % 360; // 0 - 359
|
||||
new_fireball->orient = Random::next(360); // 0 - 359
|
||||
break;
|
||||
|
||||
case FIREBALL_WARP_EFFECT:
|
||||
@@ -978,9 +986,9 @@ int fireball_ship_explosion_type(ship_info *sip)
|
||||
}
|
||||
|
||||
if(ship_fireballs > 0){
|
||||
index = sip->explosion_bitmap_anims[rand()%ship_fireballs];
|
||||
index = sip->explosion_bitmap_anims[Random::next(ship_fireballs)];
|
||||
} else if(objecttype_fireballs > 0){
|
||||
index = Ship_types[sip->class_type].explosion_bitmap_anims[rand()%objecttype_fireballs];
|
||||
index = Ship_types[sip->class_type].explosion_bitmap_anims[Random::next(objecttype_fireballs)];
|
||||
}
|
||||
|
||||
return index;
|
||||
@@ -997,7 +1005,7 @@ int fireball_asteroid_explosion_type(asteroid_info *aip)
|
||||
int roid_fireballs = (int)aip->explosion_bitmap_anims.size();
|
||||
|
||||
if (roid_fireballs > 0) {
|
||||
index = aip->explosion_bitmap_anims[rand()%roid_fireballs];
|
||||
index = aip->explosion_bitmap_anims[Random::next(roid_fireballs)];
|
||||
}
|
||||
|
||||
return index;
|
||||
@@ -1037,19 +1045,24 @@ void fireball_render(object* obj, model_draw_list *scene)
|
||||
|
||||
if ( fb->current_bitmap < 0 )
|
||||
return;
|
||||
|
||||
float alpha = 1.0f;
|
||||
|
||||
if (The_mission.flags[Mission::Mission_Flags::Fullneb] && Neb_affects_fireballs)
|
||||
alpha *= neb2_get_fog_visibility(&obj->pos, NEB_FOG_VISIBILITY_MULT_FIREBALL(obj->radius));
|
||||
|
||||
g3_transfer_vertex(&p, &obj->pos);
|
||||
|
||||
switch ( fb->fireball_render_type ) {
|
||||
|
||||
case FIREBALL_MEDIUM_EXPLOSION: {
|
||||
batching_add_volume_bitmap(fb->current_bitmap, &p, fb->orient, obj->radius);
|
||||
batching_add_volume_bitmap(fb->current_bitmap, &p, fb->orient, obj->radius, alpha);
|
||||
}
|
||||
break;
|
||||
|
||||
case FIREBALL_LARGE_EXPLOSION: {
|
||||
// Make the big explosions rotate with the viewer.
|
||||
batching_add_volume_bitmap_rotated(fb->current_bitmap, &p, (i2fl(fb->orient)*PI) / 180.0f, obj->radius);
|
||||
batching_add_volume_bitmap_rotated(fb->current_bitmap, &p, (i2fl(fb->orient)*PI) / 180.0f, obj->radius, alpha);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ void warpin_queue_render(model_draw_list *scene, object *obj, matrix *orient, ve
|
||||
|
||||
g3_transfer_vertex( &verts[4], &vecs[4] );
|
||||
|
||||
float alpha = (The_mission.flags[Mission::Mission_Flags::Fullneb]) ? (1.0f - neb2_get_fog_intensity(obj)) : 1.0f;
|
||||
float alpha = (The_mission.flags[Mission::Mission_Flags::Fullneb]) ? neb2_get_fog_visibility(&obj->pos, 1.0f) : 1.0f;
|
||||
|
||||
//batch_add_bitmap(warp_glow_bitmap, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT, &verts[4], 0, r, alpha);
|
||||
batching_add_bitmap(warp_glow_bitmap, &verts[4], 0, r, alpha);
|
||||
|
||||
@@ -239,7 +239,10 @@ bool GameState_Stack_Valid()
|
||||
|
||||
// returns one of the GS_STATE_ macros
|
||||
int gameseq_get_state(int depth)
|
||||
{
|
||||
{
|
||||
if (!GameState_Stack_Valid())
|
||||
return GS_STATE_INVALID;
|
||||
|
||||
Assert(depth <= gs_current_stack);
|
||||
|
||||
return gs[gs_current_stack - depth].current_state;
|
||||
|
||||
+20
-20
@@ -526,7 +526,7 @@ void parse_iface_sound_list(const char* tag, SCP_vector<interface_snd_id>& desti
|
||||
//if we're using the old format, double check the size)
|
||||
if(!scp_list && (destination.size() != (unsigned)check))
|
||||
{
|
||||
mprintf(("%s in '%s' has " SIZE_T_ARG " entries. This does not match entered size of %i.", tag, object_name, destination.size(), check));
|
||||
mprintf(("%s in '%s' has " SIZE_T_ARG " entries. This does not match entered size of %i.\n", tag, object_name, destination.size(), check));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -544,12 +544,12 @@ void gamesnd_preload_common_sounds()
|
||||
return;
|
||||
|
||||
Assert( Snds.size() <= INT_MAX );
|
||||
for (SCP_vector<game_snd>::iterator gs = Snds.begin(); gs != Snds.end(); ++gs) {
|
||||
if ( gs->preload ) {
|
||||
for (auto& entry : gs->sound_entries) {
|
||||
for (auto& gs: Snds) {
|
||||
if ( gs.preload ) {
|
||||
for (auto& entry : gs.sound_entries) {
|
||||
if ( entry.filename[0] != 0 && strnicmp(entry.filename, NOX("none.wav"), 4) != 0 ) {
|
||||
game_busy( NOX("** preloading common game sounds **") ); // Animate loading cursor... does nothing if loading screen not active.
|
||||
entry.id = snd_load(&entry, gs->flags);
|
||||
entry.id = snd_load(&entry, &gs.flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -565,12 +565,12 @@ void gamesnd_load_gameplay_sounds()
|
||||
return;
|
||||
|
||||
Assert( Snds.size() <= INT_MAX );
|
||||
for (SCP_vector<game_snd>::iterator gs = Snds.begin(); gs != Snds.end(); ++gs) {
|
||||
if ( !gs->preload ) { // don't try to load anything that's already preloaded
|
||||
for (auto& entry : gs->sound_entries) {
|
||||
for (auto& gs: Snds) {
|
||||
if ( !gs.preload ) { // don't try to load anything that's already preloaded
|
||||
for (auto& entry : gs.sound_entries) {
|
||||
if (entry.filename[0] != 0 && strnicmp(entry.filename, NOX("none.wav"), 4) != 0) {
|
||||
game_busy(NOX("** preloading gameplay sounds **")); // Animate loading cursor... does nothing if loading screen not active.
|
||||
entry.id = snd_load(&entry, gs->flags);
|
||||
entry.id = snd_load(&entry, &gs.flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -583,8 +583,8 @@ void gamesnd_load_gameplay_sounds()
|
||||
void gamesnd_unload_gameplay_sounds()
|
||||
{
|
||||
Assert( Snds.size() <= INT_MAX );
|
||||
for (SCP_vector<game_snd>::iterator gs = Snds.begin(); gs != Snds.end(); ++gs) {
|
||||
for (auto& entry : gs->sound_entries) {
|
||||
for (auto& gs: Snds) {
|
||||
for (auto& entry : gs.sound_entries) {
|
||||
if (entry.id.isValid()) {
|
||||
snd_unload(entry.id);
|
||||
entry.id = sound_load_id::invalid();
|
||||
@@ -602,10 +602,10 @@ void gamesnd_load_interface_sounds()
|
||||
return;
|
||||
|
||||
Assert( Snds_iface.size() < INT_MAX );
|
||||
for (SCP_vector<game_snd>::iterator si = Snds_iface.begin(); si != Snds_iface.end(); ++si) {
|
||||
for (auto& entry : si->sound_entries) {
|
||||
for (auto& gs: Snds) {
|
||||
for (auto& entry : gs.sound_entries) {
|
||||
if ( entry.filename[0] != 0 && strnicmp(entry.filename, NOX("none.wav"), 4) != 0 ) {
|
||||
entry.id = snd_load(&entry, si->flags);
|
||||
entry.id = snd_load(&entry, &gs.flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -751,14 +751,14 @@ bool required_string_no_create(const char* token, bool no_create)
|
||||
|
||||
static GameSoundCycleType parse_cycle_type() {
|
||||
if (optional_string("Sequential")) {
|
||||
return GameSoundCycleType::Sequential;
|
||||
return GameSoundCycleType::SequentialCycle;
|
||||
} else if (optional_string("Random")) {
|
||||
return GameSoundCycleType::Random;
|
||||
return GameSoundCycleType::RandomCycle;
|
||||
} else {
|
||||
error_display(0, "Failed to parse sound cycle type. Expected 'sequential' or 'random'. Got [%.32s]", next_tokens());
|
||||
// Ignore everything until the end of the line. That should hopefully skip the bad token.
|
||||
advance_to_eoln(nullptr);
|
||||
return GameSoundCycleType::Sequential;
|
||||
return GameSoundCycleType::SequentialCycle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1349,7 +1349,7 @@ float gamesnd_get_max_duration(game_snd* gs) {
|
||||
for (auto& entry : gs->sound_entries) {
|
||||
if (!entry.id.isValid()) {
|
||||
// Lazily load unloaded sound entries when required
|
||||
entry.id = snd_load(&entry, gs->flags);
|
||||
entry.id = snd_load(&entry, &gs->flags);
|
||||
}
|
||||
|
||||
max_length = std::max(max_length, snd_get_duration(entry.id));
|
||||
@@ -1362,14 +1362,14 @@ game_snd_entry* gamesnd_choose_entry(game_snd* gs) {
|
||||
|
||||
size_t index = 0;
|
||||
switch(gs->cycle_type) {
|
||||
case GameSoundCycleType::Random:
|
||||
case GameSoundCycleType::RandomCycle:
|
||||
if (gs->sound_entries.size() == 1) {
|
||||
index = 0;
|
||||
} else {
|
||||
index = util::UniformRange<size_t>(0, gs->sound_entries.size() - 1).next();
|
||||
}
|
||||
break;
|
||||
case GameSoundCycleType::Sequential:
|
||||
case GameSoundCycleType::SequentialCycle:
|
||||
if (gs->last_entry_index == std::numeric_limits<size_t>::max()) {
|
||||
// If this is the first time we must return the first sound to keep everything consistent
|
||||
index = 0;
|
||||
|
||||
@@ -113,6 +113,8 @@ typedef struct vec2d {
|
||||
float x, y;
|
||||
} vec2d;
|
||||
|
||||
// Euler angles for a rotation: h=heading, b=bank and p=pitch; angles are around the y, z and x axes
|
||||
// respectively and are performed in that order.
|
||||
typedef struct angles {
|
||||
float p, b, h;
|
||||
} angles_t;
|
||||
@@ -128,6 +130,7 @@ typedef struct matrix {
|
||||
};
|
||||
} matrix;
|
||||
|
||||
// You might think this is also row-major. You fool! It is actually column-major.
|
||||
typedef struct matrix4 {
|
||||
union {
|
||||
struct {
|
||||
@@ -303,8 +306,6 @@ constexpr bool LoggingEnabled = false;
|
||||
const float PI2 = (PI*2.0f);
|
||||
// half values
|
||||
const float PI_2 = (PI/2.0f);
|
||||
const int RAND_MAX_2 = (RAND_MAX/2);
|
||||
const float RAND_MAX_1f = (1.0f / RAND_MAX);
|
||||
|
||||
|
||||
extern int Fred_running; // Is Fred running, or FreeSpace?
|
||||
@@ -350,14 +351,6 @@ const size_t INVALID_SIZE = static_cast<size_t>(-1);
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
int myrand();
|
||||
|
||||
// Returns a random number between 0 and 0x7fffffff
|
||||
int rand32();
|
||||
|
||||
// Returns a random integer from low to high, inclusive
|
||||
int rand32(int low, int high);
|
||||
|
||||
|
||||
// lod checker for (modular) table parsing
|
||||
typedef struct lod_checker {
|
||||
@@ -463,7 +456,6 @@ SCP_string dump_stacktrace();
|
||||
// DEBUG compile time catch for dangerous uses of memset/memcpy/memmove
|
||||
// This is disabled for VS2013 and lower since that doesn't support the necessary features
|
||||
#if !defined(NDEBUG) && !defined(USING_THIRD_PARTY_LIBS) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
|
||||
#if SCP_COMPILER_CXX_AUTO_TYPE && SCP_COMPILER_CXX_STATIC_ASSERT && HAVE_STD_IS_TRIVIALLY_COPYABLE
|
||||
// feature support seems to be: gcc clang msvc
|
||||
// auto 4.4 2.9 2010
|
||||
// std::is_trivial 4.5 ? 2012 (2010 only duplicates std::is_pod)
|
||||
@@ -563,7 +555,6 @@ using std::memcpy_if_trivial_else_error;
|
||||
using std::memmove_if_trivial_else_error;
|
||||
using std::memset_if_trivial_else_error;
|
||||
|
||||
#endif // HAVE_CXX11
|
||||
#endif // NDEBUG
|
||||
|
||||
#endif // PS_TYPES_H
|
||||
#endif // _PSTYPES_H
|
||||
@@ -3,8 +3,6 @@
|
||||
#define SCP_DEFINES_H
|
||||
#pragma once
|
||||
|
||||
#include "scp_compiler_detection.h"
|
||||
|
||||
#define SCP_TOKEN_CONCAT1(x, y) x ## y
|
||||
#define SCP_TOKEN_CONCAT(x, y) SCP_TOKEN_CONCAT1(x, y)
|
||||
|
||||
|
||||
@@ -57,7 +57,6 @@ vci Viewer_chase_info; // View chase camera information
|
||||
vec3d leaning_position;
|
||||
|
||||
int Is_standalone;
|
||||
int Rand_count;
|
||||
|
||||
int Interface_last_tick = -1; // last timer tick on flip
|
||||
|
||||
@@ -109,47 +108,6 @@ float Noise[NOISE_NUM_FRAMES] = {
|
||||
0.000000f
|
||||
};
|
||||
|
||||
|
||||
int myrand()
|
||||
{
|
||||
int rval;
|
||||
rval = rand();
|
||||
Rand_count++;
|
||||
// nprintf(("Alan","RAND: %d\n", rval));
|
||||
return rval;
|
||||
}
|
||||
|
||||
// returns a random number between 0 and 0x7fffffff, or something close to it.
|
||||
int rand32()
|
||||
{
|
||||
if (RAND_MAX == 0x7fff) {
|
||||
int random32;
|
||||
// this gets two random 16 bit numbers and stuffs them into a 32 bit number
|
||||
random32 = (rand() << 16) | rand();
|
||||
//since rand() returns between 0 and 0x7fff, there needs to be a thing to randomly generate the 16th bit
|
||||
random32 |= ((rand() & 1) << 15);
|
||||
|
||||
return random32;
|
||||
}
|
||||
else {
|
||||
return rand();
|
||||
}
|
||||
}
|
||||
|
||||
int rand32(int low, int high)
|
||||
{
|
||||
int diff;
|
||||
|
||||
// get diff - don't allow negative or zero
|
||||
diff = high - low;
|
||||
if (diff < 0)
|
||||
diff = 0;
|
||||
|
||||
// To get a range of values between min and max, inclusive:
|
||||
// random value = min + random number % (max - min + 1)
|
||||
return (low + rand32() % (diff + 1));
|
||||
}
|
||||
|
||||
// Variables for the loading callback hooks
|
||||
static int cf_timestamp = -1;
|
||||
static void (*cf_callback)(int count) = NULL;
|
||||
@@ -370,9 +328,6 @@ void detail_level_set(int level)
|
||||
Assert( level < NUM_DEFAULT_DETAIL_LEVELS );
|
||||
|
||||
Detail = Detail_defaults[level];
|
||||
|
||||
// reset nebula stuff
|
||||
neb2_set_detail_level(level);
|
||||
}
|
||||
|
||||
// Returns the current detail level or -1 if custom.
|
||||
|
||||
@@ -94,7 +94,6 @@ extern int Game_mode;
|
||||
#define SINGLEPLAYER !(Game_mode & GM_MULTIPLAYER)
|
||||
|
||||
extern int Viewer_mode;
|
||||
extern int Rand_count;
|
||||
|
||||
extern int Game_restoring; // If set, this means we are restoring data from disk
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
#define USED_VARIABLE __attribute__((used))
|
||||
|
||||
#if SCP_COMPILER_VERSION_MAJOR >= 7
|
||||
#if __GNUC__ >= 7
|
||||
#define FALLTHROUGH [[fallthrough]]
|
||||
#else
|
||||
#define FALLTHROUGH
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
#define USED_VARIABLE __attribute__((used))
|
||||
|
||||
#if SCP_COMPILER_VERSION_MAJOR >= 7
|
||||
#if __GNUC__ >= 7
|
||||
#define FALLTHROUGH __attribute__((fallthrough))
|
||||
#else
|
||||
#define FALLTHROUGH
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "scripting/scripting.h"
|
||||
#include "tracing/tracing.h"
|
||||
#include "utils/boost/hash_combine.h"
|
||||
#include "gamesequence/gamesequence.h"
|
||||
|
||||
#ifdef WITH_OPENGL
|
||||
#include "graphics/opengl/gropengl.h"
|
||||
@@ -2547,7 +2548,8 @@ void gr_flip(bool execute_scripting)
|
||||
executor::OnFrameExecutor->process();
|
||||
|
||||
// m!m avoid running CHA_ONFRAME when the "Quit mission" popup is shown. See mantis 2446 for reference
|
||||
if (execute_scripting && !popup_active()) {
|
||||
// Cyborg - A similar bug will occur when a mission is restarted so check for that, too.
|
||||
if (execute_scripting && !popup_active() && !((gameseq_get_state() == GS_STATE_GAME_PLAY) && !(Game_mode & GM_IN_MISSION))) {
|
||||
TRACE_SCOPE(tracing::LuaOnFrame);
|
||||
|
||||
// WMC - Do conditional hooks. Yippee!
|
||||
|
||||
+1
-1
@@ -808,7 +808,7 @@ typedef struct screen {
|
||||
std::function<void(int bitmap_handle, int bpp, const ubyte* data, int width, int height)> gf_update_texture;
|
||||
std::function<void(void* data_out, int bitmap_num)> gf_get_bitmap_from_texture;
|
||||
|
||||
std::function<void(matrix4* shadow_view_matrix, const matrix* light_matrix)> gf_shadow_map_start;
|
||||
std::function<void(matrix4* shadow_view_matrix, const matrix* light_matrix, vec3d* eye_pos)> gf_shadow_map_start;
|
||||
std::function<void()> gf_shadow_map_end;
|
||||
|
||||
std::function<void()> gf_start_decal_pass;
|
||||
|
||||
@@ -630,7 +630,7 @@ void generic_anim_render_variable_frame_delay(generic_anim* ga, float frametime,
|
||||
else {
|
||||
// playing forwards
|
||||
ga->anim_time += frametime;
|
||||
if(ga->anim_time >= ga->total_time && ga->png.anim->current_frame >= ga->png.anim->nframes-1) {
|
||||
if(ga->anim_time >= ga->total_time && ga->png.anim->current_frame >= ga->png.anim->nframes) {
|
||||
if(ga->direction & GENERIC_ANIM_DIRECTION_NOLOOP) {
|
||||
ga->anim_time = ga->total_time; // stop on last frame when playing
|
||||
}
|
||||
@@ -660,7 +660,7 @@ void generic_anim_render_variable_frame_delay(generic_anim* ga, float frametime,
|
||||
}
|
||||
else {
|
||||
if (ga->anim_time >= ga->png.previous_frame_time + ga->png.anim->frame.delay &&
|
||||
ga->png.anim->current_frame < ga->png.anim->nframes-1) {
|
||||
ga->png.anim->current_frame < ga->png.anim->nframes) {
|
||||
ga->png.previous_frame_time += ga->png.anim->frame.delay;
|
||||
ga->current_frame++;
|
||||
}
|
||||
@@ -725,3 +725,17 @@ void generic_anim_render(generic_anim *ga, float frametime, int x, int y, bool m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief reset an animation back to the start
|
||||
*
|
||||
* @param [in] *ga animation data
|
||||
*/
|
||||
void generic_anim_reset(generic_anim *ga) {
|
||||
ga->anim_time = 0.0f;
|
||||
ga->current_frame = 0;
|
||||
if (ga->type == BM_TYPE_PNG) {
|
||||
ga->png.previous_frame_time = 0.0f;
|
||||
ga->png.anim->goto_start();
|
||||
}
|
||||
}
|
||||
@@ -89,5 +89,5 @@ int generic_anim_stream(generic_anim *ga, const bool cache = true);
|
||||
int generic_bitmap_load(generic_bitmap *gb);
|
||||
void generic_anim_unload(generic_anim *ga);
|
||||
void generic_anim_render(generic_anim *ga, float frametime, int x, int y, bool menu = false, const generic_extras *ge = nullptr);
|
||||
|
||||
void generic_anim_reset(generic_anim *ga);
|
||||
#endif
|
||||
@@ -285,7 +285,7 @@ int gr_stub_maybe_create_shader(shader_type /*shader_t*/, unsigned int /*flags
|
||||
return -1;
|
||||
}
|
||||
|
||||
void gr_stub_shadow_map_start(matrix4 * /*shadow_view_matrix*/, const matrix* /*light_matrix*/)
|
||||
void gr_stub_shadow_map_start(matrix4 * /*shadow_view_matrix*/, const matrix* /*light_matrix*/, vec3d* /*eye_pos*/)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@ class ShaderProgram {
|
||||
|
||||
ShaderUniforms Uniforms;
|
||||
|
||||
ShaderProgram(const ShaderProgram&) SCP_DELETED_FUNCTION;
|
||||
ShaderProgram& operator=(const ShaderProgram&) SCP_DELETED_FUNCTION;
|
||||
ShaderProgram(const ShaderProgram&) = delete;
|
||||
ShaderProgram& operator=(const ShaderProgram&) = delete;
|
||||
|
||||
ShaderProgram(ShaderProgram&& other) noexcept;
|
||||
ShaderProgram& operator=(ShaderProgram&& other) noexcept;
|
||||
|
||||
@@ -301,8 +301,8 @@ void gr_opengl_deferred_lighting_finish()
|
||||
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Scene_emissive_texture);
|
||||
GL_state.Texture.Enable(1, GL_TEXTURE_2D, Scene_depth_texture);
|
||||
|
||||
float fog_near, fog_far;
|
||||
neb2_get_adjusted_fog_values(&fog_near, &fog_far, nullptr);
|
||||
float fog_near, fog_far, fog_density;
|
||||
neb2_get_adjusted_fog_values(&fog_near, &fog_far, &fog_density);
|
||||
unsigned char r, g, b;
|
||||
neb2_get_fog_color(&r, &g, &b);
|
||||
|
||||
@@ -311,7 +311,7 @@ void gr_opengl_deferred_lighting_finish()
|
||||
|
||||
opengl_set_generic_uniform_data<graphics::generic_data::fog_data>([&](graphics::generic_data::fog_data* data) {
|
||||
data->fog_start = fog_near;
|
||||
data->fog_scale = 1.0f / (fog_far - fog_near);
|
||||
data->fog_density = fog_density;
|
||||
data->fog_color.xyz.x = r / 255.f;
|
||||
data->fog_color.xyz.y = g / 255.f;
|
||||
data->fog_color.xyz.z = b / 255.f;
|
||||
|
||||
@@ -706,8 +706,8 @@ void gr_opengl_update_distortion()
|
||||
|
||||
for(int i = 0; i < 33; i++)
|
||||
{
|
||||
distortion_verts[i].r = (ubyte)rand() % 256;
|
||||
distortion_verts[i].g = (ubyte)rand() % 256;
|
||||
distortion_verts[i].r = (ubyte)Random::next(256);
|
||||
distortion_verts[i].g = (ubyte)Random::next(256);
|
||||
distortion_verts[i].b = 255;
|
||||
distortion_verts[i].a = 255;
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ void gr_opengl_update_distortion();
|
||||
|
||||
void gr_opengl_sphere(material *material_def, float rad);
|
||||
|
||||
void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light_orient);
|
||||
void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light_orient, vec3d* eye_pos);
|
||||
void gr_opengl_shadow_map_end();
|
||||
|
||||
void gr_opengl_render_shield_impact(shield_material* material_info,
|
||||
|
||||
@@ -170,7 +170,7 @@ void opengl_post_pass_bloom()
|
||||
opengl_set_generic_uniform_data<graphics::generic_data::bloom_composition_data>(
|
||||
[](graphics::generic_data::bloom_composition_data* data) {
|
||||
data->levels = MAX_MIP_BLUR_LEVELS;
|
||||
data->bloom_intensity = Cmdline_bloom_intensity / 100.0f;
|
||||
data->bloom_intensity = gr_bloom_intensity() / 100.0f;
|
||||
});
|
||||
|
||||
GL_state.Texture.Enable(0, GL_TEXTURE_2D, Bloom_textures[0]);
|
||||
@@ -442,9 +442,9 @@ void opengl_post_lightshafts()
|
||||
float dot;
|
||||
if ( (dot = vm_vec_dot(&light_dir, &Eye_matrix.vec.fvec)) > 0.7f ) {
|
||||
|
||||
x = asinf(vm_vec_dot(&light_dir, &Eye_matrix.vec.rvec)) / PI * 1.5f +
|
||||
x = asinf_safe(vm_vec_dot(&light_dir, &Eye_matrix.vec.rvec)) / PI * 1.5f +
|
||||
0.5f; // cant get the coordinates right but this works for the limited glare fov
|
||||
y = asinf(vm_vec_dot(&light_dir, &Eye_matrix.vec.uvec)) / PI * 1.5f * gr_screen.clip_aspect + 0.5f;
|
||||
y = asinf_safe(vm_vec_dot(&light_dir, &Eye_matrix.vec.uvec)) / PI * 1.5f * gr_screen.clip_aspect + 0.5f;
|
||||
|
||||
opengl_set_generic_uniform_data<graphics::generic_data::lightshaft_data>(
|
||||
[&](graphics::generic_data::lightshaft_data* data) {
|
||||
@@ -871,7 +871,7 @@ bool opengl_post_init_shaders()
|
||||
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_BLUR, SDR_FLAG_BLUR_VERTICAL) < 0 ||
|
||||
gr_opengl_maybe_create_shader(SDR_TYPE_POST_PROCESS_BLOOM_COMP, 0) < 0) {
|
||||
// disable bloom if we don't have those shaders available
|
||||
Cmdline_bloom_intensity = 0;
|
||||
graphics::Post_processing_manager->setBloomShadersOk(false);
|
||||
}
|
||||
|
||||
if (gr_is_fxaa_mode(Gr_aa_mode))
|
||||
|
||||
@@ -679,7 +679,7 @@ bool Glowpoint_override_save;
|
||||
|
||||
extern bool gr_htl_projection_matrix_set;
|
||||
|
||||
void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light_orient)
|
||||
void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light_orient, vec3d* eye_pos)
|
||||
{
|
||||
if (Shadow_quality == ShadowQuality::Disabled)
|
||||
return;
|
||||
@@ -701,7 +701,8 @@ void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light
|
||||
Glowpoint_override = true;
|
||||
|
||||
gr_htl_projection_matrix_set = true;
|
||||
gr_set_view_matrix(&Eye_position, light_orient);
|
||||
|
||||
gr_set_view_matrix(eye_pos, light_orient);
|
||||
|
||||
*shadow_view_matrix = gr_view_matrix;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "post_processing.h"
|
||||
|
||||
#include "graphics/grinternal.h"
|
||||
#include "options/Option.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "ship/ship.h"
|
||||
|
||||
@@ -34,6 +35,31 @@ PostEffectUniformType mapUniformNameToType(const SCP_string& uniform_name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// used by In-Game Options menu
|
||||
bool Post_processing_enable_lightshafts = true;
|
||||
|
||||
auto LightshaftsOption =
|
||||
options::OptionBuilder<bool>("Graphics.Lightshafts", "Lightshafts", "Enable lightshafts (requires post-processing)")
|
||||
.category("Graphics")
|
||||
.default_val(true)
|
||||
.level(options::ExpertLevel::Advanced)
|
||||
.bind_to(&Post_processing_enable_lightshafts)
|
||||
.importance(60)
|
||||
.finish();
|
||||
|
||||
int Post_processing_bloom_intensity = 25; // using default value of Cmdline_bloom_intensity
|
||||
|
||||
auto BloomIntensityOption = options::OptionBuilder<int>("Graphics.BloomIntensity",
|
||||
"Bloom intensity",
|
||||
"Set bloom intensity (requires post-processing)")
|
||||
.category("Graphics")
|
||||
.range(0, 200)
|
||||
.level(options::ExpertLevel::Advanced)
|
||||
.default_val(25)
|
||||
.bind_to(&Post_processing_bloom_intensity)
|
||||
.importance(55)
|
||||
.finish();
|
||||
} // namespace
|
||||
|
||||
bool PostProcessingManager::parse_table()
|
||||
@@ -169,9 +195,49 @@ SCP_vector<graphics::post_effect_t>& PostProcessingManager::getPostEffects() { r
|
||||
const lightshaft_parameters& PostProcessingManager::getLightshaftParams() const { return m_lightshaftParams; }
|
||||
lightshaft_parameters& PostProcessingManager::getLightshaftParams() { return m_lightshaftParams; }
|
||||
|
||||
bool PostProcessingManager::bloomShadersOk() const
|
||||
{
|
||||
return m_bloomShadersOk;
|
||||
}
|
||||
|
||||
void PostProcessingManager::setBloomShadersOk(bool ok)
|
||||
{
|
||||
m_bloomShadersOk = ok;
|
||||
}
|
||||
} // namespace graphics
|
||||
|
||||
bool gr_lightshafts_enabled()
|
||||
{
|
||||
return graphics::Post_processing_manager->getLightshaftParams().on && !Cmdline_force_lightshaft_off;
|
||||
|
||||
if (!graphics::Post_processing_manager->getLightshaftParams().on) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Using_in_game_options) {
|
||||
return graphics::Post_processing_enable_lightshafts;
|
||||
} else {
|
||||
return !Cmdline_force_lightshaft_off;
|
||||
}
|
||||
}
|
||||
|
||||
int gr_bloom_intensity()
|
||||
{
|
||||
if (graphics::Post_processing_manager == nullptr || !graphics::Post_processing_manager->bloomShadersOk()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Using_in_game_options) {
|
||||
return graphics::Post_processing_bloom_intensity;
|
||||
} else {
|
||||
return Cmdline_bloom_intensity;
|
||||
}
|
||||
}
|
||||
|
||||
void gr_set_bloom_intensity(int intensity)
|
||||
{
|
||||
if (Using_in_game_options) {
|
||||
graphics::Post_processing_bloom_intensity = intensity;
|
||||
} else {
|
||||
Cmdline_bloom_intensity = intensity;
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ struct post_effect_t {
|
||||
};
|
||||
|
||||
struct lightshaft_parameters {
|
||||
bool on = false;
|
||||
bool on = true; // enabled by default
|
||||
float density = 0.5f;
|
||||
float weight = 0.02f;
|
||||
float falloff = 1.0f;
|
||||
@@ -56,12 +56,19 @@ class PostProcessingManager {
|
||||
const lightshaft_parameters& getLightshaftParams() const;
|
||||
lightshaft_parameters& getLightshaftParams();
|
||||
|
||||
bool bloomShadersOk() const;
|
||||
void setBloomShadersOk(bool ok);
|
||||
|
||||
private:
|
||||
SCP_vector<post_effect_t> m_postEffects;
|
||||
|
||||
lightshaft_parameters m_lightshaftParams;
|
||||
bool m_bloomShadersOk = true;
|
||||
};
|
||||
|
||||
} // namespace graphics
|
||||
|
||||
bool gr_lightshafts_enabled();
|
||||
int gr_bloom_intensity();
|
||||
// used by lab
|
||||
void gr_set_bloom_intensity(int intensity);
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "graphics/matrix.h"
|
||||
#include "lighting/lighting.h"
|
||||
#include "math/vecmat.h"
|
||||
#include "mod_table/mod_table.h"
|
||||
#include "model/model.h"
|
||||
#include "model/modelrender.h"
|
||||
#include "options/Option.h"
|
||||
@@ -405,7 +406,7 @@ matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, float fov, float
|
||||
Shadow_proj_matrix[2] = Shadow_frustums[2].proj_matrix;
|
||||
Shadow_proj_matrix[3] = Shadow_frustums[3].proj_matrix;
|
||||
|
||||
gr_shadow_map_start(&Shadow_view_matrix, &light_matrix);
|
||||
gr_shadow_map_start(&Shadow_view_matrix, &light_matrix, eye_pos);
|
||||
|
||||
return light_matrix;
|
||||
}
|
||||
@@ -433,9 +434,9 @@ void shadows_render_all(float fov, matrix *eye_orient, vec3d *eye_pos)
|
||||
gr_end_proj_matrix();
|
||||
gr_end_view_matrix();
|
||||
|
||||
// these cascade distances are a result of some arbitrary tuning to give a good balance of quality and banding.
|
||||
// the default cascade distances are a result of some arbitrary tuning to give a good balance of quality and banding.
|
||||
// maybe we could use a more programmatic algorithim?
|
||||
matrix light_matrix = shadows_start_render(eye_orient, eye_pos, fov, gr_screen.clip_aspect, 200.0f, 600.0f, 2500.0f, 8000.0f);
|
||||
matrix light_matrix = shadows_start_render(eye_orient, eye_pos, fov, gr_screen.clip_aspect, std::get<0>(Shadow_distances), std::get<1>(Shadow_distances), std::get<2>(Shadow_distances), std::get<3>(Shadow_distances));
|
||||
|
||||
model_draw_list scene;
|
||||
object *objp = Objects;
|
||||
|
||||
@@ -120,14 +120,14 @@ namespace font
|
||||
CFILE* fp = cfopen(typeface.c_str(), "rb", CFILE_NORMAL, CF_TYPE_ANY, localize);
|
||||
if (fp == NULL)
|
||||
{
|
||||
mprintf(("Unable to find font file \"%s\".", typeface.c_str()));
|
||||
mprintf(("Unable to find font file \"%s\"\n", typeface.c_str()));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::unique_ptr<font> fnt(new font());
|
||||
if (!fnt)
|
||||
{
|
||||
mprintf(("Unable to allocate memory for \"%s\"", typeface.c_str()));
|
||||
mprintf(("Unable to allocate memory for \"%s\"\n", typeface.c_str()));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ namespace font
|
||||
|
||||
if (fontFile == NULL)
|
||||
{
|
||||
mprintf(("Couldn't open font file \"%s\"", fileName.c_str()));
|
||||
mprintf(("Couldn't open font file \"%s\"\n", fileName.c_str()));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -293,14 +293,14 @@ namespace font
|
||||
|
||||
if (!fontData)
|
||||
{
|
||||
mprintf(("Couldn't allocate " SIZE_T_ARG " bytes for reading font file \"%s\"!", size, fileName.c_str()));
|
||||
mprintf(("Couldn't allocate " SIZE_T_ARG " bytes for reading font file \"%s\"!\n", size, fileName.c_str()));
|
||||
cfclose(fontFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!cfread(fontData.get(), (int)size, 1, fontFile))
|
||||
{
|
||||
mprintf(("Error while reading font data from \"%s\"", fileName.c_str()));
|
||||
mprintf(("Error while reading font data from \"%s\"\n", fileName.c_str()));
|
||||
cfclose(fontFile);
|
||||
return NULL;
|
||||
}
|
||||
@@ -323,7 +323,7 @@ namespace font
|
||||
|
||||
if (handle < 0)
|
||||
{
|
||||
mprintf(("Couldn't couldn't create font for file \"%s\"", fileName.c_str()));
|
||||
mprintf(("Couldn't couldn't create font for file \"%s\"\n", fileName.c_str()));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,10 +33,8 @@ namespace font {
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
TrueTypeFontData(const TrueTypeFontData &) SCP_DELETED_FUNCTION;
|
||||
|
||||
TrueTypeFontData &operator=(const TrueTypeFontData &) SCP_DELETED_FUNCTION;
|
||||
TrueTypeFontData(const TrueTypeFontData &) = delete;
|
||||
TrueTypeFontData &operator=(const TrueTypeFontData &) = delete;
|
||||
};
|
||||
|
||||
// Forward declarations
|
||||
|
||||
@@ -40,9 +40,7 @@ class UniformAligner {
|
||||
{
|
||||
Assertion(sizeof(T) == _dataSize,
|
||||
"Sizes of template parameter and runtime size do not match! This probably uses the wrong type.");
|
||||
#if HAVE_STD_IS_TRIVIALLY_COPYABLE
|
||||
static_assert(std::is_trivially_copyable<T>::value, "Element type must be trivially copyable!");
|
||||
#endif
|
||||
|
||||
return reinterpret_cast<T*>(addElement());
|
||||
}
|
||||
@@ -50,9 +48,7 @@ class UniformAligner {
|
||||
template<typename THeader>
|
||||
THeader* getHeader() {
|
||||
Assertion(sizeof(THeader) == _headerSize, "Header size does not match requested header type!");
|
||||
#if HAVE_STD_IS_TRIVIALLY_COPYABLE
|
||||
static_assert(std::is_trivially_copyable<THeader>::value, "Header type must be trivially copyable!");
|
||||
#endif
|
||||
|
||||
return reinterpret_cast<THeader*>(_buffer);
|
||||
}
|
||||
@@ -63,9 +59,7 @@ class UniformAligner {
|
||||
T* getTypedElement(size_t index) {
|
||||
Assertion(sizeof(T) == _dataSize,
|
||||
"Sizes of template parameter and runtime size do not match! This probably uses the wrong type.");
|
||||
#if HAVE_STD_IS_TRIVIALLY_COPYABLE
|
||||
static_assert(std::is_trivially_copyable<T>::value, "Element type must be trivially copyable!");
|
||||
#endif
|
||||
|
||||
return reinterpret_cast<T*>(getElement(index));
|
||||
}
|
||||
@@ -82,9 +76,7 @@ class UniformAligner {
|
||||
T* nextTypedElement(T* currentEl) {
|
||||
Assertion(sizeof(T) == _dataSize,
|
||||
"Sizes of template parameter and runtime size do not match! This probably uses the wrong type.");
|
||||
#if HAVE_STD_IS_TRIVIALLY_COPYABLE
|
||||
static_assert(std::is_trivially_copyable<T>::value, "Element type must be trivially copyable!");
|
||||
#endif
|
||||
|
||||
return reinterpret_cast<T*>(nextElement(reinterpret_cast<void*>(currentEl)));
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ struct fog_data {
|
||||
vec3d fog_color;
|
||||
float fog_start;
|
||||
|
||||
float fog_scale;
|
||||
float fog_density;
|
||||
float zNear;
|
||||
float zFar;
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ bool stub_bm_data(int /*n*/, bitmap* /*bm*/) { return true; }
|
||||
|
||||
int stub_maybe_create_shader(shader_type /*shader_t*/, unsigned int /*flags*/) { return -1; }
|
||||
|
||||
void stub_shadow_map_start(matrix4* /*shadow_view_matrix*/, const matrix* /*light_matrix*/) {}
|
||||
void stub_shadow_map_start(matrix4* /*shadow_view_matrix*/, const matrix* /*light_matrix*/, vec3d* /*eye_pos*/) {}
|
||||
|
||||
void stub_shadow_map_end() {}
|
||||
|
||||
|
||||
Executable → Regular
+35
-24
@@ -23,6 +23,7 @@
|
||||
#include "object/object.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "sound/sound.h"
|
||||
#include "utils/Random.h"
|
||||
#include "weapon/beam.h"
|
||||
#include "weapon/weapon.h"
|
||||
|
||||
@@ -302,7 +303,7 @@ void ssm_create(object *target, vec3d *start, size_t ssm_index, ssm_firing_info
|
||||
|
||||
count = Ssm_info[ssm_index].count;
|
||||
if (Ssm_info[ssm_index].max_count != -1) {
|
||||
count += rand32(count, Ssm_info[ssm_index].max_count);
|
||||
count += Random::next(count, Ssm_info[ssm_index].max_count);
|
||||
}
|
||||
|
||||
// override in multiplayer
|
||||
@@ -399,35 +400,45 @@ void ssm_process()
|
||||
if(moveup->fireballs[idx] >= 0){
|
||||
if ((1.0f - fireball_lifeleft_percent(&Objects[moveup->fireballs[idx]])) >= moveup->sinfo.duration) {
|
||||
weapon_info *wip = &Weapon_info[si->weapon_info_index];
|
||||
|
||||
// get an orientation
|
||||
vec3d temp;
|
||||
matrix orient;
|
||||
|
||||
vm_vec_sub(&temp, &moveup->sinfo.target->pos, &moveup->sinfo.start_pos[idx]);
|
||||
vm_vec_normalize(&temp);
|
||||
vm_vector_2_matrix(&orient, &temp, nullptr, nullptr);
|
||||
|
||||
// are we a beam? -MageKing17
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Beam]) {
|
||||
beam_fire_info fire_info;
|
||||
memset(&fire_info, 0, sizeof(beam_fire_info));
|
||||
int num_beams = wip->b_info.beam_type == 5 && (int)wip->b_info.t5info.burst_rot_pattern.size() > 1 ?
|
||||
(int)wip->b_info.t5info.burst_rot_pattern.size() : 1;
|
||||
for (int i = 0; i < num_beams; i++) {
|
||||
beam_fire_info fire_info;
|
||||
memset(&fire_info, 0, sizeof(beam_fire_info));
|
||||
|
||||
fire_info.accuracy = 0.000001f; // this will guarantee a hit
|
||||
fire_info.shooter = NULL;
|
||||
fire_info.turret = NULL;
|
||||
fire_info.target = moveup->sinfo.target;
|
||||
fire_info.target_subsys = NULL;
|
||||
fire_info.bfi_flags |= BFIF_FLOATING_BEAM;
|
||||
fire_info.starting_pos = moveup->sinfo.start_pos[idx];
|
||||
fire_info.beam_info_index = si->weapon_info_index;
|
||||
fire_info.team = static_cast<char>(moveup->sinfo.ssm_team);
|
||||
fire_info.fire_method = BFM_SUBSPACE_STRIKE;
|
||||
fire_info.accuracy = 0.000001f; // this will guarantee a hit
|
||||
fire_info.shooter = nullptr;
|
||||
fire_info.turret = nullptr;
|
||||
fire_info.target = moveup->sinfo.target;
|
||||
fire_info.target_subsys = nullptr;
|
||||
fire_info.bfi_flags |= BFIF_FLOATING_BEAM;
|
||||
fire_info.starting_pos = moveup->sinfo.start_pos[idx];
|
||||
fire_info.beam_info_index = si->weapon_info_index;
|
||||
fire_info.team = static_cast<char>(moveup->sinfo.ssm_team);
|
||||
fire_info.fire_method = BFM_SUBSPACE_STRIKE;
|
||||
fire_info.burst_index = i;
|
||||
|
||||
// fire the beam
|
||||
beam_fire(&fire_info);
|
||||
// Fill target_pos but DONT turn on BFIF_TARGETING_COORDS
|
||||
// It would mess up normal beams but type 5s can still make use of this
|
||||
fire_info.target_pos1 = orient.vec.fvec + fire_info.starting_pos;
|
||||
|
||||
moveup->done_flags[idx] = true;
|
||||
// fire the beam
|
||||
beam_fire(&fire_info);
|
||||
|
||||
moveup->done_flags[idx] = true;
|
||||
}
|
||||
} else {
|
||||
// get an orientation
|
||||
vec3d temp;
|
||||
matrix orient;
|
||||
|
||||
vm_vec_sub(&temp, &moveup->sinfo.target->pos, &moveup->sinfo.start_pos[idx]);
|
||||
vm_vec_normalize(&temp);
|
||||
vm_vector_2_matrix(&orient, &temp, NULL, NULL);
|
||||
|
||||
// fire the missile and flash the screen
|
||||
weapon_objnum = weapon_create(&moveup->sinfo.start_pos[idx], &orient, si->weapon_info_index, -1, -1, 1);
|
||||
|
||||
|
||||
+329
-344
@@ -31,7 +31,7 @@
|
||||
#include "weapon/emp.h"
|
||||
|
||||
|
||||
int Show_escort_view;
|
||||
static int Show_escort_view;
|
||||
|
||||
typedef struct escort_info
|
||||
{
|
||||
@@ -45,14 +45,31 @@ typedef struct escort_info
|
||||
int escort_hit_timer;
|
||||
int escort_hit_next_flash;
|
||||
bool escort_show_bright;
|
||||
|
||||
escort_info() : objnum(-1), obj_signature(-1), priority(-1), np_id(-1),
|
||||
escort_hit_timer(0), escort_hit_next_flash(0), escort_show_bright(false) {}
|
||||
} escort_info;
|
||||
|
||||
escort_info Escort_ships[MAX_COMPLETE_ESCORT_LIST];
|
||||
int Num_escort_ships;
|
||||
int Max_escort_ships = 3;
|
||||
static SCP_list<escort_info> Escort_ships;
|
||||
int Max_escort_ships = 3;
|
||||
|
||||
static int Last_target_index; // index into Escort_gauges for last targeted via 'Next Escort Target' key
|
||||
|
||||
|
||||
static SCP_list<escort_info>::iterator get_escort_entry_from_index(int index)
|
||||
{
|
||||
int e_idx = 0;
|
||||
|
||||
auto it = Escort_ships.begin();
|
||||
|
||||
while ( (e_idx != index) && (it != Escort_ships.end()) ) {
|
||||
++it;
|
||||
++e_idx;
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
HudGaugeEscort::HudGaugeEscort():
|
||||
HudGauge(HUD_OBJECT_ESCORT, HUD_ESCORT_VIEW, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
|
||||
{
|
||||
@@ -153,6 +170,7 @@ int HudGaugeEscort::setGaugeColorEscort(int index, int team)
|
||||
int is_flashing = 0;
|
||||
int is_bright = 0;
|
||||
int seen_from_team = (Player_ship != NULL) ? Player_ship->team : -1;
|
||||
int objnum = -1;
|
||||
|
||||
// multiplayer dogfight
|
||||
if(MULTI_DOGFIGHT)
|
||||
@@ -160,19 +178,39 @@ int HudGaugeEscort::setGaugeColorEscort(int index, int team)
|
||||
setGaugeColor();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
auto eship = get_escort_entry_from_index(index);
|
||||
|
||||
if (eship == Escort_ships.end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( (Game_mode & GM_MULTIPLAYER) && (eship->np_id >= 0) ) {
|
||||
int np_index = find_player_index(eship->np_id);
|
||||
|
||||
if ( (np_index >= 0) && (Net_players[np_index].m_player != nullptr) ) {
|
||||
objnum = Net_players[np_index].m_player->objnum;
|
||||
}
|
||||
} else {
|
||||
objnum = eship->objnum;
|
||||
}
|
||||
|
||||
if (objnum < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// set flashing color
|
||||
if (!timestamp_elapsed(Escort_ships[index].escort_hit_timer))
|
||||
if (!timestamp_elapsed(eship->escort_hit_timer))
|
||||
{
|
||||
is_flashing = 1;
|
||||
if (Escort_ships[index].escort_show_bright)
|
||||
if (eship->escort_show_bright)
|
||||
{
|
||||
is_bright = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Goober5000 - now base this on team color
|
||||
gr_set_color_fast(iff_get_color_by_team_and_object(team, seen_from_team, is_bright, &Objects[Escort_ships[index].objnum]));
|
||||
gr_set_color_fast(iff_get_color_by_team_and_object(team, seen_from_team, is_bright, &Objects[objnum]));
|
||||
|
||||
|
||||
// Goober5000 - an alternative; same as original but incorporating teams for non-friendlies
|
||||
@@ -204,7 +242,7 @@ void HudGaugeEscort::render(float /*frametime*/)
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !Num_escort_ships ) {
|
||||
if ( Escort_ships.empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -219,12 +257,12 @@ void HudGaugeEscort::render(float /*frametime*/)
|
||||
int y = position[1] + list_start_offsets[1];
|
||||
|
||||
//This is temporary
|
||||
Num_escort_ships--;
|
||||
int num_escort_ships = std::min(hud_escort_num_ships_on_list(), Max_escort_ships) - 1;
|
||||
i=0;
|
||||
|
||||
if(Num_escort_ships)
|
||||
if(num_escort_ships > 0)
|
||||
{
|
||||
for(; i < Num_escort_ships; i++)
|
||||
for(; i < num_escort_ships; i++)
|
||||
{
|
||||
if(i != 0)
|
||||
{
|
||||
@@ -242,9 +280,6 @@ void HudGaugeEscort::render(float /*frametime*/)
|
||||
y += entry_h;
|
||||
}
|
||||
|
||||
//Back to right #
|
||||
Num_escort_ships++;
|
||||
|
||||
//Show the last escort entry
|
||||
renderBitmap(Escort_gauges[2].first_frame, x, y + bottom_bg_offset);
|
||||
renderIcon(x, y, i);
|
||||
@@ -260,10 +295,32 @@ void HudGaugeEscort::renderIcon(int x, int y, int index)
|
||||
}
|
||||
|
||||
float shields, integrity;
|
||||
int screen_integrity, offset;
|
||||
int screen_integrity, offset, objnum = -1;
|
||||
char buf[255];
|
||||
const player *pl = nullptr;
|
||||
|
||||
object *objp = &Objects[Escort_ships[index].objnum];
|
||||
auto eship = get_escort_entry_from_index(index);
|
||||
|
||||
if (eship == Escort_ships.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( (Game_mode & GM_MULTIPLAYER) && (eship->np_id >= 0) ) {
|
||||
int np_index = find_player_index(eship->np_id);
|
||||
|
||||
if (np_index >= 0) {
|
||||
pl = Net_players[np_index].m_player;
|
||||
objnum = pl->objnum;
|
||||
}
|
||||
} else {
|
||||
objnum = eship->objnum;
|
||||
}
|
||||
|
||||
if (objnum < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
object *objp = &Objects[objnum];
|
||||
ship *sp = &Ships[objp->instance];
|
||||
|
||||
// determine if its "friendly" or not
|
||||
@@ -284,6 +341,20 @@ void HudGaugeEscort::renderIcon(int x, int y, int index)
|
||||
|
||||
// print out ship name
|
||||
strcpy_s(buf, sp->get_display_name());
|
||||
|
||||
// maybe add callsign if multi and player ship
|
||||
if (pl) {
|
||||
SCP_string callsign;
|
||||
|
||||
callsign.reserve(32);
|
||||
|
||||
callsign += " (";
|
||||
callsign += pl->short_callsign;
|
||||
callsign += ")";
|
||||
|
||||
strcat_s(buf, callsign.c_str());
|
||||
}
|
||||
|
||||
const int w = font::force_fit_string(buf, 255, ship_name_max_width);
|
||||
|
||||
if (right_align_names) {
|
||||
@@ -323,8 +394,15 @@ void HudGaugeEscort::renderIconDogfight(int x, int y, int index)
|
||||
// always use the standard color to avoid confusion
|
||||
setGaugeColor();
|
||||
|
||||
auto eship = get_escort_entry_from_index(index);
|
||||
|
||||
if (eship == Escort_ships.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// netplayer index
|
||||
np_index = find_player_id(Escort_ships[index].np_id);
|
||||
np_index = find_player_index(eship->np_id);
|
||||
|
||||
if((np_index < 0) || (np_index >= MAX_PLAYERS) || (Net_players[np_index].m_player == NULL)){
|
||||
return;
|
||||
}
|
||||
@@ -360,17 +438,11 @@ void HudGaugeEscort::renderIconDogfight(int x, int y, int index)
|
||||
|
||||
void hud_escort_update_list()
|
||||
{
|
||||
if(Num_escort_ships)
|
||||
{
|
||||
for(int i = 0; i < Num_escort_ships; i++)
|
||||
{
|
||||
if (!timestamp_elapsed(Escort_ships[i].escort_hit_timer))
|
||||
{
|
||||
if (timestamp_elapsed(Escort_ships[i].escort_hit_next_flash))
|
||||
{
|
||||
Escort_ships[i].escort_hit_next_flash = timestamp(SHIELD_FLASH_INTERVAL);
|
||||
Escort_ships[i].escort_show_bright = !Escort_ships[i].escort_show_bright; // toggle between default and bright frames
|
||||
}
|
||||
for (auto &es : Escort_ships) {
|
||||
if ( !timestamp_elapsed(es.escort_hit_timer) ) {
|
||||
if (timestamp_elapsed(es.escort_hit_next_flash)) {
|
||||
es.escort_hit_next_flash = timestamp(SHIELD_FLASH_INTERVAL);
|
||||
es.escort_show_bright = !es.escort_show_bright; // toggle between default and bright frames
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,99 +463,75 @@ void hud_escort_init()
|
||||
//
|
||||
void hud_escort_clear_all(bool clear_flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
Num_escort_ships = 0;
|
||||
for ( i = 0; i < Max_escort_ships; i++ ) {
|
||||
if(clear_flags && (Escort_ships[i].objnum >= 0) && (Objects[Escort_ships[i].objnum].type == OBJ_SHIP) && (Objects[Escort_ships[i].objnum].instance >= 0)){
|
||||
Ships[Objects[Escort_ships[i].objnum].instance].flags.remove(Ship::Ship_Flags::Escort);
|
||||
if (clear_flags) {
|
||||
for (auto &es : Escort_ships) {
|
||||
if ( (es.objnum >= 0) && (Objects[es.objnum].type == OBJ_SHIP) && (Objects[es.objnum].instance >= 0) ) {
|
||||
Ships[Objects[es.objnum].instance].flags.remove(Ship::Ship_Flags::Escort);
|
||||
}
|
||||
}
|
||||
Escort_ships[i].obj_signature = -99;
|
||||
Escort_ships[i].np_id = -1;
|
||||
Escort_ships[i].escort_hit_timer = 0;
|
||||
Escort_ships[i].escort_hit_next_flash = 0;
|
||||
Escort_ships[i].escort_show_bright = false;
|
||||
}
|
||||
|
||||
Escort_ships.clear();
|
||||
}
|
||||
|
||||
// internal helper function for sort.
|
||||
// sorts first by priority number and then alphabetically
|
||||
int escort_compare_func(const void *e1, const void *e2)
|
||||
static bool escort_compare(const escort_info &escort1, const escort_info &escort2)
|
||||
{
|
||||
escort_info *escort1, *escort2;
|
||||
int diff;
|
||||
int ret;
|
||||
if (MULTI_DOGFIGHT) {
|
||||
int n1 = find_player_index(escort1.np_id);
|
||||
int n2 = find_player_index(escort2.np_id);
|
||||
|
||||
escort1 = (escort_info*) e1;
|
||||
escort2 = (escort_info*) e2;
|
||||
if ( (n1 < 0) || (n2 < 0) || (Net_players[n1].m_player == nullptr) || (Net_players[n2].m_player == nullptr) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// multiplayer dogfight
|
||||
if(MULTI_DOGFIGHT){
|
||||
int n1, n2;
|
||||
|
||||
n1 = find_player_id(escort1->np_id);
|
||||
n2 = find_player_id(escort2->np_id);
|
||||
if((n1 < 0) || (n2 < 0) || (Net_players[n1].m_player == NULL) || (Net_players[n2].m_player == NULL)){
|
||||
ret = 0;
|
||||
} else {
|
||||
// player 1 is higher than player 2
|
||||
if(Net_players[n1].m_player->stats.m_kill_count_ok >= Net_players[n2].m_player->stats.m_kill_count_ok){
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = 1;
|
||||
return (Net_players[n1].m_player->stats.m_kill_count_ok >= Net_players[n2].m_player->stats.m_kill_count_ok);
|
||||
} else {
|
||||
// in multi, players go to the top
|
||||
if (Game_mode & GM_MULTIPLAYER) {
|
||||
if ( (escort1.np_id >= 0) && (escort2.np_id < 0) ) {
|
||||
return true;
|
||||
} else if ( (escort1.np_id < 0) && (escort2.np_id >= 0) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
diff = escort2->priority - escort1->priority;
|
||||
|
||||
if (diff != 0) {
|
||||
ret = diff;
|
||||
} else {
|
||||
char *name1, *name2;
|
||||
name1 = Ships[Objects[escort1->objnum].instance].ship_name;
|
||||
name2 = Ships[Objects[escort2->objnum].instance].ship_name;
|
||||
if (escort1.priority != escort2.priority) {
|
||||
return (escort1.priority > escort2.priority);
|
||||
} else if ( (escort1.objnum >= 0) && (Objects[escort1.objnum].instance >= 0) &&
|
||||
(escort2.objnum >= 0) && (Objects[escort2.objnum].instance >= 0) )
|
||||
{
|
||||
const char *name1 = Ships[Objects[escort1.objnum].instance].ship_name;
|
||||
const char *name2 = Ships[Objects[escort2.objnum].instance].ship_name;
|
||||
|
||||
ret = stricmp(name1, name2);
|
||||
return (stricmp(name1, name2) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
// create complete priority sorted escort list for all active ships
|
||||
// escorts - array of escort info
|
||||
// num_escorts - number of escorts requests in field of active ships
|
||||
// This will be culled to MAX_ESCORTS, selecting the top set from escorts
|
||||
void hud_create_complete_escort_list(escort_info *escorts, int *num_escorts)
|
||||
void hud_create_complete_escort_list()
|
||||
{
|
||||
ship_obj *so;
|
||||
object *objp;
|
||||
|
||||
// start with none on list
|
||||
*num_escorts = 0;
|
||||
|
||||
object *objp;
|
||||
int idx;
|
||||
|
||||
// multiplayer dogfight
|
||||
if(MULTI_DOGFIGHT){
|
||||
for(idx=0; idx<MAX_PLAYERS; idx++){
|
||||
// break out of the loop when we have reached our max
|
||||
if ( *num_escorts == MAX_COMPLETE_ESCORT_LIST ) {
|
||||
mprintf(("exceeded max ships in big escort list\n"));
|
||||
break;
|
||||
}
|
||||
|
||||
// is this a valid player
|
||||
if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
|
||||
if (MULTI_DOGFIGHT) {
|
||||
for (idx = 0; idx < MAX_PLAYERS; idx++) {
|
||||
// is this a valid player
|
||||
if ( MULTI_CONNECTED(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) ) {
|
||||
// add the ship
|
||||
escorts[*num_escorts].objnum = -1;
|
||||
escorts[*num_escorts].obj_signature = -1;
|
||||
escorts[*num_escorts].priority = -1;
|
||||
escorts[*num_escorts].np_id = Net_players[idx].player_id;
|
||||
escorts[*num_escorts].escort_hit_timer = 0;
|
||||
escorts[*num_escorts].escort_hit_next_flash = 0;
|
||||
escorts[*num_escorts].escort_show_bright = false;
|
||||
(*num_escorts)++;
|
||||
escort_info einfo;
|
||||
einfo.np_id = Net_players[idx].player_id;
|
||||
|
||||
Escort_ships.push_back(einfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -500,19 +548,13 @@ void hud_create_complete_escort_list(escort_info *escorts, int *num_escorts)
|
||||
continue;
|
||||
}
|
||||
|
||||
// break out of the loop when we have reached our max
|
||||
if ( *num_escorts == MAX_COMPLETE_ESCORT_LIST ) {
|
||||
mprintf(("exceeded max ships in big escort list\n"));
|
||||
break;
|
||||
}
|
||||
|
||||
// only process ships that might be on the list
|
||||
if ( !(Ships[objp->instance].flags[Ship::Ship_Flags::Escort]) ){
|
||||
if ( !(Ships[objp->instance].flags[Ship::Ship_Flags::Escort]) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only process ships that can be seen by sensors
|
||||
if ( (Ships[objp->instance].flags[Ship::Ship_Flags::Hidden_from_sensors]) ){
|
||||
if ( (Ships[objp->instance].flags[Ship::Ship_Flags::Hidden_from_sensors]) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -540,14 +582,12 @@ void hud_create_complete_escort_list(escort_info *escorts, int *num_escorts)
|
||||
}
|
||||
|
||||
// add the ship
|
||||
escorts[*num_escorts].objnum = so->objnum;
|
||||
escorts[*num_escorts].obj_signature = objp->signature;
|
||||
escorts[*num_escorts].priority = Ships[objp->instance].escort_priority;
|
||||
escorts[*num_escorts].np_id = -1;
|
||||
escorts[*num_escorts].escort_hit_timer = 0;
|
||||
escorts[*num_escorts].escort_hit_next_flash = 0;
|
||||
escorts[*num_escorts].escort_show_bright = false;
|
||||
(*num_escorts)++;
|
||||
escort_info einfo;
|
||||
einfo.objnum = so->objnum;
|
||||
einfo.obj_signature = objp->signature;
|
||||
einfo.priority = Ships[objp->instance].escort_priority;
|
||||
|
||||
Escort_ships.push_back(einfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -556,36 +596,26 @@ void hud_create_complete_escort_list(escort_info *escorts, int *num_escorts)
|
||||
// ----------------------------------------------------------------------
|
||||
// hud_init_escort_info()
|
||||
//
|
||||
// Set up the escort list
|
||||
// Set up the initial escort list
|
||||
//
|
||||
void hud_setup_escort_list(int level)
|
||||
{
|
||||
int num_escorts, num_complete_escorts;
|
||||
escort_info complete_escorts[MAX_COMPLETE_ESCORT_LIST];
|
||||
|
||||
hud_escort_clear_all();
|
||||
|
||||
// get complete escort list
|
||||
hud_create_complete_escort_list(complete_escorts, &num_complete_escorts);
|
||||
|
||||
// sort escort list by priority
|
||||
insertion_sort(complete_escorts, num_complete_escorts, sizeof(escort_info), escort_compare_func);
|
||||
|
||||
// set number in escort list
|
||||
num_escorts = num_complete_escorts;
|
||||
if (num_escorts > Max_escort_ships) {
|
||||
num_escorts = Max_escort_ships;
|
||||
// this should never be called while in a mission
|
||||
if (Game_mode & GM_IN_MISSION) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add ships to escort list
|
||||
for (Num_escort_ships=0; Num_escort_ships<num_escorts; Num_escort_ships++) {
|
||||
Escort_ships[Num_escort_ships].obj_signature = complete_escorts[Num_escort_ships].obj_signature;
|
||||
Escort_ships[Num_escort_ships].priority = complete_escorts[Num_escort_ships].priority;
|
||||
Escort_ships[Num_escort_ships].objnum = complete_escorts[Num_escort_ships].objnum;
|
||||
Escort_ships[Num_escort_ships].np_id = complete_escorts[Num_escort_ships].np_id;
|
||||
Escort_ships[Num_escort_ships].escort_hit_timer = 0;
|
||||
Escort_ships[Num_escort_ships].escort_hit_next_flash = 0;
|
||||
Escort_ships[Num_escort_ships].escort_show_bright = false;
|
||||
Escort_ships.clear();
|
||||
|
||||
// build escort list
|
||||
hud_create_complete_escort_list();
|
||||
|
||||
// and sort it by priority
|
||||
Escort_ships.sort(escort_compare);
|
||||
|
||||
// then resize the list to fit max
|
||||
if (hud_escort_num_ships_on_list() > MAX_COMPLETE_ESCORT_LIST) {
|
||||
Escort_ships.resize(MAX_COMPLETE_ESCORT_LIST);
|
||||
}
|
||||
|
||||
if(level){
|
||||
@@ -593,151 +623,81 @@ void hud_setup_escort_list(int level)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// combine complete escort list with Escort_ships, keeping valid hit info
|
||||
void merge_escort_lists(escort_info *complete_escorts, int num_complete_escorts)
|
||||
// re-sort the escort list
|
||||
void hud_resort_escort_list()
|
||||
{
|
||||
int i, j, top_complete_escorts;
|
||||
int valid_hit_info[MAX_COMPLETE_ESCORT_LIST];
|
||||
|
||||
// may be > 1 ship change to list (ie, 2 or 3 culled during same frame)
|
||||
// set Num_escort_ships and cap
|
||||
Num_escort_ships = num_complete_escorts;
|
||||
if (Num_escort_ships > Max_escort_ships) {
|
||||
Num_escort_ships = Max_escort_ships;
|
||||
}
|
||||
|
||||
// nothing to do
|
||||
if (Num_escort_ships == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check used as a flag whether top slots in complete_escorts were copied
|
||||
// this is important re. hit info
|
||||
for (i=0; i<Max_escort_ships; i++) {
|
||||
valid_hit_info[i] = 0;
|
||||
}
|
||||
|
||||
// get the top slots in complete escort list that will be copied onto Escort_ships
|
||||
top_complete_escorts = num_complete_escorts;
|
||||
if (top_complete_escorts > Max_escort_ships) {
|
||||
top_complete_escorts = Max_escort_ships;
|
||||
}
|
||||
|
||||
// copy for Escort_ships to complete_escorts to retain hit_info
|
||||
if(!MULTI_DOGFIGHT) {
|
||||
for (i=0; i<top_complete_escorts; i++) {
|
||||
for (j=0; j<Num_escort_ships; j++) {
|
||||
if (Escort_ships[j].obj_signature == complete_escorts[i].obj_signature) {
|
||||
complete_escorts[i] = Escort_ships[j];
|
||||
valid_hit_info[i] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy top slots to Escort_ships
|
||||
for (i=0; i<top_complete_escorts; i++) {
|
||||
Escort_ships[i] = complete_escorts[i];
|
||||
// check all ships are valid
|
||||
int objnum = Escort_ships[i].objnum;
|
||||
Assert( objnum >=0 && objnum < MAX_OBJECTS );
|
||||
if((objnum < 0) || (objnum >= MAX_OBJECTS)){
|
||||
continue;
|
||||
}
|
||||
if ( !valid_hit_info[i] ) {
|
||||
Escort_ships[i].escort_hit_timer = 0;
|
||||
Escort_ships[i].escort_hit_next_flash = 0;
|
||||
Escort_ships[i].escort_show_bright = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset Num_escort_ships
|
||||
Num_escort_ships = top_complete_escorts;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// hud_remove_ship_from_escort_index()
|
||||
//
|
||||
// Take a ship out of the escort list
|
||||
void hud_remove_ship_from_escort_index(int dead_index, int objnum)
|
||||
{
|
||||
int i, count, num_complete_escorts;
|
||||
escort_info bakup_arr[MAX_COMPLETE_ESCORT_LIST], complete_escorts[MAX_COMPLETE_ESCORT_LIST];
|
||||
|
||||
// remove him from escort list
|
||||
if((objnum >= 0) && (Objects[objnum].type == OBJ_SHIP) && (Objects[objnum].instance >= 0)){
|
||||
Ships[Objects[objnum].instance].flags.remove(Ship::Ship_Flags::Escort);
|
||||
}
|
||||
|
||||
count = 0;
|
||||
for ( i = 0; i < Num_escort_ships; i++ ) {
|
||||
if ( i != dead_index ) {
|
||||
bakup_arr[count++] = Escort_ships[i];
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0; i < count; i++ ) {
|
||||
Escort_ships[i] = bakup_arr[i];
|
||||
}
|
||||
|
||||
Num_escort_ships--;
|
||||
Assert(Num_escort_ships >= 0);
|
||||
|
||||
// get complete escort list
|
||||
hud_create_complete_escort_list(complete_escorts, &num_complete_escorts);
|
||||
|
||||
// sort escort list by priority
|
||||
insertion_sort(complete_escorts, num_complete_escorts, sizeof(escort_info), escort_compare_func);
|
||||
|
||||
// merge list
|
||||
merge_escort_lists(complete_escorts, num_complete_escorts);
|
||||
|
||||
hud_gauge_popup_start(HUD_ESCORT_VIEW);
|
||||
|
||||
Escort_ships.sort(escort_compare);
|
||||
}
|
||||
|
||||
// called once per frame to refresh the escort list if important flags changed
|
||||
void hud_escort_cull_list()
|
||||
{
|
||||
int i;
|
||||
if ( Escort_ships.empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
int np_index;
|
||||
auto it = Escort_ships.begin();
|
||||
|
||||
// multiplayer dogfight
|
||||
if(MULTI_DOGFIGHT){
|
||||
for ( i = 0; i < Num_escort_ships; i++ ) {
|
||||
np_index = find_player_id(Escort_ships[i].np_id);
|
||||
|
||||
while (it != Escort_ships.end()) {
|
||||
np_index = find_player_index(it->np_id);
|
||||
|
||||
// maybe remove him if he left
|
||||
if ( np_index < 0 ) {
|
||||
hud_setup_escort_list(0);
|
||||
break;
|
||||
if (np_index < 0) {
|
||||
it = Escort_ships.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// everything else
|
||||
else {
|
||||
for ( i = 0; i < Num_escort_ships; i++ ) {
|
||||
int objnum = Escort_ships[i].objnum;
|
||||
Assert( objnum >=0 && objnum < MAX_OBJECTS );
|
||||
int objnum;
|
||||
bool remove;
|
||||
|
||||
if ( Objects[objnum].flags[Object::Object_Flags::Should_be_dead] ) {
|
||||
hud_setup_escort_list(0);
|
||||
break;
|
||||
} else if ( Objects[objnum].type == OBJ_SHIP ) {
|
||||
while (it != Escort_ships.end()) {
|
||||
remove = false;
|
||||
objnum = it->objnum;
|
||||
|
||||
// player ships should stay so long as they are valid
|
||||
if ( (Game_mode & GM_MULTIPLAYER) && (it->np_id >= 0) ) {
|
||||
np_index = find_player_index(it->np_id);
|
||||
|
||||
// maybe remove him if he left
|
||||
if (np_index < 0) {
|
||||
remove = true;
|
||||
}
|
||||
} else if (Objects[objnum].flags[Object::Object_Flags::Should_be_dead]) {
|
||||
remove = true;
|
||||
} else if (Objects[objnum].type == OBJ_SHIP) {
|
||||
int shipnum = Objects[objnum].instance;
|
||||
Assert( shipnum >= 0 && shipnum < MAX_SHIPS );
|
||||
|
||||
if ( (Ships[shipnum].flags[Ship::Ship_Flags::Hidden_from_sensors])
|
||||
|| ((Ships[shipnum].flags[Ship::Ship_Flags::Stealth]) && ((Ships[shipnum].team != Player_ship->team) || (Ships[shipnum].flags[Ship::Ship_Flags::Friendly_stealth_invis])))
|
||||
) {
|
||||
hud_setup_escort_list(0);
|
||||
break;
|
||||
if ( !(Ships[shipnum].flags[Ship::Ship_Flags::Escort]) ) {
|
||||
remove = true;
|
||||
} else if (Ships[shipnum].flags[Ship::Ship_Flags::Hidden_from_sensors]) {
|
||||
remove = true;
|
||||
} else if (Ships[shipnum].flags[Ship::Ship_Flags::Stealth]) {
|
||||
if (Ships[shipnum].team != Player_ship->team) {
|
||||
// friendly stealths are only not seen when explicitly specified
|
||||
if ( Ships[shipnum].flags[Ship::Ship_Flags::Friendly_stealth_invis]) {
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
// non-friendly stealths are never seen
|
||||
else {
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
it = Escort_ships.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -758,62 +718,68 @@ void hud_escort_view_toggle()
|
||||
// try to add a ship to the escort list, if slot available
|
||||
void hud_add_ship_to_escort(int objnum, int supress_feedback)
|
||||
{
|
||||
escort_info complete_escorts[MAX_COMPLETE_ESCORT_LIST];
|
||||
int num_complete_escorts, idx, found;
|
||||
// no ships on the escort list in multiplayer dogfight
|
||||
if (MULTI_DOGFIGHT) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get complete escort list
|
||||
hud_create_complete_escort_list(complete_escorts, &num_complete_escorts);
|
||||
if (objnum < 0) {
|
||||
UNREACHABLE("Invalid objnum passed to hud_add_ship_to_escort()!");
|
||||
return;
|
||||
}
|
||||
|
||||
// ensure the complete escort list is not full already
|
||||
if (num_complete_escorts == MAX_COMPLETE_ESCORT_LIST)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Objects[objnum].type != OBJ_SHIP) {
|
||||
if ( !supress_feedback ) {
|
||||
snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// check if ship is already on complete escort list
|
||||
found = 0;
|
||||
for (idx=0; idx<num_complete_escorts; idx++) {
|
||||
if (complete_escorts[idx].obj_signature == Objects[objnum].signature) {
|
||||
found = 1;
|
||||
bool found = false;
|
||||
|
||||
for (auto &es : Escort_ships) {
|
||||
if (es.obj_signature == Objects[objnum].signature) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add new ship into complete list
|
||||
if ( !found ) {
|
||||
complete_escorts[num_complete_escorts].objnum = objnum;
|
||||
complete_escorts[num_complete_escorts].obj_signature = Objects[objnum].signature;
|
||||
complete_escorts[num_complete_escorts].priority = Ships[Objects[objnum].instance].escort_priority;
|
||||
complete_escorts[num_complete_escorts].escort_hit_timer = 0;
|
||||
complete_escorts[num_complete_escorts].escort_hit_next_flash = 0;
|
||||
complete_escorts[num_complete_escorts].escort_show_bright = false;
|
||||
escort_info einfo;
|
||||
|
||||
// add him to escort list
|
||||
Ships[Objects[objnum].instance].flags.set(Ship::Ship_Flags::Escort);
|
||||
einfo.objnum = objnum;
|
||||
einfo.obj_signature = Objects[objnum].signature;
|
||||
einfo.priority = Ships[Objects[objnum].instance].escort_priority;
|
||||
|
||||
num_complete_escorts++;
|
||||
// add him to escort list and re-sort
|
||||
Escort_ships.push_back(einfo);
|
||||
Escort_ships.sort(escort_compare);
|
||||
|
||||
Ships[Objects[objnum].instance].flags.set(Ship::Ship_Flags::Escort);
|
||||
}
|
||||
|
||||
// sort escort list by priority
|
||||
insertion_sort(complete_escorts, num_complete_escorts, sizeof(escort_info), escort_compare_func);
|
||||
if (hud_escort_num_ships_on_list() > MAX_COMPLETE_ESCORT_LIST) {
|
||||
Escort_ships.resize(MAX_COMPLETE_ESCORT_LIST);
|
||||
|
||||
// merge list
|
||||
merge_escort_lists(complete_escorts, num_complete_escorts);
|
||||
// maybe do feedback
|
||||
if ( !supress_feedback ) {
|
||||
found = false;
|
||||
|
||||
// maybe do feedback
|
||||
if ( (Num_escort_ships == Max_escort_ships) && !supress_feedback) {
|
||||
found = 0;
|
||||
// search thru list for objnum
|
||||
for (idx=0; idx<Num_escort_ships; idx++) {
|
||||
if (Escort_ships[idx].objnum == objnum) {
|
||||
found = 1;
|
||||
break;
|
||||
// search thru list for objnum
|
||||
for (auto &es : Escort_ships) {
|
||||
if (es.objnum == objnum) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Escort list is full with %d ships", 288), Num_escort_ships);
|
||||
snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
|
||||
if ( !found ) {
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Escort list is full with %d ships", 288), hud_escort_num_ships_on_list());
|
||||
snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -826,8 +792,6 @@ void hud_add_ship_to_escort(int objnum, int supress_feedback)
|
||||
//
|
||||
void hud_add_remove_ship_escort(int objnum, int supress_feedback)
|
||||
{
|
||||
int in_escort, i;
|
||||
|
||||
// no ships on the escort list in multiplayer dogfight
|
||||
if(MULTI_DOGFIGHT){
|
||||
return;
|
||||
@@ -845,26 +809,27 @@ void hud_add_remove_ship_escort(int objnum, int supress_feedback)
|
||||
return;
|
||||
}
|
||||
|
||||
in_escort = 0;
|
||||
for ( i = 0; i < Num_escort_ships; i++ ) {
|
||||
if ( Escort_ships[i].obj_signature == Objects[objnum].signature ) {
|
||||
in_escort = 1;
|
||||
break;
|
||||
// if it's in the list already just remove it
|
||||
for (auto it = Escort_ships.begin(); it != Escort_ships.end(); ++it) {
|
||||
if (it->obj_signature == Objects[objnum].signature) {
|
||||
if ( (Objects[objnum].type == OBJ_SHIP) && (Objects[objnum].instance >= 0) ) {
|
||||
Ships[Objects[objnum].instance].flags.remove(Ship::Ship_Flags::Escort);
|
||||
}
|
||||
|
||||
Escort_ships.erase(it);
|
||||
|
||||
hud_gauge_popup_start(HUD_ESCORT_VIEW);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( in_escort ) {
|
||||
hud_remove_ship_from_escort_index(i, objnum);
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise add ship to list
|
||||
hud_add_ship_to_escort(objnum, supress_feedback);
|
||||
}
|
||||
|
||||
void hud_remove_ship_from_escort(int objnum)
|
||||
{
|
||||
int in_escort, i;
|
||||
|
||||
// no ships on the escort list in multiplayer dogfight
|
||||
if(MULTI_DOGFIGHT){
|
||||
return;
|
||||
@@ -875,18 +840,19 @@ void hud_remove_ship_from_escort(int objnum)
|
||||
return;
|
||||
}
|
||||
|
||||
in_escort = 0;
|
||||
for ( i = 0; i < Num_escort_ships; i++ ) {
|
||||
if ( Escort_ships[i].obj_signature == Objects[objnum].signature ) {
|
||||
in_escort = 1;
|
||||
for (auto it = Escort_ships.begin(); it != Escort_ships.end(); ++it) {
|
||||
if (it->obj_signature == Objects[objnum].signature) {
|
||||
if ( (Objects[objnum].type == OBJ_SHIP) && (Objects[objnum].instance >= 0) ) {
|
||||
Ships[Objects[objnum].instance].flags.remove(Ship::Ship_Flags::Escort);
|
||||
}
|
||||
|
||||
Escort_ships.erase(it);
|
||||
|
||||
hud_gauge_popup_start(HUD_ESCORT_VIEW);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( in_escort ) {
|
||||
hud_remove_ship_from_escort_index(i, objnum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -903,11 +869,14 @@ void hud_escort_ship_hit(object *objp, int /*quadrant*/)
|
||||
return;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < Num_escort_ships; i++ ) {
|
||||
if ( Escort_ships[i].objnum == OBJ_INDEX(objp) ) {
|
||||
int objnum = OBJ_INDEX(objp);
|
||||
|
||||
for (auto &es : Escort_ships) {
|
||||
if (es.objnum == objnum) {
|
||||
hud_gauge_popup_start(HUD_ESCORT_VIEW);
|
||||
Escort_ships[i].escort_hit_timer = timestamp(SHIELD_HIT_DURATION);
|
||||
Escort_ships[i].escort_hit_next_flash = timestamp(SHIELD_FLASH_INTERVAL);
|
||||
es.escort_hit_timer = timestamp(SHIELD_HIT_DURATION);
|
||||
es.escort_hit_next_flash = timestamp(SHIELD_FLASH_INTERVAL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -917,17 +886,24 @@ void hud_escort_target_next()
|
||||
{
|
||||
int objnum;
|
||||
|
||||
if ( Num_escort_ships == 0 ) {
|
||||
if ( Escort_ships.empty() ) {
|
||||
snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL), 0.0f );
|
||||
return;
|
||||
}
|
||||
|
||||
Last_target_index++;
|
||||
if ( Last_target_index >= Num_escort_ships ) {
|
||||
if ( Last_target_index >= hud_escort_num_ships_on_list() ) {
|
||||
Last_target_index = 0;
|
||||
}
|
||||
|
||||
objnum = Escort_ships[Last_target_index].objnum;
|
||||
auto eship = get_escort_entry_from_index(Last_target_index);
|
||||
|
||||
if ( (eship == Escort_ships.end()) || (eship->objnum < 0) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
objnum = eship->objnum;
|
||||
|
||||
set_target_objnum( Player_ai, objnum);
|
||||
hud_restore_subsystem_target(&Ships[Objects[objnum].instance]);
|
||||
}
|
||||
@@ -935,30 +911,33 @@ void hud_escort_target_next()
|
||||
// return the number of ships currently on the escort list
|
||||
int hud_escort_num_ships_on_list()
|
||||
{
|
||||
return Num_escort_ships;
|
||||
return static_cast<int>(Escort_ships.size());
|
||||
}
|
||||
|
||||
// Return the object number for the ship at index position in the escort list
|
||||
int hud_escort_return_objnum(int index)
|
||||
{
|
||||
int escort_objnum, escort_sig;
|
||||
if ( index >= Num_escort_ships || index >= MAX_COMPLETE_ESCORT_LIST) {
|
||||
|
||||
if (index < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
escort_objnum = Escort_ships[index].objnum;
|
||||
escort_sig = Escort_ships[index].obj_signature;
|
||||
auto eship = get_escort_entry_from_index(index);
|
||||
|
||||
if ( escort_objnum < 0 ) {
|
||||
if ( (eship == Escort_ships.end()) || (eship->objnum < 0) ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
escort_objnum = eship->objnum;
|
||||
escort_sig = eship->obj_signature;
|
||||
|
||||
// ensure this is still a valid index
|
||||
if ( Objects[escort_objnum].signature != escort_sig ) {
|
||||
if (Objects[escort_objnum].signature != escort_sig) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return Escort_ships[index].objnum;
|
||||
return escort_objnum;
|
||||
}
|
||||
|
||||
void hud_escort_add_player(short id)
|
||||
@@ -968,17 +947,24 @@ void hud_escort_add_player(short id)
|
||||
return;
|
||||
}
|
||||
|
||||
int idx;
|
||||
|
||||
// just go through and add as long as its not a duplicate
|
||||
for(idx=0; idx<Num_escort_ships; idx++){
|
||||
if(Escort_ships[idx].np_id == id){
|
||||
for (auto it = Escort_ships.begin(); it != Escort_ships.end(); ++it) {
|
||||
if (it->np_id == id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// re-setup the escort list
|
||||
hud_setup_escort_list(0);
|
||||
escort_info einfo;
|
||||
|
||||
einfo.np_id = id;
|
||||
|
||||
Escort_ships.push_back(einfo);
|
||||
Escort_ships.sort(escort_compare);
|
||||
|
||||
// resize the list to fit
|
||||
if (hud_escort_num_ships_on_list() > MAX_COMPLETE_ESCORT_LIST) {
|
||||
Escort_ships.resize(MAX_COMPLETE_ESCORT_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
void hud_escort_remove_player(short id)
|
||||
@@ -988,12 +974,11 @@ void hud_escort_remove_player(short id)
|
||||
return;
|
||||
}
|
||||
|
||||
int idx;
|
||||
|
||||
// find the instance and remove it if possible
|
||||
for(idx=0; idx<Num_escort_ships; idx++){
|
||||
if(Escort_ships[idx].np_id == id){
|
||||
hud_remove_ship_from_escort_index(idx, -1);
|
||||
for (auto it = Escort_ships.begin(); it != Escort_ships.end(); ++it) {
|
||||
if (it->np_id == id) {
|
||||
Escort_ships.erase(it);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ class object;
|
||||
void hud_escort_update_list();
|
||||
void hud_escort_init();
|
||||
void hud_setup_escort_list(int level = 1);
|
||||
void hud_resort_escort_list();
|
||||
void hud_add_remove_ship_escort(int objnum, int supress_feedback = 0);
|
||||
void hud_escort_clear_all(bool clear_flags = false);
|
||||
void hud_escort_ship_hit(object *objp, int quadrant);
|
||||
|
||||
@@ -103,6 +103,9 @@ void update_ets(object* objp, float fl_frametime)
|
||||
shield_delta = Energy_levels[ship_p->shield_recharge_index] * max_new_shield_energy;
|
||||
}
|
||||
|
||||
if (Missiontime - Ai_info[ship_p->ai_index].last_hit_time < fl2f(sinfo_p->shield_regen_hit_delay))
|
||||
shield_delta = 0.0f;
|
||||
|
||||
shield_add_strength(objp, shield_delta);
|
||||
|
||||
// if strength now exceeds max, scale back segments proportionally
|
||||
|
||||
@@ -55,7 +55,13 @@
|
||||
#define HUD_SUPPORT_GAUGE 37
|
||||
#define HUD_LAG_GAUGE 38
|
||||
|
||||
extern const char *HUD_gauge_text[NUM_HUD_GAUGES]; // defined in sexp.cpp!!!!
|
||||
struct Legacy_HUD_gauge_pair
|
||||
{
|
||||
const char *hud_gauge_text;
|
||||
int hud_gauge_type;
|
||||
};
|
||||
|
||||
extern Legacy_HUD_gauge_pair Legacy_HUD_gauges[NUM_HUD_GAUGES]; // defined in hudparse.cpp!!!!
|
||||
|
||||
|
||||
#endif /* __HUD_COMMON_H__ */
|
||||
+37
-112
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "ai/ai.h"
|
||||
#include "debugconsole/console.h"
|
||||
#include "gamesequence/gamesequence.h"
|
||||
#include "gamesnd/gamesnd.h"
|
||||
#include "globalincs/linklist.h"
|
||||
#include "hud/hudlock.h"
|
||||
@@ -404,46 +405,10 @@ int hud_lock_has_homing_point()
|
||||
int Nebula_sec_range = 0;
|
||||
DCF_BOOL(nebula_sec_range, Nebula_sec_range);
|
||||
|
||||
int hud_lock_world_pos_in_range(vec3d *target_world_pos, vec3d *vec_to_target)
|
||||
{
|
||||
float dist_to_target, weapon_range;
|
||||
weapon_info *wip;
|
||||
ship_weapon *swp;
|
||||
|
||||
int target_in_range=1;
|
||||
|
||||
swp = &Player_ship->weapons;
|
||||
wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
|
||||
|
||||
vm_vec_sub(vec_to_target, target_world_pos, &Player_obj->pos);
|
||||
dist_to_target = vm_vec_mag(vec_to_target);
|
||||
|
||||
//local ssms are always in range :)
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Local_ssm])
|
||||
weapon_range=wip->lssm_lock_range;
|
||||
else
|
||||
// calculate the range of the weapon, and only display the lead target indicator when
|
||||
// if the weapon can actually hit the target
|
||||
weapon_range = MIN((wip->max_speed * wip->lifetime), wip->weapon_range);
|
||||
|
||||
|
||||
|
||||
// reduce firing range in nebula
|
||||
if ((The_mission.flags[Mission::Mission_Flags::Fullneb]) && Nebula_sec_range) {
|
||||
weapon_range *= 0.8f;
|
||||
}
|
||||
|
||||
if (dist_to_target > weapon_range) {
|
||||
target_in_range=0;
|
||||
}
|
||||
|
||||
return target_in_range;
|
||||
}
|
||||
|
||||
// Determine if point to lock on is in range
|
||||
int hud_lock_target_in_range()
|
||||
{
|
||||
vec3d target_world_pos, vec_to_target;
|
||||
vec3d target_world_pos;
|
||||
object *targetp;
|
||||
|
||||
if ( !hud_lock_has_homing_point() ) {
|
||||
@@ -464,8 +429,10 @@ int hud_lock_target_in_range()
|
||||
target_world_pos = targetp->pos;
|
||||
}
|
||||
}
|
||||
ship_weapon* swp = &Player_ship->weapons;
|
||||
weapon_info* wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
|
||||
|
||||
return hud_lock_world_pos_in_range(&target_world_pos, &vec_to_target);
|
||||
return weapon_secondary_world_pos_in_range(Player_obj, wip, &target_world_pos);
|
||||
}
|
||||
|
||||
int hud_abort_lock()
|
||||
@@ -763,6 +730,9 @@ void hud_lock_acquire_current_target(object *target_objp, ship_subsys *target_su
|
||||
target_shipp = &Ships[target_objp->instance];
|
||||
}
|
||||
|
||||
ship_weapon* swp = &Player_ship->weapons;
|
||||
weapon_info* wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
|
||||
|
||||
// Reset target subsys in case it isn't needed
|
||||
if (target_subsys != nullptr) target_subsys = nullptr;
|
||||
|
||||
@@ -771,8 +741,8 @@ void hud_lock_acquire_current_target(object *target_objp, ship_subsys *target_su
|
||||
// check all the subsystems and the center of the ship
|
||||
|
||||
// assume best lock pos is the center of the ship
|
||||
lock_in_range = hud_lock_world_pos_in_range(&target_objp->pos, &vec_to_lock);
|
||||
vm_vec_normalize(&vec_to_lock);
|
||||
lock_in_range = weapon_secondary_world_pos_in_range(Player_obj, wip, &target_objp->pos);
|
||||
vm_vec_normalized_dir(&vec_to_lock, &target_objp->pos, &Player_obj->pos);
|
||||
if ( lock_in_range ) {
|
||||
best_lock_dot=vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_lock);
|
||||
}
|
||||
@@ -788,8 +758,8 @@ void hud_lock_acquire_current_target(object *target_objp, ship_subsys *target_su
|
||||
// get world pos of subsystem
|
||||
get_subsystem_world_pos(target_objp, ss, &subsys_world_pos);
|
||||
|
||||
if ( hud_lock_world_pos_in_range(&subsys_world_pos, &vec_to_lock) ) {
|
||||
vm_vec_normalize(&vec_to_lock);
|
||||
if ( weapon_secondary_world_pos_in_range(Player_obj, wip, &subsys_world_pos) ) {
|
||||
vm_vec_normalized_dir(&vec_to_lock, &subsys_world_pos, &Player_obj->pos);
|
||||
lock_dot=vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_lock);
|
||||
if ( lock_dot > best_lock_dot ) {
|
||||
best_lock_dot=lock_dot;
|
||||
@@ -807,36 +777,16 @@ void hud_lock_acquire_uncaged_subsystem(weapon_info *wip, lock_info *lock, float
|
||||
Assert( lock->obj->type == OBJ_SHIP );
|
||||
|
||||
ship *sp = &Ships[lock->obj->instance];
|
||||
ship_subsys *ss;
|
||||
|
||||
float ss_dot;
|
||||
vec3d ss_pos;
|
||||
vec3d vec_to_target;
|
||||
|
||||
int current_num_locks = 0;
|
||||
|
||||
if ( Ship_info[sp->ship_info_index].is_big_or_huge() ) {
|
||||
for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
|
||||
if (ss->flags[Ship::Subsystem_Flags::Untargetable]) {
|
||||
for (ship_subsys* ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) {
|
||||
|
||||
if (!weapon_multilock_can_lock_on_subsys(Player_obj, lock->obj, ss, wip, &ss_dot))
|
||||
continue;
|
||||
}
|
||||
|
||||
get_subsystem_world_pos(lock->obj, ss, &ss_pos);
|
||||
|
||||
if ( !hud_lock_world_pos_in_range(&ss_pos, &vec_to_target) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vm_vec_normalize(&vec_to_target);
|
||||
ss_dot = vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_target);
|
||||
|
||||
if ( ss_dot < wip->lock_fov ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !ship_subsystem_in_sight(lock->obj, ss, &Eye_position, &ss_pos) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for existing locks
|
||||
current_num_locks = 0;
|
||||
@@ -871,11 +821,8 @@ void hud_lock_acquire_uncaged_subsystem(weapon_info *wip, lock_info *lock, float
|
||||
void hud_lock_acquire_uncaged_target(lock_info *current_lock, weapon_info *wip)
|
||||
{
|
||||
object *A;
|
||||
vec3d vec_to_target;
|
||||
float dot;
|
||||
|
||||
ship *sp;
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
object* best_obj = nullptr;
|
||||
@@ -888,45 +835,12 @@ void hud_lock_acquire_uncaged_target(lock_info *current_lock, weapon_info *wip)
|
||||
bool actively_locking = false;
|
||||
|
||||
for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) {
|
||||
if ( A->type != OBJ_SHIP ) {
|
||||
ship* sp = &Ships[A->instance];
|
||||
|
||||
if (!weapon_multilock_can_lock_on_target(Player_obj, A, wip, &dot))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( hud_target_invalid_awacs(A) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sp = &Ships[A->instance];
|
||||
|
||||
if ( A->flags[Object::Object_Flags::Should_be_dead] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( sp->flags[Ship::Ship_Flags::Dying] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( should_be_ignored(sp) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if this is part of the same team and doesn't have any iff restrictions, reject lock
|
||||
if ( !weapon_has_iff_restrictions(wip) && Player_ship->team == obj_team(A) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int in_range = hud_lock_world_pos_in_range(&A->pos, &vec_to_target);
|
||||
vm_vec_normalize(&vec_to_target);
|
||||
dot = vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_target);
|
||||
|
||||
/*if ( dot < 0.95f ) {
|
||||
// broad test to see if we should bother to even check
|
||||
continue;
|
||||
}*/
|
||||
|
||||
if (!weapon_target_satisfies_lock_restrictions(wip, A)) {
|
||||
continue;
|
||||
}
|
||||
bool in_range = weapon_secondary_world_pos_in_range(Player_obj, wip, &A->pos);
|
||||
|
||||
if ( Ship_info[sp->ship_info_index].is_big_or_huge() ) {
|
||||
lock_info temp_lock;
|
||||
@@ -1013,7 +927,7 @@ void hud_lock_determine_lock_target(lock_info *lock_slot, weapon_info *wip)
|
||||
vm_vec_normalized_dir(&vec_to_target, &target_pos, &Eye_position);
|
||||
dot = vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_target);
|
||||
|
||||
if ( !hud_lock_world_pos_in_range(&target_pos, &vec_to_target) || dot < wip->lock_fov) {
|
||||
if ( !weapon_secondary_world_pos_in_range(Player_obj, wip, &lock_slot->obj->pos) || dot < wip->lock_fov) {
|
||||
// set this lock slot to empty
|
||||
ship_clear_lock(lock_slot);
|
||||
hud_lock_acquire_uncaged_target(lock_slot, wip);
|
||||
@@ -1459,7 +1373,7 @@ void hud_calculate_lock_slot_position(lock_info *current_lock, float frametime)
|
||||
// Determine if point to lock on is in range
|
||||
int hud_lock_target_in_range(lock_info *lock_slot)
|
||||
{
|
||||
vec3d target_world_pos, vec_to_target;
|
||||
vec3d target_world_pos;
|
||||
|
||||
if ( lock_slot == nullptr || lock_slot->obj == nullptr) {
|
||||
return 0;
|
||||
@@ -1476,8 +1390,9 @@ int hud_lock_target_in_range(lock_info *lock_slot)
|
||||
target_world_pos = lock_slot->obj->pos;
|
||||
}
|
||||
}
|
||||
ship_weapon* swp = &Player_ship->weapons;
|
||||
|
||||
return hud_lock_world_pos_in_range(&target_world_pos, &vec_to_target);
|
||||
return weapon_secondary_world_pos_in_range(Player_obj, &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]], &target_world_pos);
|
||||
}
|
||||
|
||||
void hud_do_lock_indicators(float frametime)
|
||||
@@ -1490,6 +1405,11 @@ void hud_do_lock_indicators(float frametime)
|
||||
return;
|
||||
}
|
||||
|
||||
// do not continue locking if the game is paused (viewer pause still has hud on)
|
||||
if (gameseq_get_state() == GS_STATE_GAME_PAUSED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// be sure to unset this flag, then possibly set later in this function so that
|
||||
// threat indicators work properly.
|
||||
Player_ai->ai_flags.remove(AI::AI_Flags::Seek_lock);
|
||||
@@ -1565,6 +1485,11 @@ void hud_do_lock_indicators(float frametime)
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !wip->wi_flags[Weapon::Info_Flags::Multilock_target_dead_subsys] && lock_slot->subsys != nullptr && lock_slot->subsys->current_hits <= 0.0f) {
|
||||
ship_clear_lock(lock_slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
hud_lock_determine_lock_point(lock_slot);
|
||||
|
||||
hud_lock_check_if_target_in_lock_cone(lock_slot, wip);
|
||||
@@ -2103,8 +2028,8 @@ void hud_lock_get_new_lock_pos(object *target_objp)
|
||||
Player->locking_on_center=1;
|
||||
Player->locking_subsys=NULL;
|
||||
Player->locking_subsys_parent=-1;
|
||||
lock_in_range = hud_lock_world_pos_in_range(&lock_world_pos, &vec_to_lock);
|
||||
vm_vec_normalize(&vec_to_lock);
|
||||
lock_in_range = weapon_secondary_world_pos_in_range(Player_obj, wip, &lock_world_pos);
|
||||
vm_vec_normalized_dir(&vec_to_lock, &lock_world_pos, &Player_obj->pos);
|
||||
if ( lock_in_range ) {
|
||||
best_lock_dot=vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_lock);
|
||||
}
|
||||
@@ -2120,8 +2045,8 @@ void hud_lock_get_new_lock_pos(object *target_objp)
|
||||
// get world pos of subsystem
|
||||
get_subsystem_world_pos(target_objp, ss, &subsys_world_pos);
|
||||
|
||||
if ( hud_lock_world_pos_in_range(&subsys_world_pos, &vec_to_lock) ) {
|
||||
vm_vec_normalize(&vec_to_lock);
|
||||
if ( weapon_secondary_world_pos_in_range(Player_obj, wip, &subsys_world_pos) ) {
|
||||
vm_vec_normalized_dir(&vec_to_lock, &subsys_world_pos, &Player_obj->pos);
|
||||
lock_dot=vm_vec_dot(&Player_obj->orient.vec.fvec, &vec_to_lock);
|
||||
if ( lock_dot > best_lock_dot ) {
|
||||
best_lock_dot=lock_dot;
|
||||
|
||||
+65
-24
@@ -53,9 +53,9 @@ int Hud_font = -1;
|
||||
|
||||
bool Chase_view_only_ex = false;
|
||||
|
||||
//WARNING: If you add gauges to this array, make sure to bump num_default_gauges!
|
||||
int num_default_gauges = 42;
|
||||
static int retail_gauges[] = {
|
||||
//WARNING: If you add gauges to this array, make sure to bump Num_builtin_gauges!
|
||||
const int Num_builtin_gauges = 42;
|
||||
static int Builtin_gauges[Num_builtin_gauges] = {
|
||||
HUD_OBJECT_MESSAGES,
|
||||
HUD_OBJECT_TRAINING_MESSAGES,
|
||||
HUD_OBJECT_SUPPORT,
|
||||
@@ -100,9 +100,52 @@ static int retail_gauges[] = {
|
||||
HUD_OBJECT_ETS_RETAIL
|
||||
};
|
||||
|
||||
Legacy_HUD_gauge_pair Legacy_HUD_gauges[NUM_HUD_GAUGES] =
|
||||
{
|
||||
{ "LEAD_INDICATOR", HUD_OBJECT_LEAD },
|
||||
{ "ORIENTATION_TEE", HUD_OBJECT_ORIENTATION_TEE },
|
||||
{ "HOSTILE_TRIANGLE", HUD_OBJECT_HOSTILE_TRI },
|
||||
{ "TARGET_TRIANGLE", HUD_OBJECT_TARGET_TRI },
|
||||
{ "MISSION_TIME", HUD_OBJECT_MISSION_TIME },
|
||||
{ "RETICLE_CIRCLE", -1 },
|
||||
{ "THROTTLE_GAUGE", HUD_OBJECT_THROTTLE },
|
||||
{ "RADAR", HUD_OBJECT_RADAR_STD },
|
||||
{ "TARGET_MONITOR", HUD_OBJECT_TARGET_MONITOR },
|
||||
{ "CENTER_RETICLE", HUD_OBJECT_CENTER_RETICLE },
|
||||
{ "TARGET_MONITOR_EXTRA_DATA", HUD_OBJECT_EXTRA_TARGET_DATA },
|
||||
{ "TARGET_SHIELD_ICON", HUD_OBJECT_TARGET_SHIELD },
|
||||
{ "PLAYER_SHIELD_ICON", HUD_OBJECT_PLAYER_SHIELD },
|
||||
{ "ETS_GAUGE", HUD_OBJECT_ETS_RETAIL },
|
||||
{ "AUTO_TARGET", HUD_OBJECT_AUTO_TARGET },
|
||||
{ "AUTO_SPEED", HUD_OBJECT_AUTO_SPEED },
|
||||
{ "WEAPONS_GAUGE", HUD_OBJECT_WEAPONS },
|
||||
{ "ESCORT_VIEW", HUD_OBJECT_ESCORT },
|
||||
{ "DIRECTIVES_VIEW", HUD_OBJECT_DIRECTIVES },
|
||||
{ "THREAT_GAUGE", HUD_OBJECT_THREAT },
|
||||
{ "AFTERBURNER_ENERGY", HUD_OBJECT_AFTERBURNER },
|
||||
{ "WEAPONS_ENERGY", HUD_OBJECT_WEAPON_ENERGY },
|
||||
{ "WEAPON_LINKING_GAUGE", HUD_OBJECT_WEAPON_LINKING },
|
||||
{ "TARGER_MINI_ICON", HUD_OBJECT_MINI_SHIELD },
|
||||
{ "OFFSCREEN_INDICATOR", HUD_OBJECT_OFFSCREEN },
|
||||
{ "TALKING_HEAD", HUD_OBJECT_TALKING_HEAD },
|
||||
{ "DAMAGE_GAUGE", HUD_OBJECT_DAMAGE },
|
||||
{ "MESSAGE_LINES", HUD_OBJECT_MESSAGES },
|
||||
{ "MISSILE_WARNING_ARROW", HUD_OBJECT_MISSILE_TRI },
|
||||
{ "CMEASURE_GAUGE", HUD_OBJECT_CMEASURES },
|
||||
{ "OBJECTIVES_NOTIFY_GAUGE", HUD_OBJECT_OBJ_NOTIFY },
|
||||
{ "WINGMEN_STATUS", HUD_OBJECT_WINGMAN_STATUS },
|
||||
{ "OFFSCREEN RANGE", -1 },
|
||||
{ "KILLS GAUGE", HUD_OBJECT_KILLS },
|
||||
{ "ATTACKING TARGET COUNT", -1 },
|
||||
{ "TEXT FLASH", HUD_OBJECT_TEXT_WARNINGS },
|
||||
{ "MESSAGE BOX", HUD_OBJECT_SQUAD_MSG },
|
||||
{ "SUPPORT GUAGE", HUD_OBJECT_SUPPORT },
|
||||
{ "LAG GUAGE", HUD_OBJECT_LAG },
|
||||
};
|
||||
|
||||
flag_def_list Hud_gauge_types[] = {
|
||||
{ "Messages", HUD_OBJECT_MESSAGES, 0},
|
||||
{ "Training messages", HUD_OBJECT_TRAINING_MESSAGES, 0},
|
||||
{ "Training messages", HUD_OBJECT_TRAINING_MESSAGES, 0}, // Not in legacy list
|
||||
{ "Support", HUD_OBJECT_SUPPORT, 0},
|
||||
{ "Damage", HUD_OBJECT_DAMAGE, 0},
|
||||
{ "Wingman status", HUD_OBJECT_WINGMAN_STATUS, 0},
|
||||
@@ -120,14 +163,14 @@ flag_def_list Hud_gauge_types[] = {
|
||||
{ "Target shield", HUD_OBJECT_TARGET_SHIELD, 0},
|
||||
{ "Escort list", HUD_OBJECT_ESCORT, 0},
|
||||
{ "Mission time", HUD_OBJECT_MISSION_TIME, 0},
|
||||
{ "Ets weapons", HUD_OBJECT_ETS_WEAPONS, 0},
|
||||
{ "Ets shields", HUD_OBJECT_ETS_SHIELDS, 0},
|
||||
{ "Ets engines", HUD_OBJECT_ETS_ENGINES, 0},
|
||||
{ "Ets weapons", HUD_OBJECT_ETS_WEAPONS, 0}, // Not in legacy list
|
||||
{ "Ets shields", HUD_OBJECT_ETS_SHIELDS, 0}, // Not in legacy list
|
||||
{ "Ets engines", HUD_OBJECT_ETS_ENGINES, 0}, // Not in legacy list
|
||||
{ "Target monitor", HUD_OBJECT_TARGET_MONITOR, 0},
|
||||
{ "Extra target data", HUD_OBJECT_EXTRA_TARGET_DATA, 0},
|
||||
{ "Radar", HUD_OBJECT_RADAR_STD, 0},
|
||||
{ "Radar orb", HUD_OBJECT_RADAR_ORB, 0},
|
||||
{ "Radar BSG", HUD_OBJECT_RADAR_BSG, 0},
|
||||
{ "Radar orb", HUD_OBJECT_RADAR_ORB, 0}, // Not in legacy list
|
||||
{ "Radar BSG", HUD_OBJECT_RADAR_BSG, 0}, // Not in legacy list
|
||||
{ "Afterburner energy", HUD_OBJECT_AFTERBURNER, 0},
|
||||
{ "Weapon energy", HUD_OBJECT_WEAPON_ENERGY, 0},
|
||||
{ "Text warnings", HUD_OBJECT_TEXT_WARNINGS, 0},
|
||||
@@ -136,12 +179,12 @@ flag_def_list Hud_gauge_types[] = {
|
||||
{ "Threat indicator", HUD_OBJECT_THREAT, 0},
|
||||
{ "Lead indicator", HUD_OBJECT_LEAD, 0},
|
||||
{ "Lead sight", HUD_OBJECT_LEAD_SIGHT, 0},
|
||||
{ "Lock indicator", HUD_OBJECT_LOCK, 0},
|
||||
{ "Lock indicator", HUD_OBJECT_LOCK, 0}, // Not in legacy list
|
||||
{ "Weapon linking", HUD_OBJECT_WEAPON_LINKING, 0},
|
||||
{ "Multiplayer messages", HUD_OBJECT_MULTI_MSG, 0},
|
||||
{ "Voice status", HUD_OBJECT_VOICE_STATUS, 0},
|
||||
{ "Ping", HUD_OBJECT_PING, 0},
|
||||
{ "Supernova", HUD_OBJECT_SUPERNOVA, 0},
|
||||
{ "Multiplayer messages", HUD_OBJECT_MULTI_MSG, 0}, // Not in legacy list
|
||||
{ "Voice status", HUD_OBJECT_VOICE_STATUS, 0}, // Not in legacy list
|
||||
{ "Ping", HUD_OBJECT_PING, 0}, // Not in legacy list
|
||||
{ "Supernova", HUD_OBJECT_SUPERNOVA, 0}, // Not in legacy list
|
||||
{ "Offscreen indicator", HUD_OBJECT_OFFSCREEN, 0},
|
||||
{ "Targeting brackets", HUD_OBJECT_BRACKETS, 0},
|
||||
{ "Orientation", HUD_OBJECT_ORIENTATION_TEE, 0},
|
||||
@@ -149,10 +192,9 @@ flag_def_list Hud_gauge_types[] = {
|
||||
{ "Target direction", HUD_OBJECT_TARGET_TRI, 0},
|
||||
{ "Missile indicator", HUD_OBJECT_MISSILE_TRI, 0},
|
||||
{ "Kills", HUD_OBJECT_KILLS, 0},
|
||||
{ "Fixed messages", HUD_OBJECT_FIXED_MESSAGES, 0},
|
||||
{ "Fixed messages", HUD_OBJECT_FIXED_MESSAGES, 0}, // Not in legacy list
|
||||
{ "Ets retail", HUD_OBJECT_ETS_RETAIL, 0}
|
||||
};
|
||||
|
||||
int Num_hud_gauge_types = sizeof(Hud_gauge_types)/sizeof(flag_def_list);
|
||||
|
||||
int parse_ship_start()
|
||||
@@ -522,7 +564,6 @@ void hud_positions_init()
|
||||
|
||||
// load missing retail gauges for the default and ship-specific HUDs
|
||||
load_missing_retail_gauges();
|
||||
|
||||
}
|
||||
|
||||
void load_missing_retail_gauges()
|
||||
@@ -533,11 +574,11 @@ void load_missing_retail_gauges()
|
||||
if(Hud_retail) {
|
||||
int num_loaded_gauges = (int)default_hud_gauges.size();
|
||||
|
||||
for(int i = 0; i < num_default_gauges; i++) {
|
||||
for(int i = 0; i < Num_builtin_gauges; i++) {
|
||||
retail_gauge_loaded = false;
|
||||
|
||||
for(int j = 0; j < num_loaded_gauges; j++) {
|
||||
if(retail_gauges[i] == default_hud_gauges[j]->getObjectType()) {
|
||||
if(Builtin_gauges[i] == default_hud_gauges[j]->getObjectType()) {
|
||||
retail_gauge_loaded = true;
|
||||
break;
|
||||
}
|
||||
@@ -545,7 +586,7 @@ void load_missing_retail_gauges()
|
||||
|
||||
if(!retail_gauge_loaded) {
|
||||
gauge_settings settings;
|
||||
load_gauge(retail_gauges[i], &settings);
|
||||
load_gauge(Builtin_gauges[i], &settings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,9 +630,9 @@ void load_missing_retail_gauges()
|
||||
if(it->hud_enabled && it->hud_retail) {
|
||||
int num_loaded_gauges = (int)it->hud_gauges.size();
|
||||
|
||||
for(int i = 0; i < num_default_gauges; i++) {
|
||||
for(int i = 0; i < Num_builtin_gauges; i++) {
|
||||
for(int j = 0; j < num_loaded_gauges; j++) {
|
||||
if(retail_gauges[i] == it->hud_gauges[j]->getObjectType()) {
|
||||
if(Builtin_gauges[i] == it->hud_gauges[j]->getObjectType()) {
|
||||
retail_gauge_loaded = true;
|
||||
}
|
||||
}
|
||||
@@ -599,7 +640,7 @@ void load_missing_retail_gauges()
|
||||
if(!retail_gauge_loaded) {
|
||||
gauge_settings settings;
|
||||
settings.ship_idx = &sindex;
|
||||
load_gauge(retail_gauges[i], &settings);
|
||||
load_gauge(Builtin_gauges[i], &settings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1452,7 +1493,7 @@ void load_gauge_custom(gauge_settings* settings)
|
||||
stuff_string(gauge_string, F_NAME, MAX_FILENAME_LEN);
|
||||
|
||||
for(i = 0; i < NUM_HUD_GAUGES; i++) {
|
||||
if(!strcmp(HUD_gauge_text[i], gauge_string)) {
|
||||
if(!strcmp(Legacy_HUD_gauges[i].hud_gauge_text, gauge_string)) {
|
||||
gauge_type = i;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ void HudGaugeReticle::initBitmaps(char *fname)
|
||||
{
|
||||
crosshair.first_frame = bm_load_animation(fname, &crosshair.num_frames);
|
||||
if (crosshair.first_frame < 0) {
|
||||
mprintf(("Cannot load hud ani: %s\n", fname));
|
||||
Warning(LOCATION, "Cannot load hud ani: %s\n", fname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,7 +495,7 @@ void HudGaugeThrottle::initBitmaps(char *fname)
|
||||
throttle_frames.first_frame = bm_load_animation(fname, &throttle_frames.num_frames);
|
||||
|
||||
if (throttle_frames.first_frame < 0) {
|
||||
mprintf(("Cannot load hud ani: %s\n", fname));
|
||||
Warning(LOCATION, "Cannot load hud ani: %s\n", fname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,17 +748,17 @@ void HudGaugeThreatIndicator::initBitmaps(char *fname_arc, char *fname_laser, ch
|
||||
{
|
||||
threat_arc.first_frame = bm_load_animation(fname_arc, &threat_arc.num_frames);
|
||||
if (threat_arc.first_frame < 0) {
|
||||
mprintf(("Cannot load hud ani: %s\n", fname_arc));
|
||||
Warning(LOCATION, "Cannot load hud ani: %s\n", fname_arc);
|
||||
}
|
||||
|
||||
laser_warn.first_frame = bm_load_animation(fname_laser, &laser_warn.num_frames);
|
||||
if (laser_warn.first_frame < 0) {
|
||||
mprintf(("Cannot load hud ani: %s\n", fname_laser));
|
||||
Warning(LOCATION, "Cannot load hud ani: %s\n", fname_laser);
|
||||
}
|
||||
|
||||
lock_warn.first_frame = bm_load_animation(fname_lock, &lock_warn.num_frames);
|
||||
if (lock_warn.first_frame < 0) {
|
||||
mprintf(("Cannot load hud ani: %s\n", fname_lock));
|
||||
Warning(LOCATION, "Cannot load hud ani: %s\n", fname_lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-4
@@ -378,7 +378,13 @@ void hud_shield_show_mini(object *objp, int x_force, int y_force, int x_hull_off
|
||||
break;
|
||||
}
|
||||
|
||||
if (objp->shield_quadrant[Quadrant_xlate[i]] < 0.1f ) {
|
||||
int num;
|
||||
if (!(Ship_info[Ships[objp->instance].ship_info_index].flags[Ship::Info_Flags::Model_point_shields]))
|
||||
num = Quadrant_xlate[i];
|
||||
else
|
||||
num = i;
|
||||
|
||||
if (objp->shield_quadrant[num] < 0.1f ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -389,7 +395,7 @@ void hud_shield_show_mini(object *objp, int x_force, int y_force, int x_hull_off
|
||||
}
|
||||
|
||||
range = HUD_color_alpha;
|
||||
hud_color_index = (int)std::lround((objp->shield_quadrant[Quadrant_xlate[i]] / max_shield) * range);
|
||||
hud_color_index = (int)std::lround((objp->shield_quadrant[num] / max_shield) * range);
|
||||
Assert(hud_color_index >= 0 && hud_color_index <= range);
|
||||
|
||||
if ( hud_color_index < 0 ) {
|
||||
@@ -975,7 +981,13 @@ void HudGaugeShieldMini::showMiniShields(object *objp)
|
||||
break;
|
||||
}
|
||||
|
||||
if ( objp->shield_quadrant[Quadrant_xlate[i]] < 0.1f ) {
|
||||
int num;
|
||||
if (!(Ship_info[Ships[objp->instance].ship_info_index].flags[Ship::Info_Flags::Model_point_shields]))
|
||||
num = Quadrant_xlate[i];
|
||||
else
|
||||
num = i;
|
||||
|
||||
if (objp->shield_quadrant[num] < 0.1f ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -986,7 +998,7 @@ void HudGaugeShieldMini::showMiniShields(object *objp)
|
||||
}
|
||||
|
||||
range = HUD_color_alpha;
|
||||
hud_color_index = (int)std::lround((objp->shield_quadrant[Quadrant_xlate[i]] / max_shield) * range);
|
||||
hud_color_index = (int)std::lround((objp->shield_quadrant[num] / max_shield) * range);
|
||||
Assert(hud_color_index >= 0 && hud_color_index <= range);
|
||||
|
||||
if ( hud_color_index < 0 ) {
|
||||
|
||||
@@ -1823,7 +1823,7 @@ void hud_squadmsg_call_reinforcement(int reinforcement_num, int /*player_num*/)
|
||||
break;
|
||||
|
||||
//if ( i > 0 )
|
||||
// message_send_to_player( rp->yes_messages[myrand() % i], rp->name, MESSAGE_PRIORITY_NORMAL, HUD_SOURCE_FRIENDLY );
|
||||
// message_send_to_player( rp->yes_messages[Random::next(i)], rp->name, MESSAGE_PRIORITY_NORMAL, HUD_SOURCE_FRIENDLY );
|
||||
*/
|
||||
|
||||
mission_log_add_entry(LOG_PLAYER_CALLED_FOR_REINFORCEMENT, rp->name, NULL);
|
||||
|
||||
+56
-16
@@ -4304,19 +4304,38 @@ void HudGaugeLeadSight::render(float /*frametime*/)
|
||||
}
|
||||
}
|
||||
|
||||
// hud_cease_subsystem_targeting() will cease targeting the current targets subsystems
|
||||
//
|
||||
void hud_cease_subsystem_targeting(bool print_message)
|
||||
{
|
||||
int ship_index;
|
||||
|
||||
ship_index = Objects[Player_ai->target_objnum].instance;
|
||||
if ( ship_index < 0 )
|
||||
Assertion(Player_ai->target_objnum < MAX_OBJECTS, "Invalid player target objnum");
|
||||
|
||||
if (Player_ai->target_objnum < 0) {
|
||||
// Nothing selected
|
||||
if (print_message) {
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("No target selected.", 322));
|
||||
snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) {
|
||||
// Object isn't a ship, so it doesn't have any subsystems
|
||||
if (print_message) {
|
||||
snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ship_index = Objects[Player_ai->target_objnum].instance;
|
||||
|
||||
Assertion(ship_index >= 0 && ship_index < MAX_SHIPS, "Invalid ship index");
|
||||
Assertion(Player_num >= 0 && Player_num < MAX_PLAYERS, "Invalid player number");
|
||||
|
||||
Ships[ship_index].last_targeted_subobject[Player_num] = NULL;
|
||||
Player_ai->targeted_subsys = NULL;
|
||||
Player_ai->targeted_subsys_parent = -1;
|
||||
|
||||
if ( print_message ) {
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Deactivating sub-system targeting", 324));
|
||||
}
|
||||
@@ -4325,14 +4344,14 @@ void hud_cease_subsystem_targeting(bool print_message)
|
||||
hud_lock_reset();
|
||||
}
|
||||
|
||||
// hud_cease_targeting() will cease targeting main target and subsystem
|
||||
// does not turn off auto-targeting by default
|
||||
void hud_cease_targeting(bool deliberate)
|
||||
{
|
||||
set_target_objnum( Player_ai, -1 );
|
||||
// Turn off subsys targeting before we invalidate the player target_objnum...
|
||||
hud_cease_subsystem_targeting(false);
|
||||
set_target_objnum(Player_ai, -1);
|
||||
|
||||
if (deliberate) {
|
||||
// Player wants to stop targeting, so turn off auto-target so we don't immediately aquire another target
|
||||
Players[Player_num].flags &= ~PLAYER_FLAGS_AUTO_TARGETING;
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Deactivating targeting system", 325));
|
||||
}
|
||||
@@ -7185,13 +7204,17 @@ void HudGaugeHardpoints::render(float /*frametime*/)
|
||||
//secondary weapons
|
||||
int num_secondaries_rendered = 0;
|
||||
if ( draw_secondary_models ) {
|
||||
auto ship_pm = model_get(sip->model_num);
|
||||
|
||||
for (i = 0; i < swp->num_secondary_banks; i++) {
|
||||
if (Weapon_info[swp->secondary_bank_weapons[i]].external_model_num == -1 || !sip->draw_secondary_models[i])
|
||||
auto wip = &Weapon_info[swp->secondary_bank_weapons[i]];
|
||||
|
||||
if (wip->external_model_num == -1 || !sip->draw_secondary_models[i])
|
||||
continue;
|
||||
|
||||
auto bank = &(model_get(sip->model_num))->missile_banks[i];
|
||||
auto bank = &ship_pm->missile_banks[i];
|
||||
|
||||
if (Weapon_info[swp->secondary_bank_weapons[i]].wi_flags[Weapon::Info_Flags::External_weapon_lnch]) {
|
||||
if (wip->wi_flags[Weapon::Info_Flags::External_weapon_lnch]) {
|
||||
for(k = 0; k < bank->num_slots; k++) {
|
||||
model_render_params weapon_render_info;
|
||||
|
||||
@@ -7202,7 +7225,12 @@ void HudGaugeHardpoints::render(float /*frametime*/)
|
||||
vec3d world_position;
|
||||
vm_vec_unrotate(&world_position, &bank->pnt[k], &object_orient);
|
||||
|
||||
model_render_immediate(&weapon_render_info, Weapon_info[swp->secondary_bank_weapons[i]].external_model_num, &object_orient, &bank->pnt[k]);
|
||||
// "Bank" the external model by the angle offset
|
||||
angles angs = { 0.0f, bank->external_model_angle_offset[k], 0.0f };
|
||||
matrix model_orient = object_orient;
|
||||
vm_rotate_matrix_by_angles(&model_orient, &angs);
|
||||
|
||||
model_render_immediate(&weapon_render_info, wip->external_model_num, &model_orient, &bank->pnt[k]);
|
||||
}
|
||||
} else {
|
||||
num_secondaries_rendered = 0;
|
||||
@@ -7214,7 +7242,7 @@ void HudGaugeHardpoints::render(float /*frametime*/)
|
||||
if (num_secondaries_rendered >= sp->weapons.secondary_bank_ammo[i])
|
||||
break;
|
||||
|
||||
if(sp->secondary_point_reload_pct[i][k] <= 0.0)
|
||||
if(sp->secondary_point_reload_pct.get(i, k) <= 0.0)
|
||||
continue;
|
||||
|
||||
model_render_params weapon_render_info;
|
||||
@@ -7227,7 +7255,7 @@ void HudGaugeHardpoints::render(float /*frametime*/)
|
||||
|
||||
num_secondaries_rendered++;
|
||||
|
||||
vm_vec_scale_add2(&secondary_weapon_pos, &vmd_z_vector, -(1.0f-sp->secondary_point_reload_pct[i][k]) * model_get(Weapon_info[swp->secondary_bank_weapons[i]].external_model_num)->rad);
|
||||
vm_vec_scale_add2(&secondary_weapon_pos, &vmd_z_vector, -(1.0f-sp->secondary_point_reload_pct.get(i, k)) * model_get(wip->external_model_num)->rad);
|
||||
|
||||
weapon_render_info.set_detail_level_lock(detail_level_lock);
|
||||
weapon_render_info.set_flags(render_flags);
|
||||
@@ -7236,7 +7264,12 @@ void HudGaugeHardpoints::render(float /*frametime*/)
|
||||
vec3d world_position;
|
||||
vm_vec_unrotate(&world_position, &secondary_weapon_pos, &object_orient);
|
||||
|
||||
model_render_immediate(&weapon_render_info, Weapon_info[swp->secondary_bank_weapons[i]].external_model_num, &object_orient, &world_position);
|
||||
// "Bank" the external model by the angle offset
|
||||
angles angs = { 0.0f, bank->external_model_angle_offset[k], 0.0f };
|
||||
matrix model_orient = object_orient;
|
||||
vm_rotate_matrix_by_angles(&model_orient, &angs);
|
||||
|
||||
model_render_immediate(&weapon_render_info, wip->external_model_num, &model_orient, &world_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7248,9 +7281,11 @@ void HudGaugeHardpoints::render(float /*frametime*/)
|
||||
|
||||
//primary weapons
|
||||
if ( draw_primary_models ) {
|
||||
auto ship_pm = model_get(sip->model_num);
|
||||
|
||||
for ( i = 0; i < swp->num_primary_banks; i++ ) {
|
||||
auto wip = &Weapon_info[swp->primary_bank_weapons[i]];
|
||||
auto bank = &model_get(sip->model_num)->gun_banks[i];
|
||||
auto bank = &ship_pm->gun_banks[i];
|
||||
|
||||
for ( k = 0; k < bank->num_slots; k++ ) {
|
||||
if ( wip->external_model_num < 0 || !sip->draw_primary_models[i] ) {
|
||||
@@ -7278,7 +7313,12 @@ void HudGaugeHardpoints::render(float /*frametime*/)
|
||||
vec3d world_position;
|
||||
vm_vec_unrotate(&world_position, &bank->pnt[k], &object_orient);
|
||||
|
||||
model_render_immediate(&weapon_render_info, wip->external_model_num, swp->primary_bank_external_model_instance[i], &object_orient, &world_position);
|
||||
// "Bank" the external model by the angle offset
|
||||
angles angs = { 0.0f, bank->external_model_angle_offset[k], 0.0f };
|
||||
matrix model_orient = object_orient;
|
||||
vm_rotate_matrix_by_angles(&model_orient, &angs);
|
||||
|
||||
model_render_immediate(&weapon_render_info, wip->external_model_num, swp->primary_bank_external_model_instance[i], &model_orient, &world_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-2
@@ -113,8 +113,21 @@ void hud_target_random_ship();
|
||||
|
||||
void hud_target_next_subobject();
|
||||
void hud_target_prev_subobject();
|
||||
void hud_cease_subsystem_targeting(bool print_message = true);
|
||||
void hud_cease_targeting(bool deliberate = false);
|
||||
|
||||
/**
|
||||
* @brief Cease targeting subsystems
|
||||
*
|
||||
* @param[in] print_message If true, provide feedback to the player
|
||||
*/
|
||||
void hud_cease_subsystem_targeting(bool print_message = true);
|
||||
|
||||
/**
|
||||
* @brief Cease targeting objects and their subsystems.
|
||||
*
|
||||
* @param[in] deliberate If true, turn off auto-targeting and provide feedback to player
|
||||
*/
|
||||
void hud_cease_targeting(bool deliberate = false);
|
||||
|
||||
void hud_restore_subsystem_target(ship* shipp);
|
||||
vec3d* get_subsystem_world_pos(object* parent_obj, ship_subsys* subsys, vec3d* world_pos);
|
||||
void hud_target_change_check();
|
||||
|
||||
@@ -99,24 +99,6 @@ void hud_set_wingman_status_alive( int wing_index, int wing_pos)
|
||||
HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_ALIVE;
|
||||
}
|
||||
|
||||
void hud_wingman_status_init_late_wings()
|
||||
{
|
||||
/*
|
||||
int i, j, wing_index;
|
||||
|
||||
for ( i = 0; i < Num_wings; i++ ) {
|
||||
wing_index = ship_squadron_wing_lookup(Wings[i].name);
|
||||
|
||||
if ( (wing_index >= 0) && (Wings[i].total_arrived_count == 0) ) {
|
||||
HUD_wingman_status[wing_index].used = 1;
|
||||
for (j = 0; j < Wings[i].wave_count; j++) {
|
||||
HUD_wingman_status[wing_index].status[j] = HUD_WINGMAN_STATUS_NOT_HERE;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// function which marks the other team wing as not used for the wingman status gauge
|
||||
void hud_wingman_kill_multi_teams()
|
||||
{
|
||||
@@ -161,7 +143,6 @@ void hud_init_wingman_status_gauge()
|
||||
}
|
||||
}
|
||||
|
||||
hud_wingman_status_init_late_wings();
|
||||
hud_wingman_kill_multi_teams();
|
||||
hud_wingman_status_update();
|
||||
}
|
||||
|
||||
+11
-7
@@ -542,9 +542,11 @@ void key_mark( uint code, int state, uint latency )
|
||||
Current_key_down |= KEY_CTRLED;
|
||||
}
|
||||
|
||||
Script_system.SetHookVar("Key", 's', textify_scancode_universal(Current_key_down));
|
||||
Script_system.RunCondition(CHA_KEYRELEASED);
|
||||
Script_system.RemHookVar("Key");
|
||||
if (Script_system.IsActiveAction(CHA_KEYRELEASED)) {
|
||||
Script_system.SetHookVar("Key", 's', textify_scancode_universal(Current_key_down));
|
||||
Script_system.RunCondition(CHA_KEYRELEASED);
|
||||
Script_system.RemHookVar("Key");
|
||||
}
|
||||
} else {
|
||||
// Key going down
|
||||
keyd_last_pressed = scancode;
|
||||
@@ -570,10 +572,12 @@ void key_mark( uint code, int state, uint latency )
|
||||
Current_key_down |= KEY_CTRLED;
|
||||
}
|
||||
|
||||
// We use the universal value here to keep the scripting interface consistent regardless of the current language
|
||||
Script_system.SetHookVar("Key", 's', textify_scancode_universal(Current_key_down));
|
||||
Script_system.RunCondition(CHA_KEYPRESSED);
|
||||
Script_system.RemHookVar("Key");
|
||||
if (Script_system.IsActiveAction(CHA_KEYPRESSED)) {
|
||||
// We use the universal value here to keep the scripting interface consistent regardless of the current language
|
||||
Script_system.SetHookVar("Key", 's', textify_scancode_universal(Current_key_down));
|
||||
Script_system.RunCondition(CHA_KEYPRESSED);
|
||||
Script_system.RemHookVar("Key");
|
||||
}
|
||||
} else if (!keyd_repeat) {
|
||||
// Don't buffer repeating key if repeat mode is off
|
||||
scancode = 0xAA;
|
||||
|
||||
+4
-13
@@ -692,15 +692,6 @@ void process_debug_keys(int k)
|
||||
case KEY_DEBUGGED + KEY_H:
|
||||
hud_target_toggle_hidden_from_sensors();
|
||||
break;
|
||||
|
||||
case KEY_DEBUGGED + KEY_F:
|
||||
extern int wacky_scheme;
|
||||
if(wacky_scheme == 3){
|
||||
wacky_scheme = 0;
|
||||
} else {
|
||||
wacky_scheme++;
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_DEBUGGED + KEY_ALTED + KEY_F:
|
||||
Framerate_delay += 10;
|
||||
@@ -785,8 +776,8 @@ void process_debug_keys(int k)
|
||||
// remove guardian flag -- kazan
|
||||
Ships[objp->instance].ship_guardian_threshold = 0;
|
||||
|
||||
ship_apply_local_damage( objp, Player_obj, &objp->pos, 100000.0f, MISS_SHIELDS, CREATE_SPARKS);
|
||||
ship_apply_local_damage( objp, Player_obj, &objp->pos, 1.0f, MISS_SHIELDS, CREATE_SPARKS);
|
||||
ship_apply_local_damage( objp, Player_obj, &objp->pos, 100000.0f, -1, MISS_SHIELDS, CREATE_SPARKS);
|
||||
ship_apply_local_damage( objp, Player_obj, &objp->pos, 1.0f, -1, MISS_SHIELDS, CREATE_SPARKS);
|
||||
break;
|
||||
case OBJ_WEAPON:
|
||||
Weapons[objp->instance].lifeleft = 0.01f;
|
||||
@@ -856,7 +847,7 @@ void process_debug_keys(int k)
|
||||
object *objp = &Objects[Player_ai->target_objnum];
|
||||
|
||||
if (objp->type == OBJ_SHIP) {
|
||||
ship_apply_local_damage( objp, Player_obj, &objp->pos, Ships[objp->instance].ship_max_hull_strength * 0.1f + 10.0f, MISS_SHIELDS, CREATE_SPARKS);
|
||||
ship_apply_local_damage( objp, Player_obj, &objp->pos, Ships[objp->instance].ship_max_hull_strength * 0.1f + 10.0f, -1, MISS_SHIELDS, CREATE_SPARKS);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -894,7 +885,7 @@ void process_debug_keys(int k)
|
||||
|
||||
vm_vec_rand_vec_quick(&randvec);
|
||||
vm_vec_scale_add(&pos, &Player_obj->pos, &randvec, Player_obj->radius);
|
||||
ship_apply_local_damage(Player_obj, Player_obj, &pos, 25.0f, MISS_SHIELDS, CREATE_SPARKS);
|
||||
ship_apply_local_damage(Player_obj, Player_obj, &pos, 25.0f, -1, MISS_SHIELDS, CREATE_SPARKS);
|
||||
hud_get_target_strength(Player_obj, &shield, &integrity);
|
||||
HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "You whacked yourself down to %7.3f percent hull.\n", 9), 100.0f * integrity);
|
||||
break;
|
||||
|
||||
+9
-10
@@ -99,7 +99,7 @@ namespace
|
||||
break;
|
||||
default:
|
||||
// SDL gave us an unknown button. Just log it
|
||||
mprintf(("Unknown SDL button %i", e.button.button));
|
||||
mprintf(("Unknown SDL button %i\n", e.button.button));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -270,14 +270,13 @@ void mouse_mark_button( uint flags, int set)
|
||||
|
||||
Script_system.SetHookVar("MouseButton", 'i', flags);
|
||||
|
||||
//WMC - On Mouse Pressed and On Mouse Released hooks
|
||||
if (set == 1)
|
||||
{
|
||||
Script_system.RunCondition(CHA_MOUSEPRESSED);
|
||||
}
|
||||
else if (set == 0)
|
||||
{
|
||||
Script_system.RunCondition(CHA_MOUSERELEASED);
|
||||
if (Script_system.IsActiveAction(CHA_MOUSEPRESSED) || Script_system.IsActiveAction(CHA_MOUSERELEASED)) {
|
||||
//WMC - On Mouse Pressed and On Mouse Released hooks
|
||||
if (set == 1) {
|
||||
Script_system.RunCondition(CHA_MOUSEPRESSED);
|
||||
} else if (set == 0) {
|
||||
Script_system.RunCondition(CHA_MOUSERELEASED);
|
||||
}
|
||||
}
|
||||
|
||||
Script_system.RemHookVar("MouseButton");
|
||||
@@ -464,7 +463,7 @@ void mouse_event(int x, int y, int dx, int dy)
|
||||
Mouse_dx += dx;
|
||||
Mouse_dy += dy;
|
||||
|
||||
if(Mouse_dx != 0 || Mouse_dy != 0)
|
||||
if (Script_system.IsActiveAction(CHA_MOUSEMOVED) && (Mouse_dx != 0 || Mouse_dy != 0))
|
||||
{
|
||||
Script_system.RunCondition(CHA_MOUSEMOVED);
|
||||
}
|
||||
|
||||
+5
-1
@@ -14,8 +14,12 @@
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
#include "utils/Random.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
using Random = util::Random;
|
||||
|
||||
//==========================================================================
|
||||
// This installs the timer services and interrupts at the rate specified by
|
||||
// count_val. If 'function' isn't 0, the function pointed to by function will
|
||||
@@ -96,7 +100,7 @@ int timestamp();
|
||||
// gets a timestamp randomly between a and b milliseconds in
|
||||
// the future.
|
||||
inline int timestamp_rand(int a, int b) {
|
||||
return timestamp(myrand() % (b - a + 1) + a);
|
||||
return timestamp(Random::next(a, b));
|
||||
}
|
||||
|
||||
// Example that makes a ship fire in 1/2 second
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "lab/labv2_internal.h"
|
||||
#include "ship/shiphit.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
/**********************Click event handlers********************************************/
|
||||
|
||||
void destroy_subsystem(Tree* caller) {
|
||||
@@ -62,8 +64,8 @@ void destroy_ship(Button* /*caller*/) {
|
||||
std::map<AnimationTriggerType, std::map<int, bool>> manual_animation_triggers = {};
|
||||
std::map<AnimationTriggerType, bool> manual_animations = {};
|
||||
|
||||
bool triggered_primary_banks[MAX_SHIP_PRIMARY_BANKS];
|
||||
bool triggered_secondary_banks[MAX_SHIP_SECONDARY_BANKS];
|
||||
std::array<bool, MAX_SHIP_PRIMARY_BANKS> triggered_primary_banks;
|
||||
std::array<bool, MAX_SHIP_SECONDARY_BANKS> triggered_secondary_banks;
|
||||
|
||||
void reset_animations(Tree*) {
|
||||
if (getLabManager()->isSafeForShips()) {
|
||||
@@ -176,7 +178,8 @@ void Actions::open(Button* /*caller*/) {
|
||||
getLabManager()->loadWeapons();
|
||||
|
||||
dialogWindow = (DialogWindow*)getLabManager()->Screen->Add(new DialogWindow("Actions", gr_screen.center_offset_x + 250, gr_screen.center_offset_y + 50));
|
||||
dialogWindow->SetOwner(this);
|
||||
Assert(Opener != nullptr);
|
||||
dialogWindow->SetOwner(Opener->getDialog());
|
||||
|
||||
int y = 0;
|
||||
|
||||
@@ -184,8 +187,9 @@ void Actions::open(Button* /*caller*/) {
|
||||
dialogWindow->AddChild(btn);
|
||||
y += btn->GetHeight();
|
||||
|
||||
for (auto dialog : subDialogs) {
|
||||
auto dgo = new DialogOpener(dialog, 0, y);
|
||||
for (auto &dialog : subDialogs) {
|
||||
auto *dgo = new DialogOpener(dialog, 0, y);
|
||||
dialog->setOpener(dgo);
|
||||
dialogWindow->AddChild(dgo);
|
||||
y += dgo->GetHeight();
|
||||
}
|
||||
@@ -194,7 +198,7 @@ void Actions::open(Button* /*caller*/) {
|
||||
}
|
||||
|
||||
void Actions::update(LabMode newLabMode, int classIndex) {
|
||||
for (auto dialog : subDialogs) {
|
||||
for (auto &dialog : subDialogs) {
|
||||
dialog->update(newLabMode, classIndex);
|
||||
}
|
||||
}
|
||||
@@ -204,7 +208,8 @@ void DestroySubsystems::open(Button* /*caller*/) {
|
||||
return;
|
||||
|
||||
dialogWindow = (DialogWindow*)getLabManager()->Screen->Add(new DialogWindow("Destroy Subsystems", gr_screen.center_offset_x + 400, gr_screen.center_offset_y + 50));
|
||||
dialogWindow->SetOwner(this);
|
||||
Assert(Opener != nullptr);
|
||||
dialogWindow->SetOwner(Opener->getDialog());
|
||||
|
||||
update(getLabManager()->CurrentMode, getLabManager()->CurrentClass);
|
||||
}
|
||||
@@ -234,7 +239,8 @@ void ChangeLoadout::open(Button* /*caller*/) {
|
||||
return;
|
||||
|
||||
dialogWindow = (DialogWindow*)getLabManager()->Screen->Add(new DialogWindow("Change Loadout", gr_screen.center_offset_x + 400, gr_screen.center_offset_y + 50));
|
||||
dialogWindow->SetOwner(this);
|
||||
Assert(Opener != nullptr);
|
||||
dialogWindow->SetOwner(Opener->getDialog());
|
||||
|
||||
update(getLabManager()->CurrentMode, getLabManager()->CurrentClass);
|
||||
}
|
||||
@@ -349,7 +355,8 @@ void WeaponFire::open(Button* /*caller*/) {
|
||||
dialogWindow = (DialogWindow*)getLabManager()->Screen->Add(
|
||||
new DialogWindow("Fire weapons", gr_screen.center_offset_x + 400, gr_screen.center_offset_y + 50)
|
||||
);
|
||||
dialogWindow->SetOwner(this);
|
||||
Assert(Opener != nullptr);
|
||||
dialogWindow->SetOwner(Opener->getDialog());
|
||||
|
||||
update(getLabManager()->CurrentMode, getLabManager()->CurrentClass);
|
||||
}
|
||||
@@ -387,7 +394,8 @@ void AnimationTrigger::open(Button* /*caller*/) {
|
||||
dialogWindow = (DialogWindow*)getLabManager()->Screen->Add(
|
||||
new DialogWindow("Trigger animations", gr_screen.center_offset_x + 400, gr_screen.center_offset_y + 50)
|
||||
);
|
||||
dialogWindow->SetOwner(this);
|
||||
Assert(Opener != nullptr);
|
||||
dialogWindow->SetOwner(Opener->getDialog());
|
||||
|
||||
update(getLabManager()->CurrentMode, getLabManager()->CurrentClass);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
|
||||
|
||||
class DestroySubsystems : public LabDialog {
|
||||
public:
|
||||
~DestroySubsystems() override = default;
|
||||
|
||||
private:
|
||||
// Called when this dialog is opened via the top toolbar
|
||||
void open(Button* /*caller*/) override;
|
||||
|
||||
@@ -31,7 +34,10 @@ private:
|
||||
};
|
||||
|
||||
class ChangeLoadout : public LabDialog {
|
||||
public:
|
||||
~ChangeLoadout() override = default;
|
||||
|
||||
private:
|
||||
// Called when this dialog is opened via the top toolbar
|
||||
void open(Button* /*caller*/) override;
|
||||
|
||||
@@ -57,7 +63,10 @@ private:
|
||||
};
|
||||
|
||||
class WeaponFire : public LabDialog {
|
||||
public:
|
||||
~WeaponFire() override = default;
|
||||
|
||||
private:
|
||||
// Called when this dialog is opened via the top toolbar
|
||||
void open(Button* /*caller*/) override;
|
||||
|
||||
@@ -83,6 +92,10 @@ private:
|
||||
};
|
||||
|
||||
class AnimationTrigger : public LabDialog {
|
||||
public:
|
||||
~AnimationTrigger() override = default;
|
||||
|
||||
private:
|
||||
// Called when this dialog is opened via the top toolbar
|
||||
void open(Button* /*caller*/) override;
|
||||
|
||||
@@ -105,12 +118,14 @@ private:
|
||||
class Actions : public LabDialog {
|
||||
public:
|
||||
Actions() {
|
||||
subDialogs.push_back(new DestroySubsystems());
|
||||
subDialogs.push_back(new ChangeLoadout());
|
||||
subDialogs.push_back(new WeaponFire());
|
||||
subDialogs.push_back(new AnimationTrigger());
|
||||
subDialogs.emplace_back(std::make_shared<DestroySubsystems>());
|
||||
subDialogs.emplace_back(std::make_shared<ChangeLoadout>());
|
||||
subDialogs.emplace_back(std::make_shared<WeaponFire>());
|
||||
subDialogs.emplace_back(std::make_shared<AnimationTrigger>());
|
||||
}
|
||||
|
||||
~Actions() override = default;
|
||||
|
||||
// Called when this dialog is opened via the top toolbar
|
||||
void open(Button* /*caller*/) override;
|
||||
|
||||
@@ -133,5 +148,5 @@ public:
|
||||
|
||||
private:
|
||||
DialogWindow* dialogWindow = nullptr;
|
||||
SCP_vector<LabDialog*> subDialogs;
|
||||
SCP_vector<std::shared_ptr<LabDialog>> subDialogs;
|
||||
};
|
||||
@@ -36,7 +36,8 @@ void BackgroundDialog::open(Button* /*caller*/) {
|
||||
return;
|
||||
|
||||
dialogWindow = (DialogWindow*)getLabManager()->Screen->Add(new DialogWindow("Mission Backgrounds", gr_screen.center_offset_x + 250, gr_screen.center_offset_y + 50));
|
||||
dialogWindow->SetOwner(this);
|
||||
Assert(Opener != nullptr);
|
||||
dialogWindow->SetOwner(Opener->getDialog());
|
||||
|
||||
SCP_vector<SCP_string> missions;
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
|
||||
|
||||
class BackgroundDialog : public LabDialog {
|
||||
public:
|
||||
~BackgroundDialog() override = default;
|
||||
|
||||
private:
|
||||
// Called when this dialog is opened via the top toolbar
|
||||
void open(Button* /*caller*/) override;
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ void Descriptions::open(Button* /*caller*/) {
|
||||
gr_screen.center_offset_y + gr_screen.center_h - gr_screen.center_h / 6 - 50, gr_screen.center_w / 3,
|
||||
gr_screen.center_h / 6)
|
||||
);
|
||||
dialogWindow->SetOwner(this);
|
||||
Assert(Opener != nullptr);
|
||||
dialogWindow->SetOwner(Opener->getDialog());
|
||||
}
|
||||
|
||||
dialogWindow->DeleteChildren();
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
#include "lab/dialogs/lab_dialog.h"
|
||||
|
||||
class Descriptions : public LabDialog {
|
||||
public:
|
||||
~Descriptions() override = default;
|
||||
|
||||
private:
|
||||
void open(Button* caller) override;
|
||||
|
||||
void close() override {
|
||||
|
||||
@@ -7,7 +7,8 @@ void Options::open(Button* /*caller*/) {
|
||||
if (dialogWindow == nullptr) {
|
||||
dialogWindow = (DialogWindow*)getLabManager()->Screen->Add(new DialogWindow(
|
||||
"Flags Window", gr_screen.center_offset_x + gr_screen.center_w - 205, gr_screen.center_offset_y + 200));
|
||||
dialogWindow->SetOwner(this);
|
||||
Assert(Opener != nullptr);
|
||||
dialogWindow->SetOwner(Opener->getDialog());
|
||||
}
|
||||
update(getLabManager()->CurrentMode, getLabManager()->CurrentClass);
|
||||
}
|
||||
|
||||
Loaded 100 of 357 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user