mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
Compare commits
640
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19ca467c41 | ||
|
|
73736280f9 | ||
|
|
d5546f95e8 | ||
|
|
29edfeedea | ||
|
|
8cf20b0175 | ||
|
|
c85040efc4 | ||
|
|
a16be863f3 | ||
|
|
de80c254ae | ||
|
|
d703fcacc3 | ||
|
|
ddef6ea377 | ||
|
|
2b2333f692 | ||
|
|
4a819d7b20 | ||
|
|
a79dd70138 | ||
|
|
d5af6b5775 | ||
|
|
e3b24ea9e2 | ||
|
|
8645210c0a | ||
|
|
8c39bf54d4 | ||
|
|
bb43411bb1 | ||
|
|
622ae3ef2c | ||
|
|
ab7a1d8fd7 | ||
|
|
a246ef21aa | ||
|
|
e8d379a91c | ||
|
|
45862e06a9 | ||
|
|
f96a27feb4 | ||
|
|
f935a4ac38 | ||
|
|
601addd5c2 | ||
|
|
7cfbdcd6cf | ||
|
|
a009dc188c | ||
|
|
bb6d7515df | ||
|
|
f33b8d987e | ||
|
|
2fd98c908b | ||
|
|
bf1a9f192a | ||
|
|
ec8cb1ba71 | ||
|
|
e9e722244b | ||
|
|
2449dd6f70 | ||
|
|
2748cc6f97 | ||
|
|
660f72dcc7 | ||
|
|
f1506987eb | ||
|
|
e0d2d76dc2 | ||
|
|
09b35edae1 | ||
|
|
390ddb3fc8 | ||
|
|
0ab523bcf4 | ||
|
|
d72c5dbe75 | ||
|
|
324ccf37cb | ||
|
|
20094df4cd | ||
|
|
8db280ea83 | ||
|
|
a12e74fe9d | ||
|
|
82e2b67ed8 | ||
|
|
3311704253 | ||
|
|
7f7eed7a48 | ||
|
|
a95938a418 | ||
|
|
557a6a989c | ||
|
|
0f168ecc5b | ||
|
|
1768f5bb1b | ||
|
|
9e6790f317 | ||
|
|
993a9e9f18 | ||
|
|
516548d6be | ||
|
|
d1800d8622 | ||
|
|
c9dd97d223 | ||
|
|
c0ea9f5ffc | ||
|
|
319c8cc2a1 | ||
|
|
07b8e8d6cb | ||
|
|
80e2908aa3 | ||
|
|
25a6bf2b87 | ||
|
|
1930943d2d | ||
|
|
ed16a81e37 | ||
|
|
0121e439aa | ||
|
|
6fb72499f9 | ||
|
|
eb69133a57 | ||
|
|
2fcc3ecbf8 | ||
|
|
30b2b6caa7 | ||
|
|
4250aab601 | ||
|
|
a9e6c328cf | ||
|
|
07b6c17db0 | ||
|
|
2ce953aee8 | ||
|
|
05f71a30fb | ||
|
|
5dfef48bf6 | ||
|
|
f280f11824 | ||
|
|
e7b73da90e | ||
|
|
8698993d44 | ||
|
|
7b04d401fe | ||
|
|
a2b05f085b | ||
|
|
6e612cdc97 | ||
|
|
9a55d90f5d | ||
|
|
cc2b9fcf94 | ||
|
|
ed8af9d197 | ||
|
|
43f8b123a8 | ||
|
|
a930301ec1 | ||
|
|
112f2bb5ba | ||
|
|
5b8dce1dda | ||
|
|
b6c684644a | ||
|
|
d754f7fe0d | ||
|
|
2beaa886ca | ||
|
|
53bc7861bf | ||
|
|
9de4a49c42 | ||
|
|
6e5626722a | ||
|
|
1fa5068555 | ||
|
|
d7f0051394 | ||
|
|
6b9dacb598 | ||
|
|
eef9a340b1 | ||
|
|
4a9c6567cc | ||
|
|
ead7f10006 | ||
|
|
c7abceaf30 | ||
|
|
a057917958 | ||
|
|
010441fdbf | ||
|
|
b32698012a | ||
|
|
581fec1079 | ||
|
|
a44adde0e5 | ||
|
|
e88542580e | ||
|
|
b5043417fc | ||
|
|
049fa72df3 | ||
|
|
b8231a4ec1 | ||
|
|
1b563360ed | ||
|
|
3ee1321d9d | ||
|
|
fb9304bae0 | ||
|
|
87e6a1350d | ||
|
|
bf05cadd14 | ||
|
|
29fe214d16 | ||
|
|
e27bf0c925 | ||
|
|
83c4858f18 | ||
|
|
6104e6fcd3 | ||
|
|
113b57df29 | ||
|
|
a25e12a041 | ||
|
|
fa29b91e68 | ||
|
|
6b91a6210c | ||
|
|
6098675cc0 | ||
|
|
01331436c9 | ||
|
|
dbdc2457e0 | ||
|
|
7013981de5 | ||
|
|
8b9ac5f8a9 | ||
|
|
7cf2832113 | ||
|
|
6f5de692b9 | ||
|
|
67b3c6ce46 | ||
|
|
b3f0016d0e | ||
|
|
bd864baead | ||
|
|
e6956b1b4c | ||
|
|
e0fd189833 | ||
|
|
28f06adc12 | ||
|
|
3f830fbe78 | ||
|
|
0371b2d889 | ||
|
|
1bae96df07 | ||
|
|
f3880eb03f | ||
|
|
263bf57c79 | ||
|
|
d665c1ccf1 | ||
|
|
5bde8bfebf | ||
|
|
a002219373 | ||
|
|
a923f64691 | ||
|
|
7e06ccac7d | ||
|
|
2ff2732cc4 | ||
|
|
b328495b31 | ||
|
|
9acbd9bc94 | ||
|
|
3a1e1b23d5 | ||
|
|
e1ddbd5875 | ||
|
|
1dfe0ab644 | ||
|
|
22a71ea3e7 | ||
|
|
8821cdf2cd | ||
|
|
3ad92cce4e | ||
|
|
90576274fb | ||
|
|
3b49699b19 | ||
|
|
3a4794305f | ||
|
|
7facbcc262 | ||
|
|
d38cc1f3db | ||
|
|
b893075efe | ||
|
|
e3ad6664e0 | ||
|
|
9abfb1e3f3 | ||
|
|
2004d00588 | ||
|
|
abb8bbaf68 | ||
|
|
6baadfef6f | ||
|
|
b6378d6b0b | ||
|
|
a6cb299f28 | ||
|
|
88c788a7b4 | ||
|
|
06c3087038 | ||
|
|
5e8815146d | ||
|
|
01a4d01fd3 | ||
|
|
619e0e125e | ||
|
|
f18c4d39cc | ||
|
|
ba5406f13c | ||
|
|
779594b66a | ||
|
|
2163cfc5d3 | ||
|
|
b4c1c83a84 | ||
|
|
d0d12b572a | ||
|
|
7b61f20eda | ||
|
|
e38c30bca6 | ||
|
|
7d87aad3f3 | ||
|
|
6a37be55ba | ||
|
|
1f625f3a7f | ||
|
|
de98902de4 | ||
|
|
13db223f03 | ||
|
|
92c1ca2bb7 | ||
|
|
7fb8e64434 | ||
|
|
91b516c1c4 | ||
|
|
764e730e80 | ||
|
|
d10309d3d4 | ||
|
|
b7e99089d7 | ||
|
|
154e5f6b4b | ||
|
|
6e9745c671 | ||
|
|
709de972e6 | ||
|
|
43b89d29b6 | ||
|
|
6bfe6071d0 | ||
|
|
0bbd099853 | ||
|
|
12f9ccc46f | ||
|
|
bc1f36a423 | ||
|
|
5672f0ed53 | ||
|
|
9514f9babb | ||
|
|
151bfd2f26 | ||
|
|
f7d83978fc | ||
|
|
014b73e54e | ||
|
|
cd267972b9 | ||
|
|
97a67958a7 | ||
|
|
d2f4a982e6 | ||
|
|
a745f6179a | ||
|
|
e3544e687e | ||
|
|
3088477a3e | ||
|
|
b92f362a80 | ||
|
|
ee1330669a | ||
|
|
c1003f76b8 | ||
|
|
aa4a58e53b | ||
|
|
85ed537960 | ||
|
|
a5ae3e7de8 | ||
|
|
52fd857736 | ||
|
|
370e12dba5 | ||
|
|
5b44ea6a34 | ||
|
|
28631b743c | ||
|
|
8c1693df46 | ||
|
|
c5be6095b2 | ||
|
|
3f227e7eeb | ||
|
|
d2ddcfe4e0 | ||
|
|
859d44f3a3 | ||
|
|
7780cf4617 | ||
|
|
45b6362777 | ||
|
|
c6c5d37f48 | ||
|
|
753856421d | ||
|
|
90eb343aa9 | ||
|
|
39d9251e2c | ||
|
|
5f5d210a9b | ||
|
|
cfdeeb7109 | ||
|
|
62cacb3f08 | ||
|
|
d1397147e4 | ||
|
|
f5cbed9cdb | ||
|
|
f193c5233d | ||
|
|
65998e8d04 | ||
|
|
d6c4fb8e53 | ||
|
|
f6f16b6a32 | ||
|
|
39434fe7b1 | ||
|
|
84023002db | ||
|
|
e22b8e44b7 | ||
|
|
3fb034ab7f | ||
|
|
6ef7616dcc | ||
|
|
46310c0667 | ||
|
|
13d245a101 | ||
|
|
53aec7e4e4 | ||
|
|
c06007bcac | ||
|
|
50ef38bf21 | ||
|
|
7dbb16bf06 | ||
|
|
ca3207de47 | ||
|
|
00078b9c77 | ||
|
|
7155163223 | ||
|
|
cfb5a04aa9 | ||
|
|
cd2aac4faa | ||
|
|
c8ace18317 | ||
|
|
54413fa0fb | ||
|
|
dc31a9f663 | ||
|
|
663e3ade36 | ||
|
|
bfddd5f758 | ||
|
|
48baf8c0af | ||
|
|
1699bf1570 | ||
|
|
805e858123 | ||
|
|
260526cbd6 | ||
|
|
ede0645d3b | ||
|
|
e473ff011c | ||
|
|
7222c8a398 | ||
|
|
48d5211e68 | ||
|
|
f707d471da | ||
|
|
5e79021da3 | ||
|
|
4d66740f7a | ||
|
|
0d3290f14d | ||
|
|
b4f95c3b0c | ||
|
|
932c91b526 | ||
|
|
0f9fee8d7c | ||
|
|
4581897b09 | ||
|
|
883566ad05 | ||
|
|
62f41ceb73 | ||
|
|
df6cf572ac | ||
|
|
1d16bb28a4 | ||
|
|
862cf0d82c | ||
|
|
ead4376f47 | ||
|
|
a8618a569c | ||
|
|
98846a3f88 | ||
|
|
dbd7b2f49a | ||
|
|
743cd5035d | ||
|
|
22b1f08592 | ||
|
|
530118785a | ||
|
|
d84486c357 | ||
|
|
2744af109a | ||
|
|
5c4be3d392 | ||
|
|
701a8de527 | ||
|
|
303480eecf | ||
|
|
f9aad01349 | ||
|
|
1269feae44 | ||
|
|
a214488395 | ||
|
|
7dbb831efa | ||
|
|
4587c33e05 | ||
|
|
3c22bf9c49 | ||
|
|
fa52bf747d | ||
|
|
2ffa4ca982 | ||
|
|
833326aa76 | ||
|
|
ba225dcf20 | ||
|
|
0d461c1fc4 | ||
|
|
f8fdda34a6 | ||
|
|
798f1c8c2f | ||
|
|
1228a56e42 | ||
|
|
ab483eeea8 | ||
|
|
d1c12cc57f | ||
|
|
3d646e591d | ||
|
|
20efcb9717 | ||
|
|
b1a59ec8b3 | ||
|
|
7bece7a946 | ||
|
|
7606df6082 | ||
|
|
e534adf107 | ||
|
|
1acd763e4d | ||
|
|
6101627381 | ||
|
|
741231be77 | ||
|
|
e21575558e | ||
|
|
dd287c4288 | ||
|
|
aa316433f1 | ||
|
|
9f63b17328 | ||
|
|
2e37aceab9 | ||
|
|
42a7861990 | ||
|
|
e44456333e | ||
|
|
be8cfe1180 | ||
|
|
b16d953bed | ||
|
|
047458fc0c | ||
|
|
1273cbcdc9 | ||
|
|
71a861bd6f | ||
|
|
d7a4fc5eb1 | ||
|
|
55be80dd6a | ||
|
|
b278e95877 | ||
|
|
215d90ccc0 | ||
|
|
18a311e779 | ||
|
|
7fd20c667a | ||
|
|
6f8e60eb17 | ||
|
|
b29f183aee | ||
|
|
2f23a794fb | ||
|
|
ba88b01f51 | ||
|
|
1091a93752 | ||
|
|
a654b61d0c | ||
|
|
1947f0d9bd | ||
|
|
4aba6defdb | ||
|
|
29f7a38482 | ||
|
|
5c0b5480c8 | ||
|
|
20e139baf9 | ||
|
|
bd9d16fd69 | ||
|
|
6c473e7c09 | ||
|
|
e03bb48bb3 | ||
|
|
e3927104bb | ||
|
|
2e4a568e2b | ||
|
|
326a0c32d0 | ||
|
|
4344642d3e | ||
|
|
0e13c32d73 | ||
|
|
79769ba76f | ||
|
|
b404b137a0 | ||
|
|
c7a16a109a | ||
|
|
99bbc23b64 | ||
|
|
aeedcc22a1 | ||
|
|
0fb6fbec6d | ||
|
|
4dbaf40991 | ||
|
|
505f8a93d0 | ||
|
|
9cafab6e26 | ||
|
|
bb2bd3fc1d | ||
|
|
5e436dd276 | ||
|
|
4c948569c8 | ||
|
|
5bde7cd1b6 | ||
|
|
4b935731ca | ||
|
|
c1fdb640b1 | ||
|
|
670665d0af | ||
|
|
298265c831 | ||
|
|
65d4423b41 | ||
|
|
30c168693b | ||
|
|
ba18cecf44 | ||
|
|
fff19927e7 | ||
|
|
316ef398e8 | ||
|
|
53a2533169 | ||
|
|
d698175670 | ||
|
|
de39f591a1 | ||
|
|
898bbd7260 | ||
|
|
3a322edbcf | ||
|
|
fd56d8f850 | ||
|
|
8951dc19b1 | ||
|
|
a061a08d4d | ||
|
|
69e0f66d4b | ||
|
|
f392c66c93 | ||
|
|
3d9b0c70cd | ||
|
|
3b5f6424a1 | ||
|
|
5de12e2e50 | ||
|
|
32c0f246e2 | ||
|
|
b3e3fcf1d4 | ||
|
|
3f39cbd76a | ||
|
|
2e2a632055 | ||
|
|
e2dd86ba99 | ||
|
|
79c8d67281 | ||
|
|
855b7fe78c | ||
|
|
3aa6cd8a87 | ||
|
|
acaa93a9d2 | ||
|
|
a2f6f791e4 | ||
|
|
76bb1b6232 | ||
|
|
0f3f5eeeb9 | ||
|
|
95a0e2f17c | ||
|
|
33c53198a6 | ||
|
|
05b94bb26b | ||
|
|
64a213c667 | ||
|
|
3e00b94a89 | ||
|
|
fe236b337f | ||
|
|
1de7ffc782 | ||
|
|
ecac97c58a | ||
|
|
97092997e9 | ||
|
|
e08805a5e2 | ||
|
|
84ea99c371 | ||
|
|
435a525e4f | ||
|
|
30ae011d10 | ||
|
|
7466595478 | ||
|
|
ca6a9017c6 | ||
|
|
f394708f09 | ||
|
|
c636ea5659 | ||
|
|
47bd900918 | ||
|
|
a6e3c74ad1 | ||
|
|
0fc7c334a1 | ||
|
|
83bd293a3a | ||
|
|
eb5be1c577 | ||
|
|
df39ac45d7 | ||
|
|
dca2ae715f | ||
|
|
884e722109 | ||
|
|
7184fbe781 | ||
|
|
a6611bf5b6 | ||
|
|
a5091a4cb9 | ||
|
|
66619b9e46 | ||
|
|
59064fdf89 | ||
|
|
af0b1fc9bb | ||
|
|
a64a587c63 | ||
|
|
675a806912 | ||
|
|
e9ad970c20 | ||
|
|
d11c89d8b1 | ||
|
|
ce6458b64f | ||
|
|
0a20827620 | ||
|
|
fa4c66dfac | ||
|
|
8a188c56f6 | ||
|
|
87ed583b28 | ||
|
|
3e2030bf01 | ||
|
|
1396d44265 | ||
|
|
d0c07f5dd8 | ||
|
|
527b46443b | ||
|
|
f7dd1172c6 | ||
|
|
2e4a657b57 | ||
|
|
13a465825e | ||
|
|
943e5c395b | ||
|
|
c035f1341c | ||
|
|
19ef658f56 | ||
|
|
34ba050242 | ||
|
|
f9b6bf56c9 | ||
|
|
4101d01e82 | ||
|
|
d785b9c141 | ||
|
|
bc2fca0469 | ||
|
|
681b2005ea | ||
|
|
a464e8615c | ||
|
|
32fe227f6d | ||
|
|
1ecebe3ef3 | ||
|
|
20cea22a05 | ||
|
|
c7979129c7 | ||
|
|
75cdc8efa2 | ||
|
|
10b1fa89cf | ||
|
|
a804d21764 | ||
|
|
618804c1ff | ||
|
|
bcc0d51040 | ||
|
|
61a0973ad4 | ||
|
|
a387fdeb36 | ||
|
|
e742920c93 | ||
|
|
2688e7cec5 | ||
|
|
8066ac02cc | ||
|
|
30e7747d6e | ||
|
|
38e02566c7 | ||
|
|
60c39c698a | ||
|
|
4f562c73dc | ||
|
|
658860db57 | ||
|
|
2a7a93bbe4 | ||
|
|
59528308a5 | ||
|
|
c294d2b4ff | ||
|
|
0c7350dc23 | ||
|
|
ae8529e4c4 | ||
|
|
00e2ef29f5 | ||
|
|
b6310539e7 | ||
|
|
0d982ecf52 | ||
|
|
fce754f899 | ||
|
|
51b57bc2d8 | ||
|
|
06f3bb77d1 | ||
|
|
8f48c1e3e2 | ||
|
|
9ed3eb1f2e | ||
|
|
b85f688080 | ||
|
|
3c695af35c | ||
|
|
2c64cd946d | ||
|
|
32a9cace6d | ||
|
|
56351c6fb9 | ||
|
|
f3a94d4fc8 | ||
|
|
916a5653dd | ||
|
|
7cc47c8233 | ||
|
|
06e560ae76 | ||
|
|
4933906d18 | ||
|
|
b15c00b012 | ||
|
|
e0d89e41ba | ||
|
|
2ff2aa46e4 | ||
|
|
0e07b07aa5 | ||
|
|
dc458964c1 | ||
|
|
256f3a46a2 | ||
|
|
8fa332b869 | ||
|
|
93eeab4f67 | ||
|
|
e37574d6d5 | ||
|
|
a2558bec5f | ||
|
|
284ba488f3 | ||
|
|
a159eba0ce | ||
|
|
69e9d26c71 | ||
|
|
7aaca0a84a | ||
|
|
d3a6d9c719 | ||
|
|
c20ae7cd6b | ||
|
|
ccff68164a | ||
|
|
0d12b0554c | ||
|
|
9e26d4a664 | ||
|
|
1b9a02ff5f | ||
|
|
493c9e004f | ||
|
|
857c643253 | ||
|
|
552a8ff32b | ||
|
|
798eecdadf | ||
|
|
e2504bd162 | ||
|
|
e577e8db81 | ||
|
|
abfa4b8fb5 | ||
|
|
063d7afcfe | ||
|
|
5ac80c26aa | ||
|
|
4d33ed6c92 | ||
|
|
7153f6b12e | ||
|
|
a8ab80be21 | ||
|
|
68c8cdec1e | ||
|
|
10bfc687d9 | ||
|
|
87eb2945f2 | ||
|
|
de31fc4dc9 | ||
|
|
47d5582a31 | ||
|
|
8a410957ff | ||
|
|
e3c872b621 | ||
|
|
db0598720d | ||
|
|
8062b67442 | ||
|
|
636b4e81df | ||
|
|
da02709fb0 | ||
|
|
ceb202e336 | ||
|
|
3acf2d69b0 | ||
|
|
3863c8fd14 | ||
|
|
a75ad7573e | ||
|
|
dfeefad3f9 | ||
|
|
5e8c5934ed | ||
|
|
5324d90425 | ||
|
|
3d983207f2 | ||
|
|
67a2732c69 | ||
|
|
a91833e13a | ||
|
|
363b5743eb | ||
|
|
ddb69482cf | ||
|
|
81e86e6173 | ||
|
|
8513783049 | ||
|
|
3b7427c898 | ||
|
|
184bb632f6 | ||
|
|
ecf90fe3fe | ||
|
|
7cde6a8805 | ||
|
|
de3d8779e8 | ||
|
|
bffd3750f8 | ||
|
|
534c1f2c33 | ||
|
|
1d3e62f007 | ||
|
|
4b0c366328 | ||
|
|
e362105261 | ||
|
|
8ac7853dee | ||
|
|
2232ec228f | ||
|
|
132818ff86 | ||
|
|
963e06e390 | ||
|
|
d24175a393 | ||
|
|
10b68c17d2 | ||
|
|
70d553a663 | ||
|
|
3c583e53f2 | ||
|
|
d195c1333c | ||
|
|
77f96638d7 | ||
|
|
79cff94a88 | ||
|
|
12d391a7a5 | ||
|
|
3cb66670d0 | ||
|
|
63445dec5a | ||
|
|
6198474b78 | ||
|
|
ceab0f82a9 | ||
|
|
ae5d458dde | ||
|
|
1440200304 | ||
|
|
e84fc4fcd8 | ||
|
|
1454161b40 | ||
|
|
4ed2f34cd4 | ||
|
|
15ab1c9afe | ||
|
|
827c00cf40 | ||
|
|
ad841af1b2 | ||
|
|
3bf67572cb | ||
|
|
d13268e78d | ||
|
|
d92e8f4155 | ||
|
|
db9261d9d9 | ||
|
|
70d1abc6ff | ||
|
|
475e3eb95e | ||
|
|
d37b4a4482 | ||
|
|
89a50da355 | ||
|
|
375ae25ac9 | ||
|
|
76a8433b5b | ||
|
|
acc6686f67 | ||
|
|
32c5f17d15 | ||
|
|
12dd79a0c7 | ||
|
|
2ca4616f37 | ||
|
|
8c976a174f | ||
|
|
cb38d89a1b | ||
|
|
4b29f51b0e | ||
|
|
4dbdcd3783 | ||
|
|
d7fc0bdf8e | ||
|
|
e41077e07f | ||
|
|
9ca14b6dd2 | ||
|
|
bd1cf2d07d | ||
|
|
4b50fe5eca | ||
|
|
48ea08219e | ||
|
|
e2e689d454 | ||
|
|
d86ebd5eb2 | ||
|
|
471c4991eb | ||
|
|
26417e2154 | ||
|
|
222f3e00a7 | ||
|
|
9ab7a4626a | ||
|
|
593372eea4 | ||
|
|
5f3dc91a24 | ||
|
|
3e2008c23c | ||
|
|
a047be61d5 | ||
|
|
79c096a488 | ||
|
|
9b34225522 | ||
|
|
b01137ab53 | ||
|
|
45ead0645c | ||
|
|
fe0b3d2099 | ||
|
|
d7c2a962ff | ||
|
|
310898dfd9 | ||
|
|
18a8e280dc | ||
|
|
d129bcb6b7 | ||
|
|
2403cd7c44 |
No files matched your search
@@ -1,6 +1,8 @@
|
||||
|
||||
cmake/ @asarium
|
||||
|
||||
ci/ @asarium @z64555
|
||||
|
||||
code/executor/ @asarium
|
||||
|
||||
code/scpui/ @asarium
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
configuration: [FastDebug, Release]
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-8a6bbbe
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
@@ -210,3 +210,96 @@ jobs:
|
||||
DATACORDER_USER: ${{ secrets.DATACORDER_USER }}
|
||||
DATACORDER_SSHPASS: ${{ secrets.DATACORDER_PASSWORD }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh nightly
|
||||
|
||||
build_mac:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [FastDebug, Release]
|
||||
compiler: [clang]
|
||||
arch: [x86_64]
|
||||
name: Mac
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-mac
|
||||
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-mac.outputs.cache-hit }}
|
||||
setup-python: 'false'
|
||||
aqtversion: ==1.1.3
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: Set up test version
|
||||
shell: bash
|
||||
run: |
|
||||
[[ "${{ github.ref }}" =~ ^refs\/heads\/test\/(.*)$ ]]
|
||||
# Override the revision string so that the builds are named correctly
|
||||
echo "set(FSO_VERSION_REVISION_STR ${BASH_REMATCH[1]})" > "version_override.cmake"]
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
ARCHITECTURE: ${{ matrix.arch }}
|
||||
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 all
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
XDG_RUNTIME_DIR: /root
|
||||
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
|
||||
- name: Package build result
|
||||
working-directory: ./build
|
||||
run: cd bin && tar -cvzf macos-build.tar *.app
|
||||
- name: Upload build result
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: mac-${{ matrix.configuration }}
|
||||
path: ${{ github.workspace }}/build/bin/macos-build.tar
|
||||
mac_zip:
|
||||
name: Build Mac distribution zip
|
||||
needs: build_mac
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-748b19d
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: '0'
|
||||
ref: '${{ github.ref }}'
|
||||
- name: Download Release builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: mac-Release
|
||||
path: builds
|
||||
- name: Download FastDebug builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: mac-FastDebug
|
||||
path: builds
|
||||
- name: Create Distribution package
|
||||
working-directory: ./builds
|
||||
run: $GITHUB_WORKSPACE/ci/linux/create_dist_pack.sh Mac
|
||||
- name: Upload result package
|
||||
working-directory: ./builds
|
||||
env:
|
||||
INDIEGAMES_USER: ${{ secrets.INDIEGAMES_USER }}
|
||||
INDIEGAMES_SSHPASS: ${{ secrets.INDIEGAMES_PASSWORD }}
|
||||
DATACORDER_USER: ${{ secrets.DATACORDER_USER }}
|
||||
DATACORDER_SSHPASS: ${{ secrets.DATACORDER_PASSWORD }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh nightly
|
||||
@@ -34,23 +34,6 @@ jobs:
|
||||
release_name: Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
- name: Store upload URL
|
||||
run: |
|
||||
echo -n "${{ steps.create_release.outputs.upload_url }}" > upload_url
|
||||
- name: Persist upload URL
|
||||
uses: actions/upload-artifact@v2
|
||||
# Upload the url file for use with other runners
|
||||
# upload-artifact.inputs:
|
||||
# if-no-files-found=warn What to do if path fails to find any files.
|
||||
# warn Display a warning but do not fail the action
|
||||
# error Display an error and fail the action
|
||||
# ignore No display, No fail
|
||||
# retention-days=0 Days to keep the artifact.
|
||||
# 0 Use default retention policy of the repo
|
||||
# 1~90 Min 1, Max 90 days to keep the artifact unless changed by repo settings page
|
||||
with:
|
||||
name: upload_url # name of artifact
|
||||
path: ${{ github.workspace }}/upload_url # source location of artifact to upload
|
||||
|
||||
build_linux:
|
||||
strategy:
|
||||
@@ -61,7 +44,7 @@ jobs:
|
||||
name: Linux
|
||||
needs: create_release # Don't run this job until create_release is done and successful
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-8a6bbbe
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
@@ -134,6 +117,15 @@ jobs:
|
||||
run: $GITHUB_WORKSPACE/ci/linux/generate_appimage.sh $GITHUB_WORKSPACE/build/install
|
||||
- name: Upload build result
|
||||
uses: actions/upload-artifact@v2
|
||||
# Upload the url file for use with other runners
|
||||
# upload-artifact.inputs:
|
||||
# if-no-files-found=warn What to do if path fails to find any files.
|
||||
# warn Display a warning but do not fail the action
|
||||
# error Display an error and fail the action
|
||||
# ignore No display, No fail
|
||||
# retention-days=0 Days to keep the artifact.
|
||||
# 0 Use default retention policy of the repo
|
||||
# 1~90 Min 1, Max 90 days to keep the artifact unless changed by repo settings page
|
||||
with:
|
||||
name: linux-${{ matrix.configuration }}
|
||||
path: ${{ github.workspace }}/build/install/*.AppImage
|
||||
@@ -148,21 +140,6 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: '0' # value 0 to fetch all history and tags for all branches
|
||||
- name: Download upload URL
|
||||
uses: actions/download-artifact@v1
|
||||
# Downloads and extracts uploaded artifact by name
|
||||
# github_token default ${{secrets.GITHUB_TOKEN}}
|
||||
# name name of artifact to download
|
||||
# latest Download latest artifact (default true)
|
||||
# path Where to save the artifact, (default extract_here)
|
||||
# repo Source repo of artifact (default ${{github.repository}})
|
||||
with:
|
||||
name: upload_url
|
||||
path: .
|
||||
- name: Extract upload URL
|
||||
# Extract the URL from file and stuff it into a shell variable
|
||||
id: extractUploadUrl
|
||||
run: echo "::set-output name=upload_url::$(cat upload_url)"
|
||||
- name: Download Release builds
|
||||
# Grab the release builds
|
||||
uses: actions/download-artifact@v2
|
||||
@@ -291,16 +268,6 @@ jobs:
|
||||
submodules: true
|
||||
fetch-depth: '0'
|
||||
|
||||
- name: Download upload URL
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: upload_url
|
||||
path: .
|
||||
|
||||
- name: Extract upload URL
|
||||
id: extractUploadUrl
|
||||
run: echo "::set-output name=upload_url::$(cat upload_url)"
|
||||
|
||||
- name: Download Release builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
@@ -348,6 +315,6 @@ jobs:
|
||||
needs: [linux_zip, windows_zip]
|
||||
uses: ./.github/workflows/post-build-release.yaml
|
||||
with:
|
||||
linux_result: ${{ needs.build_linux.result }}
|
||||
windows_result: ${{ needs.build_windows.result }}
|
||||
linux_result: ${{ needs.linux_zip.result }}
|
||||
windows_result: ${{ needs.windows_zip.result }}
|
||||
releaseTag: ${{ github.ref_name }}
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
configuration: [FastDebug, Release]
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-11e553c
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-8a6bbbe
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
@@ -224,3 +224,95 @@ jobs:
|
||||
DATACORDER_USER: ${{ secrets.DATACORDER_USER }}
|
||||
DATACORDER_SSHPASS: ${{ secrets.DATACORDER_PASSWORD }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh test
|
||||
|
||||
build_mac:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [FastDebug, Release]
|
||||
compiler: [clang]
|
||||
arch: [x86_64]
|
||||
name: Mac
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-mac
|
||||
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-mac.outputs.cache-hit }}
|
||||
setup-python: 'false'
|
||||
aqtversion: ==1.1.3
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: Set up test version
|
||||
shell: bash
|
||||
run: |
|
||||
[[ "${{ github.ref }}" =~ ^refs\/heads\/test\/(.*)$ ]]
|
||||
# Override the revision string so that the builds are named correctly
|
||||
echo "set(FSO_VERSION_REVISION_STR ${BASH_REMATCH[1]})" > "version_override.cmake"
|
||||
- name: Configure CMake
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
ARCHITECTURE: ${{ matrix.arch }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/configure_cmake.sh
|
||||
- name: Compile
|
||||
working-directory: ./build
|
||||
run: LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH ninja all
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
XDG_RUNTIME_DIR: /root
|
||||
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
|
||||
- name: Package build result
|
||||
working-directory: ./build
|
||||
run: cd bin && tar -cvzf macos-build.tar *.app
|
||||
- name: Upload build result
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: mac-${{ matrix.configuration }}
|
||||
path: ${{ github.workspace }}/build/bin/macos-build.tar
|
||||
mac_zip:
|
||||
name: Build Mac distribution zip
|
||||
needs: build_mac
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/scp-fs2open/sftp_upload:sha-748b19d
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
name: Checkout
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: '0'
|
||||
ref: '${{ github.ref }}'
|
||||
- name: Download Release builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: mac-Release
|
||||
path: builds
|
||||
- name: Download FastDebug builds
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: mac-FastDebug
|
||||
path: builds
|
||||
- name: Create Distribution package
|
||||
working-directory: ./builds
|
||||
run: $GITHUB_WORKSPACE/ci/linux/create_dist_pack.sh Mac
|
||||
- name: Upload result package
|
||||
working-directory: ./builds
|
||||
env:
|
||||
INDIEGAMES_USER: ${{ secrets.INDIEGAMES_USER }}
|
||||
INDIEGAMES_SSHPASS: ${{ secrets.INDIEGAMES_PASSWORD }}
|
||||
DATACORDER_USER: ${{ secrets.DATACORDER_USER }}
|
||||
DATACORDER_SSHPASS: ${{ secrets.DATACORDER_PASSWORD }}
|
||||
run: $GITHUB_WORKSPACE/ci/linux/upload_dist_package.sh test
|
||||
@@ -25,7 +25,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-11e553c
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-8a6bbbe
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
|
||||
@@ -6,10 +6,10 @@ on:
|
||||
inputs:
|
||||
linux_result: # job.result; either success, failure, canceled or skipped
|
||||
required: true
|
||||
type: boolean
|
||||
type: string
|
||||
windows_result: # job.result
|
||||
required: true
|
||||
type: boolean
|
||||
type: string
|
||||
releaseTag:
|
||||
required: true
|
||||
type: string
|
||||
@@ -30,26 +30,33 @@ env:
|
||||
NEBULA_PASSWORD: ${{ secrets.NEBULA_PASSWORD }}
|
||||
HLP_API: ${{ secrets.HLP_API }}
|
||||
HLP_KEY: ${{ secrets.HLP_KEY }}
|
||||
RELEASE_TAG: ${{ github.event.inputs.releaseTag }}
|
||||
RELEASE_TAG: ${{ inputs.releaseTag }}
|
||||
|
||||
jobs:
|
||||
post_builds:
|
||||
name: Post builds on Nebula and the forums
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Caller
|
||||
# Name of what called this yaml. Used to debug auto/manual step being triggered
|
||||
run: echo "github.event_name= ${{ github.event_name }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
ref: ${{ env.RELEASE_TAG }}
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: pip install -r ci/post/requirements.txt
|
||||
|
||||
- name: Post Builds (Auto trigger)
|
||||
if: ${{ github.event_name == 'workflow_call' }}
|
||||
if: ${{ (github.event_name == 'workflow_call') || (github.event_name == 'push') }}
|
||||
env:
|
||||
LINUX_RESULT: ${{ github.event.inputs.linux_result }}
|
||||
WINDOWS_RESULT: ${{ github.event.inputs.windows_result }}
|
||||
LINUX_RESULT: ${{ inputs.linux_result }}
|
||||
WINDOWS_RESULT: ${{ inputs.windows_result }}
|
||||
run: python ci/post/main.py release
|
||||
|
||||
|
||||
- name: Post Builds (Manual trigger)
|
||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
# assume user knows what they're doing...
|
||||
@@ -57,6 +64,6 @@ jobs:
|
||||
LINUX_RESULT: success
|
||||
WINDOWS_RESULT: success
|
||||
run: |
|
||||
echo "releaseTag= ${{ github.event.inputs.releaseTag }}"
|
||||
echo "releaseTag= ${{ env.RELEASE_TAG }}"
|
||||
echo "repository= ${{ github.repository }}"
|
||||
python ci/post/main.py release
|
||||
@@ -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-11e553c
|
||||
container: ghcr.io/scp-fs2open/linux_build:sha-8a6bbbe
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-lin
|
||||
@@ -156,3 +156,64 @@ jobs:
|
||||
else
|
||||
"./bin/$CONFIGURATION/unittests" --gtest_shuffle
|
||||
fi
|
||||
|
||||
build_mac:
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
compiler: [clang]
|
||||
arch: [x86_64]
|
||||
cmake_options: [""]
|
||||
name: Mac
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Cache Qt
|
||||
id: cache-qt-mac
|
||||
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-mac.outputs.cache-hit }}
|
||||
setup-python: 'false'
|
||||
aqtversion: ==1.1.3
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
name: Checkout
|
||||
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 }}
|
||||
COMPILER: ${{ matrix.compiler }}
|
||||
ARCHITECTURE: ${{ matrix.arch }}
|
||||
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 all
|
||||
- name: Run Tests
|
||||
working-directory: ./build
|
||||
env:
|
||||
CONFIGURATION: ${{ matrix.configuration }}
|
||||
XDG_RUNTIME_DIR: /root
|
||||
run: $GITHUB_WORKSPACE/ci/linux/run_tests.sh
|
||||
- name: Run Clang Tidy
|
||||
# Clang-tidy reuses the precompiled headers so this only makes sense for the clang compilers
|
||||
if: startsWith(matrix.compiler, 'clang-')
|
||||
run: $GITHUB_WORKSPACE/ci/linux/clang_tidy.sh ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}
|
||||
- name: Process clang-tidy warnings
|
||||
if: startsWith(matrix.compiler, 'clang-')
|
||||
uses: asarium/clang-tidy-action@v1
|
||||
with:
|
||||
fixesFile: clang-fixes.yaml
|
||||
+2
-1
@@ -43,7 +43,8 @@ PROJECT(FS2_Open)
|
||||
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
# Currently only GCC support ccache properly without generating additional warnings
|
||||
if(CCACHE_PROGRAM AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
# Enable ccache on Mac even for clang
|
||||
if(CCACHE_PROGRAM AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR APPLE))
|
||||
if(NOT CMAKE_C_COMPILER_LAUNCHER)
|
||||
message(STATUS "Using ccache for C compilation: ${CCACHE_PROGRAM}")
|
||||
SET(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
|
||||
|
||||
@@ -3,6 +3,52 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
|
||||
## [22.0.0] - 2022-04-01 ([Thread](https://www.hard-light.net/forums/index.php?topic=98125.0))
|
||||
### Changed
|
||||
- Controls5 PR (aka multi-joy)
|
||||
- Timing system upgrade
|
||||
- Model animation code upgrades
|
||||
|
||||
|
||||
## [21.4.0] - 2021-09-27 ([Thread](https://www.hard-light.net/forums/index.php?topic=97802.0))
|
||||
### Changed
|
||||
- Multiple features; see thread
|
||||
- Multiple bugfixes; see thread
|
||||
### Deprecations
|
||||
- `script-eval` has been deprecated in favor of `script-eval-block`
|
||||
- renamed `hud-set-retail-gauge-active` to `hud-set-builtin-gauge-active`
|
||||
|
||||
|
||||
## [21.2.0] - 2021-05-02 ([Thread](https://www.hard-light.net/forums/index.php?topic=97561.0))
|
||||
### Changed
|
||||
- Added a dynamic action system for more modder control of effects
|
||||
- Removed fireball limit
|
||||
- Fixed bomb intercept in multiplayer
|
||||
- Changed nebula fog distance from a linear to exponential model
|
||||
- Turrets on moving subobjects enabled
|
||||
- LZ4 Compression added for game files
|
||||
|
||||
|
||||
## [21.0.0] - 2021-01-27 ([Thread](https://www.hard-light.net/forums/index.php?topic=97293.0))
|
||||
### Changed
|
||||
- More OpenGL optimizations
|
||||
- Full type information output for Lua documentation. Very useful for writing Lua scripts.
|
||||
- A new -weaponspew command-line option for printing MediaVP-style statistics, plus a weapon comparison spreadsheet, to the debug log.
|
||||
- Improved ship lookup behavior in SEXPs for improved performance.
|
||||
- BPTC/BC7 texture compression support
|
||||
- Refactoring and new features for the ship lab.
|
||||
- Missile multi-lock. It's finally in an official build!
|
||||
- Enhancements to the model code in preparation for cool features in 21.2.
|
||||
- IPv6 support for multiplayer
|
||||
- A lot of general fixes for multiplayer.
|
||||
### Deprecated
|
||||
- Dropped support for Windows XP
|
||||
- Scripting:
|
||||
- `ba.getFrametime()`: The parameter value was used incorrectly inside the implementation. To avoid breaking existing scripts and to improve readability this has been split into `ba.getMissionFrametime()` and `ba.getRealFrametime()`.
|
||||
- `gr.drawMonochromeImage()`: `gr.drawImage()` got a new parameter for drawing monochrome images and is more flexible in general so the monochrome variant is no longer needed.
|
||||
|
||||
|
||||
## [19.0.0] - 2020-01-25 ([Thread](https://www.hard-light.net/forums/index.php?topic=96226.0))
|
||||
### Meta
|
||||
- With this release we decided to drop the "`3.Major_revision.Minor_revision`" versioning scheme in favor of a year based scheme since the Major and Minor versions did not have much meaning anymore. Instead the scheme will now be "`<year>.<number that is incremented every release>.0`". The last 0 is still there because some of our systems expect that. It will be gone at some point.
|
||||
@@ -25,6 +71,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
### Fixed
|
||||
- Refactored bitmap slot handling and removed the fixed upper limit on the number of bitmaps. No more bmpman corruption!
|
||||
|
||||
|
||||
## [3.8.0] - 2017-08-22 ([Release Thread](https://www.hard-light.net/forums/index.php?topic=93812.0))
|
||||
### Added
|
||||
- Support for APNG animations: [APNG](https://en.wikipedia.org/wiki/APNG) is a variant of the established PNG format which allows to store animations instead of single frames in a PNG file. This change allows to use these animation files in FSO which significantly improves usability for modders since they no longer have to store all individual frames as separate images.
|
||||
@@ -49,6 +96,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
### Fixed
|
||||
- "Tons" of bugfixes
|
||||
|
||||
|
||||
## [3.7.2] - 2015-04-23 ([Release thread](http://www.hard-light.net/forums/index.php?topic=89597.0))
|
||||
### Added
|
||||
- MediaVPS 2014 Support
|
||||
@@ -79,16 +127,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- Raised the per-model debris limit to 48 (previously 32).
|
||||
- Increased the per-frame debris limit to 96 (previously 64)
|
||||
|
||||
|
||||
## [3.6.18] - 2013-03-01 ([Release thread](http://www.hard-light.net/forums/index.php?topic=83889.0))
|
||||
### Fixed
|
||||
- Critical damage bug which caused heavy balance issues in the game
|
||||
|
||||
|
||||
## [3.6.16] - 2013-01-31 ([Release thread](http://www.hard-light.net/forums/index.php?topic=83577.0))
|
||||
### Added
|
||||
- Diaspora TC support
|
||||
### Changed
|
||||
- Performance Improvements
|
||||
|
||||
|
||||
## [3.6.14] - 2012-10-23 - ([Release thread](https://www.hard-light.net/forums/index.php?topic=82648.0))
|
||||
### Added
|
||||
- Objecttype tables support for deciding how enemies respond to ship classes when disabled
|
||||
|
||||
@@ -19,5 +19,5 @@ BASE_COMMIT=$(git merge-base $1 $2)
|
||||
echo "Running clang-tidy on changed files"
|
||||
git diff -U0 --no-color "$BASE_COMMIT..$2" | \
|
||||
$HERE/clang-tidy-diff.py -path "$(pwd)/build" -p1 \
|
||||
-regex '(code(?!\/graphics\/shaders\/compiled)|freespace2|qtfred|test|build|tools)\/.*\.(cpp|h)' \
|
||||
-regex '(code(?!((\/graphics\/shaders\/compiled)|(\/globalincs\/windebug)))|freespace2|qtfred|test|build|tools)\/.*\.(cpp|h)' \
|
||||
-clang-tidy-binary /usr/bin/clang-tidy-9 -j$(nproc) -export-fixes "$(pwd)/clang-fixes.yaml"
|
||||
@@ -15,8 +15,16 @@ if [ "$COMPILER" = "clang-9" ]; then
|
||||
fi
|
||||
|
||||
LD_LIBRARY_PATH=$Qt5_DIR/lib:$LD_LIBRARY_PATH
|
||||
CXXFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
CFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
if [ "$RUNNER_OS" = "macOS" ]; then
|
||||
CXXFLAGS="-mtune=generic -pipe -Wno-unknown-pragmas"
|
||||
CFLAGS="-mtune=generic -pipe -Wno-unknown-pragmas"
|
||||
PLATFORM_CMAKE_OPTIONS=""
|
||||
export CMAKE_OSX_ARCHITECTURES="$ARCHITECTURE"
|
||||
else
|
||||
CXXFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
CFLAGS="-m64 -mtune=generic -mfpmath=sse -msse -msse2 -pipe -Wno-unknown-pragmas"
|
||||
PLATFORM_CMAKE_OPTIONS="-DFSO_BUILD_APPIMAGE=ON"
|
||||
fi
|
||||
|
||||
CMAKE_OPTIONS="$JOB_CMAKE_OPTIONS"
|
||||
if [[ "$COMPILER" =~ ^clang.*$ ]]; then
|
||||
@@ -30,7 +38,10 @@ export CXXFLAGS
|
||||
export CFLAGS
|
||||
export LD_LIBRARY_PATH
|
||||
|
||||
cmake -G Ninja -DFSO_FATAL_WARNINGS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON $CMAKE_OPTIONS \
|
||||
-DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DFSO_BUILD_APPIMAGE=ON -DCMAKE_BUILD_TYPE=$CONFIGURATION \
|
||||
if [ "$RUNNER_OS" = "macOS" ]; then
|
||||
brew install ninja
|
||||
fi
|
||||
cmake -G Ninja -DFSO_FATAL_WARNINGS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON $CMAKE_OPTIONS $PLATFORM_CMAKE_OPTIONS \
|
||||
-DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DCMAKE_BUILD_TYPE=$CONFIGURATION \
|
||||
-DFFMPEG_USE_PRECOMPILED=ON -DFSO_BUILD_TESTS=ON -DFSO_BUILD_INCLUDED_LIBS=ON -DFSO_BUILD_QTFRED=ON \
|
||||
-DSHADERS_ENABLE_COMPILATION=ON ..
|
||||
@@ -25,6 +25,12 @@ elif [ "$OS" = "Windows" ]; then
|
||||
echo "::set-output name=debug_path::$(pwd)/$(get_package_name)-debug-$ARCH-$SIMD.7z"
|
||||
echo "::set-output name=debug_name::$(get_package_name)-debug-$ARCH-$SIMD.7z"
|
||||
echo "::set-output name=debug_mime::$(file -b --mime-type "$(pwd)/$(get_package_name)-debug-$ARCH-$SIMD.7z")"
|
||||
elif [ "$OS" = "Mac" ]; then
|
||||
tar -cvzf "$(get_package_name)-builds-Mac.tar.gz" *
|
||||
|
||||
echo "::set-output name=package_path::$(pwd)/$(get_package_name)-builds-Mac.tar.gz"
|
||||
echo "::set-output name=package_name::$(get_package_name)-builds-Mac.tar.gz"
|
||||
echo "::set-output name=package_mime::$(file -b --mime-type "$(pwd)/$(get_package_name)-builds-Mac.tar.gz")"
|
||||
else
|
||||
echo "Invalid OS: $OS"
|
||||
fi
|
||||
@@ -5,7 +5,7 @@ HERE=$(dirname "$SCRIPT")
|
||||
|
||||
export LD_LIBRARY_PATH=$(pwd)/bin/lib:$LD_LIBRARY_PATH
|
||||
|
||||
if [ "$CONFIGURATION" = "Debug" ]; then
|
||||
if [ "$CONFIGURATION" = "Debug"] && [[ "$RUNNER_OS" != "macOS" ]] ; then
|
||||
valgrind --leak-check=full --error-exitcode=1 --gen-suppressions=all \
|
||||
--suppressions="$HERE/valgrind.supp" ./bin/unittests --gtest_shuffle
|
||||
else
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
# Retrives a log of commit messages and attempts to pretty them into a human-readbile format
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
import sys
|
||||
|
||||
from datetime import datetime
|
||||
from subprocess import check_output
|
||||
|
||||
MAX_PAGES = 100 # maximum number of PR pages to go through
|
||||
warnings = 0
|
||||
|
||||
def get_last_page(response: requests.Response) -> int:
|
||||
"""
|
||||
@brief Extract the last page number from the response header
|
||||
|
||||
@param[in] response The response from a GET call
|
||||
|
||||
@returns The page number of the last page, or MAX_PAGES
|
||||
"""
|
||||
|
||||
# The link header is a dictionary in string form, and for that matter the
|
||||
# value is in front of the key. It is of the form '<<url1>>; rel=<key1>, <url2>, <key2>'
|
||||
link = response.headers['link']
|
||||
link = link.split(',')
|
||||
for x in link:
|
||||
y = x.split(';') # <url>; rel=<key>
|
||||
if "last" not in y[1]: # look for rel='last'
|
||||
continue
|
||||
# grab the page number from the URL
|
||||
# group(0) is `page=123`, group(1) is `123`
|
||||
last_page = int( re.search(r'page=(\d+)', y[0]).group(1) )
|
||||
if not last_page:
|
||||
print("Error: Could not find page number in url! URL: %s", y[0])
|
||||
sys.exit(1)
|
||||
|
||||
if last_page > MAX_PAGES:
|
||||
return MAX_PAGES
|
||||
else:
|
||||
return last_page
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Retrieves a log of commit messages and attempts to pretty them into a human-readable format")
|
||||
parser.add_argument("start_date",
|
||||
help="Day of the earliest commit to add to the log.")
|
||||
parser.add_argument("-u", "--user", nargs=2, metavar=("NAME", "TOKEN"), default=["", ""],
|
||||
help="Github username and token/password")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
os.chdir(os.path.dirname(__file__)) # Change working directory to this file's directory
|
||||
start_date = datetime.strptime(args.start_date, "%Y-%m-%d")
|
||||
|
||||
print("start_date: {}".format(start_date))
|
||||
|
||||
# Retrive the git log for master, since start date, order by date, in ascending order, use YYYY-MM-DD date format,
|
||||
# each line is of the form "<author short date> | <message>", only grab commits from master,
|
||||
# output to `fsofordigest.txt`,
|
||||
|
||||
# check_output(("git log master", "--after=2021-09-15", "--date-order", "--reverse", "--date=short",
|
||||
# "--pretty=\"%as | %s\"", "--first-parent",
|
||||
# "--output fsofordigest.txt"), text=True)
|
||||
|
||||
# The commit messages are either of the form "<body> (#<PR_number>)" or "Merge pull request #<PR_number> from <source_repo>"
|
||||
# For our purposes, we want them be all of the form "<body> (#<PR_number>)"
|
||||
# Typically, the second form does not have a usable body, so we'll have to pull the PR title from github
|
||||
|
||||
# Github's REST API allows us to retrieve PR's, but due to it being HTTP requests it has to page in the data.
|
||||
# We can loop over the GET request and check for a merge date.
|
||||
# The GET request doesn't have sorting by merge date, but does have sorting by the last time it was updated... which is usually
|
||||
# also the merge date but not always.
|
||||
|
||||
# Key attributes within the PR JSON objects:
|
||||
# "merged_at: <date>" # Date the PR was merged in
|
||||
# "merged: <bool>" # If the PR was merged yet
|
||||
# "html_url: <string>" # URL of the PR's HEAD commit, used by some clients to pull in the PR branch
|
||||
# "number: <int>" # The PR id number
|
||||
# "title: <string>" # Title of the PR
|
||||
# "body: <string>" # The body paragraph detailing the PR
|
||||
# "labels: [label_object]" # All labels attached to this PR
|
||||
|
||||
changelog = {}
|
||||
url = "https://api.github.com/repos/scp-fs2open/fs2open.github.com/pulls"
|
||||
page = 1
|
||||
last_page = MAX_PAGES
|
||||
while page <= last_page:
|
||||
# GET <url>?base=master&sort=updated&direction=desc&state=closed&page=1
|
||||
response = requests.get(url,
|
||||
auth=(args.user[0], args.user[1]),
|
||||
params={
|
||||
"base": "master",
|
||||
"direction": "desc",
|
||||
"page": page,
|
||||
"state": "closed",
|
||||
"sort": "updated"
|
||||
})
|
||||
|
||||
# Check if good response
|
||||
if (response.status_code != 200) :
|
||||
print("Error: ", response.status_code)
|
||||
print(response.json()['message'])
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("Request successful: 200")
|
||||
print("page={}".format(page))
|
||||
|
||||
# Update the last_page index at first iteration and possibly last iteration
|
||||
# This should cover the edge case of a new PR being added to the database before this script is finished
|
||||
if (page == 1) or (page == last_page):
|
||||
last_page = get_last_page(response)
|
||||
print("last_page={}".format(last_page))
|
||||
|
||||
# assume pulls are organized from newest to oldest
|
||||
for pull in response.json():
|
||||
if (pull['state'] != 'closed'):
|
||||
# This pull is not merged, so ignore
|
||||
print("Found irrelevant pull, skipping...")
|
||||
print("state: {}; number: {}".format(pull['state'], pull['number']))
|
||||
continue
|
||||
|
||||
if (pull['merged_at'] == None):
|
||||
# This pull wasn't merged, skip
|
||||
print("Found closed pull #{} ({}), skipping...".format(pull['number'], pull['closed_at']))
|
||||
continue
|
||||
|
||||
merge_date = datetime.strptime(pull['merged_at'], "%Y-%m-%dT%H:%M:%SZ")
|
||||
updated_date = datetime.strptime(pull['updated_at'], "%Y-%m-%dT%H:%M:%SZ")
|
||||
if (merge_date < start_date) and (updated_date >= start_date):
|
||||
# This pull is too old, but was updated within our timeframe. skip
|
||||
print("Found old pull #{} ({}), skipping...".format(pull['number'], pull['merged_at']))
|
||||
continue
|
||||
|
||||
if (updated_date < start_date):
|
||||
# This pull is too old
|
||||
print("Found old pull #{} ({}), stopping loop...".format(pull['number'], pull['updated_at']))
|
||||
page = last_page + 1
|
||||
break
|
||||
|
||||
# Add the pull to the changelog, indexed by its pull.number
|
||||
if pull['number'] not in changelog:
|
||||
# only add if the key isn't there already
|
||||
changelog[pull['number']] = {
|
||||
"date": merge_date,
|
||||
"title": pull['title'],
|
||||
"labels": pull['labels'],
|
||||
"url": pull['html_url']
|
||||
}
|
||||
print("Added pull #{} ({})".format(pull['number'], merge_date))
|
||||
# Else, silently ignore duplicate (for now...)
|
||||
|
||||
page += 1 # iterate to next page
|
||||
|
||||
|
||||
# Write the changelog to file
|
||||
# 2021-09-28 | (#3661) Fix homing child weapons in niche circumstances
|
||||
print("Writing to change.log...")
|
||||
str = ''
|
||||
for key, value in changelog.items():
|
||||
str += "{} | (#{}) {} \n".format(value['date'].strftime("%Y-%m-%d"), key, value['title'])
|
||||
|
||||
with open("change.log", "w") as file:
|
||||
file.write(str)
|
||||
|
||||
print("Done!")
|
||||
|
||||
main()
|
||||
+41
-3
@@ -8,12 +8,27 @@ from file_list import ReleaseFile
|
||||
|
||||
|
||||
class ForumAPI:
|
||||
POST_SIZE_MAX = 50000
|
||||
"""!
|
||||
Maximum number of characters allowed in a post, including BBCode (03/17/2022)
|
||||
|
||||
Check with HLP admins occasionally to verify this limit, they can set it in the forum's admin panel
|
||||
"""
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
def create_post(self, title, content, board):
|
||||
print(self.config["hlp"]["api"])
|
||||
print(self.config["hlp"]["key"])
|
||||
"""!
|
||||
@brief Create a post on the forums
|
||||
|
||||
@param[in] title Title of the post
|
||||
@param[in] content Body of the post
|
||||
@param[in] board Which board/forum to post to
|
||||
|
||||
@returns { "thread_url": <url_of_created_thread> }
|
||||
"""
|
||||
|
||||
if (self.config["hlp"]["api"] == "") or (self.config["hlp"]["key"] == ""):
|
||||
print("Post failed! No API or API_KEY given!")
|
||||
return
|
||||
@@ -27,8 +42,19 @@ class ForumAPI:
|
||||
|
||||
if resp.text != "OK":
|
||||
print("Post failed! Response: %s" %resp.text)
|
||||
|
||||
return resp.json()
|
||||
|
||||
def post_nightly(self, date, revision, files, log, success):
|
||||
"""!
|
||||
@brief Post a new Nightly topic on the forums using nightly.mako
|
||||
|
||||
@param[in] date The date to use
|
||||
@param[in] version The build's version
|
||||
@param[in] groups dict[Any, file_list.FileGroup]; FileGroups of SSE2, AVX, etc.
|
||||
@param[in] sources URL's of builds on github or fs2net
|
||||
"""
|
||||
|
||||
print("Posting nightly thread...")
|
||||
|
||||
title = "Nightly: {} - Revision {}".format(date, revision)
|
||||
@@ -46,6 +72,17 @@ class ForumAPI:
|
||||
self.create_post(title, rendered, self.config["nightly"]["hlp_board"])
|
||||
|
||||
def post_release(self, date, version: semantic_version.Version, groups, sources):
|
||||
"""!
|
||||
@brief Post a new Release topic on the forums using release.mako.
|
||||
|
||||
@param[in] date The date to use
|
||||
@param[in] version The build's version
|
||||
@param[in] groups dict[Any, file_list.FileGroup]; FileGroups of SSE2, AVX, etc.
|
||||
@param[in] sources URL's of builds on github or fs2net
|
||||
|
||||
@returns If Successful, the URL of the created post
|
||||
"""
|
||||
|
||||
print("Posting release thread...")
|
||||
|
||||
title = "Release: {}".format(version)
|
||||
@@ -62,4 +99,5 @@ class ForumAPI:
|
||||
|
||||
print("Creating post...")
|
||||
|
||||
self.create_post(title, rendered, self.config["release"]["hlp_board"])
|
||||
json = self.create_post(title, rendered, self.config["release"]["hlp_board"])
|
||||
return json["thread_url"]
|
||||
@@ -0,0 +1,85 @@
|
||||
# Class for use with interfacing with github via HTTP requests
|
||||
|
||||
import requests
|
||||
|
||||
from util import retry_multi, GLOBAL_TIMEOUT
|
||||
|
||||
class GithubAPI:
|
||||
headers = {
|
||||
"Accept": "application/vnd.github.v3+json"
|
||||
}
|
||||
url_root = "https://api.github.com"
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
@retry_multi(5)
|
||||
def get(self, path):
|
||||
"""!
|
||||
@brief Performs a GET request with the given path. To be used with Github's REST API.
|
||||
@returns If successful, returns a .JSON object
|
||||
"""
|
||||
url = self.url_root + path
|
||||
|
||||
# GET https://api.github.com/<path> Accept: "application/vnd.github.v3+json"
|
||||
|
||||
response = requests.get(url, headers=self.headers, timeout=GLOBAL_TIMEOUT)
|
||||
|
||||
response.raise_for_status() # Raise a RequestException if we failed, and trigger retry
|
||||
|
||||
return response.json()
|
||||
|
||||
def get_release(self, tag_name):
|
||||
"""!
|
||||
@brief Retrieves the Github Release metadata as a JSON object
|
||||
@see https://docs.github.com/en/rest/reference/releases#get-a-release-by-tag-name
|
||||
"""
|
||||
return self.get("/repos/{}/releases/tags/{}".format(self.config["github"]["repo"], tag_name))
|
||||
|
||||
|
||||
def patch(self, path, data):
|
||||
"""!
|
||||
@brief Performs a PATCH request with the given path and body data (as dictionary)
|
||||
@returns If successful, returns a .JSON object
|
||||
"""
|
||||
url = self.url_root + path
|
||||
|
||||
response = requests.patch(url, headers=self.headers, timeout=GLOBAL_TIMEOUT, data=data)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def update_release(self, release_id, tag_name="", name="", body="", draft=None, prerelease=None):
|
||||
"""!
|
||||
@brief Updates a Github Release
|
||||
@param[in] release_id The id of the release, as obtained by get_release()
|
||||
@param[in] tag_name (optional) The git tag this release is associated with
|
||||
@param[in] name (optional) The name of the release
|
||||
@param[in] body (optional) Body text describing the release
|
||||
@param[in] draft (optional) If true, the release is marked as a "draft"
|
||||
@param[in] prerelease (optional) If true, the release is marked as a "prerelease"
|
||||
|
||||
@note If an optional argument is not provided, then its associated data is not changed
|
||||
"""
|
||||
path = "/repos/{}/releases/{}".format(self.config["github"]["repo"], release_id)
|
||||
|
||||
# Add to the data JSON if options are specified
|
||||
data = {}
|
||||
if tag_name != "":
|
||||
data["tag_name"] = tag_name
|
||||
|
||||
if name != "":
|
||||
data["name"] = name
|
||||
|
||||
if body != "":
|
||||
data["body"] = body
|
||||
|
||||
if draft != None:
|
||||
data["draft"] = data
|
||||
|
||||
if prerelease != None:
|
||||
data["prerelease"] = prerelease
|
||||
|
||||
self.patch(path, data)
|
||||
|
||||
+101
-34
@@ -38,9 +38,11 @@ import nebula
|
||||
import forum
|
||||
|
||||
# Compile regexes for extracting version components
|
||||
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+)\)")
|
||||
MAJOR_VERSION_PATTERN = re.compile("(?:set_if_not_defined|set)\(FSO_VERSION_MAJOR (\d+)\)")
|
||||
MINOR_VERSION_PATTERN = re.compile("(?:set_if_not_defined|set)\(FSO_VERSION_MINOR (\d+)\)")
|
||||
BUILD_VERSION_PATTERN = re.compile("(?:set_if_not_defined|set)\(FSO_VERSION_BUILD (\d+)\)")
|
||||
REVISION_VERSION_PATTERN = re.compile("(?:set_if_not_defined|set)\(FSO_VERSION_REVISION (\d+)\)")
|
||||
REVISION_STR_VERSION_PATTERN = re.compile("(?:set_if_not_defined|set)\(FSO_VERSION_REVISION_STR (\w+)\)")
|
||||
|
||||
LOG_FORMAT = """
|
||||
------------------------------------------------------------------------%n
|
||||
@@ -60,24 +62,65 @@ def _match_version_number(text, regex):
|
||||
"""
|
||||
match = regex.search(text)
|
||||
return int(match.group(1))
|
||||
|
||||
def _match_version_str(text, regex):
|
||||
"""! Extracts the version component represented by `regex` from `text`
|
||||
|
||||
@param [in] `text` Filename with version string 'MAJOR.MINOR.BUILD'
|
||||
@param [in] `regex` Regex pattern of component to look for
|
||||
|
||||
@return The version component as integer
|
||||
"""
|
||||
match = regex.search(text)
|
||||
return match.group(1)
|
||||
|
||||
|
||||
def get_source_version(date_version):
|
||||
"""! Retrieves the build's version from `version.cmake`, forms it as a string, and appends the date
|
||||
def get_source_version(date_version: datetime, tag_name: str) -> semantic_version.Version:
|
||||
"""! Retrieves the version string from version_override.cmake (and version.cmake)
|
||||
|
||||
@param[in] `date_version` Date this build is published for release
|
||||
@param[in] `tag_name` Tag of this release, used to determine if release, rc, or nightly
|
||||
|
||||
@return String of the form `MAJOR_VERSION.MINOR_VERSION.BUILD_VERSION-date`
|
||||
@return If release: `FSO_VERSION_MAJOR.FSO_VERSION_MINOR.FSO_VERSION_BUILD`
|
||||
@return If rc: `FSO_VERSION_MAJOR.FSO_VERSION_MINOR.FSO_VERSION_BUILD-FSO_VERSION_REVISION_STR`
|
||||
@return If nightly: `FSO_VERSION_MAJOR.FSO_VERSION_MINOR.FSO_VERSION_BUILD-FSO_VERSION_REVISION_STR` (uses both version_override.cmake and version.cmake)
|
||||
|
||||
@note This is the version used to identify in Nebula and the forums. version_override.cmake is used for the builds
|
||||
made by CI tools and version.cmake is used for builds made by devs.
|
||||
"""
|
||||
|
||||
with open(os.path.join("..", "..", "cmake", "version.cmake"), "r") as f:
|
||||
filetext = f.read()
|
||||
|
||||
with open(os.path.join("..", "..", "version_override.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)
|
||||
# revision = _match_version_number(filetext, REVISION_VERSION_PATTERN)
|
||||
revision_str = _match_version_str(filetext, REVISION_STR_VERSION_PATTERN)
|
||||
|
||||
return semantic_version.Version("{}.{}.{}-{}".format(major, minor, build, date_version))
|
||||
print("Parsing version_override.cmake...")
|
||||
if "rc" in tag_name.lower():
|
||||
version = semantic_version.Version("{}.{}.{}-{}".format(major, minor, build, revision_str))
|
||||
|
||||
elif "release" in tag_name.lower():
|
||||
version = semantic_version.Version("{}.{}.{}".format(major, minor, build))
|
||||
|
||||
elif "nightly" in tag_name.lower():
|
||||
# Nightly's version_override.cmake only has FSO_VERSION_REVISION and FSO_VERSION_REVISION_STR set
|
||||
# So, we have to pull the rest of the version string from version.cmake
|
||||
print("Parsing version.cmake...")
|
||||
with open(os.path.join("..", "..", "cmake", "version.cmake"), "r") as f:
|
||||
major = _match_version_number(filetext, MAJOR_VERSION_PATTERN)
|
||||
minor = _match_version_number(filetext, MINOR_VERSION_PATTERN)
|
||||
build = _match_version_number(filetext, BUILD_VERSION_PATTERN)
|
||||
version = semantic_version.Version("{}.{}.{}-{}".format(major, minor, build, revision_str))
|
||||
|
||||
else:
|
||||
print(" ERROR: malformed tag_name %s" % tag_name)
|
||||
sys.exit(1)
|
||||
|
||||
print(" version: {}".format(version))
|
||||
print(" version.prerelease: {}".format(version.prerelease))
|
||||
return version
|
||||
|
||||
|
||||
def main():
|
||||
@@ -123,17 +166,43 @@ def main():
|
||||
print("ERROR: Invalid release mode %s passed. Expected release or nightly!" % sys.argv[1])
|
||||
sys.exit(1)
|
||||
|
||||
linux_success = os.environ["LINUX_RESULT"] == "success"
|
||||
windows_success = os.environ["WINDOWS_RESULT"] == "success"
|
||||
if (sys.argv[1] == "release"):
|
||||
if not (linux_success and windows_success):
|
||||
# bail if this is a release and either build workflow failed
|
||||
print("ERROR: One or more builds failed to publish! Cannot post release.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
if not (linux_success or windows_success):
|
||||
# bail if this is a nightly build and both build workflows failed
|
||||
print("ERROR: All builds failed to publish! Cannot post nightly.")
|
||||
sys.exit(1)
|
||||
|
||||
# bail if tag_name doesn't match with sys.argv[1]
|
||||
tag_name = os.environ["RELEASE_TAG"] ##!< commit tag string
|
||||
date = datetime.now() ##!< current date
|
||||
version = get_source_version(date.strftime(DATEFORMAT_VERSION)) ##!< form full version string
|
||||
success = os.environ["LINUX_RESULT"] == "success" and os.environ["WINDOWS_RESULT"] == "success" ##!< true if both linux and windows builds successful
|
||||
if (sys.argv[1] == "release") and (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)
|
||||
|
||||
# check that tag_name is actually in the git repo and find the previous tag release
|
||||
if (sys.argv[1] == "nightly") and (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)
|
||||
|
||||
# bail if tag_name does not exist in the repo
|
||||
if not check_output(("git", "show-ref", "--tags", "refs/tags/%s" % tag_name), text=True):
|
||||
print("ERROR: Couldn't find tag %s in repo (git show-ref)!" % tag_name)
|
||||
sys.exit(1)
|
||||
|
||||
# Populate version and date
|
||||
date = datetime.now() ##!< current date
|
||||
version = get_source_version(date.strftime(DATEFORMAT_VERSION), tag_name) ##!< form full version string
|
||||
|
||||
# find the previous tag release (used by nightly)
|
||||
tags = check_output(("git", "for-each-ref", "--sort=-taggerdate", "--format='%(tag)'", "refs/tags"), text=True).splitlines() # retrieve all tags in the repo by using git on the shell
|
||||
# NOTE: check_output returns canonical string representation. use repr() on strings its being compared to
|
||||
previous_tag = None
|
||||
found = False
|
||||
|
||||
for tag in tags:
|
||||
if found:
|
||||
# Look for the "previous" tag with the same configuration ('release_' or 'nightly_')
|
||||
@@ -147,24 +216,20 @@ def main():
|
||||
|
||||
if not found:
|
||||
# couldn't find tag, bail
|
||||
print("ERROR: Couldn't find tag %s in repo!" % tag_name)
|
||||
print("ERROR: Couldn't find tag %s in repo! (git for-each-ref)" % tag_name)
|
||||
sys.exit(1)
|
||||
|
||||
if not previous_tag:
|
||||
if (sys.argv[1] == "nightly") and (not previous_tag):
|
||||
# couldn't find previous tag, bail
|
||||
print("ERROR: Could not find a previous tag for %s!" % tag_name)
|
||||
sys.exit(1)
|
||||
|
||||
if sys.argv[1] == "release":
|
||||
# Release specific action
|
||||
if not tag_name.startswith("release_"):
|
||||
# Safety check tag is from a 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)
|
||||
|
||||
print("Retrieving file list...")
|
||||
files, sources = file_list.get_release_files(tag_name, config)
|
||||
|
||||
print("Hashing files")
|
||||
print("Hashing files...")
|
||||
for file in files:
|
||||
installer.get_hashed_file_list(file)
|
||||
|
||||
@@ -189,7 +254,7 @@ def main():
|
||||
print("ERROR: No x64-AVX builds were detected!")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("ERROR: Now x64 builds were detected!")
|
||||
print("ERROR: No x64 builds were detected!")
|
||||
sys.exit(1)
|
||||
|
||||
if "Linux" not in groups.keys():
|
||||
@@ -200,30 +265,27 @@ def main():
|
||||
# print(installer.render_installer_config(version, groups, config))
|
||||
|
||||
# Publish release builds to Nebula
|
||||
print("Publishing to Nebula...")
|
||||
nebula.submit_release(
|
||||
nebula.render_nebula_release(version, "rc" if version.prerelease else "stable", files, config),
|
||||
config)
|
||||
|
||||
# Publish forum post, using the release.mako template
|
||||
print("Publishing to Hard-light.net...")
|
||||
fapi = forum.ForumAPI(config)
|
||||
fapi.post_release(date.strftime(DATEFORMAT_FORUM), version, groups, sources)
|
||||
|
||||
else:
|
||||
# Dead for now, nightly publish is done elsewhere
|
||||
# Nightly specific action
|
||||
if not tag_name.startswith("nightly_"):
|
||||
# safety check tag is from a 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")
|
||||
print("Retrieving file list...")
|
||||
files = file_list.get_ftp_files("nightly", tag_name, config)
|
||||
|
||||
print("Hashing files")
|
||||
print("Hashing files...")
|
||||
for file in files:
|
||||
installer.get_hashed_file_list(file)
|
||||
|
||||
# Publish nightly builds to Nebula
|
||||
print("Publishing to Nebula...")
|
||||
nebula.submit_release(
|
||||
nebula.render_nebula_release(version, "nightly", files, config),
|
||||
config)
|
||||
@@ -232,7 +294,12 @@ def main():
|
||||
log = check_output(("git", "log", "%s^..%s^" % (previous_tag, tag_name), "--no-merges", "--stat", "--pretty=format:\"%s\"" % LOG_FORMAT.strip()))
|
||||
|
||||
# Publish forum post, using the nighty.mako template
|
||||
print("Publishing to Hard-light.net...")
|
||||
fapi = forum.ForumAPI(config)
|
||||
fapi.post_nightly(date.strftime(DATEFORMAT_FORUM), os.environ["GITHUB_SHA"], files, log, success)
|
||||
fapi.post_nightly(date.strftime(DATEFORMAT_FORUM), os.environ["GITHUB_SHA"], files, log, (linux_success and windows_success))
|
||||
|
||||
main()
|
||||
print("Done!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+1
-1
@@ -8,7 +8,7 @@ if(DOXYGEN_FOUND)
|
||||
FILE(TO_NATIVE_PATH ${OUTPUT_DIR} OUTPUT_DIR)
|
||||
SET(OUTPUT_DIR \"${OUTPUT_DIR}\")
|
||||
|
||||
SET(INPUTS "${CMAKE_CURRENT_SOURCE_DIR}/code" "${CMAKE_CURRENT_SOURCE_DIR}/freespace2" "${CMAKE_CURRENT_SOURCE_DIR}/fred2" "${CMAKE_CURRENT_SOURCE_DIR}/wxfred2")
|
||||
SET(INPUTS "${CMAKE_CURRENT_SOURCE_DIR}/code" "${CMAKE_CURRENT_SOURCE_DIR}/freespace2" "${CMAKE_CURRENT_SOURCE_DIR}/fred2" "${CMAKE_CURRENT_SOURCE_DIR}/wxfred2" "${CMAKE_CURRENT_SOURCE_DIR}/qtfred")
|
||||
|
||||
SET(INPUT_DIRS)
|
||||
FOREACH(DIR IN LISTS INPUTS)
|
||||
|
||||
@@ -100,6 +100,10 @@ set(CMAKE_C_FLAGS_DEBUG ${COMPILER_FLAGS_DEBUG})
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "")
|
||||
|
||||
if(DEFINED ENV{LDFLAGS})
|
||||
set(CMAKE_EXE_LINKER_FLAGS $ENV{LDFLAGS})
|
||||
endif()
|
||||
|
||||
if (CLANG_USE_LIBCXX)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lc++abi")
|
||||
endif()
|
||||
|
||||
@@ -27,6 +27,11 @@ endif()
|
||||
set(COMPILER_FLAGS "")
|
||||
set(LINKER_FLAGS "")
|
||||
|
||||
# Don't ignore user-set LDFLAGS
|
||||
if(DEFINED ENV{LDFLAGS})
|
||||
set(LINKER_FLAGS $ENV{LDFLAGS})
|
||||
endif()
|
||||
|
||||
if (GCC_USE_GOLD)
|
||||
OPTION(GCC_INCREMENTAL_LINKING "Use incremental linking" OFF)
|
||||
set(LINKER_FLAGS "${LINKER_FLAGS} -fuse-ld=gold")
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ if (EXISTS "${PROJECT_SOURCE_DIR}/version_override.cmake")
|
||||
include("${PROJECT_SOURCE_DIR}/version_override.cmake")
|
||||
endif()
|
||||
|
||||
set_if_not_defined(FSO_VERSION_MAJOR 21)
|
||||
set_if_not_defined(FSO_VERSION_MINOR 5)
|
||||
set_if_not_defined(FSO_VERSION_MAJOR 22)
|
||||
set_if_not_defined(FSO_VERSION_MINOR 1)
|
||||
set_if_not_defined(FSO_VERSION_BUILD 0)
|
||||
|
||||
# A numeric revision e.g. the CI build number
|
||||
|
||||
@@ -106,3 +106,6 @@ set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYM
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_COPY_PHASE_STRIP[variant=Debug] "NO")
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_STRIP_INSTALLED_PRODUCT[variant=Debug] "NO")
|
||||
set_target_properties(code PROPERTIES XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN[variant=Debug] "NO")
|
||||
|
||||
# Prevent GCC warnings in third-party BitOp... see GitHub #4366
|
||||
suppress_file_warnings(scripting/lua/bitop/bit.c)
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "actions/expression/nodes/AbstractExpression.h"
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
|
||||
#include "ParseContext.h"
|
||||
|
||||
#include "actions/expression/nodes/AbstractExpression.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "actions/expression/ParseContext.h"
|
||||
#include "actions/expression/Value.h"
|
||||
|
||||
#include <Parser.h>
|
||||
#include <Token.h>
|
||||
|
||||
#include "actions/expression/ParseContext.h"
|
||||
#include "actions/expression/Value.h"
|
||||
|
||||
namespace actions {
|
||||
namespace expression {
|
||||
namespace nodes {
|
||||
|
||||
+20
-2
@@ -17,6 +17,7 @@
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "object/waypoint.h"
|
||||
#include "parse/sexp.h"
|
||||
#include "physics/physics.h"
|
||||
#include "ship/ship_flags.h"
|
||||
|
||||
@@ -71,6 +72,7 @@ extern ai_flag_name Ai_flag_names[];
|
||||
#define AITTV_VIA_SEXP (1<<1) // Goober5000 - via sexp
|
||||
#define AITTV_IGNORE_BANK (1<<2) // Goober5000 - ignore bank when turning
|
||||
#define AITTV_SLOW_BANK_ACCEL (1<<3) // Asteroth - used by formation flying
|
||||
#define AITTV_FORCE_DELTA_BANK (1<<4) // Lafiel - Always use the provided delta bank and override calculated values
|
||||
|
||||
#define KAMIKAZE_HULL_ON_DEATH -1000.0f // Hull strength ship gets set to if it crash-dies.
|
||||
|
||||
@@ -103,11 +105,13 @@ typedef struct ai_goal {
|
||||
int index;
|
||||
} dockee;
|
||||
|
||||
object_ship_wing_point_team lua_ai_target;
|
||||
|
||||
} ai_goal;
|
||||
|
||||
#define AIM_CHASE 0
|
||||
#define AIM_EVADE 1
|
||||
#define AIM_GET_BEHIND 2
|
||||
#define AIM_GET_BEHIND 2 // This mode is not actually implemented.
|
||||
#define AIM_STAY_NEAR 3 // Stay near another ship.
|
||||
#define AIM_STILL 4 // Sit still. Don't move. Hold your breath. Don't blink.
|
||||
#define AIM_GUARD 5 // Guard an object
|
||||
@@ -127,8 +131,9 @@ typedef struct ai_goal {
|
||||
#define AIM_SENTRYGUN 19 // AI mode for sentry guns only (floating turrets)
|
||||
#define AIM_WARP_OUT 20 // Commence warp out sequence. Point in legal direction. Then call John's code.
|
||||
#define AIM_FLY_TO_SHIP 21 // [Kazan] Fly to a ship, doesn't matter if it's hostile or friendly -- for Autopilot usage
|
||||
#define AIM_LUA 22 // Generic Lua-based AI mode
|
||||
|
||||
#define MAX_AI_BEHAVIORS 22 // Number of AIM_xxxx types
|
||||
#define MAX_AI_BEHAVIORS 23 // Number of AIM_xxxx types
|
||||
|
||||
#define MAX_WAYPOINTS_PER_LIST 20
|
||||
#define MAX_ENEMY_DISTANCE 2500.0f // Maximum distance from which a ship will pursue an enemy.
|
||||
@@ -267,6 +272,8 @@ extern pnode *Ppfp; // Free pointer in path points.
|
||||
// Goober5000 (based on the "you can only remember 7 things in short-term memory" assumption)
|
||||
#define MAX_IGNORE_NEW_OBJECTS 7
|
||||
|
||||
#define AI_DYNAMIC_PATH_RECHECK_DELAY 1*1000
|
||||
|
||||
typedef struct ai_info {
|
||||
flagset<AI::AI_Flags> ai_flags; // Special flags for AI behavior.
|
||||
int shipnum; // Ship using this slot, -1 means none.
|
||||
@@ -343,6 +350,9 @@ typedef struct ai_info {
|
||||
int submode_parm1; // SUSHI: Another optional parameter
|
||||
fix next_predict_pos_time; // Next time to predict position.
|
||||
|
||||
int next_dynamic_path_check_time; //ETP: Time of next path check in ai_new_maybe_reposition_attack_subsys
|
||||
vec3d last_dynamic_path_goal; //ETP: Remebers last target location in ai_new_maybe_reposition_attack_subsys
|
||||
|
||||
//SUSHI: like last_predicted_enemy_pos, but for aiming (which currently ignores predicted position)
|
||||
//Unlike the predicted position stuff, also takes into account velocity
|
||||
//Only used against small ships
|
||||
@@ -493,6 +503,8 @@ typedef struct ai_info {
|
||||
|
||||
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
|
||||
|
||||
object_ship_wing_point_team lua_ai_target;
|
||||
} ai_info;
|
||||
|
||||
// Goober5000
|
||||
@@ -536,6 +548,8 @@ extern int Ai_firing_enabled;
|
||||
extern const char *Skill_level_names(int skill_level, int translate = 1);
|
||||
extern int Ai_goal_signature;
|
||||
|
||||
extern control_info AI_ci;
|
||||
|
||||
// need access to following data in AiBig.cpp
|
||||
extern object *Pl_objp;
|
||||
extern object *En_objp;
|
||||
@@ -573,6 +587,7 @@ extern void ai_do_default_behavior(object *obj);
|
||||
extern void ai_start_waypoints(object *objp, waypoint_list *wp_list, int wp_flags);
|
||||
extern void ai_ship_hit(object *objp_ship, object *hit_objp, vec3d *hit_normal);
|
||||
extern void ai_ship_destroy(int shipnum);
|
||||
extern vec3d ai_get_acc_limit(vec3d* vel_limit, const object* objp);
|
||||
extern void ai_turn_towards_vector(vec3d *dest, object *objp, vec3d *slide_vec, vec3d *rel_pos, float bank_override, int flags, vec3d *rvec = nullptr, vec3d* turnrate_mod = nullptr);
|
||||
extern void init_ai_object(int objnum);
|
||||
extern void ai_init(void); // Call this one to parse ai.tbl.
|
||||
@@ -671,4 +686,7 @@ void do_random_sidethrust(ai_info *aip, ship_info *sip);
|
||||
|
||||
void ai_formation_object_recalculate_slotnums(int form_objnum, int exiting_objnum = -1);
|
||||
|
||||
|
||||
bool test_line_of_sight(vec3d* from, vec3d* to, std::unordered_set<const object*>&& excluded_objects = {}, float threshold = 10.0f, bool test_for_shields = false, bool test_for_hull = true, float* first_intersect_dist = nullptr);
|
||||
|
||||
#endif
|
||||
@@ -144,6 +144,7 @@ namespace AI {
|
||||
Fighterbay_departures_use_carrier_orient,
|
||||
Prevent_negative_turret_ammo,
|
||||
Fix_keep_safe_distance,
|
||||
Ignore_aspect_when_leading,
|
||||
|
||||
NUM_VALUES
|
||||
};
|
||||
|
||||
@@ -598,6 +598,8 @@ void parse_ai_profiles_tbl(const char *filename)
|
||||
|
||||
set_flag(profile, "$fix keep-safe-distance:", AI::Profile_Flags::Fix_keep_safe_distance);
|
||||
|
||||
set_flag(profile, "$AI ignores aspect lock for leading:", AI::Profile_Flags::Ignore_aspect_when_leading);
|
||||
|
||||
|
||||
// if we've been through once already and are at the same place, force a move
|
||||
if (saved_Mp && (saved_Mp == Mp))
|
||||
|
||||
+58
-50
@@ -560,61 +560,69 @@ bool ai_new_maybe_reposition_attack_subsys() {
|
||||
return false;
|
||||
|
||||
// not in sight, gotta get there
|
||||
vec3d tgt2pl = Pl_objp->pos - target_objp->pos;
|
||||
vec3d tgt2subsys = gsubpos - target_objp->pos;
|
||||
float pl_dist = vm_vec_mag(&tgt2pl);
|
||||
float subsys_dist = vm_vec_mag(&tgt2subsys) * 1.3f; // add a bit to this, we don't want to go directly at the subsys, we want to go above it
|
||||
|
||||
float angle2subsys = vm_vec_delta_ang(&tgt2pl, &tgt2subsys, nullptr);
|
||||
float angle2goal = angle2subsys;
|
||||
if (angle2goal > 1.2f) // let's not try to go more than about 70 degrees around the ship
|
||||
angle2goal = 1.2f;
|
||||
|
||||
// we'll start by travelling along the sphere at our current distance around the ship
|
||||
// remember that goal_pos will be relative to our target_ship (so we can scale in and out easily)
|
||||
vec3d goal_pos, cross;
|
||||
vm_vec_cross(&cross, &tgt2pl, &tgt2subsys);
|
||||
vm_vec_normalize(&cross);
|
||||
vm_vec_negate(&cross);
|
||||
vm_rot_point_around_line(&goal_pos, &tgt2pl, angle2goal, &vmd_zero_vector, &cross);
|
||||
|
||||
// then scale in (or out) towards our subsys
|
||||
vec3d world_goal_pos;
|
||||
float new_radius = ((subsys_dist - pl_dist) * (angle2goal / angle2subsys)) + pl_dist;
|
||||
vm_vec_copy_scale(&world_goal_pos, &goal_pos, new_radius / pl_dist);
|
||||
world_goal_pos += target_objp->pos;
|
||||
|
||||
vec3d* good_pos = nullptr;
|
||||
if (pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f)) {
|
||||
// ok best case won't work
|
||||
if (pl_dist > subsys_dist) {
|
||||
//we're farther away than the subsys, let's try at our current distance
|
||||
world_goal_pos = goal_pos + target_objp->pos;
|
||||
if (!pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f))
|
||||
// For performance reasons we only recheck once a second, otherwise we reuse the last found target.
|
||||
if (timestamp_elapsed(aip->next_dynamic_path_check_time)) {
|
||||
vec3d tgt2pl = Pl_objp->pos - target_objp->pos;
|
||||
vec3d tgt2subsys = gsubpos - target_objp->pos;
|
||||
float pl_dist = vm_vec_mag(&tgt2pl);
|
||||
float subsys_dist = vm_vec_mag(&tgt2subsys) * 1.3f; // add a bit to this, we don't want to go directly at the subsys, we want to go above it
|
||||
|
||||
float angle2subsys = vm_vec_delta_ang(&tgt2pl, &tgt2subsys, nullptr);
|
||||
float angle2goal = angle2subsys;
|
||||
if (angle2goal > 1.2f) // let's not try to go more than about 70 degrees around the ship
|
||||
angle2goal = 1.2f;
|
||||
|
||||
// we'll start by travelling along the sphere at our current distance around the ship
|
||||
// remember that goal_pos will be relative to our target_ship (so we can scale in and out easily)
|
||||
vec3d goal_pos, cross;
|
||||
vm_vec_cross(&cross, &tgt2pl, &tgt2subsys);
|
||||
vm_vec_normalize(&cross);
|
||||
vm_vec_negate(&cross);
|
||||
vm_rot_point_around_line(&goal_pos, &tgt2pl, angle2goal, &vmd_zero_vector, &cross);
|
||||
|
||||
// then scale in (or out) towards our subsys
|
||||
vec3d world_goal_pos;
|
||||
float new_radius = ((subsys_dist - pl_dist) * (angle2goal / angle2subsys)) + pl_dist;
|
||||
vm_vec_copy_scale(&world_goal_pos, &goal_pos, new_radius / pl_dist);
|
||||
world_goal_pos += target_objp->pos;
|
||||
|
||||
if (pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f)) {
|
||||
// ok best case won't work
|
||||
if (pl_dist > subsys_dist) {
|
||||
//we're farther away than the subsys, let's try at our current distance
|
||||
world_goal_pos = goal_pos + target_objp->pos;
|
||||
if (!pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f))
|
||||
good_pos = &world_goal_pos;
|
||||
}
|
||||
|
||||
// ok, let's start moving it away and see if that's better
|
||||
for (float i = 1.0f; i < 4.0f; i += 1.0f) {
|
||||
if (good_pos)
|
||||
break;
|
||||
|
||||
vm_vec_copy_scale(&world_goal_pos, &goal_pos, (new_radius / pl_dist) * powf(1.3f,i));
|
||||
world_goal_pos += target_objp->pos;
|
||||
if (!pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f))
|
||||
good_pos = &world_goal_pos;
|
||||
}
|
||||
|
||||
// welp this sucks, just run away i guess
|
||||
if (!good_pos) {
|
||||
vm_vec_copy_normalize(&world_goal_pos, &tgt2pl);
|
||||
world_goal_pos *= target_objp->radius + pl_dist;
|
||||
world_goal_pos += target_objp->pos;
|
||||
good_pos = &world_goal_pos;
|
||||
}
|
||||
}
|
||||
|
||||
// ok, let's start moving it away and see if that's better
|
||||
for (float i = 1.0f; i < 4.0f; i += 1.0f) {
|
||||
if (good_pos)
|
||||
break;
|
||||
|
||||
vm_vec_copy_scale(&world_goal_pos, &goal_pos, (new_radius / pl_dist) * powf(1.3f,i));
|
||||
world_goal_pos += target_objp->pos;
|
||||
if (!pp_collide(&Pl_objp->pos, &world_goal_pos, target_objp, Pl_objp->radius * 4.0f))
|
||||
good_pos = &world_goal_pos;
|
||||
}
|
||||
|
||||
// welp this sucks, just run away i guess
|
||||
if (!good_pos) {
|
||||
vm_vec_copy_normalize(&world_goal_pos, &tgt2pl);
|
||||
world_goal_pos *= target_objp->radius + pl_dist;
|
||||
world_goal_pos += target_objp->pos;
|
||||
else
|
||||
good_pos = &world_goal_pos;
|
||||
}
|
||||
aip->next_dynamic_path_check_time = timestamp( AI_DYNAMIC_PATH_RECHECK_DELAY );
|
||||
|
||||
}
|
||||
else{ //Not enough time has passed till recheck.
|
||||
good_pos = &aip->last_dynamic_path_goal;
|
||||
}
|
||||
else
|
||||
good_pos = &world_goal_pos;
|
||||
|
||||
ai_turn_towards_vector(good_pos, Pl_objp, nullptr, nullptr, 0.0f, 0);
|
||||
float subsys_distance = vm_vec_dist(&Pl_objp->pos, &gsubpos);
|
||||
|
||||
+406
-305
File diff suppressed because it is too large.
Load diff
+132
-84
@@ -11,6 +11,7 @@
|
||||
|
||||
|
||||
#include "ai/aigoals.h"
|
||||
#include "ai/ailua.h"
|
||||
#include "globalincs/linklist.h"
|
||||
#include "mission/missionlog.h"
|
||||
#include "mission/missionparse.h"
|
||||
@@ -53,12 +54,6 @@ struct ship_registry_entry;
|
||||
// goals given from the player to other ships in the game are also handled in this
|
||||
// code
|
||||
|
||||
|
||||
#define AI_GOAL_ACHIEVABLE 1
|
||||
#define AI_GOAL_NOT_ACHIEVABLE 2
|
||||
#define AI_GOAL_NOT_KNOWN 3
|
||||
#define AI_GOAL_SATISFIED 4
|
||||
|
||||
int Ai_goal_signature;
|
||||
int Num_ai_dock_names = 0;
|
||||
char Ai_dock_names[MAX_AI_DOCK_NAMES][NAME_LENGTH];
|
||||
@@ -100,7 +95,7 @@ int Num_ai_goals = sizeof(Ai_goal_names) / sizeof(ai_goal_list);
|
||||
// HUD what a ship's current orders are. If the AI goal doesn't correspond to something that
|
||||
// ought to be printable, then NULL is used.
|
||||
// JAS: Converted to a function in order to externalize the strings
|
||||
const char *Ai_goal_text(int goal)
|
||||
const char *Ai_goal_text(int goal, int submode)
|
||||
{
|
||||
switch(goal) {
|
||||
case AI_GOAL_CHASE:
|
||||
@@ -131,10 +126,15 @@ const char *Ai_goal_text(int goal)
|
||||
return XSTR( "rearm ", 484);
|
||||
case AI_GOAL_FLY_TO_SHIP:
|
||||
return XSTR( "rendezvous with ", 1597);
|
||||
case AI_GOAL_LUA:
|
||||
auto mode = ai_lua_find_mode(submode);
|
||||
if (mode == nullptr)
|
||||
return nullptr;
|
||||
return mode->hudText;
|
||||
}
|
||||
|
||||
// Avoid compiler warning
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ai_maybe_add_form_goal(wing* wingp)
|
||||
@@ -240,8 +240,8 @@ int ai_query_goal_valid( int ship, int ai_goal_type )
|
||||
{
|
||||
int accepted;
|
||||
|
||||
if (ai_goal_type == AI_GOAL_NONE)
|
||||
return 1; // anything can have no orders.
|
||||
if (ai_goal_type == AI_GOAL_NONE || ai_goal_type == AI_GOAL_LUA)
|
||||
return 1; // anything can have no orders or Lua'd orders.
|
||||
|
||||
accepted = 0;
|
||||
|
||||
@@ -266,14 +266,20 @@ void ai_remove_ship_goal( ai_info *aip, int index )
|
||||
// ai goal code look
|
||||
Assert ( index >= 0 ); // must have a valid goal
|
||||
|
||||
if (index == aip->active_goal)
|
||||
{
|
||||
// rearm/repair needs a bit of extra cleanup
|
||||
if (aip->goals[index].ai_mode == AI_GOAL_REARM_REPAIR)
|
||||
ai_abort_rearm_request(&Objects[Ships[aip->shipnum].objnum]);
|
||||
|
||||
aip->active_goal = AI_GOAL_NONE;
|
||||
}
|
||||
|
||||
aip->goals[index].ai_mode = AI_GOAL_NONE;
|
||||
aip->goals[index].signature = -1;
|
||||
aip->goals[index].priority = -1;
|
||||
aip->goals[index].flags.reset(); // must reset the flags since not doing so will screw up goal sorting.
|
||||
|
||||
if ( index == aip->active_goal )
|
||||
aip->active_goal = AI_GOAL_NONE;
|
||||
|
||||
// mwa -- removed this line 8/5/97. Just because we remove a goal doesn't mean to do the default
|
||||
// behavior. We will make the call commented out below in a more reasonable location
|
||||
//ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] );
|
||||
@@ -300,7 +306,7 @@ void ai_clear_ship_goals( ai_info *aip )
|
||||
// add scripting hook for 'On Goals Cleared' --wookieejedi
|
||||
if (Script_system.IsActiveAction(CHA_ONGOALSCLEARED)) {
|
||||
Script_system.SetHookObject("Ship", &Objects[Ships[aip->shipnum].objnum]);
|
||||
Script_system.RunCondition(CHA_ONGOALSCLEARED);
|
||||
Script_system.RunCondition(CHA_ONGOALSCLEARED, &Objects[Ships[aip->shipnum].objnum]);
|
||||
Script_system.RemHookVars({"Ship"});
|
||||
}
|
||||
}
|
||||
@@ -653,7 +659,7 @@ void ai_goal_fixup_dockpoints(ai_info *aip, ai_goal *aigp)
|
||||
// from the mission goals (i.e. those goals which come from events) in that we don't
|
||||
// use sexpressions for goals from the player...so we enumerate all the parameters
|
||||
|
||||
void ai_add_goal_sub_player(int type, int mode, int submode, char *target_name, ai_goal *aigp )
|
||||
void ai_add_goal_sub_player(int type, int mode, int submode, char *target_name, ai_goal *aigp, const object_ship_wing_point_team& lua_target )
|
||||
{
|
||||
Assert ( (type == AIG_TYPE_PLAYER_WING) || (type == AIG_TYPE_PLAYER_SHIP) );
|
||||
|
||||
@@ -661,6 +667,7 @@ void ai_add_goal_sub_player(int type, int mode, int submode, char *target_name,
|
||||
aigp->type = type; // from player for sure -- could be to ship or to wing
|
||||
aigp->ai_mode = mode; // major mode for this goal
|
||||
aigp->ai_submode = submode; // could mean different things depending on mode
|
||||
aigp->lua_ai_target = lua_target;
|
||||
|
||||
if ( mode == AI_GOAL_WARP ) {
|
||||
if (submode >= 0) {
|
||||
@@ -806,7 +813,7 @@ void ai_add_ship_goal_scripting(int mode, int submode, int priority, char *shipn
|
||||
// is issued to ship or wing (from player), mode is AI_GOAL_*. submode is the submode the
|
||||
// ship should go into. shipname is the object of the action. aip is the ai_info pointer
|
||||
// of the ship receiving the order
|
||||
void ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, ai_info *aip )
|
||||
void ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, ai_info *aip, const object_ship_wing_point_team& lua_target)
|
||||
{
|
||||
int empty_index;
|
||||
ai_goal *aigp;
|
||||
@@ -815,7 +822,7 @@ void ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, a
|
||||
|
||||
// get a pointer to the goal structure
|
||||
aigp = &aip->goals[empty_index];
|
||||
ai_add_goal_sub_player( type, mode, submode, shipname, aigp );
|
||||
ai_add_goal_sub_player( type, mode, submode, shipname, aigp, lua_target );
|
||||
|
||||
// if the goal is to dock, then we must determine which dock points on the two ships to use.
|
||||
// If the target of the dock is a cargo type container, then we should use DOCK_TYPE_CARGO
|
||||
@@ -832,7 +839,7 @@ void ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, a
|
||||
|
||||
// adds a goal from the player to the given wing (which in turn will add it to the proper
|
||||
// ships in the wing
|
||||
void ai_add_wing_goal_player( int type, int mode, int submode, char *shipname, int wingnum )
|
||||
void ai_add_wing_goal_player( int type, int mode, int submode, char *shipname, int wingnum, const object_ship_wing_point_team& lua_target)
|
||||
{
|
||||
int i, empty_index;
|
||||
wing *wingp = &Wings[wingnum];
|
||||
@@ -843,7 +850,7 @@ void ai_add_wing_goal_player( int type, int mode, int submode, char *shipname, i
|
||||
int num = wingp->ship_index[i];
|
||||
if ( num == -1 ) // ship must have been destroyed or departed
|
||||
continue;
|
||||
ai_add_ship_goal_player( type, mode, submode, shipname, &Ai_info[Ships[num].ai_index] );
|
||||
ai_add_ship_goal_player( type, mode, submode, shipname, &Ai_info[Ships[num].ai_index], lua_target );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,7 +858,7 @@ void ai_add_wing_goal_player( int type, int mode, int submode, char *shipname, i
|
||||
// there are more waves to come. We use the same method here as when adding a goal to
|
||||
// a ship -- find the first empty entry. If none exists, take the oldest entry and replace it.
|
||||
empty_index = ai_goal_find_empty_slot( wingp->ai_goals, -1 );
|
||||
ai_add_goal_sub_player( type, mode, submode, shipname, &wingp->ai_goals[empty_index] );
|
||||
ai_add_goal_sub_player( type, mode, submode, shipname, &wingp->ai_goals[empty_index], lua_target );
|
||||
}
|
||||
|
||||
|
||||
@@ -1035,7 +1042,24 @@ void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp, char *actor_name )
|
||||
break;
|
||||
|
||||
default:
|
||||
Int3(); // get ALLENDER -- invalid ai-goal specified for ai object!!!!
|
||||
const ai_mode_lua* luaAIMode = ai_lua_find_mode(op);
|
||||
if(luaAIMode != nullptr){
|
||||
//Found a LuaAI mode sexp for this
|
||||
int localnode = CDR(node);
|
||||
|
||||
aigp->ai_mode = AI_GOAL_LUA;
|
||||
aigp->ai_submode = op;
|
||||
|
||||
if(luaAIMode->needsTarget) {
|
||||
eval_object_ship_wing_point_team(&aigp->lua_ai_target, localnode);
|
||||
localnode = CDR(localnode);
|
||||
}
|
||||
|
||||
aigp->priority = atoi( CTEXT(localnode) );
|
||||
}
|
||||
else {
|
||||
UNREACHABLE("Invalid SEXP-OP number %d for an AI goal!", op);
|
||||
}
|
||||
}
|
||||
|
||||
if ( aigp->priority > MAX_GOAL_PRIORITY ) {
|
||||
@@ -1211,8 +1235,28 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
|
||||
priority = ( CDR( CDR(node) ) >= 0 ) ? atoi( CTEXT( CDR( CDR( node ) ) ) ) : -1;
|
||||
goalmode = AI_GOAL_IGNORE_NEW;
|
||||
break;
|
||||
case OP_AI_FORM_ON_WING:
|
||||
priority = 99;
|
||||
goalmode = AI_GOAL_FORM_ON_WING;
|
||||
break;
|
||||
default:
|
||||
Int3( );
|
||||
const ai_mode_lua* luaAIMode = ai_lua_find_mode(op);
|
||||
if(luaAIMode != nullptr){
|
||||
//Found a LuaAI mode sexp for this
|
||||
int localnode = CDR(node);
|
||||
|
||||
goalmode = AI_GOAL_LUA;
|
||||
goalsubmode = op;
|
||||
|
||||
if(luaAIMode->needsTarget) {
|
||||
localnode = CDR(localnode);
|
||||
}
|
||||
|
||||
priority = localnode >= 0 ? atoi( CTEXT(localnode) ) : -1;
|
||||
}
|
||||
else {
|
||||
UNREACHABLE("Invalid SEXP-OP %s (number %d) for an AI goal!", Sexp_nodes[node].text, op);
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
@@ -1232,13 +1276,13 @@ int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp )
|
||||
return goalindex;
|
||||
}
|
||||
|
||||
// code to add ai goals to wings.
|
||||
// code to remove ai goals from wings.
|
||||
void ai_remove_wing_goal_sexp(int sexp, wing *wingp)
|
||||
{
|
||||
int i;
|
||||
int goalindex = -1;
|
||||
|
||||
// add the ai goal for any ship that is currently arrived in the game (only if fred isn't running)
|
||||
// remove the ai goal for any ship that is currently arrived in the game (only if fred isn't running)
|
||||
if ( !Fred_running ) {
|
||||
for (i = 0; i < wingp->current_count; i++) {
|
||||
int num = wingp->ship_index[i];
|
||||
@@ -1250,6 +1294,8 @@ void ai_remove_wing_goal_sexp(int sexp, wing *wingp)
|
||||
}
|
||||
}
|
||||
|
||||
// remove the sexpression index from the wing's list of goal sexpressions if
|
||||
// there are more waves to come
|
||||
if ((wingp->num_waves - wingp->current_wave > 0) || Fred_running)
|
||||
{
|
||||
ai_remove_goal_sexp_sub( sexp, wingp->ai_goals );
|
||||
@@ -1403,10 +1449,10 @@ void ai_copy_mission_wing_goal( ai_goal *aigp, ai_info *aip )
|
||||
// function to determine if an ai goal is achieveable or not. Will return
|
||||
// one of the AI_GOAL_* values. Also determines is a goal was successful.
|
||||
|
||||
int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
{
|
||||
int status;
|
||||
int return_val;
|
||||
ai_achievability return_val;
|
||||
object *objp;
|
||||
ai_info *aip;
|
||||
int index = -1, sindex = -1;
|
||||
@@ -1419,8 +1465,11 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
// these orders are always achievable.
|
||||
if ( (aigp->ai_mode == AI_GOAL_KEEP_SAFE_DISTANCE)
|
||||
|| (aigp->ai_mode == AI_GOAL_CHASE_ANY) || (aigp->ai_mode == AI_GOAL_STAY_STILL)
|
||||
|| (aigp->ai_mode == AI_GOAL_PLAY_DEAD) || (aigp->ai_mode == AI_GOAL_PLAY_DEAD_PERSISTENT) )
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
|| (aigp->ai_mode == AI_GOAL_PLAY_DEAD) || (aigp->ai_mode == AI_GOAL_PLAY_DEAD_PERSISTENT))
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
|
||||
if (aigp->ai_mode == AI_GOAL_LUA)
|
||||
return ai_lua_is_achievable(aigp, objnum);
|
||||
|
||||
// warp (depart) only achievable if there's somewhere to depart to
|
||||
if (aigp->ai_mode == AI_GOAL_WARP)
|
||||
@@ -1429,14 +1478,14 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
|
||||
// always valid if has working subspace drive, not disabled, and not limited by subsystem strength
|
||||
if ( ship_can_warp_full_check(shipp) )
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
|
||||
// if we can't warp, we can only depart if the ship (or its wing) departs to a fighter bay and the mothership is present
|
||||
if (ship_can_bay_depart(shipp)) {
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
else {
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1448,10 +1497,10 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
sindex = ship_name_lookup( aigp->target_name );
|
||||
|
||||
if (sindex < 0)
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
|
||||
aigp->flags.set(AI::Goal_Flags::Goal_override);
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
|
||||
// check to see if we have a valid list. If not, then try to set one up. If that
|
||||
@@ -1462,10 +1511,10 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
|
||||
if ( aigp->wp_list == NULL ) {
|
||||
Warning(LOCATION, "Unknown waypoint list %s - not found in mission file. Killing ai goal", aigp->target_name );
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
}
|
||||
}
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
|
||||
// chasing all ships of a certain ship class is achievable if there are ships of that class present;
|
||||
@@ -1474,14 +1523,14 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
if ( aigp->ai_mode == AI_GOAL_CHASE_SHIP_CLASS ) {
|
||||
for (objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) {
|
||||
if ((objp->type == OBJ_SHIP) && !strcmp(aigp->target_name, Ship_info[Ships[objp->instance].ship_info_index].name)) {
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
}
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
}
|
||||
|
||||
|
||||
return_val = AI_GOAL_SATISFIED;
|
||||
return_val = ai_achievability::SATISFIED;
|
||||
|
||||
// next, determine if the goal has been completed successfully
|
||||
switch ( aigp->ai_mode )
|
||||
@@ -1583,7 +1632,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
}
|
||||
|
||||
if ( status )
|
||||
return_val = AI_GOAL_NOT_ACHIEVABLE;
|
||||
return_val = ai_achievability::NOT_ACHIEVABLE;
|
||||
} else {
|
||||
status = 0;
|
||||
}
|
||||
@@ -1598,7 +1647,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
if ( !status ) {
|
||||
status = mission_log_get_time( LOG_WING_DESTROYED, aigp->target_name, NULL, NULL);
|
||||
if ( status )
|
||||
return_val = AI_GOAL_NOT_ACHIEVABLE;
|
||||
return_val = ai_achievability::NOT_ACHIEVABLE;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1612,14 +1661,14 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
Assert( aigp->target_instance >= 0 );
|
||||
|
||||
if ( Weapons[aigp->target_instance].objnum == -1 )
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
|
||||
// if the signatures don't match, then goal isn't achievable.
|
||||
if ( Objects[Weapons[aigp->target_instance].objnum].signature != aigp->target_signature )
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
|
||||
// otherwise, we should be good to go
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1643,22 +1692,22 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
sindex = wing_name_lookup( aigp->target_name );
|
||||
|
||||
if (sindex < 0)
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
|
||||
wing *wingp = &Wings[sindex];
|
||||
|
||||
if ( wingp->flags[Ship::Wing_Flags::Gone] )
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
else if ( wingp->total_arrived_count == 0 )
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
else
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
// Goober5000 - undocking from an unspecified object is always achievable;
|
||||
// undocking from a specified object is handled below
|
||||
else if ( (aigp->ai_mode == AI_GOAL_UNDOCK) && (aigp->target_name == NULL) )
|
||||
{
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1699,7 +1748,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
{
|
||||
// if the ship has no turrets, we can't disarm it!
|
||||
if (Ships[ship_name_lookup(aigp->target_name)].subsys_info[SUBSYSTEM_TURRET].type_count == 0)
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
}
|
||||
|
||||
// if the goal is an ignore/disable/disarm goal, then
|
||||
@@ -1741,12 +1790,12 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
|
||||
if ( (aigp->dockee.index == -1) || (aigp->docker.index == -1) ) {
|
||||
Int3(); // for now, allender wants to know about these things!!!!
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
}
|
||||
|
||||
// if ship is disabled, don't know if it can dock or not
|
||||
if ( Ships[objp->instance].flags[Ship::Ship_Flags::Disabled] )
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
|
||||
// we must also determine if we're prevented from docking for any reason
|
||||
sindex = ship_name_lookup(aigp->target_name);
|
||||
@@ -1768,7 +1817,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
else if (obstacle_objp != objp)
|
||||
{
|
||||
// return NOT_KNOWN which will place the goal on hold until the dockpoint is clear
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1788,7 +1837,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, Ships[obstacle_objp->instance].ship_name, -1, -1, 0 );
|
||||
|
||||
// return NOT_KNOWN which will place the goal on hold until the undocking is complete.
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1802,7 +1851,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
{
|
||||
// only put it on hold if it's someone other than the guy we're undocking from right now!!
|
||||
if (aip->goal_objnum != Ships[ship_name_lookup(aigp->target_name)].objnum)
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
}
|
||||
|
||||
} else if ( (aigp->ai_mode == AI_GOAL_DESTROY_SUBSYSTEM) && (status == SHIP_STATUS_ARRIVED) ) {
|
||||
@@ -1813,11 +1862,11 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
sindex = ship_name_lookup( aigp->target_name );
|
||||
|
||||
if ( sindex != -1 ) {
|
||||
aigp->ai_submode = ship_get_subsys_index( &Ships[sindex], aigp->docker.name );
|
||||
aigp->ai_submode = ship_find_subsys( &Ships[sindex], aigp->docker.name );
|
||||
aigp->flags.remove(AI::Goal_Flags::Subsys_needs_fixup);
|
||||
} else {
|
||||
Int3();
|
||||
return AI_GOAL_NOT_ACHIEVABLE; // force this goal to be invalid
|
||||
return ai_achievability::NOT_ACHIEVABLE; // force this goal to be invalid
|
||||
}
|
||||
}
|
||||
} else if ( ((aigp->ai_mode == AI_GOAL_IGNORE) || (aigp->ai_mode == AI_GOAL_IGNORE_NEW)) && (status == SHIP_STATUS_ARRIVED) ) {
|
||||
@@ -1830,7 +1879,7 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
|
||||
ai_ignore_object(objp, ignored, (aigp->ai_mode == AI_GOAL_IGNORE_NEW));
|
||||
|
||||
return AI_GOAL_SATISFIED;
|
||||
return ai_achievability::SATISFIED;
|
||||
}
|
||||
|
||||
switch ( aigp->ai_mode )
|
||||
@@ -1851,13 +1900,13 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
case AI_GOAL_FLY_TO_SHIP:
|
||||
{
|
||||
if ( status == SHIP_STATUS_ARRIVED )
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
else if ( status == SHIP_STATUS_NOT_ARRIVED )
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
else if ( status == SHIP_STATUS_GONE )
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
else if ( status == SHIP_STATUS_UNKNOWN )
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
|
||||
Int3(); // get allender -- bad logic
|
||||
break;
|
||||
@@ -1872,66 +1921,61 @@ int ai_mission_goal_achievable( int objnum, ai_goal *aigp )
|
||||
// we mark the goal as not achievable.
|
||||
if ( status == SHIP_STATUS_NOT_ARRIVED ) {
|
||||
Int3(); // get Allender. this shouldn't happen!!!
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
}
|
||||
|
||||
if ( status == SHIP_STATUS_GONE )
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
|
||||
sindex = ship_name_lookup( aigp->target_name );
|
||||
|
||||
if ( sindex < 0 ) {
|
||||
Int3();
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
}
|
||||
|
||||
// if desitnation currently being repaired, then goal is stil active
|
||||
if ( Ai_info[Ships[sindex].ai_index].ai_flags[AI::AI_Flags::Being_repaired] )
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
|
||||
// if the destination ship is dying or departing (but not completed yet), the mark goal as
|
||||
// not achievable.
|
||||
if ( Ships[sindex].is_dying_or_departing())
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
|
||||
// if the destination object is no longer awaiting repair, then remove the item
|
||||
if ( !(Ai_info[Ships[sindex].ai_index].ai_flags[AI::AI_Flags::Awaiting_repair]) )
|
||||
return AI_GOAL_NOT_ACHIEVABLE;
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
|
||||
// not repairing anything means that he can do this goal!!!
|
||||
if ( !(aip->ai_flags[AI::AI_Flags::Repairing]) )
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
|
||||
// test code!!!
|
||||
if ( aip->goal_objnum == -1 ) {
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
|
||||
// if he is repairing something, he can satisfy his repair goal (his goal_objnum)
|
||||
// return GOAL_NOT_KNOWN which is kind of a hack which puts the goal on hold until it can be
|
||||
// satisfied.
|
||||
if ( aip->goal_objnum != Ships[sindex].objnum )
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
|
||||
return AI_GOAL_ACHIEVABLE;
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
|
||||
default:
|
||||
Int3(); // invalid case in switch:
|
||||
}
|
||||
|
||||
return AI_GOAL_NOT_KNOWN;
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
}
|
||||
|
||||
// Compare function for sorting ai_goals based on priority.
|
||||
// Return values set to sort array in _decreasing_ order.
|
||||
int ai_goal_priority_compare(const void *a, const void *b)
|
||||
int ai_goal_priority_compare(const ai_goal *ga, const ai_goal *gb)
|
||||
{
|
||||
ai_goal *ga, *gb;
|
||||
|
||||
ga = (ai_goal *) a;
|
||||
gb = (ai_goal *) b;
|
||||
|
||||
// first, sort based on whether or not the ON_HOLD flag is set for the goal.
|
||||
// If the flag is set, don't push the goal higher in the list even if priority
|
||||
// is higher since goal cannot currently be achieved.
|
||||
@@ -1979,7 +2023,7 @@ int ai_goal_priority_compare(const void *a, const void *b)
|
||||
void prioritize_goals(ai_info *aip)
|
||||
{
|
||||
// First sort based on priority field.
|
||||
insertion_sort(aip->goals, MAX_AI_GOALS, sizeof(ai_goal), ai_goal_priority_compare);
|
||||
insertion_sort(aip->goals, MAX_AI_GOALS, ai_goal_priority_compare);
|
||||
}
|
||||
|
||||
// Scan the list of goals at aip->goals.
|
||||
@@ -1993,7 +2037,7 @@ void validate_mission_goals(int objnum, ai_info *aip)
|
||||
// loop through all of the goals to determine which goal should be followed.
|
||||
// This determination will be based on priority, and the time at which it was issued.
|
||||
for ( i = 0; i < MAX_AI_GOALS; i++ ) {
|
||||
int state;
|
||||
ai_achievability state;
|
||||
ai_goal *aigp;
|
||||
|
||||
aigp = &aip->goals[i];
|
||||
@@ -2012,18 +2056,18 @@ void validate_mission_goals(int objnum, ai_info *aip)
|
||||
state = ai_mission_goal_achievable( objnum, aigp );
|
||||
|
||||
// if this order is no longer a valid one, remove it
|
||||
if ( (state == AI_GOAL_NOT_ACHIEVABLE) || (state == AI_GOAL_SATISFIED) ) {
|
||||
if ( (state == ai_achievability::NOT_ACHIEVABLE) || (state == ai_achievability::SATISFIED) ) {
|
||||
ai_remove_ship_goal( aip, i );
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the status is achievable, and the on_hold flag is set, clear the flagb
|
||||
if ( (state == AI_GOAL_ACHIEVABLE) && (aigp->flags[AI::Goal_Flags::Goal_on_hold]) )
|
||||
if ( (state == ai_achievability::ACHIEVABLE) && (aigp->flags[AI::Goal_Flags::Goal_on_hold]) )
|
||||
aigp->flags.remove(AI::Goal_Flags::Goal_on_hold);
|
||||
|
||||
// if the goal is not known, then set the ON_HOLD flag so that it doesn't get counted as
|
||||
// a goal to be pursued
|
||||
if (state == AI_GOAL_NOT_KNOWN)
|
||||
if (state == ai_achievability::NOT_KNOWN)
|
||||
aigp->flags.set(AI::Goal_Flags::Goal_on_hold); // put this goal on hold until it becomes true
|
||||
}
|
||||
|
||||
@@ -2391,13 +2435,17 @@ void ai_process_mission_orders( int objnum, ai_info *aip )
|
||||
other_obj = &Objects[Ships[shipnum].objnum];
|
||||
ai_rearm_repair( objp, current_goal->docker.index, other_obj, current_goal->dockee.index );
|
||||
break;
|
||||
|
||||
case AI_GOAL_LUA:
|
||||
ai_lua_start(current_goal, objp);
|
||||
break;
|
||||
|
||||
default:
|
||||
Int3();
|
||||
break;
|
||||
}
|
||||
|
||||
if (old_form_objnum != -1)
|
||||
if (old_form_objnum >= 0)
|
||||
ai_formation_object_recalculate_slotnums(old_form_objnum);
|
||||
}
|
||||
|
||||
|
||||
+8
-4
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "globalincs/globals.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "parse/sexp.h"
|
||||
|
||||
struct wing;
|
||||
struct ai_info;
|
||||
@@ -59,6 +60,7 @@ struct ai_goal;
|
||||
#define AI_GOAL_IGNORE_NEW (1<<24) // 0x01000000
|
||||
#define AI_GOAL_CHASE_SHIP_CLASS (1<<25) // 0x02000000
|
||||
#define AI_GOAL_PLAY_DEAD_PERSISTENT (1<<26) // 0x03000000
|
||||
#define AI_GOAL_LUA (1<<27) // 0x04000000
|
||||
|
||||
// now the masks for ship types
|
||||
|
||||
@@ -82,6 +84,8 @@ struct ai_goal;
|
||||
|
||||
#define MAX_AI_DOCK_NAMES 25
|
||||
|
||||
enum class ai_achievability { ACHIEVABLE, NOT_ACHIEVABLE, NOT_KNOWN, SATISFIED };
|
||||
|
||||
typedef flag_def_list ai_goal_list;
|
||||
|
||||
extern ai_goal_list Ai_goal_names[];
|
||||
@@ -90,7 +94,7 @@ extern int Num_ai_goals;
|
||||
extern int Num_ai_dock_names;
|
||||
extern char Ai_dock_names[MAX_AI_DOCK_NAMES][NAME_LENGTH];
|
||||
|
||||
extern const char *Ai_goal_text(int goal);
|
||||
extern const char *Ai_goal_text(int goal, int submode);
|
||||
|
||||
// extern function definitions
|
||||
extern void ai_post_process_mission();
|
||||
@@ -103,14 +107,14 @@ extern int ai_goal_num(ai_goal *goals);
|
||||
extern void ai_add_ship_goal_scripting(int mode, int submode, int priority, char *shipname, ai_info *aip);
|
||||
extern void ai_add_ship_goal_sexp( int sexp, int type, ai_info *aip );
|
||||
extern void ai_add_wing_goal_sexp( int sexp, int type, wing *wingp );
|
||||
extern void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp, char *actor_name );
|
||||
extern void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp, char *actor_name);
|
||||
|
||||
extern int ai_remove_goal_sexp_sub( int sexp, ai_goal* aigp );
|
||||
extern void ai_remove_wing_goal_sexp( int sexp, wing *wingp );
|
||||
|
||||
// adds goals to ships/sings through player orders
|
||||
extern void ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, ai_info *aip );
|
||||
extern void ai_add_wing_goal_player( int type, int mode, int submode, char *shipname, int wingnum );
|
||||
extern void ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, ai_info *aip, const object_ship_wing_point_team& lua_target = object_ship_wing_point_team());
|
||||
extern void ai_add_wing_goal_player( int type, int mode, int submode, char *shipname, int wingnum, const object_ship_wing_point_team& lua_target = object_ship_wing_point_team());
|
||||
|
||||
extern void ai_remove_ship_goal( ai_info *aip, int index );
|
||||
extern void ai_clear_ship_goals( ai_info *aip );
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
#include "ai/ailua.h"
|
||||
|
||||
#include "hud/hudsquadmsg.h"
|
||||
#include "iff_defs/iff_defs.h"
|
||||
#include "parse/sexp/sexp_lookup.h"
|
||||
#include "parse/sexp/LuaAISEXP.h"
|
||||
#include "scripting/api/objs/ai_helper.h"
|
||||
#include "scripting/api/objs/enums.h"
|
||||
#include "scripting/api/objs/oswpt.h"
|
||||
#include "scripting/api/objs/ship.h"
|
||||
#include "scripting/scripting.h"
|
||||
|
||||
static std::unordered_map<int, ai_mode_lua> Lua_ai_modes;
|
||||
static std::unordered_map<int, player_order_lua> Lua_player_orders;
|
||||
|
||||
void ai_lua_add_mode(int sexp_op, const ai_mode_lua& mode) {
|
||||
Lua_ai_modes.emplace(sexp_op, mode);
|
||||
}
|
||||
|
||||
bool ai_lua_add_order(int sexp_op, player_order_lua order) {
|
||||
Player_orders.emplace_back(vm_strdup(order.parseText.c_str()), vm_strdup(order.displayText.c_str()), -1, sexp_op);
|
||||
Player_orders.back().localize();
|
||||
|
||||
Lua_player_orders.emplace(sexp_op, std::move(order));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ai_lua_has_mode(int sexp_op){
|
||||
return Lua_ai_modes.count(sexp_op) > 0;
|
||||
}
|
||||
|
||||
const ai_mode_lua* ai_lua_find_mode(int sexp_op){
|
||||
auto aiLuaMode = Lua_ai_modes.find(sexp_op);
|
||||
|
||||
if(aiLuaMode == Lua_ai_modes.end())
|
||||
return nullptr;
|
||||
else
|
||||
return &aiLuaMode->second;
|
||||
}
|
||||
|
||||
const player_order_lua* ai_lua_find_player_order(int sexp_op) {
|
||||
auto aiLuaOrder = Lua_player_orders.find(sexp_op);
|
||||
|
||||
if (aiLuaOrder == Lua_player_orders.end())
|
||||
return nullptr;
|
||||
else
|
||||
return &aiLuaOrder->second;
|
||||
}
|
||||
|
||||
void run_ai_lua_action(const luacpp::LuaFunction& action, const ai_mode_lua& lua_ai, ai_info* aip) {
|
||||
if (!action.isValid()) {
|
||||
Error(LOCATION,
|
||||
"Lua AI SEXP called without a valid action function! A script probably failed to set the action for some reason.");
|
||||
return;
|
||||
}
|
||||
|
||||
luacpp::LuaValueList luaParameters;
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_AI_Helper.Set(object_h(&Objects[Ships[aip->shipnum].objnum]))));
|
||||
if (lua_ai.needsTarget) {
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_OSWPT.Set(aip->lua_ai_target)));
|
||||
}
|
||||
|
||||
auto retVals = action.call(Script_system.GetLuaSession(), luaParameters);
|
||||
|
||||
if (!retVals.empty() && retVals[0].getValueType() == luacpp::ValueType::BOOLEAN) {
|
||||
if (retVals[0].getValue<bool>())
|
||||
ai_mission_goal_complete(aip);
|
||||
}
|
||||
}
|
||||
|
||||
void ai_lua(ai_info* aip){
|
||||
Assertion(aip->mode == AIM_LUA, "Tried to invoke LuaAI on a ship not in LuaAI-Mode");
|
||||
|
||||
const auto& lua_ai = Lua_ai_modes.at(aip->submode);
|
||||
|
||||
auto dynamicSEXP = sexp::get_dynamic_sexp(aip->submode);
|
||||
|
||||
if (dynamicSEXP != nullptr && typeid(*dynamicSEXP) == typeid(sexp::LuaAISEXP)) {
|
||||
auto lua_ai_sexp = static_cast<sexp::LuaAISEXP*>(dynamicSEXP);
|
||||
const auto& action = lua_ai_sexp->getActionFrame();
|
||||
|
||||
run_ai_lua_action(action, lua_ai, aip);
|
||||
}
|
||||
}
|
||||
|
||||
void ai_lua_start(ai_goal* aigp, object* objp){
|
||||
|
||||
Assert(objp->type == OBJ_SHIP);
|
||||
Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
|
||||
|
||||
ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
|
||||
|
||||
aip->mode = AIM_LUA;
|
||||
aip->submode = aigp->ai_submode;
|
||||
aip->lua_ai_target = aigp->lua_ai_target;
|
||||
aip->submode_start_time = Missiontime;
|
||||
|
||||
const auto& lua_ai = Lua_ai_modes.at(aigp->ai_submode);
|
||||
|
||||
auto dynamicSEXP = sexp::get_dynamic_sexp(aigp->ai_submode);
|
||||
|
||||
if (dynamicSEXP != nullptr && typeid(*dynamicSEXP) == typeid(sexp::LuaAISEXP)) {
|
||||
auto lua_ai_sexp = static_cast<sexp::LuaAISEXP*>(dynamicSEXP);
|
||||
const auto& action = lua_ai_sexp->getActionEnter();
|
||||
|
||||
run_ai_lua_action(action, lua_ai, aip);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ai_lua_is_valid_target_intrinsic(int sexp_op, int target_objnum, ship* self) {
|
||||
ship* target = &Ships[Objects[target_objnum].instance];
|
||||
|
||||
const player_order_lua& order = *ai_lua_find_player_order(sexp_op);
|
||||
switch (order.targetRestrictions) {
|
||||
case player_order_lua::target_restrictions::TARGET_ALL:
|
||||
return true;
|
||||
case player_order_lua::target_restrictions::TARGET_OWN:
|
||||
return target->team == self->team;
|
||||
case player_order_lua::target_restrictions::TARGET_ALLIES:
|
||||
return !iff_x_attacks_y(self->team, target->team);
|
||||
case player_order_lua::target_restrictions::TARGET_ENEMIES:
|
||||
return iff_x_attacks_y(self->team, target->team);
|
||||
case player_order_lua::target_restrictions::TARGET_SAME_WING:
|
||||
return target->wingnum != -1 && self->wingnum == target->wingnum;
|
||||
case player_order_lua::target_restrictions::TARGET_PLAYER_WING:
|
||||
return target->wingnum != -1 && Ships[Player_obj->instance].wingnum == target->wingnum;
|
||||
case player_order_lua::target_restrictions::TARGET_ALL_CAPS:
|
||||
return Ship_info[target->ship_info_index].is_big_ship();
|
||||
case player_order_lua::target_restrictions::TARGET_ALLIED_CAPS:
|
||||
return !iff_x_attacks_y(self->team, target->team) && Ship_info[target->ship_info_index].is_big_ship();
|
||||
case player_order_lua::target_restrictions::TARGET_ENEMY_CAPS:
|
||||
return iff_x_attacks_y(self->team, target->team) && Ship_info[target->ship_info_index].is_big_ship();
|
||||
case player_order_lua::target_restrictions::TARGET_NOT_SELF:
|
||||
return self->objnum != target_objnum;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ai_lua_is_valid_target_lua(const ai_mode_lua& mode, int sexp_op, int target_objnum, ship* self) {
|
||||
auto dynamicSEXP = sexp::get_dynamic_sexp(sexp_op);
|
||||
|
||||
if (dynamicSEXP != nullptr && typeid(*dynamicSEXP) == typeid(sexp::LuaAISEXP)) {
|
||||
auto lua_ai_sexp = static_cast<sexp::LuaAISEXP*>(dynamicSEXP);
|
||||
const auto& action = lua_ai_sexp->getTargetRestrict();
|
||||
|
||||
if (!action.isValid())
|
||||
return true;
|
||||
|
||||
luacpp::LuaValueList luaParameters;
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_Ship.Set(object_h(&Objects[self->objnum]))));
|
||||
if (mode.needsTarget) {
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_OSWPT.Set(object_ship_wing_point_team(&Ships[Objects[target_objnum].instance]))));
|
||||
}
|
||||
|
||||
auto retVals = action.call(Script_system.GetLuaSession(), luaParameters);
|
||||
|
||||
if (!retVals.empty() && retVals[0].getValueType() == luacpp::ValueType::BOOLEAN) {
|
||||
return retVals[0].getValue<bool>();
|
||||
}
|
||||
else {
|
||||
Error(LOCATION, "LuaAI target restriction function did not return a valid boolean!");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ai_lua_is_valid_target(int sexp_op, int target_objnum, ship* self) {
|
||||
const ai_mode_lua& mode = *ai_lua_find_mode(sexp_op);
|
||||
|
||||
//All targetless AI modes are fine
|
||||
if (mode.needsTarget) {
|
||||
|
||||
//No target is then not valid
|
||||
if (target_objnum == -1)
|
||||
return false;
|
||||
|
||||
//As of now, only accept ships
|
||||
if (Objects[target_objnum].type != OBJ_SHIP)
|
||||
return false;
|
||||
|
||||
if (!ai_lua_is_valid_target_intrinsic(sexp_op, target_objnum, self))
|
||||
return false;
|
||||
}
|
||||
|
||||
//If we haven't bailed yet, query the custom callback
|
||||
return ai_lua_is_valid_target_lua(mode, sexp_op, target_objnum, self);
|
||||
}
|
||||
|
||||
ai_achievability ai_lua_is_achievable(const ai_goal* aigp, int objnum){
|
||||
const auto& lua_ai = Lua_ai_modes.at(aigp->ai_submode);
|
||||
|
||||
auto dynamicSEXP = sexp::get_dynamic_sexp(aigp->ai_submode);
|
||||
|
||||
if (dynamicSEXP != nullptr && typeid(*dynamicSEXP) == typeid(sexp::LuaAISEXP)) {
|
||||
auto lua_ai_sexp = static_cast<sexp::LuaAISEXP*>(dynamicSEXP);
|
||||
const auto& action = lua_ai_sexp->getAchievable();
|
||||
|
||||
if (!action.isValid())
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
|
||||
luacpp::LuaValueList luaParameters;
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_Ship.Set(object_h(&Objects[objnum]))));
|
||||
if (lua_ai.needsTarget) {
|
||||
luaParameters.push_back(luacpp::LuaValue::createValue(action.getLuaState(), scripting::api::l_OSWPT.Set(aigp->lua_ai_target)));
|
||||
}
|
||||
|
||||
auto retVals = action.call(Script_system.GetLuaSession(), luaParameters);
|
||||
|
||||
if (!retVals.empty() && retVals[0].getValueType() == luacpp::ValueType::USERDATA) {
|
||||
scripting::api::enum_h enumData;
|
||||
retVals[0].getValue(scripting::api::l_Enum.Get(&enumData));
|
||||
if (!enumData.IsValid()) {
|
||||
Error(LOCATION, "LuaAI SEXP achievability hook returned an invalid avilability enum.");
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
|
||||
switch (enumData.index) {
|
||||
case scripting::api::LE_LUAAI_ACHIEVABLE:
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
case scripting::api::LE_LUAAI_NOT_YET_ACHIEVABLE:
|
||||
return ai_achievability::NOT_KNOWN;
|
||||
case scripting::api::LE_LUAAI_UNACHIEVABLE:
|
||||
return ai_achievability::NOT_ACHIEVABLE;
|
||||
default:
|
||||
Error(LOCATION, "LuaAI SEXP achievability hook returned an invalid avilability enum.");
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Error(LOCATION, "LuaAI SEXP achievability hook did not return any avilability enum!");
|
||||
}
|
||||
}
|
||||
|
||||
return ai_achievability::ACHIEVABLE;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "ai/ai.h"
|
||||
#include "ai/aigoals.h"
|
||||
#include "mission/missionmessage.h"
|
||||
#include "ship/ship.h"
|
||||
#include "globalincs/globals.h"
|
||||
|
||||
struct ai_mode_lua {
|
||||
bool needsTarget;
|
||||
const char* hudText;
|
||||
};
|
||||
|
||||
struct player_order_lua {
|
||||
int ai_message = MESSAGE_YESSIR;
|
||||
SCP_string parseText = "";
|
||||
SCP_string displayText = "";
|
||||
enum class target_restrictions : int { TARGET_ALLIES, TARGET_ALL, TARGET_OWN, TARGET_ENEMIES, TARGET_SAME_WING, TARGET_PLAYER_WING, TARGET_ALL_CAPS, TARGET_ALLIED_CAPS, TARGET_ENEMY_CAPS, TARGET_NOT_SELF } targetRestrictions = target_restrictions::TARGET_ALL;
|
||||
};
|
||||
|
||||
void ai_lua_add_mode(int sexp_op, const ai_mode_lua& mode);
|
||||
bool ai_lua_add_order(int sexp_op, player_order_lua order);
|
||||
bool ai_lua_has_mode(int sexp_op);
|
||||
const ai_mode_lua* ai_lua_find_mode(int sexp_op);
|
||||
const player_order_lua* ai_lua_find_player_order(int sexp_op);
|
||||
void ai_lua(ai_info* aip);
|
||||
void ai_lua_start(ai_goal* aigp, object* objp);
|
||||
bool ai_lua_is_valid_target(int sexp_op, int target_objnum, ship* self);
|
||||
ai_achievability ai_lua_is_achievable(const ai_goal* aigp, int objnum);
|
||||
+15
-13
@@ -434,7 +434,7 @@ int valid_turret_enemy(object *objp, object *turret_parent)
|
||||
}
|
||||
|
||||
// don't shoot at ships without collision check
|
||||
if (!(objp->flags[Object::Object_Flags::Collides])) {
|
||||
if (!(objp->flags[Object::Object_Flags::Collides]) && !(objp->flags[Object::Object_Flags::Attackable_if_no_collide])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1782,6 +1782,8 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
#endif
|
||||
|
||||
if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, wip, -1, turret)) {
|
||||
auto turret_enemy_objp = (turret->turret_enemy_objnum >= 0) ? &Objects[turret->turret_enemy_objnum] : nullptr;
|
||||
|
||||
ship_weapon* swp = &turret->weapons;
|
||||
turret->turret_last_fire_direction = *firing_vec;
|
||||
|
||||
@@ -1818,13 +1820,13 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
memset(&fire_info, 0, sizeof(beam_fire_info));
|
||||
fire_info.accuracy = 1.0f;
|
||||
fire_info.beam_info_index = turret_weapon_class;
|
||||
fire_info.beam_info_override = NULL;
|
||||
fire_info.beam_info_override = nullptr;
|
||||
fire_info.shooter = &Objects[parent_objnum];
|
||||
fire_info.target = &Objects[turret->turret_enemy_objnum];
|
||||
fire_info.target = turret_enemy_objp;
|
||||
if (wip->wi_flags[Weapon::Info_Flags::Antisubsysbeam])
|
||||
fire_info.target_subsys = turret->targeted_subsys;
|
||||
else
|
||||
fire_info.target_subsys = NULL;
|
||||
fire_info.target_subsys = nullptr;
|
||||
fire_info.turret = turret;
|
||||
fire_info.burst_seed = old_burst_seed;
|
||||
fire_info.fire_method = BFM_TURRET_FIRED;
|
||||
@@ -1841,8 +1843,8 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
turret->last_fired_weapon_info_index = turret_weapon_class;
|
||||
|
||||
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.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", nullptr, "Beam", objp, "Target", turret_enemy_objp);
|
||||
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[parent_objnum], objp);
|
||||
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
|
||||
}
|
||||
}
|
||||
@@ -1984,8 +1986,8 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
wp->turret_subsys = turret;
|
||||
|
||||
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.SetHookObjects(4, "Ship", &Objects[parent_objnum], "Weapon", objp, "Beam", nullptr, "Target", turret_enemy_objp);
|
||||
Script_system.RunCondition(CHA_ONTURRETFIRED, &Objects[parent_objnum], objp);
|
||||
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
|
||||
}
|
||||
|
||||
@@ -2015,7 +2017,7 @@ bool turret_fire_weapon(int weapon_num, ship_subsys *turret, int parent_objnum,
|
||||
if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
|
||||
int subsys_index;
|
||||
|
||||
subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
|
||||
subsys_index = ship_get_subsys_index(turret);
|
||||
Assert( subsys_index != -1 );
|
||||
if(wip->wi_flags[Weapon::Info_Flags::Flak]){
|
||||
send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
|
||||
@@ -2103,7 +2105,7 @@ void turret_swarm_fire_from_turret(turret_swarm_info *tsi)
|
||||
|
||||
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.RunCondition(CHA_ONTURRETFIRED, &Objects[tsi->parent_objnum], &Objects[weapon_objnum]);
|
||||
Script_system.RemHookVars({"Ship", "Weapon", "Beam", "Target"});
|
||||
}
|
||||
|
||||
@@ -2124,7 +2126,7 @@ void turret_swarm_fire_from_turret(turret_swarm_info *tsi)
|
||||
if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
|
||||
int subsys_index;
|
||||
|
||||
subsys_index = ship_get_index_from_subsys(tsi->turret, tsi->parent_objnum );
|
||||
subsys_index = ship_get_subsys_index(tsi->turret);
|
||||
Assert( subsys_index != -1 );
|
||||
send_turret_fired_packet( tsi->parent_objnum, subsys_index, weapon_objnum );
|
||||
}
|
||||
@@ -2740,7 +2742,7 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
|
||||
{
|
||||
//something did fire, get the lowest valid timestamp
|
||||
// don't do this if we already set a turret timestamp previously in the function
|
||||
if (timestamp_until(ss->turret_next_fire_stamp) <= 0)
|
||||
if (timestamp_since(ss->turret_next_fire_stamp) > 0)
|
||||
{
|
||||
int minimum_stamp = -1;
|
||||
|
||||
@@ -2749,7 +2751,7 @@ void ai_turret_execute_behavior(ship *shipp, ship_subsys *ss)
|
||||
int stamp = (i < MAX_SHIP_PRIMARY_BANKS) ? swp->next_primary_fire_stamp[i] : swp->next_secondary_fire_stamp[i - MAX_SHIP_PRIMARY_BANKS];
|
||||
|
||||
// valid timestamps start at 2; stamp must be in the future
|
||||
if (stamp < 2 || timestamp_until(stamp) <= 0)
|
||||
if (stamp < 2 || timestamp_since(stamp) > 0)
|
||||
continue;
|
||||
|
||||
// find minimum
|
||||
|
||||
+12
-16
@@ -32,7 +32,7 @@
|
||||
#include "object/objcollide.h"
|
||||
#include "object/object.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "scripting/global_hooks.h"
|
||||
#include "particle/particle.h"
|
||||
#include "render/3d.h"
|
||||
#include "ship/ship.h"
|
||||
@@ -1092,8 +1092,8 @@ int asteroid_check_collision(object *pasteroid, object *other_obj, vec3d *hitpos
|
||||
pmi->submodel[submodel].collision_checked = true;
|
||||
}
|
||||
|
||||
// reset flags to check MC_CHECK_MODEL | MC_CHECK_SPHERELINE and maybe MC_CHECK_INVISIBLE_FACES and MC_SUBMODEL_INSTANCE
|
||||
mc.flags = copy_flags | MC_SUBMODEL_INSTANCE;
|
||||
// Only check single submodel now, since children of moving submodels are handled as moving as well
|
||||
mc.flags = copy_flags | MC_SUBMODEL;
|
||||
|
||||
// check each submodel in turn
|
||||
for (auto submodel: submodel_vector) {
|
||||
@@ -1102,15 +1102,9 @@ int asteroid_check_collision(object *pasteroid, object *other_obj, vec3d *hitpos
|
||||
// turn on just one submodel for collision test
|
||||
smi->collision_checked = false;
|
||||
|
||||
// set angles for last frame (need to set to prev to get p0)
|
||||
matrix copy_matrix = smi->canonical_orient;
|
||||
|
||||
// find the start and end positions of the sphere in submodel RF
|
||||
smi->canonical_orient = smi->canonical_prev_orient;
|
||||
model_instance_world_to_local_point(&p0, &light_obj->last_pos, pm, pmi, submodel, &heavy_obj->last_orient, &heavy_obj->last_pos);
|
||||
|
||||
smi->canonical_orient = copy_matrix;
|
||||
model_instance_world_to_local_point(&p1, &light_obj->pos, pm, pmi, submodel, &heavy_obj->orient, &heavy_obj->pos);
|
||||
model_instance_global_to_local_point(&p0, &light_obj->last_pos, pm, pmi, submodel, &heavy_obj->last_orient, &heavy_obj->last_pos, true);
|
||||
model_instance_global_to_local_point(&p1, &light_obj->pos, pm, pmi, submodel, &heavy_obj->orient, &heavy_obj->pos);
|
||||
|
||||
mc.p0 = &p0;
|
||||
mc.p1 = &p1;
|
||||
@@ -1517,11 +1511,12 @@ 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);
|
||||
bool hooked = scripting::hooks::OnDeath->isActive();
|
||||
|
||||
if (hooked) {
|
||||
Script_system.SetHookObject("Self", pasteroid_obj);
|
||||
skip = Script_system.IsConditionOverride(CHA_DEATH, pasteroid_obj);
|
||||
skip = scripting::hooks::OnDeath->isOverride(
|
||||
scripting::hook_param_list(scripting::hook_param("Self", 'o', pasteroid_obj)),
|
||||
pasteroid_obj);
|
||||
}
|
||||
if (!skip)
|
||||
{
|
||||
@@ -1629,8 +1624,9 @@ static void asteroid_maybe_break_up(object *pasteroid_obj)
|
||||
asp->final_death_time = timestamp(-1);
|
||||
}
|
||||
if (hooked) {
|
||||
Script_system.RunCondition(CHA_DEATH, pasteroid_obj);
|
||||
Script_system.RemHookVar("Self");
|
||||
scripting::hooks::OnDeath->run(
|
||||
scripting::hook_param_list(scripting::hook_param("Self", 'o', pasteroid_obj)),
|
||||
pasteroid_obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1581,3 +1581,18 @@ bool IsVisited(int nav)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
void Nav_SetColor(const char *nav, bool visited, ubyte r, ubyte g, ubyte b)
|
||||
{
|
||||
int n = FindNav(nav);
|
||||
|
||||
if (n >= 0 && n < MAX_NAVPOINTS)
|
||||
{
|
||||
auto rgb = visited ? Navs[n].visited_color : Navs[n].normal_color;
|
||||
rgb[0] = r;
|
||||
rgb[1] = g;
|
||||
rgb[2] = b;
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ class NavPoint
|
||||
public:
|
||||
char m_NavName[32];
|
||||
int flags;
|
||||
ubyte normal_color[3];
|
||||
ubyte visited_color[3];
|
||||
|
||||
const void *target_obj;
|
||||
int waypoint_num; //only used when flags & NP_WAYPOINT
|
||||
@@ -42,6 +44,14 @@ public:
|
||||
m_NavName[0] = 0;
|
||||
flags = 0;
|
||||
|
||||
normal_color[0] = 0x80;
|
||||
normal_color[1] = 0x80;
|
||||
normal_color[2] = 0xFF;
|
||||
|
||||
visited_color[0] = 0xFF;
|
||||
visited_color[1] = 0xFF;
|
||||
visited_color[2] = 0x00;
|
||||
|
||||
target_obj = nullptr;
|
||||
waypoint_num = -1;
|
||||
}
|
||||
@@ -155,6 +165,8 @@ unsigned int DistanceTo(int nav);
|
||||
bool IsVisited(const char *nav);
|
||||
bool IsVisited(int nav);
|
||||
|
||||
void Nav_SetColor(const char *nav, bool visited, ubyte r, ubyte g, ubyte b);
|
||||
|
||||
void send_autopilot_msg(const char *msg, const char *snd=nullptr);
|
||||
void send_autopilot_msgID(int msgid);
|
||||
#endif
|
||||
|
||||
@@ -592,12 +592,9 @@ void bm_convert_format(bitmap *bmp, ushort flags) {
|
||||
return;
|
||||
|
||||
if (Is_standalone) {
|
||||
Assert(bmp->bpp == 8);
|
||||
return;
|
||||
} else {
|
||||
if (flags & BMP_AABITMAP)
|
||||
Assert(bmp->bpp == 8);
|
||||
else
|
||||
if(!(flags & BMP_AABITMAP))
|
||||
Assert((bmp->bpp == 16) || (bmp->bpp == 32));
|
||||
}
|
||||
|
||||
@@ -1875,9 +1872,7 @@ bitmap * bm_lock(int handle, int bpp, ushort flags, bool nodebug) {
|
||||
}
|
||||
// otherwise do it as normal
|
||||
else {
|
||||
if (flags & BMP_AABITMAP) {
|
||||
Assert(bpp == 8);
|
||||
} else if ((flags & BMP_TEX_NONCOMP) && (!(flags & BMP_TEX_COMP))) {
|
||||
if ((flags & BMP_TEX_NONCOMP) && (!(flags & BMP_TEX_COMP))) {
|
||||
Assert(bpp >= 16); // cheating but bpp passed isn't what we normally end up with
|
||||
} else if ((flags & BMP_TEX_DXT1) || (flags & BMP_TEX_DXT3) || (flags & BMP_TEX_DXT5) || (flags & BMP_TEX_BC7)) {
|
||||
Assert(bpp >= 16); // cheating but bpp passed isn't what we normally end up with
|
||||
@@ -1887,8 +1882,6 @@ bitmap * bm_lock(int handle, int bpp, ushort flags, bool nodebug) {
|
||||
(be->type == BM_TYPE_CUBEMAP_DXT3) ||
|
||||
(be->type == BM_TYPE_CUBEMAP_DXT5));
|
||||
Assert(bpp >= 16);
|
||||
} else {
|
||||
Assert(0); //?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+97
-27
@@ -9,11 +9,13 @@
|
||||
#include "model/model.h" //polymodel, model_get
|
||||
#include "options/Option.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "parse/sexp_container.h"
|
||||
#include "playerman/player.h" //player_get_padlock_orient
|
||||
#include "ship/ship.h" //compute_slew_matrix
|
||||
|
||||
//*************************IMPORTANT GLOBALS*************************
|
||||
float VIEWER_ZOOM_DEFAULT = 0.75f; // Default viewer zoom, 0.625 as per multi-lateral agreement on 3/24/97
|
||||
float COCKPIT_ZOOM_DEFAULT = 0.75f;
|
||||
float Sexp_fov = 0.0f;
|
||||
warp_camera Warp_camera;
|
||||
|
||||
@@ -241,7 +243,16 @@ void camera::set_rotation_facing(vec3d *in_target, float in_rotation_time, float
|
||||
{
|
||||
vec3d position;
|
||||
matrix orient_buf;
|
||||
auto orient = Use_host_orientation_for_set_camera_facing ? &orient_buf : nullptr;
|
||||
matrix *orient = nullptr;
|
||||
|
||||
if (Use_host_orientation_for_set_camera_facing)
|
||||
{
|
||||
// we want to retrieve the camera's orientation, so provide a buffer for it
|
||||
orient = &orient_buf;
|
||||
|
||||
// reset the camera's orientation state because if we have no host object, that is what the camera will default to
|
||||
c_ori = vmd_identity_matrix;
|
||||
}
|
||||
|
||||
this->get_info(&position, orient, false);
|
||||
|
||||
@@ -597,11 +608,46 @@ void warp_camera::get_info(vec3d *position, matrix *orientation)
|
||||
|
||||
#define MAX_SUBTITLE_LINES 64
|
||||
subtitle::subtitle(int in_x_pos, int in_y_pos, const char* in_text, const char* in_imageanim, float in_display_time,
|
||||
float in_fade_time, const color *in_text_color, int in_text_fontnum, bool center_x, bool center_y, int in_width,
|
||||
int in_height, bool in_post_shaded, float in_line_height_factor)
|
||||
: display_time(-1.0f), fade_time(-1.0f), text_fontnum(-1), line_height_factor(1.0f),
|
||||
image_id(-1), time_displayed(-1.0f), time_displayed_end(-1.0f), post_shaded(false)
|
||||
float in_fade_time, const color* in_text_color, int in_text_fontnum, bool center_x, bool center_y, int in_width,
|
||||
int in_height, bool in_post_shaded, int in_line_height_modifier, bool in_adjust_wh)
|
||||
: display_time(-1.0f), fade_time(-1.0f), text_fontnum(-1), line_height_modifier(0),
|
||||
image_id(-1), time_displayed(-1.0f), time_displayed_end(-1.0f), post_shaded(false), do_screen_scaling(false)
|
||||
{
|
||||
if ((in_text == nullptr || *in_text == '\0') && (in_imageanim == nullptr || *in_imageanim == '\0'))
|
||||
return;
|
||||
|
||||
auto screen_w = gr_screen.center_w;
|
||||
auto screen_h = gr_screen.center_h;
|
||||
|
||||
// we might be doing screen scaling
|
||||
if (Show_subtitle_screen_base_res[0] > 0 && Show_subtitle_screen_base_res[1] > 0)
|
||||
{
|
||||
// The way this works is we specify our subtitle coordinates relative to a base resolution, e.g. 1024x768.
|
||||
// The base resolution is adjusted behind the scenes to match the aspect ratio of the current resolution
|
||||
// (and the subtitle coordinates are adjusted as well). Then the adjusted subtitles are resized by the
|
||||
// graphics code when they are drawn to the screen.
|
||||
do_screen_scaling = true;
|
||||
|
||||
// we might need to adjust the coordinates for aspect ratio
|
||||
if (in_adjust_wh)
|
||||
{
|
||||
if (Show_subtitle_screen_base_res[0] != Show_subtitle_screen_adjusted_res[0])
|
||||
{
|
||||
in_x_pos = in_x_pos * Show_subtitle_screen_adjusted_res[0] / Show_subtitle_screen_base_res[0];
|
||||
in_width = in_width * Show_subtitle_screen_adjusted_res[0] / Show_subtitle_screen_base_res[0];
|
||||
}
|
||||
if (Show_subtitle_screen_base_res[1] != Show_subtitle_screen_adjusted_res[1])
|
||||
{
|
||||
in_y_pos = in_y_pos * Show_subtitle_screen_adjusted_res[1] / Show_subtitle_screen_base_res[1];
|
||||
in_height = in_height * Show_subtitle_screen_adjusted_res[1] / Show_subtitle_screen_base_res[1];
|
||||
}
|
||||
}
|
||||
|
||||
// use these because we will be scaling the subtitle when it is displayed
|
||||
screen_w = Show_subtitle_screen_adjusted_res[0];
|
||||
screen_h = Show_subtitle_screen_adjusted_res[1];
|
||||
}
|
||||
|
||||
// Initialize color
|
||||
gr_init_color(&text_color, 0, 0, 0);
|
||||
|
||||
@@ -612,13 +658,11 @@ subtitle::subtitle(int in_x_pos, int in_y_pos, const char* in_text, const char*
|
||||
memset( &text_pos, 0, 2*sizeof(int) );
|
||||
memset( &image_pos, 0, 4*sizeof(int) );
|
||||
|
||||
if ( ((in_text != NULL) && (strlen(in_text) <= 0)) && ((in_imageanim != NULL) && (strlen(in_imageanim) <= 0)) )
|
||||
return;
|
||||
|
||||
if (in_text != NULL && in_text[0] != '\0')
|
||||
{
|
||||
text_buf = in_text;
|
||||
sexp_replace_variable_names_with_values(text_buf);
|
||||
sexp_container_replace_refs_with_values(text_buf);
|
||||
in_text = text_buf.c_str();
|
||||
}
|
||||
|
||||
@@ -644,7 +688,7 @@ subtitle::subtitle(int in_x_pos, int in_y_pos, const char* in_text, const char*
|
||||
else
|
||||
gr_init_alphacolor(&text_color, 255, 255, 255, 255);
|
||||
text_fontnum = in_text_fontnum;
|
||||
line_height_factor = in_line_height_factor;
|
||||
line_height_modifier = in_line_height_modifier;
|
||||
|
||||
//Setup display and fade time
|
||||
display_time = fl_abs(in_display_time);
|
||||
@@ -683,7 +727,15 @@ subtitle::subtitle(int in_x_pos, int in_y_pos, const char* in_text, const char*
|
||||
if(w > tw)
|
||||
tw = w;
|
||||
|
||||
th += fl2i(line_height_factor * h);
|
||||
if (line_height_modifier != 0.0f)
|
||||
{
|
||||
if (Show_subtitle_uses_pixels)
|
||||
h = line_height_modifier;
|
||||
else
|
||||
h = fl2i(h * line_height_modifier / 100.0f);
|
||||
}
|
||||
|
||||
th += h;
|
||||
}
|
||||
|
||||
// restore old font
|
||||
@@ -708,20 +760,20 @@ subtitle::subtitle(int in_x_pos, int in_y_pos, const char* in_text, const char*
|
||||
|
||||
//Center it?
|
||||
if(center_x)
|
||||
image_pos.x = (gr_screen.center_w - tw)/2 + gr_screen.center_offset_x;
|
||||
image_pos.x = (screen_w - tw)/2;
|
||||
if(center_y)
|
||||
image_pos.y = (gr_screen.center_h - th)/2 + gr_screen.center_offset_y;
|
||||
image_pos.y = (screen_h - th)/2;
|
||||
}
|
||||
|
||||
if(in_x_pos < 0 && !center_x)
|
||||
image_pos.x += gr_screen.center_offset_x + gr_screen.center_w + in_x_pos;
|
||||
image_pos.x += screen_w + in_x_pos;
|
||||
else if(!center_x)
|
||||
image_pos.x += gr_screen.center_offset_x + in_x_pos;
|
||||
image_pos.x += in_x_pos;
|
||||
|
||||
if(in_y_pos < 0 && !center_y)
|
||||
image_pos.y += gr_screen.center_offset_y + gr_screen.center_h + in_y_pos;
|
||||
image_pos.y += screen_h + in_y_pos;
|
||||
else if(!center_y)
|
||||
image_pos.y += gr_screen.center_offset_y + in_y_pos;
|
||||
image_pos.y += in_y_pos;
|
||||
|
||||
image_pos.w = in_width;
|
||||
image_pos.h = in_height;
|
||||
@@ -777,24 +829,32 @@ void subtitle::do_frame(float frametime)
|
||||
int x = text_pos.x;
|
||||
int y = text_pos.y;
|
||||
|
||||
auto sizing_mode = do_screen_scaling ? GR_RESIZE_FULL : GR_RESIZE_NONE;
|
||||
if (do_screen_scaling)
|
||||
gr_set_screen_scale(Show_subtitle_screen_adjusted_res[0], Show_subtitle_screen_adjusted_res[1]);
|
||||
|
||||
// do the actual drawing ---------------------
|
||||
|
||||
for(SCP_vector<SCP_string>::iterator line = text_lines.begin(); line != text_lines.end(); ++line)
|
||||
{
|
||||
gr_string(x, y, (char*)line->c_str(), GR_RESIZE_NONE);
|
||||
y += fl2i(line_height_factor * font_height);
|
||||
}
|
||||
gr_string(x, y, (char*)line->c_str(), sizing_mode);
|
||||
|
||||
// restore old font
|
||||
if (old_fontnum >= 0)
|
||||
{
|
||||
font::set_font(old_fontnum);
|
||||
if (line_height_modifier != 0.0f)
|
||||
{
|
||||
if (Show_subtitle_uses_pixels)
|
||||
font_height = line_height_modifier;
|
||||
else
|
||||
font_height = fl2i(font_height * line_height_modifier / 100.0f);
|
||||
}
|
||||
|
||||
y += font_height;
|
||||
}
|
||||
|
||||
if(image_id >= 0)
|
||||
{
|
||||
gr_set_bitmap(image_id, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, text_color.alpha/255.0f);
|
||||
|
||||
// scaling?
|
||||
// image scaling?
|
||||
if (image_pos.w > 0 || image_pos.h > 0)
|
||||
{
|
||||
int orig_w, orig_h;
|
||||
@@ -806,16 +866,25 @@ void subtitle::do_frame(float frametime)
|
||||
scale.xyz.z = 1.0f;
|
||||
|
||||
gr_push_scale_matrix(&scale);
|
||||
gr_bitmap(fl2i(image_pos.x / scale.xyz.x), fl2i(image_pos.y / scale.xyz.y), GR_RESIZE_NONE);
|
||||
gr_bitmap(fl2i(image_pos.x / scale.xyz.x), fl2i(image_pos.y / scale.xyz.y), sizing_mode);
|
||||
gr_pop_scale_matrix();
|
||||
}
|
||||
// no scaling
|
||||
else
|
||||
{
|
||||
gr_bitmap(image_pos.x, image_pos.y, GR_RESIZE_NONE);
|
||||
gr_bitmap(image_pos.x, image_pos.y, sizing_mode);
|
||||
}
|
||||
}
|
||||
|
||||
// finished the actual drawing ---------------
|
||||
|
||||
if (do_screen_scaling)
|
||||
gr_reset_screen_scale();
|
||||
|
||||
// restore old font
|
||||
if (old_fontnum >= 0)
|
||||
font::set_font(old_fontnum);
|
||||
|
||||
time_displayed += frametime;
|
||||
}
|
||||
|
||||
@@ -832,7 +901,7 @@ void subtitle::clone(const subtitle &sub)
|
||||
{
|
||||
text_lines = sub.text_lines;
|
||||
text_fontnum = sub.text_fontnum;
|
||||
line_height_factor = sub.line_height_factor;
|
||||
line_height_modifier = sub.line_height_modifier;
|
||||
|
||||
// copy the structs
|
||||
text_pos = sub.text_pos;
|
||||
@@ -855,6 +924,7 @@ void subtitle::clone(const subtitle &sub)
|
||||
time_displayed_end = sub.time_displayed_end;
|
||||
|
||||
post_shaded = sub.post_shaded;
|
||||
do_screen_scaling = sub.do_screen_scaling;
|
||||
}
|
||||
|
||||
subtitle& subtitle::operator=(const subtitle &sub)
|
||||
|
||||
@@ -123,7 +123,7 @@ private:
|
||||
float fade_time;
|
||||
color text_color;
|
||||
int text_fontnum;
|
||||
float line_height_factor;
|
||||
int line_height_modifier;
|
||||
|
||||
//Done with set
|
||||
char imageanim[MAX_FILENAME_LEN];
|
||||
@@ -137,11 +137,13 @@ private:
|
||||
float time_displayed_end;
|
||||
|
||||
bool post_shaded;
|
||||
bool do_screen_scaling;
|
||||
|
||||
public:
|
||||
subtitle(int in_x_pos, int in_y_pos, const char* in_text = NULL, const char* in_imageanim = NULL,
|
||||
float in_display_time = 0, float in_fade_time = 0.0f, const color *in_text_color = NULL, int in_text_fontnum = -1,
|
||||
bool center_x = false, bool center_y = false, int in_width = 0, int in_height = 0, bool post_shaded = false,
|
||||
float in_line_height_factor = 1.0f);
|
||||
int in_line_height_modifier = 0, bool in_adjust_wh = true);
|
||||
~subtitle();
|
||||
|
||||
subtitle(const subtitle &sub) { clone(sub); }
|
||||
@@ -154,6 +156,7 @@ public:
|
||||
//Some global stuff
|
||||
extern SCP_vector<subtitle> Subtitles;
|
||||
extern float VIEWER_ZOOM_DEFAULT;
|
||||
extern float COCKPIT_ZOOM_DEFAULT;
|
||||
extern float Sexp_fov;
|
||||
|
||||
//Helpful functions
|
||||
|
||||
@@ -74,7 +74,7 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
|
||||
{ CF_TYPE_FONT, "data" DIR_SEPARATOR_STR "fonts", ".vf .ttf .otf", CF_TYPE_DATA },
|
||||
{ CF_TYPE_EFFECTS, "data" DIR_SEPARATOR_STR "effects", ".ani .eff .pcx .neb .tga .jpg .png .dds .sdr", CF_TYPE_DATA },
|
||||
{ CF_TYPE_HUD, "data" DIR_SEPARATOR_STR "hud", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
|
||||
{ CF_TYPE_PLAYERS, "data" DIR_SEPARATOR_STR "players", ".hcf", CF_TYPE_DATA },
|
||||
{ CF_TYPE_PLAYERS, "data" DIR_SEPARATOR_STR "players", ".hcf", /* DON'T add pilot files here!! */ CF_TYPE_DATA },
|
||||
{ CF_TYPE_PLAYER_IMAGES, "data" DIR_SEPARATOR_STR "players" DIR_SEPARATOR_STR "images", ".pcx .png .dds", CF_TYPE_PLAYERS },
|
||||
{ CF_TYPE_SQUAD_IMAGES, "data" DIR_SEPARATOR_STR "players" DIR_SEPARATOR_STR "squads", ".pcx .png .dds", CF_TYPE_PLAYERS },
|
||||
{ CF_TYPE_SINGLE_PLAYERS, "data" DIR_SEPARATOR_STR "players" DIR_SEPARATOR_STR "single", ".pl2 .cs2 .plr .csg .css .json", CF_TYPE_PLAYERS },
|
||||
@@ -86,7 +86,7 @@ cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
|
||||
{ CF_TYPE_DEMOS, "data" DIR_SEPARATOR_STR "demos", ".fsd", CF_TYPE_DATA },
|
||||
{ CF_TYPE_CBANIMS, "data" DIR_SEPARATOR_STR "cbanims", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
|
||||
{ CF_TYPE_INTEL_ANIMS, "data" DIR_SEPARATOR_STR "intelanims", ".pcx .ani .eff .tga .jpg .png .dds", CF_TYPE_DATA },
|
||||
{ CF_TYPE_SCRIPTS, "data" DIR_SEPARATOR_STR "scripts", ".lua .lc", CF_TYPE_DATA },
|
||||
{ CF_TYPE_SCRIPTS, "data" DIR_SEPARATOR_STR "scripts", ".lua .lc .fnl", CF_TYPE_DATA },
|
||||
{ CF_TYPE_FICTION, "data" DIR_SEPARATOR_STR "fiction", ".txt", CF_TYPE_DATA },
|
||||
{ CF_TYPE_FREDDOCS, "data" DIR_SEPARATOR_STR "freddocs", ".html", CF_TYPE_DATA },
|
||||
{ CF_TYPE_INTERFACE_MARKUP, "data" DIR_SEPARATOR_STR "interface" DIR_SEPARATOR_STR "markup", ".rml", CF_TYPE_INTERFACE },
|
||||
@@ -635,7 +635,7 @@ void cf_create_directory(int dir_type, uint32_t location_flags)
|
||||
//
|
||||
// parameters: *filepath ==> name of file to open (may be path+name)
|
||||
// *mode ==> specifies how file should be opened (eg "rb" for read binary)
|
||||
// passing NULL to mode deletes the file if it exists and returns NULL
|
||||
// passing NULL to mode triggers an assert and returns NULL
|
||||
// type ==> one of: CFILE_NORMAL
|
||||
// CFILE_MEMORY_MAPPED
|
||||
// dir_type => override extension check, value is one of CF_TYPE* #defines
|
||||
@@ -782,7 +782,7 @@ CFILE* _cfopen(const char* source, int line, const char* file_path, const char*
|
||||
//
|
||||
// parameters: *filepath ==> name of file to open (may be path+name)
|
||||
// *mode ==> specifies how file should be opened (eg "rb" for read binary)
|
||||
// passing NULL to mode deletes the file if it exists and returns NULL
|
||||
// passing NULL to mode triggers an assert and returns NULL
|
||||
|
||||
// dir_type => override extension check, value is one of CF_TYPE* #defines
|
||||
//
|
||||
|
||||
@@ -1551,7 +1551,7 @@ int cf_get_file_list(SCP_vector<SCP_string>& list, int pathtype, const char* fil
|
||||
}
|
||||
|
||||
bool skip_packfiles = false;
|
||||
if ( (pathtype == CF_TYPE_PLAYERS) || (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS) ) {
|
||||
if ( (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS) ) {
|
||||
skip_packfiles = true;
|
||||
} else if (Get_file_list_child != NULL) {
|
||||
skip_packfiles = true;
|
||||
@@ -1708,7 +1708,7 @@ int cf_get_file_list(int max, char** list, int pathtype, const char* filter, int
|
||||
}
|
||||
|
||||
bool skip_packfiles = false;
|
||||
if ((pathtype == CF_TYPE_PLAYERS) || (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS)) {
|
||||
if ((pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS)) {
|
||||
skip_packfiles = true;
|
||||
}
|
||||
else if (Get_file_list_child != NULL) {
|
||||
@@ -1887,7 +1887,7 @@ int cf_get_file_list_preallocated(int max, char arr[][MAX_FILENAME_LEN], char**
|
||||
}
|
||||
|
||||
bool skip_packfiles = false;
|
||||
if ((pathtype == CF_TYPE_PLAYERS) || (pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS)) {
|
||||
if ((pathtype == CF_TYPE_SINGLE_PLAYERS) || (pathtype == CF_TYPE_MULTI_PLAYERS)) {
|
||||
skip_packfiles = true;
|
||||
}
|
||||
else if (Get_file_list_child != NULL) {
|
||||
@@ -1981,8 +1981,10 @@ int cf_get_file_list_preallocated(int max, char arr[][MAX_FILENAME_LEN], char**
|
||||
// Output: path - Fully qualified pathname.
|
||||
//Returns 0 if the result would be too long (invalid result)
|
||||
int cf_create_default_path_string(char* path, uint path_max, int pathtype, const char* filename, bool localize,
|
||||
uint32_t location_flags)
|
||||
uint32_t _location_flags)
|
||||
{
|
||||
uint32_t location_flags = _location_flags;
|
||||
|
||||
if ( filename && strchr(filename, DIR_SEPARATOR_CHAR) ) {
|
||||
// Already has full path
|
||||
strncpy( path, filename, path_max );
|
||||
@@ -1990,6 +1992,16 @@ int cf_create_default_path_string(char* path, uint path_max, int pathtype, const
|
||||
} else {
|
||||
cf_root* root = nullptr;
|
||||
|
||||
// override location flags for path types which should always come from the root (NOT mod directories)
|
||||
// NOTE: cache directories should NOT be added here to avoid possible mod breakage
|
||||
switch(pathtype) {
|
||||
case CF_TYPE_PLAYERS:
|
||||
case CF_TYPE_MULTI_PLAYERS:
|
||||
case CF_TYPE_SINGLE_PLAYERS:
|
||||
location_flags = CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT;
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < Num_roots; ++i) {
|
||||
auto current_root = cf_get_root(i);
|
||||
|
||||
|
||||
+66
-116
@@ -19,6 +19,7 @@
|
||||
#include "hud/hudconfig.h"
|
||||
#include "io/joy.h"
|
||||
#include "network/multi.h"
|
||||
#include "network/multi_log.h"
|
||||
#include "options/OptionsManager.h"
|
||||
#include "osapi/osapi.h"
|
||||
#include "parse/sexp.h"
|
||||
@@ -42,6 +43,7 @@
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
@@ -195,6 +197,7 @@ Flag exe_params[] =
|
||||
{ "-no_enhanced_sound", "Disable enhanced sound", false, 0, EASY_DEFAULT, "Audio", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-no_enhanced_sound", },
|
||||
|
||||
{ "-portable_mode", "Store config in portable location", false, 0, EASY_DEFAULT, "Launcher", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-portable_mode", },
|
||||
{ "-joy_info", "Outputs SDL joystick info", true, 0, EASY_DEFAULT, "Launcher", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-joy_info",},
|
||||
|
||||
{ "-standalone", "Run as standalone server", false, 0, EASY_DEFAULT, "Multiplayer", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-standalone", },
|
||||
{ "-startgame", "Skip mainhall and start hosting", false, 0, EASY_DEFAULT, "Multiplayer", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-startgame", },
|
||||
@@ -222,6 +225,7 @@ Flag exe_params[] =
|
||||
{ "-noshadercache", "Disables the shader cache", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-noshadercache", },
|
||||
{ "-prefer_ipv4", "Prefer IPv4 DNS lookups", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-prefer_ipv4", },
|
||||
{ "-prefer_ipv6", "Prefer IPv6 DNS lookups", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-prefer_ipv6", },
|
||||
{ "-log_multi_packet", "Log multi packet types ", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-log_multi_packet",},
|
||||
#ifdef WIN32
|
||||
{ "-fix_registry", "Use a different registry path", true, 0, EASY_DEFAULT, "Troubleshoot", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-fix_registry", },
|
||||
#endif
|
||||
@@ -242,6 +246,7 @@ Flag exe_params[] =
|
||||
{ "-output_sexps", "Output SEXPs to sexps.html", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_sexps", },
|
||||
{ "-output_scripting", "Output scripting to scripting.html", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_scripting", },
|
||||
{ "-output_script_json", "Output scripting doc to scripting.json", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_script_json", },
|
||||
{ "-controlconfig_tbl", "Save control presets to table", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-controlconfig_tbl", },
|
||||
{ "-save_render_target", "Save render targets to file", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-save_render_target", },
|
||||
{ "-verify_vps", "Spew VP CRCs to vp_crcs.txt", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-verify_vps", },
|
||||
{ "-reparse_mainhall", "Reparse mainhall.tbl when loading halls", false, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-reparse_mainhall", },
|
||||
@@ -310,6 +315,7 @@ int Cmdline_use_last_pilot = 0;
|
||||
|
||||
// Graphics related
|
||||
cmdline_parm fov_arg("-fov", "Vertical field-of-view factor", AT_FLOAT); // Cmdline_fov -- comand line FOV -Bobboau
|
||||
cmdline_parm fov_cockpit_arg("-fov_cockpit", "Vertical field-of-view factor for Cockpits", AT_FLOAT);
|
||||
cmdline_parm clip_dist_arg("-clipdist", "Changes the distance from the viewpoint for the near-clipping plane", AT_FLOAT); // Cmdline_clip_dist
|
||||
cmdline_parm spec_static_arg("-spec_static", "Adjusts suns contribution to specular highlights", AT_FLOAT);
|
||||
cmdline_parm spec_point_arg("-spec_point", "Adjusts laser weapons contribution to specular highlights", AT_FLOAT);
|
||||
@@ -423,6 +429,7 @@ char *Cmdline_gateway_ip = nullptr;
|
||||
|
||||
// Launcher related options
|
||||
cmdline_parm portable_mode("-portable_mode", NULL, AT_NONE);
|
||||
cmdline_parm joy_info("-joy_info", "Outputs SDL joystick info", AT_NONE);
|
||||
|
||||
bool Cmdline_portable_mode = false;
|
||||
|
||||
@@ -444,6 +451,7 @@ cmdline_parm nograb_arg("-nograb", NULL, AT_NONE);
|
||||
cmdline_parm noshadercache_arg("-noshadercache", NULL, AT_NONE);
|
||||
cmdline_parm prefer_ipv4_arg("-prefer_ipv4", nullptr, AT_NONE);
|
||||
cmdline_parm prefer_ipv6_arg("-prefer_ipv6", nullptr, AT_NONE);
|
||||
cmdline_parm log_multi_packet_arg("-log_multi_packet", nullptr, AT_NONE);
|
||||
#ifdef WIN32
|
||||
cmdline_parm fix_registry("-fix_registry", NULL, AT_NONE);
|
||||
#endif
|
||||
@@ -464,6 +472,7 @@ bool Cmdline_nograb = false;
|
||||
bool Cmdline_noshadercache = false;
|
||||
bool Cmdline_prefer_ipv4 = false;
|
||||
bool Cmdline_prefer_ipv6 = false;
|
||||
bool Cmdline_dump_packet_type = false;
|
||||
#ifdef WIN32
|
||||
bool Cmdline_alternate_registry_path = false;
|
||||
#endif
|
||||
@@ -537,6 +546,7 @@ cmdline_parm get_flags_arg(GET_FLAGS_STRING, "Output the launcher flags file", A
|
||||
cmdline_parm output_sexp_arg("-output_sexps", NULL, AT_NONE); //WMC - outputs all SEXPs to sexps.html
|
||||
cmdline_parm output_scripting_arg("-output_scripting", NULL, AT_NONE); //WMC
|
||||
cmdline_parm output_script_json_arg("-output_script_json", nullptr, AT_NONE); // m!m
|
||||
cmdline_parm generate_controlconfig_arg("-controlconfig_tbl", nullptr, AT_NONE);
|
||||
|
||||
// Deprecated flags - CommanderDJ
|
||||
cmdline_parm deprecated_spec_arg("-spec", "Deprecated", AT_NONE);
|
||||
@@ -553,18 +563,6 @@ cmdline_parm deprecated_cache_bitmaps_arg("-cache_bitmaps", "Deprecated", AT_NON
|
||||
cmdline_parm deprecated_no_emissive_arg("-no_emissive_light", "Deprecated", AT_NONE);
|
||||
cmdline_parm deprecated_postprocess_arg("-post_process", "Deprecated", AT_NONE);
|
||||
|
||||
int Cmdline_deprecated_spec = 0;
|
||||
int Cmdline_deprecated_glow = 0;
|
||||
int Cmdline_deprecated_normal = 0;
|
||||
int Cmdline_deprecated_env = 0;
|
||||
int Cmdline_deprecated_tbp = 0;
|
||||
int Cmdline_deprecated_jpgtga = 0;
|
||||
int Cmdline_deprecated_nohtl = 0;
|
||||
bool Cmdline_deprecated_brief_lighting = 0;
|
||||
bool Cmdline_deprecated_missile_lighting = false;
|
||||
bool Cmdline_deprecated_cache_bitmaps = false;
|
||||
bool Cmdline_deprecated_postprocess = false;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// NOTE: this assumes that os_init() has already been called but isn't a fatal error if it hasn't
|
||||
void cmdline_debug_print_cmdline()
|
||||
@@ -590,61 +588,41 @@ void cmdline_debug_print_cmdline()
|
||||
mprintf(("\n"));
|
||||
|
||||
//Print log messages about any deprecated flags we found - CommanderDJ
|
||||
if(Cmdline_deprecated_spec == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-spec' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_glow == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-glow' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_normal == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-normal' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_env == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-env' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_tbp == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-tbp' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_jpgtga == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-jpgtga' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_nohtl == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-nohtl' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if(Cmdline_deprecated_brief_lighting == 1)
|
||||
{
|
||||
mprintf(("Deprecated flag '-brief_lighting' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if (Cmdline_deprecated_missile_lighting)
|
||||
{
|
||||
mprintf(("Deprecated flag '-missile_lighting' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if (Cmdline_deprecated_cache_bitmaps) {
|
||||
mprintf(("Deprecated flag '-cache_bitmaps' found. Please remove from your cmdline.\n"));
|
||||
}
|
||||
|
||||
if (Cmdline_deprecated_postprocess) {
|
||||
mprintf(("Deprecated flag '-post_process' found. Please remove from your cmdline.\n"));
|
||||
//Do it programmatically, rather than enumerating them by hand - MageKing17
|
||||
for (parmp = GET_FIRST(&Parm_list); parmp != END_OF_LIST(&Parm_list); parmp = GET_NEXT(parmp)) {
|
||||
if (parmp->name_found && !stricmp("deprecated", parmp->help)) {
|
||||
mprintf(("Deprecated flag '%s' found. Please remove from your cmdline.\n", parmp->name));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// prints simple cmdline to multi.log
|
||||
void cmdline_print_cmdline_multi()
|
||||
{
|
||||
cmdline_parm *parmp;
|
||||
int found = 0;
|
||||
std::ostringstream cmdline;
|
||||
|
||||
for (parmp = GET_FIRST(&Parm_list); parmp !=END_OF_LIST(&Parm_list); parmp = GET_NEXT(parmp) ) {
|
||||
if ( parmp->name_found ) {
|
||||
cmdline << " " << parmp->name;
|
||||
|
||||
if (parmp->args) {
|
||||
cmdline << " " << parmp->args;
|
||||
}
|
||||
|
||||
found++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !found ) {
|
||||
cmdline << " <none>";
|
||||
}
|
||||
|
||||
ml_printf("Command line:%s", cmdline.str().c_str());
|
||||
}
|
||||
|
||||
// Return true if this character is an extra char (white space and quotes)
|
||||
int is_extra_space(char ch)
|
||||
{
|
||||
@@ -1526,6 +1504,11 @@ bool SetCmdlineParams()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (joy_info.found()) {
|
||||
io::joystick::printJoyJSON();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (no_fpscap.found())
|
||||
{
|
||||
Cmdline_NoFPSCap = 1;
|
||||
@@ -1557,6 +1540,9 @@ bool SetCmdlineParams()
|
||||
// is this a standalone server??
|
||||
if (standalone_arg.found()) {
|
||||
Is_standalone = 1;
|
||||
|
||||
// also enable non-interactive by default here
|
||||
Cmdline_noninteractive = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1813,6 +1799,16 @@ bool SetCmdlineParams()
|
||||
}
|
||||
}
|
||||
|
||||
if ( fov_cockpit_arg.found() ) {
|
||||
auto val = fov_cockpit_arg.get_float();
|
||||
if (val > 0.1) {
|
||||
COCKPIT_ZOOM_DEFAULT = val;
|
||||
}
|
||||
else {
|
||||
COCKPIT_ZOOM_DEFAULT = VIEWER_ZOOM_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
if( clip_dist_arg.found() ) {
|
||||
Min_draw_distance = Cmdline_clip_dist = clip_dist_arg.get_float();
|
||||
}
|
||||
@@ -1941,6 +1937,9 @@ bool SetCmdlineParams()
|
||||
Cmdline_output_sexp_info = true;
|
||||
}
|
||||
|
||||
if (generate_controlconfig_arg.found())
|
||||
Generate_controlconfig_table = true;
|
||||
|
||||
if ( no_pbo_arg.found() )
|
||||
{
|
||||
Cmdline_no_pbo = 1;
|
||||
@@ -2168,63 +2167,10 @@ bool SetCmdlineParams()
|
||||
}
|
||||
|
||||
//Deprecated flags - CommanderDJ
|
||||
if( deprecated_spec_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_spec = 1;
|
||||
}
|
||||
|
||||
if( deprecated_glow_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_glow = 1;
|
||||
}
|
||||
|
||||
if( deprecated_normal_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_normal = 1;
|
||||
}
|
||||
|
||||
if( deprecated_env_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_env = 1;
|
||||
}
|
||||
|
||||
if( deprecated_tbp_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_tbp = 1;
|
||||
}
|
||||
|
||||
if( deprecated_jpgtga_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_jpgtga = 1;
|
||||
}
|
||||
|
||||
if ( deprecated_htl_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_nohtl = 1;
|
||||
}
|
||||
|
||||
if ( deprecated_brieflighting_arg.found() )
|
||||
{
|
||||
Cmdline_deprecated_brief_lighting = 1;
|
||||
}
|
||||
|
||||
if (deprecated_missile_lighting_arg.found())
|
||||
{
|
||||
Cmdline_deprecated_missile_lighting = true;
|
||||
}
|
||||
|
||||
if (deprecated_cache_bitmaps_arg.found()) {
|
||||
Cmdline_deprecated_cache_bitmaps = true;
|
||||
}
|
||||
|
||||
if (deprecated_no_emissive_arg.found()) {
|
||||
Cmdline_emissive = 0;
|
||||
}
|
||||
|
||||
if (deprecated_postprocess_arg.found()) {
|
||||
Cmdline_deprecated_postprocess = true;
|
||||
}
|
||||
|
||||
if (deprecated_fxaa_arg.found() ) {
|
||||
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
|
||||
|
||||
@@ -2280,6 +2226,10 @@ bool SetCmdlineParams()
|
||||
}
|
||||
}
|
||||
|
||||
if (log_multi_packet_arg.found()) {
|
||||
Cmdline_dump_packet_type = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,7 @@ extern bool Cmdline_nograb;
|
||||
extern bool Cmdline_noshadercache;
|
||||
extern bool Cmdline_prefer_ipv4;
|
||||
extern bool Cmdline_prefer_ipv6;
|
||||
extern bool Cmdline_dump_packet_type;
|
||||
#ifdef WIN32
|
||||
extern bool Cmdline_alternate_registry_path;
|
||||
#endif
|
||||
|
||||
@@ -190,8 +190,8 @@ static unsigned int Defaults_cycle_pos = 0; // the controls preset that was last
|
||||
int Control_config_overlay_id;
|
||||
|
||||
struct conflict {
|
||||
int first = -1; // index of other control in conflict with this one
|
||||
int second = -1; // index of other control in conflict with this one
|
||||
int first = -1; // index of other control in conflict with this one
|
||||
int second = -1; // index of other control in conflict with this one
|
||||
};
|
||||
|
||||
SCP_vector<conflict> Conflicts;
|
||||
@@ -1137,8 +1137,7 @@ void control_config_toggle_invert()
|
||||
item.second.invert_toggle();
|
||||
break;
|
||||
default:
|
||||
// unhandled
|
||||
mprintf(("Unhandled selItem in control_config_toggle_invert(): %i", static_cast<int>(Selected_item)));
|
||||
UNREACHABLE("Unhandled selItem in control_config_toggle_invert(): %i\n", static_cast<int>(Selected_item));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1287,6 +1286,15 @@ int control_config_accept()
|
||||
}
|
||||
|
||||
SCP_string str = cstr;
|
||||
|
||||
// Check if a hardcoded preset with name already exists. If so, complain to user and force retry
|
||||
auto it = std::find_if(Control_config_presets.begin(), Control_config_presets.end(),
|
||||
[str](CC_preset& p) { return (p.name == str) && ((p.type == Preset_t::tbl) || (p.type == Preset_t::hardcode)); });
|
||||
|
||||
if (it != Control_config_presets.end()) {
|
||||
popup(flags, 1, POPUP_OK, "You may not overwrite a default preset. Please choose another name.");
|
||||
goto retry;
|
||||
}
|
||||
|
||||
// Check if a preset file with name already exists. If so, prompt the user
|
||||
CFILE* fp = cfopen((str + ".json").c_str(), "r", CFILE_NORMAL, CF_TYPE_PLAYER_BINDS, false,
|
||||
@@ -1657,8 +1665,7 @@ void control_config_draw_selected_preset() {
|
||||
auto preset_it = control_config_get_current_preset();
|
||||
|
||||
if (preset_it != Control_config_presets.end()) {
|
||||
sprintf(preset_str, XSTR("Preset: %s", 1659), preset_it->name.c_str());
|
||||
|
||||
sprintf(preset_str, XSTR("Preset: %s", 1659), preset_it->name.c_str());
|
||||
} else {
|
||||
sprintf(preset_str, XSTR("Preset: custom", 1660), "");
|
||||
}
|
||||
@@ -2532,7 +2539,7 @@ int check_control_used(int id, int key)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (item.disabled)
|
||||
if (item.disabled || item.locked)
|
||||
return 0;
|
||||
|
||||
short z = item.get_btn(CID_KEYBOARD); // Get the key that's bound to this control
|
||||
@@ -2601,30 +2608,43 @@ int check_control_used(int id, int key)
|
||||
|
||||
int check_control(int id, int key)
|
||||
{
|
||||
if (check_control_used(id, key)) {
|
||||
if (Ignored_keys[id]) {
|
||||
if (Ignored_keys[id] > 0) {
|
||||
Ignored_keys[id]--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
const bool is_control_used = check_control_used(id, key) != 0;
|
||||
const bool is_ignored = Ignored_keys[id] != 0;
|
||||
|
||||
//Decrement Ignored_keys if key was pressed and is ignored
|
||||
if (is_control_used && Ignored_keys[id] > 0) {
|
||||
Ignored_keys[id]--;
|
||||
}
|
||||
|
||||
if (Control_config[id].continuous_ongoing) {
|
||||
int control_triggered = 0; // boolean as int (0 = false, 1 = true); Weirdness for compatibility with control_run_lua();
|
||||
if (is_control_used && !is_ignored) {
|
||||
control_triggered = 1;
|
||||
}
|
||||
|
||||
if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
|
||||
// Only call lua in here when it's a continuous button
|
||||
|
||||
if (control_run_lua(static_cast<IoActionId>(id), control_triggered)) {
|
||||
// lua has taken care of the control already. Mark as not triggered to prevent hardcode from executing.
|
||||
control_triggered = 0;
|
||||
|
||||
} // Else, let hardcode process the control
|
||||
}
|
||||
|
||||
if (!is_control_used && Control_config[id].continuous_ongoing) {
|
||||
// If we reach this point, then it means this is a continuous control
|
||||
// which has just been released
|
||||
|
||||
if (Script_system.IsActiveAction(CHA_ONACTIONSTOPPED)) {
|
||||
Script_system.SetHookVar("Action", 's', Control_config[id].text);
|
||||
Script_system.RunCondition(CHA_ONACTIONSTOPPED, nullptr, id);
|
||||
Script_system.RunCondition(CHA_ONACTIONSTOPPED, nullptr, nullptr, id);
|
||||
Script_system.RemHookVar("Action");
|
||||
}
|
||||
|
||||
Control_config[id].continuous_ongoing = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return control_triggered;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2753,7 +2773,8 @@ void control_get_axes_readings(int *axis_v, float frame_time)
|
||||
mouse_get_delta(&axe[MOUSE_ID][MOUSE_X_AXIS], &axe[MOUSE_ID][MOUSE_Y_AXIS], &axe[MOUSE_ID][MOUSE_Z_AXIS]);
|
||||
|
||||
for (int action = 0; action < Action::NUM_VALUES; ++action) {
|
||||
CCI & item = Control_config[action + JOY_AXIS_BEGIN];
|
||||
const auto action_id = static_cast<IoActionId>(action + JOY_AXIS_BEGIN);
|
||||
CCI & item = Control_config[action_id];
|
||||
|
||||
// Assume actions are all axis actions, no need to check
|
||||
// Assumes all axes are uniquely bound to an action
|
||||
@@ -2766,6 +2787,28 @@ void control_get_axes_readings(int *axis_v, float frame_time)
|
||||
if (!item.second.empty()) {
|
||||
scale_invert(item.second, action, item.type, frame_time, axe, axis_v);
|
||||
}
|
||||
|
||||
//Call Lua hooks
|
||||
if (control_run_lua(action_id, axis_v[action])) {
|
||||
Assert(item.type == CC_TYPE_AXIS_ABS || item.type == CC_TYPE_AXIS_REL);
|
||||
|
||||
switch (item.type) {
|
||||
case CC_TYPE_AXIS_ABS:
|
||||
axis_v[action] = item.used;
|
||||
break;
|
||||
case CC_TYPE_AXIS_REL:
|
||||
axis_v[action] = 0;
|
||||
break;
|
||||
case CC_TYPE_AXIS_BTN_NEG:
|
||||
case CC_TYPE_AXIS_BTN_POS:
|
||||
default:
|
||||
//This should never happen, especially with the above Assertion. This is required as incomplete switches on an enum generate warnings
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Store values for possible lua override
|
||||
item.used = axis_v[action];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2781,7 +2824,7 @@ void control_used(int id)
|
||||
if (Control_config[id].used < Last_frame_timestamp) {
|
||||
if (!Control_config[id].continuous_ongoing) {
|
||||
Script_system.SetHookVar("Action", 's', Control_config[id].text);
|
||||
Script_system.RunCondition(CHA_ONACTION, nullptr, id);
|
||||
Script_system.RunCondition(CHA_ONACTION, nullptr, nullptr, id);
|
||||
Script_system.RemHookVar("Action");
|
||||
|
||||
if (Control_config[id].type == CC_TYPE_CONTINUOUS)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#define CONTROLS_CONFIG_H
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "scripting/scripting.h"
|
||||
|
||||
#define CONTROL_CONFIG_XSTR 507
|
||||
|
||||
@@ -21,6 +22,7 @@
|
||||
#define CCF_AXIS 0x08 //!< btn is an axis
|
||||
#define CCF_HAT 0x04 //!< btn is a hat
|
||||
#define CCF_BALL 0x02 //!< btn is a ball
|
||||
#define CCF_BUTTON 0x01 //!< btn is actually a button
|
||||
|
||||
/*!
|
||||
* These are used to index a corresponding axis value from an array.
|
||||
@@ -320,6 +322,15 @@ enum {
|
||||
};
|
||||
}
|
||||
|
||||
/*!
|
||||
* Where the preset is located in memory
|
||||
*/
|
||||
enum class Preset_t {
|
||||
hardcode, // Preset is defined hardcode
|
||||
tbl, // Preset is defined in controlconfigdefaults.tbl
|
||||
pst // Preset is defined in a .json
|
||||
};
|
||||
|
||||
class CCB;
|
||||
|
||||
/*!
|
||||
@@ -361,6 +372,16 @@ public:
|
||||
*/
|
||||
bool empty() const;
|
||||
|
||||
/**
|
||||
* Takes the given binding.
|
||||
*/
|
||||
void take(CID _cid, short _btn, char _flags = 0);
|
||||
|
||||
/**
|
||||
* Validates the binding, clearing it if necassary
|
||||
*/
|
||||
void validate();
|
||||
|
||||
/*!
|
||||
* Returns a copy of ::btn
|
||||
*/
|
||||
@@ -376,16 +397,6 @@ public:
|
||||
*/
|
||||
char get_flags() const;
|
||||
|
||||
/**
|
||||
* Takes the given binding.
|
||||
*/
|
||||
void take(CID _cid, short _btn, char _flags = 0);
|
||||
|
||||
/**
|
||||
* Validates the binding, clearing it if necassary
|
||||
*/
|
||||
void validate();
|
||||
|
||||
/*!
|
||||
* Returns a human-readable string of this binding
|
||||
*/
|
||||
@@ -468,12 +479,12 @@ public:
|
||||
/*!
|
||||
* Checks if the given CCB is exactly equal to this
|
||||
*/
|
||||
bool operator==(const CCB&);
|
||||
bool operator==(const CCB&) const;
|
||||
|
||||
/*!
|
||||
* Checks if the given CCB differs from this
|
||||
*/
|
||||
bool operator!=(const CCB&);
|
||||
bool operator!=(const CCB&) const;
|
||||
|
||||
/*!
|
||||
* Returns True if this CCB's first isn't empty and the given CCB has a binding that can conflict with it
|
||||
@@ -499,6 +510,21 @@ public:
|
||||
* Returns a pointer to first, or second, whichever has *all* flags in the given mask
|
||||
*/
|
||||
CC_bind* find_flags(const char);
|
||||
|
||||
/*!
|
||||
* Sets the inversion state of both bindings
|
||||
*/
|
||||
void invert(bool);
|
||||
|
||||
/*!
|
||||
* Toggles the inversion state of Primary and copies it to Secondary
|
||||
*/
|
||||
void invert_toggle();
|
||||
|
||||
/*!
|
||||
* Is true if both bindings are inverted, false otherwise
|
||||
*/
|
||||
bool is_inverted() const;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -508,8 +534,13 @@ class CC_preset {
|
||||
public:
|
||||
SCP_vector<CCB> bindings;
|
||||
SCP_string name;
|
||||
Preset_t type;
|
||||
|
||||
public:
|
||||
CC_preset() = default;
|
||||
CC_preset(const CC_preset& A) = default;
|
||||
|
||||
CC_preset& operator=(const CC_preset&);
|
||||
/*!
|
||||
* Checks if the given preset is a duplicate of this one
|
||||
*/
|
||||
@@ -534,8 +565,10 @@ public:
|
||||
CC_type type; //!< manner control should be checked in
|
||||
|
||||
// Items used during gameplay
|
||||
int used; //!< has control been used yet in mission? If so, this is the timestamp
|
||||
int used; //!< has control been used yet in mission? If so, this is the timestamp. For axes, this denotes the last frame's value
|
||||
bool disabled = true; //!< whether this action should be available at all
|
||||
bool locked = false; //!< whether this action will be triggered by the respectively bound key
|
||||
bool scriptEnabledByDefault = false; //!< whether this binding will execute it's registered hooks if triggered. Resets each mission
|
||||
bool continuous_ongoing; //!< whether this action is a continuous one and is currently ongoing
|
||||
|
||||
public:
|
||||
@@ -613,9 +646,10 @@ extern int Control_config_overlay_id;
|
||||
|
||||
extern SCP_vector<CCI> Control_config; //!< Stores the keyboard configuration
|
||||
extern SCP_vector<CC_preset> Control_config_presets; // tabled control presets; pointers to config_item arrays
|
||||
extern const char **Scan_code_text;
|
||||
extern const char **Joy_button_text; // String table of button labels. XSTR'd on init.
|
||||
|
||||
extern bool Generate_controlconfig_table;
|
||||
|
||||
// string table constants for labels and stuff.
|
||||
#define NUM_AXIS_TEXT JOY_NUM_AXES
|
||||
#define NUM_MOUSE_TEXT MOUSE_NUM_BUTTONS
|
||||
@@ -690,7 +724,10 @@ bool control_config_use_preset_by_name(const SCP_string &name);
|
||||
*
|
||||
* @returns an iterator to the current preset, or
|
||||
* @returns ::iterator Control_config_presets.end() if current bindings are not in a preset
|
||||
*
|
||||
* @note Similar to preset_find_duplicate, this function has additional logic in its search to ignore disabled controls
|
||||
*/
|
||||
|
||||
SCP_vector<CC_preset>::iterator control_config_get_current_preset(bool invert_agnostic = false);
|
||||
|
||||
/*!
|
||||
@@ -726,10 +763,9 @@ int translate_key_to_index(const char *key, bool find_override=true);
|
||||
const char *translate_key(char *key);
|
||||
|
||||
/**
|
||||
* @brief Converts the specified key code to a human readable string, according to the selected locale
|
||||
* @brief Converts the specified key code to a human readable string, according to the selected locale and keyboard layout
|
||||
*
|
||||
* @param[in] code The key code to convert
|
||||
* @param[in] use_default_locale If true, return the default locale (English) translation of the key
|
||||
* @param[in] code The key scancode to convert
|
||||
*
|
||||
* @return The text representation of the code.
|
||||
*
|
||||
@@ -737,7 +773,18 @@ const char *translate_key(char *key);
|
||||
*
|
||||
* @note The return value is translated according to localization settings
|
||||
*/
|
||||
const char *textify_scancode(int code, bool use_default_locale = false);
|
||||
const char *textify_scancode(int code);
|
||||
|
||||
/**
|
||||
* @brief Converts the specified key code to a human readable string. Uses the English locale and QWERTY layout.
|
||||
*
|
||||
* @param[in] code The key scancode to convert
|
||||
*
|
||||
* @return The text representation of the code
|
||||
*
|
||||
* @note Not thread safe. Has an internal buffer for the return value which is overwritten on each call.
|
||||
*/
|
||||
const char *textify_scancode_universal(int code);
|
||||
|
||||
/*!
|
||||
* @brief Checks how long a control has been active
|
||||
@@ -784,6 +831,35 @@ void control_get_axes_readings(int *axis_v, float frame_time);
|
||||
*/
|
||||
void control_used(int id);
|
||||
|
||||
/**
|
||||
* @brief Runs a given controlbindings lua hook added in ccd.tbl
|
||||
*
|
||||
* @return Whether the control should be overridden
|
||||
*/
|
||||
bool control_run_lua(IoActionId id, int value);
|
||||
|
||||
/**
|
||||
* @brief Resets the cache for script evaluations for continuous buttons. Should be called once per frame.
|
||||
*
|
||||
*/
|
||||
void control_reset_lua_cache();
|
||||
|
||||
/**
|
||||
* @brief Registers a new hook for the keybinding action system.
|
||||
* Parameters set if it is a normal or an override function, as well as if it is enabled by default or needs to be enabled manually each mission
|
||||
*/
|
||||
void control_register_hook(IoActionId id, const luacpp::LuaFunction& hook, bool is_override, bool enabledByDefault);
|
||||
|
||||
/**
|
||||
* @brief Enables or disables the respective Lua hook
|
||||
*/
|
||||
void control_enable_hook(IoActionId id, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Resets every hooks enable state to the tabled value. Call on Mission start
|
||||
*/
|
||||
void control_reset_hook();
|
||||
|
||||
/**
|
||||
* @brief Clears the bindings of all controls
|
||||
*/
|
||||
@@ -836,18 +912,17 @@ CID CIDToVal(const char * str);
|
||||
|
||||
/**
|
||||
* Reverse lookups the CID to get its stringified name
|
||||
* @return Pointer to the stringified name of the CID, or
|
||||
* @return nullptr if not found
|
||||
* @return The stringified name of the CID, or
|
||||
* @return "NONE" if not found
|
||||
*/
|
||||
const char * ValToCID(CID id);
|
||||
SCP_string ValToCID(CID id);
|
||||
|
||||
/**
|
||||
* Reverse lookups the CID to get its stringified name
|
||||
* @return Pointer to the stringified name of the CID, or
|
||||
* @return nullptr if not found, or invalid id
|
||||
* @return The stringified name of the CID, or
|
||||
* @return "NONE" if not found, or invalid id
|
||||
*/
|
||||
const char * ValToCID(int id);
|
||||
|
||||
SCP_string ValToCID(int id);
|
||||
/**
|
||||
* Lookups the given stringified enum to find its value
|
||||
*/
|
||||
@@ -855,8 +930,8 @@ char CCFToVal(const char * str);
|
||||
|
||||
/**
|
||||
* Constructs a enum string from the CCF_FLAGS
|
||||
* @return Pointer to the stringified name of the CCF, or
|
||||
* @return nullptr if not found, or invalid id
|
||||
* @return The stringified name of the CCF, or
|
||||
* @return "NONE" if not found, or invalid id
|
||||
*/
|
||||
SCP_string ValToCCF(char id);
|
||||
|
||||
@@ -868,8 +943,8 @@ short InputToVal(CID cid, const char * str);
|
||||
/**
|
||||
* Constructs a enumstring from the input binding, depending on the CID
|
||||
*
|
||||
* @return Pointer to the stringified name of the input, or
|
||||
* @return nullptr if not found, or invalid CC_bind
|
||||
* @return The stringified name of the input, or
|
||||
* @return "NONE" if not found, or invalid CC_bind
|
||||
*
|
||||
* @note This requires a CCB due to the way things are encoded
|
||||
*/
|
||||
@@ -883,8 +958,8 @@ short MouseToVal(const char * str);
|
||||
/**
|
||||
* Constructs a enum string from the mouse input
|
||||
*
|
||||
* @return Pointer to the stringified name of the input, or
|
||||
* @return nullptr if not found, or invalid CC_bind
|
||||
* @return The stringified name of the input, or
|
||||
* @return "NONE" if not found, or invalid CC_bind
|
||||
* TODO XSTR
|
||||
*
|
||||
* @note This requires a CCB due to the way things are encoded
|
||||
@@ -899,8 +974,8 @@ short KeyboardToVal(const char * str);
|
||||
/**
|
||||
* Constructs an enum string from the key binding
|
||||
*
|
||||
* @return Pointer to the stringified name of the input, or
|
||||
* @return nullptr if not found, or invalid CC_bind
|
||||
* @return The stringified name of the input, or
|
||||
* @return "NONE" f not found, or invalid CC_bind
|
||||
*
|
||||
* @note This requires a CCB due to the way things are encoded
|
||||
* TODO XSTR
|
||||
@@ -915,11 +990,37 @@ short JoyToVal(const char * str);
|
||||
/**
|
||||
* Constructs a enum string from the Joystick input
|
||||
*
|
||||
* @return Pointer to the stringified name of the input, or
|
||||
* @return nullptr if not found, or invalid CC_bind
|
||||
* @return The stringified name of the input, or
|
||||
* @return "NONE" if not found, or invalid CC_bind
|
||||
*
|
||||
* @note This requires a CCB due to the way things are encoded
|
||||
* TODO XSTR
|
||||
*/
|
||||
SCP_string ValToJoy(const CC_bind &bind);
|
||||
|
||||
/**
|
||||
* Lookups the given string to find its tab value
|
||||
*/
|
||||
char CCTabToVal(const char *str);
|
||||
|
||||
/**
|
||||
* Reverse lookups the given tab in mCCTabToVal to retrieve the string name
|
||||
*
|
||||
* @return The stringified name of the input, or
|
||||
* @return nullptr if not found, or invalid tab
|
||||
*
|
||||
*/
|
||||
SCP_string ValToCCTab(char tab);
|
||||
|
||||
/**
|
||||
* Lookups the given string to find its CC_type value. =
|
||||
*/
|
||||
CC_type CCTypeToVal(const char *str);
|
||||
|
||||
/**
|
||||
* Reverse lookups the given CC_type to retrieve the string name
|
||||
|
||||
*/
|
||||
SCP_string ValToCCType(CC_type type);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -47,7 +47,7 @@ void load_preset_files() {
|
||||
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
||||
|
||||
if (!fp) {
|
||||
mprintf(("PLR => Unable to open '%s' for loading!\n", file.c_str()));
|
||||
mprintf(("PST => Unable to open '%s' for loading!\n", file.c_str()));
|
||||
// try next
|
||||
continue;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ void load_preset_files() {
|
||||
try {
|
||||
handler.reset(new PresetFileHandler(fp, true));
|
||||
} catch (const std::exception& e) {
|
||||
mprintf(("PLR => Failed to open JSON: `%s`\n", e.what()));
|
||||
mprintf(("PST => Failed to open JSON: `%s`\n", e.what()));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ void load_preset_files() {
|
||||
// Start reading in data
|
||||
CC_preset preset;
|
||||
preset.name = file;
|
||||
preset.type = Preset_t::pst;
|
||||
preset.bindings.resize(Control_config.size());
|
||||
|
||||
size_t size;
|
||||
@@ -104,6 +105,7 @@ void load_preset_files() {
|
||||
str = handler->readString("input");
|
||||
auto btn = InputToVal(cid, str.c_str());
|
||||
item.first.take(cid, btn, flags);
|
||||
|
||||
handler->endSectionRead(); // Primary
|
||||
|
||||
|
||||
@@ -124,21 +126,16 @@ void load_preset_files() {
|
||||
handler->endArrayRead(); // Actions
|
||||
|
||||
// Done with the file
|
||||
bool unique = true;
|
||||
auto it = Control_config_presets.begin();
|
||||
for (; it != Control_config_presets.end(); ++it) {
|
||||
if (preset.is_duplicate_of(*it)) {
|
||||
unique = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto it = preset_find_duplicate(preset);
|
||||
|
||||
if (unique) {
|
||||
if (it == Control_config_presets.end()) {
|
||||
Control_config_presets.push_back(preset);
|
||||
} else {
|
||||
Warning(LOCATION, "Preset '%s' is a duplicate of '%s', ignoring", preset.name.c_str(), it->name.c_str());
|
||||
}
|
||||
|
||||
} else if ((it->name != preset.name) || (it->type != Preset_t::pst)) {
|
||||
// Complain and ignore if the preset names or the type differs
|
||||
Warning(LOCATION, "PST => Preset '%s' is a duplicate of an existing preset, ignoring", preset.name.c_str());
|
||||
|
||||
} // else, silent ignore
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +217,27 @@ bool save_preset_file(CC_preset preset, bool overwrite) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SCP_vector<CC_preset>::iterator preset_find_duplicate(const CC_preset& new_preset) {
|
||||
SCP_vector<CC_preset>::iterator it = Control_config_presets.begin(); // NOLINT(modernize-use-auto)
|
||||
|
||||
for (; it != Control_config_presets.end(); ++it) {
|
||||
size_t i;
|
||||
for (i = 0; i < it->bindings.size(); ++i) {
|
||||
if (new_preset.bindings[i] != it->bindings[i]) {
|
||||
// These two differ, check the next in the vector
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == it->bindings.size()) {
|
||||
// These two are equal
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
PresetFileHandler::PresetFileHandler(CFILE* cfp, bool reading) : _cfp(cfp) {
|
||||
Assertion(cfp != nullptr, "File pointer must be valid!");
|
||||
|
||||
|
||||
@@ -216,6 +216,14 @@ bool save_preset_file(CC_preset preset, bool overwrite);
|
||||
*/
|
||||
void load_preset_files();
|
||||
|
||||
/**
|
||||
* @brief Checks if the given preset is a duplicate within Control_config_presets vector
|
||||
* @returns iterator to the duplicate if found, or
|
||||
* @returns iterator to Control_config_presets.end() otherwise
|
||||
*/
|
||||
SCP_vector<CC_preset>::iterator preset_find_duplicate(const CC_preset& new_preset);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns true if a preset file with the given name exists.
|
||||
*/
|
||||
|
||||
@@ -51,6 +51,7 @@ void processEvents()
|
||||
{
|
||||
io::mouse::CursorManager::get()->showCursor(false);
|
||||
|
||||
os_remove_deferred_cutscene_key_events();
|
||||
os_poll();
|
||||
}
|
||||
|
||||
|
||||
+9
-13
@@ -668,9 +668,11 @@ object *debris_create(object *source_obj, int model_num, int submodel_num, vec3d
|
||||
// ensure vel is valid
|
||||
Assert( !vm_is_vec_nan(&obj->phys_info.vel) );
|
||||
|
||||
OnDebrisCreatedHook->run(scripting::hook_param_list(
|
||||
scripting::hook_param("Debris", 'o', obj),
|
||||
scripting::hook_param("Source", 'o', source_obj)));
|
||||
if (OnDebrisCreatedHook->isActive()) {
|
||||
OnDebrisCreatedHook->run(scripting::hook_param_list(
|
||||
scripting::hook_param("Debris", 'o', obj),
|
||||
scripting::hook_param("Source", 'o', source_obj)));
|
||||
}
|
||||
|
||||
if (db->is_hull) {
|
||||
MONITOR_INC(NumHullDebris,1);
|
||||
@@ -864,8 +866,8 @@ int debris_check_collision(object *pdebris, object *other_obj, vec3d *hitpos, co
|
||||
pmi->submodel[submodel].collision_checked = true;
|
||||
}
|
||||
|
||||
// reset flags to check MC_CHECK_MODEL | MC_CHECK_SPHERELINE and maybe MC_CHECK_INVISIBLE_FACES and MC_SUBMODEL_INSTANCE
|
||||
mc.flags = copy_flags | MC_SUBMODEL_INSTANCE;
|
||||
// Only check single submodel now, since children of moving submodels are handled as moving as well
|
||||
mc.flags = copy_flags | MC_SUBMODEL;
|
||||
|
||||
if (Ship_info[Ships[pship_obj->instance].ship_info_index].collision_lod > -1) {
|
||||
mc.lod = Ship_info[Ships[pship_obj->instance].ship_info_index].collision_lod;
|
||||
@@ -878,15 +880,9 @@ int debris_check_collision(object *pdebris, object *other_obj, vec3d *hitpos, co
|
||||
// turn on just one submodel for collision test
|
||||
smi->collision_checked = false;
|
||||
|
||||
// set angles for last frame (need to set to prev to get p0)
|
||||
matrix copy_matrix = smi->canonical_orient;
|
||||
|
||||
// find the start and end positions of the sphere in submodel RF
|
||||
smi->canonical_orient = smi->canonical_prev_orient;
|
||||
model_instance_world_to_local_point(&p0, &light_obj->last_pos, pm, pmi, submodel, &heavy_obj->last_orient, &heavy_obj->last_pos);
|
||||
|
||||
smi->canonical_orient = copy_matrix;
|
||||
model_instance_world_to_local_point(&p1, &light_obj->pos, pm, pmi, submodel, &heavy_obj->orient, &heavy_obj->pos);
|
||||
model_instance_global_to_local_point(&p0, &light_obj->last_pos, pm, pmi, submodel, &heavy_obj->last_orient, &heavy_obj->last_pos, true);
|
||||
model_instance_global_to_local_point(&p1, &light_obj->pos, pm, pmi, submodel, &heavy_obj->orient, &heavy_obj->pos);
|
||||
|
||||
mc.p0 = &p0;
|
||||
mc.p1 = &p1;
|
||||
|
||||
@@ -488,6 +488,21 @@ void dc_putc(char c)
|
||||
line_str->push_back(c);
|
||||
}
|
||||
|
||||
bool handle_textInputEvent(const SDL_Event& event) {
|
||||
if (event.text.text[0] != '\0' && event.text.text[0] != '\1') {
|
||||
for (char c : event.text.text) {
|
||||
if (c < 32)
|
||||
break;
|
||||
|
||||
if (dc_command_buf.size() < MAX_CLI_LEN) {
|
||||
dc_command_buf.push_back(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void debug_console(void (*_func)(void))
|
||||
{
|
||||
int done = 0;
|
||||
@@ -500,6 +515,9 @@ void debug_console(void (*_func)(void))
|
||||
dc_init();
|
||||
}
|
||||
|
||||
auto textListener = os::events::addEventListener(SDL_TEXTINPUT, 10000, &handle_textInputEvent);
|
||||
SDL_StartTextInput();
|
||||
|
||||
dc_draw(TRUE);
|
||||
|
||||
while (!done) {
|
||||
@@ -586,11 +604,9 @@ void debug_console(void (*_func)(void))
|
||||
break;
|
||||
|
||||
default:
|
||||
// Not any of the control key codes, so it's probably a letter or number.
|
||||
ubyte c = (ubyte)key_to_ascii(k);
|
||||
if ((c != 255) && (dc_command_buf.size() < MAX_CLI_LEN)) {
|
||||
dc_command_buf.push_back(c);
|
||||
}
|
||||
// Leave handling of key input to SDL
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// Do the passed function
|
||||
@@ -602,6 +618,9 @@ void debug_console(void (*_func)(void))
|
||||
dc_draw(TRUE);
|
||||
}
|
||||
|
||||
SDL_StopTextInput();
|
||||
os::events::removeEventListener(textListener);
|
||||
|
||||
while( key_inkey() ) {
|
||||
os_poll();
|
||||
}
|
||||
|
||||
+187
-211
@@ -9,179 +9,158 @@
|
||||
#include "tracing/tracing.h"
|
||||
#include "ship/ship.h"
|
||||
|
||||
namespace {
|
||||
namespace decals {
|
||||
|
||||
class DecalDefinition {
|
||||
SCP_string _name;
|
||||
DecalDefinition::DecalDefinition(SCP_string name) : _name(std::move(name)) {
|
||||
}
|
||||
|
||||
SCP_string _diffuseFilename;
|
||||
bool _loopDiffuse = true;
|
||||
|
||||
SCP_string _glowFilename;
|
||||
bool _loopGlow = true;
|
||||
|
||||
SCP_string _normalMapFilename;
|
||||
bool _loopNormal = true;
|
||||
|
||||
int _diffuseBitmap = -1;
|
||||
int _glowBitmap = -1;
|
||||
int _normalBitmap = -1;
|
||||
|
||||
public:
|
||||
explicit DecalDefinition(SCP_string name) : _name(std::move(name)) {
|
||||
DecalDefinition::~DecalDefinition() {
|
||||
if (_diffuseBitmap >= 0) {
|
||||
bm_release(_diffuseBitmap);
|
||||
}
|
||||
if (_glowBitmap >= 0) {
|
||||
bm_release(_glowBitmap);
|
||||
}
|
||||
if (_normalBitmap >= 0) {
|
||||
bm_release(_normalBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
~DecalDefinition() {
|
||||
if (_diffuseBitmap >= 0) {
|
||||
bm_release(_diffuseBitmap);
|
||||
DecalDefinition::DecalDefinition(DecalDefinition&& other) noexcept {
|
||||
*this = std::move(other); // Use operator implementation
|
||||
}
|
||||
|
||||
DecalDefinition& DecalDefinition::operator=(DecalDefinition&& other) noexcept {
|
||||
std::swap(_name, other._name);
|
||||
|
||||
std::swap(_diffuseFilename, other._diffuseFilename);
|
||||
std::swap(_glowFilename, other._glowFilename);
|
||||
std::swap(_normalMapFilename, other._normalMapFilename);
|
||||
|
||||
std::swap(_diffuseBitmap, other._diffuseBitmap);
|
||||
std::swap(_glowBitmap, other._glowBitmap);
|
||||
std::swap(_normalBitmap, other._normalBitmap);
|
||||
|
||||
std::swap(_loopDiffuse, other._loopDiffuse);
|
||||
std::swap(_loopGlow, other._loopGlow);
|
||||
std::swap(_loopNormal, other._loopNormal);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DecalDefinition::parse() {
|
||||
if (optional_string("+Diffuse:")) {
|
||||
stuff_string(_diffuseFilename, F_FILESPEC);
|
||||
|
||||
if (!bm_validate_filename(_diffuseFilename, true, true)) {
|
||||
error_display(0, "Animation '%s' is not valid!", _diffuseFilename.c_str());
|
||||
_diffuseFilename = "";
|
||||
}
|
||||
if (_glowBitmap >= 0) {
|
||||
bm_release(_glowBitmap);
|
||||
}
|
||||
if (_normalBitmap >= 0) {
|
||||
bm_release(_normalBitmap);
|
||||
|
||||
if (optional_string("+Loop:")) {
|
||||
stuff_boolean(&_loopDiffuse);
|
||||
}
|
||||
}
|
||||
if (optional_string("+Glow:")) {
|
||||
stuff_string(_glowFilename, F_FILESPEC);
|
||||
|
||||
// Disallow copying
|
||||
DecalDefinition(const DecalDefinition&) = delete;
|
||||
DecalDefinition& operator=(const DecalDefinition&) = delete;
|
||||
|
||||
// Move constructor and operator
|
||||
DecalDefinition(DecalDefinition&& other) noexcept {
|
||||
*this = std::move(other); // Use operator implementation
|
||||
}
|
||||
DecalDefinition& operator=(DecalDefinition&& other) noexcept {
|
||||
std::swap(_name, other._name);
|
||||
|
||||
std::swap(_diffuseFilename, other._diffuseFilename);
|
||||
std::swap(_glowFilename, other._glowFilename);
|
||||
std::swap(_normalMapFilename, other._normalMapFilename);
|
||||
|
||||
std::swap(_diffuseBitmap, other._diffuseBitmap);
|
||||
std::swap(_glowBitmap, other._glowBitmap);
|
||||
std::swap(_normalBitmap, other._normalBitmap);
|
||||
|
||||
std::swap(_loopDiffuse, other._loopDiffuse);
|
||||
std::swap(_loopGlow, other._loopGlow);
|
||||
std::swap(_loopNormal, other._loopNormal);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void parse() {
|
||||
if (optional_string("+Diffuse:")) {
|
||||
stuff_string(_diffuseFilename, F_FILESPEC);
|
||||
|
||||
if (!bm_validate_filename(_diffuseFilename, true, true)) {
|
||||
error_display(0, "Animation '%s' is not valid!", _diffuseFilename.c_str());
|
||||
_diffuseFilename = "";
|
||||
}
|
||||
|
||||
if (optional_string("+Loop:")) {
|
||||
stuff_boolean(&_loopDiffuse);
|
||||
}
|
||||
if (!bm_validate_filename(_glowFilename, true, true)) {
|
||||
error_display(0, "Animation '%s' is not valid!", _glowFilename.c_str());
|
||||
_glowFilename = "";
|
||||
}
|
||||
if (optional_string("+Glow:")) {
|
||||
stuff_string(_glowFilename, F_FILESPEC);
|
||||
|
||||
if (!bm_validate_filename(_glowFilename, true, true)) {
|
||||
error_display(0, "Animation '%s' is not valid!", _glowFilename.c_str());
|
||||
_glowFilename = "";
|
||||
}
|
||||
|
||||
if (optional_string("+Loop:")) {
|
||||
stuff_boolean(&_loopGlow);
|
||||
}
|
||||
}
|
||||
if (optional_string("+Normal:")) {
|
||||
stuff_string(_normalMapFilename, F_FILESPEC);
|
||||
|
||||
if (!bm_validate_filename(_normalMapFilename, true, true)) {
|
||||
error_display(0, "Animation '%s' is not valid!", _normalMapFilename.c_str());
|
||||
_normalMapFilename = "";
|
||||
}
|
||||
|
||||
if (optional_string("+Loop:")) {
|
||||
stuff_boolean(&_loopNormal);
|
||||
}
|
||||
if (optional_string("+Loop:")) {
|
||||
stuff_boolean(&_loopGlow);
|
||||
}
|
||||
}
|
||||
if (optional_string("+Normal:")) {
|
||||
stuff_string(_normalMapFilename, F_FILESPEC);
|
||||
|
||||
void loadBitmaps() {
|
||||
if (_diffuseBitmap == -1 && VALID_FNAME(_diffuseFilename)) {
|
||||
_diffuseBitmap = bm_load_either(_diffuseFilename.c_str());
|
||||
if (_diffuseBitmap == -1) {
|
||||
Warning(LOCATION,
|
||||
"Bitmap '%s' failed to load for decal definition %s!",
|
||||
_diffuseFilename.c_str(),
|
||||
_name.c_str());
|
||||
}
|
||||
if (!bm_validate_filename(_normalMapFilename, true, true)) {
|
||||
error_display(0, "Animation '%s' is not valid!", _normalMapFilename.c_str());
|
||||
_normalMapFilename = "";
|
||||
}
|
||||
if (_glowBitmap == -1 && VALID_FNAME(_glowFilename)) {
|
||||
_glowBitmap = bm_load_either(_glowFilename.c_str());
|
||||
if (_glowBitmap == -1) {
|
||||
Warning(LOCATION,
|
||||
"Bitmap '%s' failed to load for decal definition %s!",
|
||||
_glowFilename.c_str(),
|
||||
_name.c_str());
|
||||
}
|
||||
}
|
||||
if (_normalBitmap == -1 && VALID_FNAME(_normalMapFilename)) {
|
||||
_normalBitmap = bm_load_either(_normalMapFilename.c_str());
|
||||
if (_normalBitmap == -1) {
|
||||
Warning(LOCATION,
|
||||
"Bitmap '%s' failed to load for decal definition %s!",
|
||||
_normalMapFilename.c_str(),
|
||||
_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pageIn() {
|
||||
if (_diffuseBitmap >= 0) {
|
||||
bm_page_in_texture(_diffuseBitmap);
|
||||
}
|
||||
if (_glowBitmap >= 0) {
|
||||
bm_page_in_texture(_glowBitmap);
|
||||
}
|
||||
if (_normalBitmap >= 0) {
|
||||
bm_page_in_texture(_normalBitmap);
|
||||
if (optional_string("+Loop:")) {
|
||||
stuff_boolean(&_loopNormal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool bitmapsLoaded() {
|
||||
// Since both bitmap types are optional we need to check if either is loaded to determine if any bitmap is loaded
|
||||
return _diffuseBitmap >= 0 || _glowBitmap >= 0 || _normalBitmap >= 0;
|
||||
void DecalDefinition::loadBitmaps() {
|
||||
if (_diffuseBitmap == -1 && VALID_FNAME(_diffuseFilename)) {
|
||||
_diffuseBitmap = bm_load_either(_diffuseFilename.c_str());
|
||||
if (_diffuseBitmap == -1) {
|
||||
Warning(LOCATION,
|
||||
"Bitmap '%s' failed to load for decal definition %s!",
|
||||
_diffuseFilename.c_str(),
|
||||
_name.c_str());
|
||||
}
|
||||
}
|
||||
if (_glowBitmap == -1 && VALID_FNAME(_glowFilename)) {
|
||||
_glowBitmap = bm_load_either(_glowFilename.c_str());
|
||||
if (_glowBitmap == -1) {
|
||||
Warning(LOCATION,
|
||||
"Bitmap '%s' failed to load for decal definition %s!",
|
||||
_glowFilename.c_str(),
|
||||
_name.c_str());
|
||||
}
|
||||
}
|
||||
if (_normalBitmap == -1 && VALID_FNAME(_normalMapFilename)) {
|
||||
_normalBitmap = bm_load_either(_normalMapFilename.c_str());
|
||||
if (_normalBitmap == -1) {
|
||||
Warning(LOCATION,
|
||||
"Bitmap '%s' failed to load for decal definition %s!",
|
||||
_normalMapFilename.c_str(),
|
||||
_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SCP_string& getName() const {
|
||||
return _name;
|
||||
void DecalDefinition::pageIn() {
|
||||
if (_diffuseBitmap >= 0) {
|
||||
bm_page_in_texture(_diffuseBitmap);
|
||||
}
|
||||
int getDiffuseBitmap() const {
|
||||
return _diffuseBitmap;
|
||||
if (_glowBitmap >= 0) {
|
||||
bm_page_in_texture(_glowBitmap);
|
||||
}
|
||||
int getGlowBitmap() const {
|
||||
return _glowBitmap;
|
||||
if (_normalBitmap >= 0) {
|
||||
bm_page_in_texture(_normalBitmap);
|
||||
}
|
||||
int getNormalBitmap() const {
|
||||
return _normalBitmap;
|
||||
}
|
||||
bool isDiffuseLooping() const {
|
||||
return _loopDiffuse;
|
||||
}
|
||||
bool isGlowLooping() const {
|
||||
return _loopGlow;
|
||||
}
|
||||
bool isNormalLooping() const {
|
||||
return _loopNormal;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
SCP_vector<DecalDefinition> decalDefinitions;
|
||||
bool DecalDefinition::bitmapsLoaded() {
|
||||
// Since both bitmap types are optional we need to check if either is loaded to determine if any bitmap is loaded
|
||||
return _diffuseBitmap >= 0 || _glowBitmap >= 0 || _normalBitmap >= 0;
|
||||
}
|
||||
|
||||
const SCP_string& DecalDefinition::getName() const {
|
||||
return _name;
|
||||
}
|
||||
int DecalDefinition::getDiffuseBitmap() const {
|
||||
return _diffuseBitmap;
|
||||
}
|
||||
int DecalDefinition::getGlowBitmap() const {
|
||||
return _glowBitmap;
|
||||
}
|
||||
int DecalDefinition::getNormalBitmap() const {
|
||||
return _normalBitmap;
|
||||
}
|
||||
bool DecalDefinition::isDiffuseLooping() const {
|
||||
return _loopDiffuse;
|
||||
}
|
||||
bool DecalDefinition::isGlowLooping() const {
|
||||
return _loopGlow;
|
||||
}
|
||||
bool DecalDefinition::isNormalLooping() const {
|
||||
return _loopNormal;
|
||||
}
|
||||
|
||||
|
||||
SCP_vector<DecalDefinition> DecalDefinitions;
|
||||
|
||||
// Variable to indicate if the system is able to work correctly on the current system
|
||||
bool decal_system_active = true;
|
||||
bool Decal_system_active = true;
|
||||
|
||||
void parse_decals_table(const char* filename) {
|
||||
try {
|
||||
@@ -197,7 +176,7 @@ void parse_decals_table(const char* filename) {
|
||||
DecalDefinition def(name);
|
||||
def.parse();
|
||||
|
||||
decalDefinitions.push_back(std::move(def));
|
||||
DecalDefinitions.push_back(std::move(def));
|
||||
}
|
||||
|
||||
required_string("#End");
|
||||
@@ -208,17 +187,17 @@ void parse_decals_table(const char* filename) {
|
||||
|
||||
if (!gr_is_capable(CAPABILITY_DEFERRED_LIGHTING)) {
|
||||
// we need deferred lighting
|
||||
decal_system_active = false;
|
||||
Decal_system_active = false;
|
||||
mprintf(("Note: Decal system has been disabled due to lack of deferred lighting.\n"));
|
||||
}
|
||||
if (!gr_is_capable(CAPABILITY_NORMAL_MAP)) {
|
||||
// We need normal mapping for the full feature range
|
||||
decal_system_active = false;
|
||||
Decal_system_active = false;
|
||||
mprintf(("Note: Decal system has been disabled due to lack of normal mapping.\n"));
|
||||
}
|
||||
if (!gr_is_capable(CAPABILITY_SEPARATE_BLEND_FUNCTIONS)) {
|
||||
// WWe need separate blending functions for different color buffers
|
||||
decal_system_active = false;
|
||||
Decal_system_active = false;
|
||||
mprintf(("Note: Decal system has been disabled due to lack of separate color buffer blend functions.\n"));
|
||||
}
|
||||
}
|
||||
@@ -244,6 +223,9 @@ struct Decal {
|
||||
if (!object.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
if (object.objp->flags[Object::Object_Flags::Should_be_dead]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (orig_obj_type != object.objp->type) {
|
||||
mprintf(("Decal object type for object %d has changed from %s to %s. Please let m!m know about this\n",
|
||||
@@ -270,6 +252,9 @@ struct Decal {
|
||||
if (smi->blown_off) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Assertion(false, "Only ships are currently supported for decals!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -308,35 +293,41 @@ float smoothstep(float edge0, float edge1, float x) {
|
||||
namespace decals {
|
||||
|
||||
void initialize() {
|
||||
decalDefinitions.clear();
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
Decal_system_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
DecalDefinitions.clear();
|
||||
graphics::decal_draw_list::globalInit();
|
||||
|
||||
parse_modular_table(NOX("*-dcl.tbm"), parse_decals_table);
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
graphics::decal_draw_list::globalShutdown();
|
||||
|
||||
// Free allocated resources
|
||||
decalDefinitions.clear();
|
||||
DecalDefinitions.clear();
|
||||
}
|
||||
|
||||
int getDecalConfig(const SCP_string& name) {
|
||||
int index = 0;
|
||||
for (auto& def : decalDefinitions) {
|
||||
if (!stricmp(def.getName().c_str(), name.c_str())) {
|
||||
return index;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
int findDecalDefinition(const SCP_string& name) {
|
||||
auto ii = std::find_if(DecalDefinitions.begin(), DecalDefinitions.end(), [&](const DecalDefinition& def)
|
||||
{
|
||||
return def.getName() == name;
|
||||
});
|
||||
|
||||
return -1;
|
||||
if (ii == DecalDefinitions.end())
|
||||
return -1;
|
||||
|
||||
return static_cast<int>(std::distance(DecalDefinitions.begin(), ii));
|
||||
}
|
||||
|
||||
void parseDecalReference(creation_info& dest_info, bool is_new_entry) {
|
||||
SCP_string name;
|
||||
stuff_string(name, F_NAME);
|
||||
|
||||
auto decalRef = getDecalConfig(name);
|
||||
auto decalRef = findDecalDefinition(name);
|
||||
|
||||
if (decalRef < 0) {
|
||||
error_display(0, "Decal definition '%s' is unknown!", name.c_str());
|
||||
@@ -361,8 +352,9 @@ void parseDecalReference(creation_info& dest_info, bool is_new_entry) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loadBitmaps(const creation_info& info) {
|
||||
if (!decal_system_active) {
|
||||
if (!Decal_system_active) {
|
||||
return;
|
||||
}
|
||||
// Silently ignore invalid definition handle since weapons use the default values if the decal option is not present
|
||||
@@ -370,15 +362,16 @@ void loadBitmaps(const creation_info& info) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assertion(info.definition_handle >= 0 && info.definition_handle < (int) decalDefinitions.size(),
|
||||
Assertion(info.definition_handle >= 0 && info.definition_handle < (int) DecalDefinitions.size(),
|
||||
"Invalid decal handle detected!");
|
||||
|
||||
auto& def = decalDefinitions[info.definition_handle];
|
||||
auto& def = DecalDefinitions[info.definition_handle];
|
||||
|
||||
def.loadBitmaps();
|
||||
}
|
||||
|
||||
void pageInDecal(const creation_info& info) {
|
||||
if (!decal_system_active) {
|
||||
if (!Decal_system_active) {
|
||||
return;
|
||||
}
|
||||
// Silently ignore invalid definition handle since weapons use the default values if the decal option is not present
|
||||
@@ -386,10 +379,10 @@ void pageInDecal(const creation_info& info) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assertion(info.definition_handle >= 0 && info.definition_handle < (int) decalDefinitions.size(),
|
||||
Assertion(info.definition_handle >= 0 && info.definition_handle < (int) DecalDefinitions.size(),
|
||||
"Invalid decal handle detected!");
|
||||
|
||||
decalDefinitions[info.definition_handle].pageIn();
|
||||
DecalDefinitions[info.definition_handle].pageIn();
|
||||
}
|
||||
|
||||
void initializeMission() {
|
||||
@@ -405,48 +398,25 @@ matrix4 getDecalTransform(Decal& decal) {
|
||||
auto pm = model_get(pmi->model_num);
|
||||
|
||||
vec3d worldPos;
|
||||
model_instance_local_to_global_point(&worldPos,
|
||||
matrix worldOrient;
|
||||
model_instance_local_to_global_point_orient(&worldPos,
|
||||
&worldOrient,
|
||||
&decal.position,
|
||||
&decal.orientation,
|
||||
pm,
|
||||
pmi,
|
||||
decal.submodel,
|
||||
&objp->orient,
|
||||
&objp->pos);
|
||||
|
||||
vec3d worldDir;
|
||||
model_instance_local_to_global_dir(&worldDir,
|
||||
&decal.orientation.vec.fvec,
|
||||
pm,
|
||||
pmi,
|
||||
decal.submodel,
|
||||
&objp->orient);
|
||||
|
||||
// The decal API sees the "direction" of a decal to be along the normal of the surface it is attached to. However,
|
||||
// this will lead to a situation where we would look at the decal texture "from behind" causing the texture to
|
||||
// appear flipped. We fix that here for the graphics transform.
|
||||
vm_vec_scale(&worldDir, -1.0f);
|
||||
|
||||
vec3d worldUp;
|
||||
model_instance_local_to_global_dir(&worldUp,
|
||||
&decal.orientation.vec.uvec,
|
||||
pm,
|
||||
pmi,
|
||||
decal.submodel,
|
||||
&objp->orient);
|
||||
|
||||
matrix worldOrient;
|
||||
vm_vector_2_matrix(&worldOrient, &worldDir, &worldUp);
|
||||
// appear flipped. We fix that here for the graphics transform by inverting the Z scaling.
|
||||
|
||||
// Apply scaling
|
||||
worldOrient.a2d[0][0] *= decal.scale.xyz.x;
|
||||
worldOrient.a2d[0][1] *= decal.scale.xyz.x;
|
||||
worldOrient.a2d[0][2] *= decal.scale.xyz.x;
|
||||
worldOrient.a2d[1][0] *= decal.scale.xyz.y;
|
||||
worldOrient.a2d[1][1] *= decal.scale.xyz.y;
|
||||
worldOrient.a2d[1][2] *= decal.scale.xyz.y;
|
||||
worldOrient.a2d[2][0] *= decal.scale.xyz.z;
|
||||
worldOrient.a2d[2][1] *= decal.scale.xyz.z;
|
||||
worldOrient.a2d[2][2] *= decal.scale.xyz.z;
|
||||
vm_vec_scale(&worldOrient.vec.rvec, decal.scale.xyz.x);
|
||||
vm_vec_scale(&worldOrient.vec.uvec, decal.scale.xyz.y);
|
||||
vm_vec_scale(&worldOrient.vec.fvec, -decal.scale.xyz.z);
|
||||
|
||||
matrix4 mat4;
|
||||
vm_matrix4_set_transform(&mat4, &worldOrient, &worldPos);
|
||||
@@ -455,7 +425,7 @@ matrix4 getDecalTransform(Decal& decal) {
|
||||
}
|
||||
|
||||
void renderAll() {
|
||||
if (!decal_system_active) {
|
||||
if (!Decal_system_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -486,9 +456,9 @@ void renderAll() {
|
||||
int glow_bm = -1;
|
||||
int normal_bm = -1;
|
||||
|
||||
Assertion(decal.definition_handle >= 0 && decal.definition_handle < (int) decalDefinitions.size(),
|
||||
Assertion(decal.definition_handle >= 0 && decal.definition_handle < (int) DecalDefinitions.size(),
|
||||
"Invalid decal handle detected!");
|
||||
auto& decalDef = decalDefinitions[decal.definition_handle];
|
||||
auto& decalDef = DecalDefinitions[decal.definition_handle];
|
||||
|
||||
auto decal_time = mission_time - decal.creation_time;
|
||||
auto progress = decal_time / decal.lifetime;
|
||||
@@ -519,9 +489,9 @@ void renderAll() {
|
||||
|
||||
draw_list.render();
|
||||
}
|
||||
void
|
||||
addDecal(creation_info& info, object* host, int submodel, const vec3d& local_pos, const matrix& local_orient) {
|
||||
if (!decal_system_active) {
|
||||
|
||||
void addDecal(creation_info& info, object* host, int submodel, const vec3d& local_pos, const matrix& local_orient) {
|
||||
if (!Decal_system_active) {
|
||||
return;
|
||||
}
|
||||
// Silently ignore invalid definition handle since weapons use the default values if the decal option is not present
|
||||
@@ -529,9 +499,9 @@ addDecal(creation_info& info, object* host, int submodel, const vec3d& local_pos
|
||||
return;
|
||||
}
|
||||
|
||||
Assertion(info.definition_handle >= 0 && info.definition_handle < (int) decalDefinitions.size(),
|
||||
Assertion(info.definition_handle >= 0 && info.definition_handle < (int) DecalDefinitions.size(),
|
||||
"Invalid decal handle detected!");
|
||||
auto& def = decalDefinitions[info.definition_handle];
|
||||
auto& def = DecalDefinitions[info.definition_handle];
|
||||
|
||||
if (!def.bitmapsLoaded()) {
|
||||
// If this decal was never used before then the bitmaps are not loaded so we need to do that here.
|
||||
@@ -548,9 +518,15 @@ addDecal(creation_info& info, object* host, int submodel, const vec3d& local_pos
|
||||
|
||||
newDecal.position = local_pos;
|
||||
newDecal.orientation = local_orient;
|
||||
newDecal.scale.xyz.x = info.radius;
|
||||
newDecal.scale.xyz.y = info.radius;
|
||||
newDecal.scale.xyz.z = info.radius;
|
||||
if (info.width < 0.0f || info.height < 0.0f) {
|
||||
newDecal.scale.xyz.x = info.radius;
|
||||
newDecal.scale.xyz.y = info.radius;
|
||||
newDecal.scale.xyz.z = info.radius;
|
||||
} else {
|
||||
newDecal.scale.xyz.x = info.width;
|
||||
newDecal.scale.xyz.y = info.height;
|
||||
newDecal.scale.xyz.z = MIN(info.width, info.height);
|
||||
}
|
||||
|
||||
active_decals.push_back(newDecal);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,52 @@
|
||||
|
||||
namespace decals {
|
||||
|
||||
class DecalDefinition {
|
||||
SCP_string _name;
|
||||
|
||||
SCP_string _diffuseFilename;
|
||||
bool _loopDiffuse = true;
|
||||
|
||||
SCP_string _glowFilename;
|
||||
bool _loopGlow = true;
|
||||
|
||||
SCP_string _normalMapFilename;
|
||||
bool _loopNormal = true;
|
||||
|
||||
int _diffuseBitmap = -1;
|
||||
int _glowBitmap = -1;
|
||||
int _normalBitmap = -1;
|
||||
|
||||
public:
|
||||
explicit DecalDefinition(SCP_string name);
|
||||
~DecalDefinition();
|
||||
|
||||
// Disallow copying
|
||||
DecalDefinition(const DecalDefinition&) = delete;
|
||||
DecalDefinition& operator=(const DecalDefinition&) = delete;
|
||||
|
||||
// Move constructor and operator
|
||||
DecalDefinition(DecalDefinition&& other) noexcept;
|
||||
DecalDefinition& operator=(DecalDefinition&& other) noexcept;
|
||||
|
||||
void parse();
|
||||
void loadBitmaps();
|
||||
void pageIn();
|
||||
bool bitmapsLoaded();
|
||||
|
||||
const SCP_string& getName() const;
|
||||
int getDiffuseBitmap() const;
|
||||
int getGlowBitmap() const;
|
||||
int getNormalBitmap() const;
|
||||
bool isDiffuseLooping() const;
|
||||
bool isGlowLooping() const;
|
||||
bool isNormalLooping() const;
|
||||
};
|
||||
|
||||
extern SCP_vector<DecalDefinition> DecalDefinitions;
|
||||
extern bool Decal_system_active;
|
||||
|
||||
|
||||
/**
|
||||
* @brief A reference to a decal definition
|
||||
*/
|
||||
@@ -19,6 +65,8 @@ typedef int DecalReference;
|
||||
struct creation_info {
|
||||
DecalReference definition_handle = -1;
|
||||
float radius = -1.0f;
|
||||
float width = -1.0f;
|
||||
float height = -1.0f;
|
||||
util::UniformFloatRange lifetime = ::util::UniformFloatRange(-1.0f);
|
||||
};
|
||||
|
||||
@@ -28,6 +76,11 @@ struct creation_info {
|
||||
void initialize();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the index of this decal, or -1 if not found
|
||||
*/
|
||||
int findDecalDefinition(const SCP_string& name);
|
||||
|
||||
/**
|
||||
* @brief Parses the information for a decal reference
|
||||
*
|
||||
|
||||
@@ -56,7 +56,7 @@ void GetLightInfo(vec3 position, out vec3 lightDir, out float attenuation)
|
||||
// Positional light source
|
||||
lightDir = lightPosition - position.xyz;
|
||||
float dist = length(lightDir);
|
||||
attenuation = 1.0 - clamp(dist / lightRadius, 0.0, 1.0);
|
||||
attenuation = 1.0 - clamp(sqrt(dist / lightRadius), 0.0, 1.0);
|
||||
|
||||
if(dist > lightRadius && lightType != LT_TUBE) {
|
||||
discard;
|
||||
@@ -81,7 +81,7 @@ void GetLightInfo(vec3 position, out vec3 lightDir, out float attenuation)
|
||||
if(dist > lightRadius) {
|
||||
discard;
|
||||
}
|
||||
attenuation = 1.0 - clamp(dist / lightRadius, 0.0, 1.0);
|
||||
attenuation = 1.0 - clamp(sqrt(dist / lightRadius), 0.0, 1.0);
|
||||
} else if (lightType == LT_CONE) {
|
||||
float coneDot = dot(normalize(-lightDir), coneDir);
|
||||
if(dualCone) {
|
||||
@@ -98,7 +98,7 @@ void GetLightInfo(vec3 position, out vec3 lightDir, out float attenuation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attenuation *= attenuation;
|
||||
lightDir = normalize(lightDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
uniform samplerCube envmap;
|
||||
|
||||
in vec4 fragTexCoord;
|
||||
|
||||
out vec4 fragOut0;
|
||||
|
||||
layout (location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
const float theta = -fragTexCoord.x * (2.0 * M_PI);
|
||||
const float phi = fragTexCoord.y * (M_PI) - (0.5 * M_PI);
|
||||
const float st = sin(theta);
|
||||
const float ct = cos(theta);
|
||||
const float sp = sin(phi);
|
||||
const float cp = cos(phi);
|
||||
const vec3 sampVec = vec3(cp * ct, sp, cp * st);
|
||||
|
||||
fragOut0 = vec4(texture(envmap, sampVec).rgb, 1.0);
|
||||
}
|
||||
@@ -407,10 +407,21 @@ void fireball_parse_tbl()
|
||||
{
|
||||
if (fi.lod_count > 1)
|
||||
{
|
||||
Assertion(fi.lod_count <= MAX_FIREBALL_LOD, "Fireball LOD (%d) greater than MAX_FIREBALL_LOD %d.", fi.lod_count, MAX_FIREBALL_LOD);
|
||||
static_assert(MAX_FIREBALL_LOD < 10, "The fireball LOD naming scheme needs to be changed for LODs > 9");
|
||||
|
||||
auto lod0 = fi.lod[0].filename;
|
||||
constexpr int MAX_BASENAME_LEN = MAX_FILENAME_LEN - 3; // ensure base file name + _*lod* fits in filename field
|
||||
|
||||
if (strlen(lod0) > MAX_BASENAME_LEN)
|
||||
{
|
||||
Warning(LOCATION, "A fireball base file name may not be longer than %d characters due to the LOD naming scheme.\nLODs other than LOD0 will be skipped for fireball %s", MAX_BASENAME_LEN, lod0);
|
||||
fi.lod_count = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 1; j < fi.lod_count; ++j)
|
||||
sprintf(fi.lod[j].filename, "%s_%d", lod0, j);
|
||||
sprintf(fi.lod[j].filename, "%.*s_%d", MAX_BASENAME_LEN, lod0, j % MAX_FIREBALL_LOD /*to show gcc12 format string is safe*/);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+51
-68
@@ -68,8 +68,8 @@ typedef struct tagSNDPATTERN {
|
||||
SNDPATTERN Patterns[MAX_PATTERNS]; // holds data on sections of a SoundTrack
|
||||
|
||||
// Holds filenames for the different sections of a soundtrack
|
||||
SOUNDTRACK_INFO Soundtracks[MAX_SOUNDTRACKS];
|
||||
int Num_soundtracks;
|
||||
SCP_vector<SOUNDTRACK_INFO> Soundtracks;
|
||||
|
||||
int Current_soundtrack_num; // Active soundtrack for the current mission.. index into Soundtracks[]
|
||||
|
||||
int Current_pattern = -1; // currently playing part of track
|
||||
@@ -87,12 +87,6 @@ static int Victory2_music_played;
|
||||
static int Next_arrival_timestamp;
|
||||
static int Check_for_battle_music;
|
||||
|
||||
// stores the number of measures for the different patterns (data from music.tbl)
|
||||
float Pattern_num_measures[MAX_SOUNDTRACKS][MAX_PATTERNS];
|
||||
|
||||
// stores the number of bytes per measure (data from music.tbl)
|
||||
int Pattern_samples_per_measure[MAX_SOUNDTRACKS][MAX_PATTERNS];
|
||||
|
||||
typedef struct pattern_info
|
||||
{
|
||||
const char *pattern_name;
|
||||
@@ -256,11 +250,10 @@ extern int hud_target_invalid_awacs(object *objp);
|
||||
|
||||
// Holds file names of spooled music that is played at menus, briefings, credits etc.
|
||||
// Indexed into by a #define enumeration of the different kinds of spooled music
|
||||
menu_music Spooled_music[MAX_SPOOLED_MUSIC];
|
||||
int Num_music_files; // Number of spooled music files
|
||||
SCP_vector<menu_music> Spooled_music;
|
||||
|
||||
// Array that holds indicies into Spooled_music[], these specify which music is played in briefing/debriefing
|
||||
int Mission_music[NUM_SCORES];
|
||||
int Mission_music[NUM_SCORES];
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
@@ -285,23 +278,11 @@ void event_music_init()
|
||||
return;
|
||||
}
|
||||
|
||||
int i, j;
|
||||
|
||||
//MUST be called before parsing stuffzors.
|
||||
Num_music_files = 0;
|
||||
Num_soundtracks = 0; // Global
|
||||
Soundtracks.clear();
|
||||
Spooled_music.clear();
|
||||
event_music_reset_choices();
|
||||
|
||||
// Goober5000
|
||||
// set all the filenames to "none" so we're compatible with the extra NRMLs in FS1 music
|
||||
memset(Soundtracks, 0, MAX_SOUNDTRACKS * sizeof(SOUNDTRACK_INFO));
|
||||
for (i = 0; i < MAX_SOUNDTRACKS; i++)
|
||||
for (j = 0; j < MAX_PATTERNS; j++)
|
||||
strcpy_s(Soundtracks[i].pattern_fnames[j], NOX("none.wav"));
|
||||
|
||||
// Goober5000
|
||||
memset(Spooled_music, 0, MAX_SPOOLED_MUSIC * sizeof(menu_music));
|
||||
|
||||
//Do teh parsing
|
||||
event_music_parse_musictbl("music.tbl");
|
||||
|
||||
@@ -567,9 +548,9 @@ void event_music_level_start(int force_soundtrack)
|
||||
return;
|
||||
}
|
||||
|
||||
Assert(Current_soundtrack_num >= 0 && Current_soundtrack_num < Num_soundtracks);
|
||||
Assert(Current_soundtrack_num >= 0 && Current_soundtrack_num < (int)Soundtracks.size());
|
||||
|
||||
if (Current_soundtrack_num < 0 || Current_soundtrack_num > Num_soundtracks)
|
||||
if (Current_soundtrack_num < 0 || Current_soundtrack_num > (int)Soundtracks.size())
|
||||
return;
|
||||
|
||||
strack = &Soundtracks[Current_soundtrack_num];
|
||||
@@ -582,13 +563,13 @@ void event_music_level_start(int force_soundtrack)
|
||||
// tracks because their patterns weren't -1
|
||||
for (i = 0; i < MAX_PATTERNS; i++)
|
||||
{
|
||||
if (!strnicmp(strack->pattern_fnames[i], NOX("none.wav"), 4))
|
||||
if (!strnicmp(strack->patterns[i].fname, NOX("none.wav"), 4))
|
||||
{
|
||||
Patterns[i].handle = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
Patterns[i].handle = audiostream_open( strack->pattern_fnames[i], ASF_EVENTMUSIC );
|
||||
Patterns[i].handle = audiostream_open( strack->patterns[i].fname, ASF_EVENTMUSIC );
|
||||
|
||||
if (Patterns[i].handle >= 0)
|
||||
{
|
||||
@@ -609,8 +590,8 @@ void event_music_level_start(int force_soundtrack)
|
||||
Patterns[i].loop_for = Patterns[i].default_loop_for;
|
||||
Patterns[i].force_pattern = FALSE;
|
||||
Patterns[i].can_force = pip->pattern_can_force;
|
||||
Patterns[i].samples_per_measure = Pattern_samples_per_measure[Current_soundtrack_num][i];
|
||||
Patterns[i].num_measures = Pattern_num_measures[Current_soundtrack_num][i];
|
||||
Patterns[i].samples_per_measure = strack->patterns[i].samples_per_measure;
|
||||
Patterns[i].num_measures = strack->patterns[i].num_measures;
|
||||
}
|
||||
|
||||
Num_enemy_arrivals = 0;
|
||||
@@ -679,6 +660,18 @@ void event_music_first_pattern()
|
||||
// this yields a relative start time of 1 second. Mission start will now use relative rather than absolute delay.
|
||||
void event_music_set_start_delay()
|
||||
{
|
||||
if (Event_music_inited == FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Event_music_enabled == FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Event_music_level_inited == FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pattern_timer_id = timestamp(1000);
|
||||
}
|
||||
|
||||
@@ -694,11 +687,8 @@ void event_music_level_close()
|
||||
if ( Event_music_level_inited == FALSE )
|
||||
return;
|
||||
|
||||
if ( Current_soundtrack_num >= 0 && Current_soundtrack_num < MAX_SOUNDTRACKS ) {
|
||||
SOUNDTRACK_INFO *strack;
|
||||
|
||||
Assert( Current_soundtrack_num >= 0 && Current_soundtrack_num < MAX_SOUNDTRACKS );
|
||||
strack = &Soundtracks[Current_soundtrack_num];
|
||||
if ( Current_soundtrack_num >= 0 && Current_soundtrack_num < (int)Soundtracks.size() ) {
|
||||
auto strack = &Soundtracks[Current_soundtrack_num];
|
||||
|
||||
// close the pattern files
|
||||
for ( i = 0; i < strack->num_patterns; i++ ) {
|
||||
@@ -1156,16 +1146,16 @@ bool parse_soundtrack_line(int strack_idx, int pattern_idx)
|
||||
}
|
||||
|
||||
|
||||
if ( count == 0 ) {
|
||||
Pattern_num_measures[strack_idx][pattern_idx] = (float)atof(token); //Num_measures
|
||||
} else if(count == 1) {
|
||||
Pattern_samples_per_measure[strack_idx][pattern_idx] = atoi(token); //Samples per measure
|
||||
if (count == 0) {
|
||||
Soundtracks[strack_idx].patterns[pattern_idx].num_measures = (float)atof(token); //Num_measures
|
||||
} else if (count == 1) {
|
||||
Soundtracks[strack_idx].patterns[pattern_idx].samples_per_measure = atoi(token); //Samples per measure
|
||||
}
|
||||
|
||||
count++;
|
||||
} // end while
|
||||
|
||||
strcpy_s(Soundtracks[strack_idx].pattern_fnames[pattern_idx], fname);
|
||||
strcpy_s(Soundtracks[strack_idx].patterns[pattern_idx].fname, fname);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1189,28 +1179,27 @@ void parse_soundtrack()
|
||||
}
|
||||
|
||||
//Get a valid strack_idx
|
||||
if(strack_idx < 0 && (nocreate || Num_soundtracks >= MAX_SOUNDTRACKS))
|
||||
if (strack_idx < 0 && nocreate)
|
||||
{
|
||||
if(Num_soundtracks >= MAX_SOUNDTRACKS) {
|
||||
Warning(LOCATION, "Maximum number of soundtracks reached after '%s'; max is '%d'", Soundtracks[MAX_SOUNDTRACKS-1].name, MAX_SOUNDTRACKS);
|
||||
}
|
||||
|
||||
//Track doesn't exist and has nocreate, so don't create it
|
||||
if ( !skip_to_start_of_string_either("#SoundTrack Start", "#Menu Music Start") && !skip_to_string("#SoundTrack End")) {
|
||||
error_display(1, "Couldn't find #Soundtrack Start, #Menu Music Start or #Soundtrack End.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if(strack_idx < 0)
|
||||
else if (strack_idx < 0)
|
||||
{
|
||||
//If we don't have this soundtrack already, create it
|
||||
strack_idx = Num_soundtracks;
|
||||
strack_idx = (int)Soundtracks.size();
|
||||
Soundtracks.emplace_back();
|
||||
|
||||
strcpy_s(Soundtracks[strack_idx].name, namebuf);
|
||||
Soundtracks[strack_idx].flags = 0;
|
||||
Soundtracks[strack_idx].num_patterns = 0;
|
||||
strcpy_s(Soundtracks[strack_idx].name, namebuf);
|
||||
|
||||
Num_soundtracks++;
|
||||
// set all the filenames to "none" so we're compatible with the extra NRMLs in FS1 music
|
||||
for (auto &pattern : Soundtracks[strack_idx].patterns)
|
||||
strcpy_s(pattern.fname, NOX("none.wav"));
|
||||
}
|
||||
|
||||
// Goober5000
|
||||
@@ -1301,11 +1290,11 @@ void parse_soundtrack()
|
||||
for (i = 0; i < Soundtracks[strack_idx].num_patterns; i++)
|
||||
{
|
||||
// check for "none"
|
||||
if (!strlen(Soundtracks[strack_idx].pattern_fnames[i]) || !strnicmp(Soundtracks[strack_idx].pattern_fnames[i], "none", 4))
|
||||
if (!strlen(Soundtracks[strack_idx].patterns[i].fname) || !strnicmp(Soundtracks[strack_idx].patterns[i].fname, "none", 4))
|
||||
continue;
|
||||
|
||||
// check for file
|
||||
if (!cf_exists_full(Soundtracks[strack_idx].pattern_fnames[i], CF_TYPE_MUSIC))
|
||||
if (!cf_exists_full_ext(Soundtracks[strack_idx].patterns[i].fname, CF_TYPE_MUSIC, NUM_AUDIO_EXT, audio_ext_list))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1328,22 +1317,19 @@ void parse_menumusic()
|
||||
|
||||
int idx = event_music_get_spooled_music_index(spoolname);
|
||||
|
||||
if(idx < 0 && (nocreate || Num_music_files >= MAX_SPOOLED_MUSIC))
|
||||
if (idx < 0 && nocreate)
|
||||
{
|
||||
if(Num_music_files >= MAX_SPOOLED_MUSIC) {
|
||||
Warning(LOCATION, "Could not load spooled music file after '%s' as maximum number of spooled music was reached (Max is %d)", Spooled_music[Num_music_files - 1].name, MAX_SPOOLED_MUSIC);
|
||||
}
|
||||
|
||||
if (!skip_to_start_of_string_either("$Name:", "#Menu Music End")) {
|
||||
Error(LOCATION, "Couldn't find $Name or #Menu Music End. Music.tbl or -mus.tbm is invalid.\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if(idx < 0)
|
||||
else if (idx < 0)
|
||||
{
|
||||
idx = Num_music_files;
|
||||
idx = (int)Spooled_music.size();
|
||||
Spooled_music.emplace_back();
|
||||
|
||||
Spooled_music[idx].flags = 0;
|
||||
strcpy_s( Spooled_music[idx].name, spoolname );
|
||||
strcpy_s( Spooled_music[idx].filename, "");
|
||||
}
|
||||
@@ -1366,9 +1352,6 @@ void parse_menumusic()
|
||||
// chief1983 - use type list defined in ffmpeg.h
|
||||
if ( cf_exists_full_ext(Spooled_music[idx].filename, CF_TYPE_MUSIC, NUM_AUDIO_EXT, audio_ext_list) )
|
||||
Spooled_music[idx].flags |= SMF_VALID;
|
||||
|
||||
if (!nocreate)
|
||||
Num_music_files++;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
@@ -1617,7 +1600,7 @@ int event_music_next_soundtrack(int delta)
|
||||
}
|
||||
|
||||
new_soundtrack = Current_soundtrack_num + delta;
|
||||
if ( new_soundtrack >= Num_soundtracks )
|
||||
if ( new_soundtrack >= (int)Soundtracks.size() )
|
||||
new_soundtrack = 0;
|
||||
|
||||
event_music_level_close();
|
||||
@@ -1633,7 +1616,7 @@ void event_sexp_change_soundtrack(const char *name)
|
||||
|
||||
int i, new_soundtrack = -1;
|
||||
|
||||
for (i = 0; i < Num_soundtracks; i++) {
|
||||
for (i = 0; i < (int)Soundtracks.size(); i++) {
|
||||
if ( !stricmp(name, Soundtracks[i].name) ) {
|
||||
new_soundtrack = i;
|
||||
}
|
||||
@@ -1677,7 +1660,7 @@ void event_music_set_soundtrack(const char *name)
|
||||
int event_music_get_soundtrack_index(const char *name)
|
||||
{
|
||||
// find the correct index for the event music
|
||||
for ( int i = 0; i < Num_soundtracks; i++ ) {
|
||||
for ( int i = 0; i < (int)Soundtracks.size(); i++ ) {
|
||||
if ( !stricmp(name, Soundtracks[i].name) ) {
|
||||
return i;
|
||||
}
|
||||
@@ -1689,7 +1672,7 @@ int event_music_get_soundtrack_index(const char *name)
|
||||
int event_music_get_spooled_music_index(const char *name)
|
||||
{
|
||||
// find the correct index for the event music
|
||||
for ( int i = 0; i < Num_music_files; i++ ) {
|
||||
for ( int i = 0; i < (int)Spooled_music.size(); i++ ) {
|
||||
if ( !stricmp(name, Spooled_music[i].name) ) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -67,32 +67,30 @@ typedef struct menu_music {
|
||||
char filename[MAX_FILENAME_LEN]; // name music is stored on disk as
|
||||
} menu_music;
|
||||
|
||||
#define MAX_SPOOLED_MUSIC 50 // max number of briefing/mainhall/credits tracks
|
||||
|
||||
// Goober5000 - spooled music flags
|
||||
#define SMF_VALID (1 << 0)
|
||||
|
||||
extern menu_music Spooled_music[MAX_SPOOLED_MUSIC];
|
||||
extern int Num_music_files;
|
||||
extern SCP_vector<menu_music> Spooled_music;
|
||||
|
||||
|
||||
// event music soundtrack storage
|
||||
typedef struct tagSOUNDTRACK_INFO {
|
||||
int flags;
|
||||
int num_patterns;
|
||||
char name[NAME_LENGTH];
|
||||
char pattern_fnames[MAX_PATTERNS][MAX_FILENAME_LEN];
|
||||
char name[NAME_LENGTH];
|
||||
struct {
|
||||
char fname[MAX_FILENAME_LEN];
|
||||
float num_measures;
|
||||
int samples_per_measure;
|
||||
} patterns[MAX_PATTERNS];
|
||||
} SOUNDTRACK_INFO;
|
||||
|
||||
#define MAX_SOUNDTRACKS 30 // max number of battle tracks
|
||||
|
||||
// Goober5000 - event music flags
|
||||
#define EMF_VALID (1 << 0)
|
||||
#define EMF_ALLIED_ARRIVAL_OVERLAY (1 << 1)
|
||||
#define EMF_CYCLE_FS1 (1 << 2)
|
||||
|
||||
extern SOUNDTRACK_INFO Soundtracks[MAX_SOUNDTRACKS];
|
||||
extern int Num_soundtracks;
|
||||
extern SCP_vector<SOUNDTRACK_INFO> Soundtracks;
|
||||
|
||||
|
||||
void event_music_init();
|
||||
|
||||
@@ -823,7 +823,7 @@ void parse_gamesnd_soundset(game_snd* gs, bool no_create) {
|
||||
int temp_limit;
|
||||
stuff_int(&temp_limit);
|
||||
|
||||
if ((temp_limit > 0) && (static_cast<uint>(temp_limit) <= SND_ENHANCED_MAX_LIMIT))
|
||||
if (temp_limit > 0)
|
||||
{
|
||||
gs->enhanced_sound_data.limit = (unsigned int)temp_limit;
|
||||
}
|
||||
@@ -927,7 +927,7 @@ void parse_gamesnd_new(game_snd* gs, bool no_create)
|
||||
int temp_limit;
|
||||
stuff_int(&temp_limit);
|
||||
|
||||
if ((temp_limit > 0) && (static_cast<uint>(temp_limit) <= SND_ENHANCED_MAX_LIMIT))
|
||||
if (temp_limit > 0)
|
||||
{
|
||||
gs->enhanced_sound_data.limit = (unsigned int)temp_limit;
|
||||
}
|
||||
|
||||
+161
-1
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <utility> // for std::move
|
||||
|
||||
#ifndef _LINKLIST_H
|
||||
#define _LINKLIST_H
|
||||
@@ -83,4 +83,164 @@ do { \
|
||||
#define NOT_EMPTY(head) ((head)->next != (head))
|
||||
#define EMPTY(head) ((head) == nullptr || (head)->next == (head))
|
||||
|
||||
// Note that since these iterators operate on pointer collections, the dereference of an iterator using operator* returns a pointer.
|
||||
template <class T>
|
||||
class volition_linked_list
|
||||
{
|
||||
T* head;
|
||||
|
||||
public:
|
||||
class iterator
|
||||
{
|
||||
T* ptr;
|
||||
|
||||
public:
|
||||
iterator(T* x)
|
||||
: ptr(x)
|
||||
{}
|
||||
|
||||
iterator(const iterator& it)
|
||||
: ptr(it.ptr)
|
||||
{}
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
ptr = GET_NEXT(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int)
|
||||
{
|
||||
iterator tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
iterator& operator--()
|
||||
{
|
||||
ptr = GET_PREV(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator--(int)
|
||||
{
|
||||
iterator tmp(*this);
|
||||
operator--();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const iterator& rhs) const
|
||||
{
|
||||
return ptr == rhs.ptr;
|
||||
|
||||
}
|
||||
|
||||
bool operator!=(const iterator& rhs) const
|
||||
{
|
||||
return ptr != rhs.ptr;
|
||||
}
|
||||
|
||||
T*& operator*()
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
|
||||
T* operator->()
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
class const_iterator
|
||||
{
|
||||
const T* ptr;
|
||||
|
||||
public:
|
||||
const_iterator(const T* x)
|
||||
: ptr(x)
|
||||
{}
|
||||
|
||||
const_iterator(const const_iterator& it)
|
||||
: ptr(it.ptr)
|
||||
{}
|
||||
|
||||
const_iterator& operator++()
|
||||
{
|
||||
ptr = GET_NEXT(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator operator++(int)
|
||||
{
|
||||
const_iterator tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
const_iterator& operator--()
|
||||
{
|
||||
ptr = GET_PREV(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator operator--(int)
|
||||
{
|
||||
const_iterator tmp(*this);
|
||||
operator--();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const const_iterator& rhs) const
|
||||
{
|
||||
return ptr == rhs.ptr;
|
||||
|
||||
}
|
||||
|
||||
bool operator!=(const const_iterator& rhs) const
|
||||
{
|
||||
return ptr != rhs.ptr;
|
||||
}
|
||||
|
||||
const T*& operator*()
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
|
||||
const T* operator->()
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin() const
|
||||
{
|
||||
return iterator(GET_FIRST(head));
|
||||
}
|
||||
|
||||
iterator end() const
|
||||
{
|
||||
return iterator(END_OF_LIST(head));
|
||||
}
|
||||
|
||||
const_iterator cbegin() const
|
||||
{
|
||||
return const_iterator(GET_FIRST(head));
|
||||
}
|
||||
|
||||
const_iterator cend() const
|
||||
{
|
||||
return const_iterator(END_OF_LIST(head));
|
||||
}
|
||||
|
||||
volition_linked_list(T* ptr)
|
||||
: head(ptr)
|
||||
{}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
volition_linked_list<T> list_range(T* head)
|
||||
{
|
||||
return volition_linked_list<T>(head);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -114,7 +114,8 @@ typedef struct vec2d {
|
||||
} 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.
|
||||
// respectively and are performed in that order. (Strictly speaking these are Tait-Bryan angles,
|
||||
// not classic Euler angles.)
|
||||
typedef struct angles {
|
||||
float p, b, h;
|
||||
} angles_t;
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
#define SCP_TOKEN_CONCAT1(x, y) x ## y
|
||||
#define SCP_TOKEN_CONCAT(x, y) SCP_TOKEN_CONCAT1(x, y)
|
||||
|
||||
// these are useful for embedding numbers in strings
|
||||
// see https://stackoverflow.com/questions/5459868/concatenate-int-to-string-using-c-preprocessor
|
||||
#define SCP_TOKEN_TO_STR1(x) #x
|
||||
#define SCP_TOKEN_TO_STR(x) SCP_TOKEN_TO_STR1(x)
|
||||
|
||||
#define SCP_UNUSED(x) (void)(x)
|
||||
|
||||
#endif // SCP_DEFINES_H
|
||||
@@ -427,46 +427,6 @@ DCF(detail, "Turns on/off parts of the game for speed testing" )
|
||||
}
|
||||
#endif
|
||||
|
||||
// Goober5000
|
||||
// (Taylor says that for optimization purposes malloc/free should be used rather than vm_malloc/vm_free here)
|
||||
// NOTE: Because this uses memcpy, it should only be used to sort POD elements!
|
||||
void insertion_sort(void *array_base, size_t array_size, size_t element_size, int (*fncompare)(const void *, const void *))
|
||||
{
|
||||
int i, j;
|
||||
void *current;
|
||||
char *array_byte_base;
|
||||
|
||||
// this is used to avoid having to cast array_base to (char *) all the time
|
||||
array_byte_base = (char *) array_base;
|
||||
|
||||
// allocate space for the element being moved
|
||||
current = malloc(element_size);
|
||||
if (current == NULL)
|
||||
{
|
||||
Int3();
|
||||
return;
|
||||
}
|
||||
|
||||
// loop
|
||||
for (i = 1; (unsigned) i < array_size; i++)
|
||||
{
|
||||
// grab the current element
|
||||
memcpy(current, array_byte_base + (i * element_size), element_size);
|
||||
|
||||
// bump other elements toward the end of the array
|
||||
for (j = i - 1; (j >= 0) && (fncompare(array_byte_base + (j * element_size), current) > 0); j--)
|
||||
{
|
||||
memcpy(array_byte_base + ((j + 1) * element_size), array_byte_base + (j * element_size), element_size);
|
||||
}
|
||||
|
||||
// insert the current element at the correct place
|
||||
memcpy(array_byte_base + ((j + 1) * element_size), current, element_size);
|
||||
}
|
||||
|
||||
// free the allocated space
|
||||
free(current);
|
||||
}
|
||||
|
||||
// Stuff that can't be included in vmallocator.h
|
||||
|
||||
std::locale SCP_default_locale("");
|
||||
|
||||
@@ -31,23 +31,23 @@
|
||||
#define GM_CAMPAIGN_MODE (1 << 10) // are we currently in a campaign.
|
||||
#define GM_LAB (1 << 11) // We are currently in the F3 lab
|
||||
|
||||
#define VM_EXTERNAL (1 << 0) // Set if not viewing from player position.
|
||||
#define VM_TRACK (1 << 1) // Set if viewer is tracking target.
|
||||
#define VM_DEAD_VIEW (1 << 2) // Set if viewer is watching from dead view.
|
||||
#define VM_CHASE (1 << 3) // Chase view.
|
||||
#define VM_OTHER_SHIP (1 << 4) // View from another ship.
|
||||
#define VM_CAMERA_LOCKED (1 << 5) // Set if player does not have control of the camera
|
||||
#define VM_WARP_CHASE (1 << 6) // View while warping out (form normal view mode)
|
||||
#define VM_PADLOCK_UP (1 << 7)
|
||||
#define VM_PADLOCK_REAR (1 << 8)
|
||||
#define VM_PADLOCK_LEFT (1 << 9)
|
||||
#define VM_PADLOCK_RIGHT (1 << 10)
|
||||
#define VM_WARPIN_ANCHOR (1 << 11) // special warpin camera mode
|
||||
#define VM_TOPDOWN (1 << 12) //Camera is looking down on ship
|
||||
#define VM_FREECAMERA (1 << 13) //Camera is not attached to any particular object, probably under SEXP control
|
||||
#define VM_CENTERING (1 << 14) // View is springing to center
|
||||
#define VM_EXTERNAL (1 << 0) // Set if not viewing from player position.
|
||||
#define VM_TRACK (1 << 1) // Set if viewer is tracking target.
|
||||
#define VM_DEAD_VIEW (1 << 2) // Set if viewer is watching from dead view.
|
||||
#define VM_CHASE (1 << 3) // Chase view.
|
||||
#define VM_OTHER_SHIP (1 << 4) // View from another ship.
|
||||
#define VM_CAMERA_LOCKED (1 << 5) // Set if player does not have control of the camera
|
||||
#define VM_WARP_CHASE (1 << 6) // View while warping out (form normal view mode)
|
||||
#define VM_PADLOCK_UP (1 << 7) // Set when player is looking up
|
||||
#define VM_PADLOCK_REAR (1 << 8) // Set when player is looking behind
|
||||
#define VM_PADLOCK_LEFT (1 << 9) // Set when player is looking left
|
||||
#define VM_PADLOCK_RIGHT (1 << 10) // Set when player is looking right
|
||||
#define VM_WARPIN_ANCHOR (1 << 11) // special warpin camera mode
|
||||
#define VM_TOPDOWN (1 << 12) // Camera is looking down on ship
|
||||
#define VM_FREECAMERA (1 << 13) // Camera is not attached to any particular object, probably under SEXP control
|
||||
#define VM_CENTERING (1 << 14) // View is springing to center
|
||||
|
||||
#define VM_PADLOCK_ANY (VM_PADLOCK_UP|VM_PADLOCK_REAR|VM_PADLOCK_LEFT|VM_PADLOCK_RIGHT)
|
||||
#define VM_PADLOCK_ANY (VM_PADLOCK_UP | VM_PADLOCK_REAR | VM_PADLOCK_LEFT | VM_PADLOCK_RIGHT)
|
||||
|
||||
//-----Cutscene stuff
|
||||
//No bars
|
||||
@@ -213,7 +213,58 @@ int current_detail_level();
|
||||
|
||||
|
||||
// Goober5000
|
||||
void insertion_sort(void *array, size_t array_size, size_t element_size, int (*fncompare)(const void *, const void *));
|
||||
// A sort for use with small or almost-sorted lists. Iteration time is O(n) for a fully-sorted list.
|
||||
// This uses a type-safe version of the function prototype for stdlib's qsort, although the size is an int rather than a size_t (for the reasons that j is an int).
|
||||
// The fncompare function should return <0, 0, or >0 as the left item is less than, equal to, or greater than the right item.
|
||||
template <typename T>
|
||||
void insertion_sort(T* array_base, int array_size, int (*fncompare)(const T*, const T*))
|
||||
{
|
||||
// NOTE: j *must* be a signed type because j reaches -1 and j+1 must be 0.
|
||||
int i, j;
|
||||
T *current, *current_buf;
|
||||
|
||||
// allocate space for the element being moved
|
||||
// (Taylor says that for optimization purposes malloc/free should be used rather than vm_malloc/vm_free here)
|
||||
current_buf = (T*)malloc(sizeof(T));
|
||||
if (current_buf == nullptr)
|
||||
{
|
||||
UNREACHABLE("Malloc failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
// loop
|
||||
for (i = 1; i < array_size; i++)
|
||||
{
|
||||
// grab the current element
|
||||
// this does a lazy move/copy because if the array is mostly sorted,
|
||||
// there's no sense copying sorted items to their own places
|
||||
bool lazily_copied = false;
|
||||
current = &array_base[i];
|
||||
|
||||
// bump other elements toward the end of the array
|
||||
for (j = i - 1; (j >= 0) && (fncompare(&array_base[j], current) > 0); j--)
|
||||
{
|
||||
if (!lazily_copied)
|
||||
{
|
||||
// this may look strange but it is just copying the data
|
||||
// into the buffer, then pointing to the buffer
|
||||
*current_buf = std::move(*current);
|
||||
current = current_buf;
|
||||
lazily_copied = true;
|
||||
}
|
||||
|
||||
array_base[j + 1] = std::move(array_base[j]);
|
||||
}
|
||||
|
||||
if (lazily_copied)
|
||||
{
|
||||
// insert the current element at the correct place
|
||||
array_base[j + 1] = std::move(*current);
|
||||
}
|
||||
}
|
||||
|
||||
// free the allocated space
|
||||
free(current_buf);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -313,11 +313,8 @@ void Add_Symbol( int section, int offset, const char *name, char *module )
|
||||
|
||||
}
|
||||
|
||||
int Sym_compare( const void *arg1, const void *arg2 )
|
||||
int Sym_compare( const MemSymbol *sym1, const MemSymbol *sym2 )
|
||||
{
|
||||
MemSymbol * sym1 = (MemSymbol *)arg1;
|
||||
MemSymbol * sym2 = (MemSymbol *)arg2;
|
||||
|
||||
if ( sym1->section < sym2->section ) {
|
||||
return -1;
|
||||
} else if ( sym1->section > sym2->section ) {
|
||||
@@ -331,11 +328,8 @@ int Sym_compare( const void *arg1, const void *arg2 )
|
||||
}
|
||||
}
|
||||
|
||||
int Sym_compare1( const void *arg1, const void *arg2 )
|
||||
int Sym_compare1( const MemSymbol *sym1, const MemSymbol *sym2 )
|
||||
{
|
||||
MemSymbol * sym1 = (MemSymbol *)arg1;
|
||||
MemSymbol * sym2 = (MemSymbol *)arg2;
|
||||
|
||||
if ( sym1->size < sym2->size ) {
|
||||
return 1;
|
||||
} else if ( sym1->size > sym2->size ) {
|
||||
@@ -349,7 +343,7 @@ void DumpSymbols()
|
||||
{
|
||||
int i;
|
||||
|
||||
insertion_sort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare );
|
||||
insertion_sort( Symbols, Num_symbols, Sym_compare );
|
||||
|
||||
for (i=0;i<Num_symbols; i++ ) {
|
||||
MemSymbol * sym1 = &Symbols[i];
|
||||
@@ -361,7 +355,7 @@ void DumpSymbols()
|
||||
}
|
||||
}
|
||||
|
||||
insertion_sort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare1 );
|
||||
insertion_sort( Symbols, Num_symbols, Sym_compare1 );
|
||||
|
||||
|
||||
FILE *fp = fopen( "dump", "wt" );
|
||||
|
||||
+56
-20
@@ -1435,7 +1435,7 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
|
||||
auto res = ResolutionOption->getValue();
|
||||
width = res.width;
|
||||
height = res.height;
|
||||
} else {
|
||||
} else if ( !Is_standalone ) {
|
||||
// We cannot continue without this, quit, but try to help the user out first
|
||||
ptr = os_config_read_string(nullptr, NOX("VideocardFs2open"), nullptr);
|
||||
|
||||
@@ -1540,6 +1540,15 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
|
||||
depth = d_depth;
|
||||
}
|
||||
|
||||
// if we are in standalone mode then just use special defaults
|
||||
if (Is_standalone) {
|
||||
mode = GR_STUB;
|
||||
width = 640;
|
||||
height = 480;
|
||||
depth = 16;
|
||||
center_aspect_ratio = -1.0f;
|
||||
}
|
||||
|
||||
if (gr_get_resolution_class(width, height) != GR_640) {
|
||||
// check for hi-res interface files so that we can verify our width/height is correct
|
||||
// if we don't have it then fall back to 640x480 mode instead
|
||||
@@ -1556,15 +1565,6 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
|
||||
}
|
||||
}
|
||||
|
||||
// if we are in standalone mode then just use special defaults
|
||||
if (Is_standalone) {
|
||||
mode = GR_STUB;
|
||||
width = 640;
|
||||
height = 480;
|
||||
depth = 16;
|
||||
center_aspect_ratio = -1.0f;
|
||||
}
|
||||
|
||||
// These compiler macros will force windowed mode at the specified resolution if
|
||||
// built in debug mode. This helps if you run with the debugger active as the
|
||||
// game won't be switching from fullscreen to minimized every time you hit a breakpoint or
|
||||
@@ -1616,14 +1616,12 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
|
||||
mprintf(("Initializing path renderer...\n"));
|
||||
graphics::paths::PathRenderer::init();
|
||||
|
||||
if ( !Is_standalone ) {
|
||||
gr_light_init();
|
||||
gr_light_init();
|
||||
|
||||
// Initialize uniform buffer managers
|
||||
uniform_buffer_managers_init();
|
||||
// Initialize uniform buffer managers
|
||||
uniform_buffer_managers_init();
|
||||
|
||||
gpu_heap_init();
|
||||
}
|
||||
gpu_heap_init();
|
||||
|
||||
mprintf(("Checking graphics capabilities:\n"));
|
||||
mprintf((" Persistent buffer mapping: %s\n",
|
||||
@@ -2534,6 +2532,10 @@ void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float a
|
||||
|
||||
static void output_uniform_debug_data()
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
int line_height = gr_get_font_height() + 1;
|
||||
|
||||
gr_set_color_fast(&Color_bright_white);
|
||||
@@ -2555,7 +2557,9 @@ void gr_flip(bool execute_scripting)
|
||||
TRACE_SCOPE(tracing::LuaOnFrame);
|
||||
|
||||
// WMC - Do conditional hooks. Yippee!
|
||||
OnFrameHook->run();
|
||||
if (OnFrameHook->isActive()) {
|
||||
OnFrameHook->run();
|
||||
}
|
||||
// WMC - Do scripting reset stuff
|
||||
Script_system.EndFrame();
|
||||
}
|
||||
@@ -2590,9 +2594,33 @@ void gr_print_timestamp(int x, int y, fix timestamp, int resize_mode)
|
||||
|
||||
gr_string(x, y, time.c_str(), resize_mode);
|
||||
}
|
||||
static void uniform_buffer_managers_init() { UniformBufferManager.reset(new graphics::util::UniformBufferManager()); }
|
||||
static void uniform_buffer_managers_deinit() { UniformBufferManager.reset(); }
|
||||
static void uniform_buffer_managers_retire_buffers() { UniformBufferManager->onFrameEnd(); }
|
||||
|
||||
static void uniform_buffer_managers_init()
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
UniformBufferManager.reset(new graphics::util::UniformBufferManager());
|
||||
}
|
||||
|
||||
static void uniform_buffer_managers_deinit()
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
UniformBufferManager.reset();
|
||||
}
|
||||
|
||||
static void uniform_buffer_managers_retire_buffers()
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
UniformBufferManager->onFrameEnd();
|
||||
}
|
||||
|
||||
graphics::util::UniformBuffer gr_get_uniform_buffer(uniform_block_type type, size_t num_elements, size_t element_size_override)
|
||||
{
|
||||
@@ -2727,6 +2755,10 @@ size_t vertex_layout::hash() const {
|
||||
static std::unique_ptr<graphics::util::GPUMemoryHeap> gpu_heaps [static_cast<size_t>(GpuHeap::NUM_VALUES)];
|
||||
|
||||
static void gpu_heap_init() {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(GpuHeap::NUM_VALUES); ++i) {
|
||||
auto enumVal = static_cast<GpuHeap>(i);
|
||||
|
||||
@@ -2822,6 +2854,10 @@ static void make_gamma_ramp(float gamma, ushort* ramp)
|
||||
|
||||
void gr_set_gamma(float gamma)
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
Gr_gamma = gamma;
|
||||
|
||||
// new way - but not while running FRED
|
||||
|
||||
@@ -204,6 +204,8 @@ enum shader_type {
|
||||
SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT,
|
||||
SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING,
|
||||
|
||||
SDR_TYPE_ENVMAP_SPHERE_WARP,
|
||||
|
||||
NUM_SHADER_TYPES
|
||||
};
|
||||
|
||||
@@ -713,6 +715,9 @@ typedef struct screen {
|
||||
// dumps the current screen to a file
|
||||
std::function<void(const char* filename)> gf_print_screen;
|
||||
|
||||
// transforms and dumps the current environment map to a file
|
||||
std::function<void(const char* filename)> gf_dump_envmap;
|
||||
|
||||
// Retrieves the zbuffer mode.
|
||||
std::function<int()> gf_zbuffer_get;
|
||||
|
||||
@@ -997,6 +1002,7 @@ extern void gr_activate(int active);
|
||||
// old Descent-style gr_xxx calls.
|
||||
|
||||
#define gr_print_screen GR_CALL(gr_screen.gf_print_screen)
|
||||
#define gr_dump_envmap GR_CALL(gr_screen.gf_dump_envmap)
|
||||
|
||||
//#define gr_flip GR_CALL(gr_screen.gf_flip)
|
||||
void gr_flip(bool execute_scripting = true);
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
#include "graphics/color.h"
|
||||
#include "graphics/2d.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/vmallocator.h"
|
||||
#include "math/floating.h"
|
||||
|
||||
void hdr_color::set_vecf(const SCP_vector<float>& input)
|
||||
{
|
||||
size_t l = input.size();
|
||||
Assertion(l >= 3, "attempted to set color values invalid component count");
|
||||
this->red = input.at(0);
|
||||
this->green = input.at(1);
|
||||
this->blue = input.at(2);
|
||||
if (l > 3)
|
||||
this->alpha = input.at(3);
|
||||
if (l > 4)
|
||||
this->intensity = input.at(4);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Fills a vector with the raw float color components of the color, in the order red, green, blue, alpha,
|
||||
* intensity
|
||||
*
|
||||
* @param to_fill: The fector that will be filled with the color compoents
|
||||
*/
|
||||
void hdr_color::get_v5f(SCP_vector<float>& to_fill) const
|
||||
{
|
||||
to_fill.clear();
|
||||
to_fill.push_back(this->red);
|
||||
to_fill.push_back(this->green);
|
||||
to_fill.push_back(this->blue);
|
||||
to_fill.push_back(this->alpha);
|
||||
to_fill.push_back(this->intensity);
|
||||
}
|
||||
|
||||
hdr_color::hdr_color()
|
||||
{
|
||||
this->red = 1.0f;
|
||||
this->green = 1.0f;
|
||||
this->blue = 1.0f;
|
||||
this->alpha = 1.0f;
|
||||
this->intensity = 1.0f;
|
||||
}
|
||||
|
||||
hdr_color::hdr_color(float new_r, float new_g, float new_b, float new_a, float new_i)
|
||||
{
|
||||
this->red = new_r;
|
||||
this->green = new_g;
|
||||
this->blue = new_b;
|
||||
this->alpha = new_a;
|
||||
this->intensity = new_i;
|
||||
}
|
||||
|
||||
hdr_color::hdr_color(const hdr_color* const source_color)
|
||||
{
|
||||
this->red = source_color->red;
|
||||
this->green = source_color->green;
|
||||
this->blue = source_color->blue;
|
||||
this->alpha = source_color->alpha;
|
||||
this->intensity = source_color->intensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets RGB values from three 0-255 ints
|
||||
*/
|
||||
void hdr_color::set_rgb(int new_r, int new_g, int new_b)
|
||||
{
|
||||
this->red = i2fl(new_r) / 255.0f;
|
||||
this->green = i2fl(new_g) / 255.0f;
|
||||
this->blue = i2fl(new_b) / 255.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets RGBA values from an old style color object
|
||||
*/
|
||||
void hdr_color::set_rgb(const color* const new_color)
|
||||
{
|
||||
this->set_rgb(new_color->red, new_color->green, new_color->blue);
|
||||
this->alpha = i2fl(new_color->alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets RGB values from an array of three 0-255 ints
|
||||
*/
|
||||
void hdr_color::set_rgb(const int* const new_rgb)
|
||||
{
|
||||
this->set_rgb(new_rgb[0], new_rgb[1], new_rgb[2]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief retreives unmultiplied 0.0f-1.0f color component.
|
||||
*/
|
||||
float hdr_color::r() const
|
||||
{
|
||||
return red;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sets and returns unmultiplied 0.0f-1.0f color component.
|
||||
*/
|
||||
float hdr_color::r(float in)
|
||||
{
|
||||
red = in;
|
||||
return red;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sets and returns unmultiplied 0.0f-1.0f color component from 0-255 int.
|
||||
*/
|
||||
float hdr_color::r(int in)
|
||||
{
|
||||
red = i2fl(in) / 255.0f;
|
||||
return red;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief retreives unmultiplied 0.0f-1.0f color component.
|
||||
*/
|
||||
float hdr_color::g() const
|
||||
{
|
||||
return green;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sets and returns unmultiplied 0.0f-1.0f color component.
|
||||
*/
|
||||
float hdr_color::g(float in)
|
||||
{
|
||||
green = in;
|
||||
return green;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sets and returns unmultiplied 0.0f-1.0f color component from 0-255 int.
|
||||
*/
|
||||
float hdr_color::g(int in)
|
||||
{
|
||||
green = i2fl(in) / 255.0f;
|
||||
return green;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief retreives unmultiplied 0.0f-1.0f color component.
|
||||
*/
|
||||
float hdr_color::b() const
|
||||
{
|
||||
return blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sets and returns unmultiplied 0.0f-1.0f color component.
|
||||
*/
|
||||
float hdr_color::b(float in)
|
||||
{
|
||||
blue = in;
|
||||
return blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sets and returns unmultiplied 0.0f-1.0f color component from 0-255 int.
|
||||
*/
|
||||
float hdr_color::b(int in)
|
||||
{
|
||||
blue = i2fl(in) / 255.0f;
|
||||
return blue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief retreives unmultiplied 0.0f-1.0f color component.
|
||||
*/
|
||||
float hdr_color::a() const
|
||||
{
|
||||
return alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sets and returns unmultiplied 0.0f-1.0f color component.
|
||||
*/
|
||||
float hdr_color::a(float in)
|
||||
{
|
||||
alpha = in;
|
||||
return alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sets and returns unmultiplied 0.0f-1.0f color component from 0-255 int.
|
||||
*/
|
||||
float hdr_color::a(int in)
|
||||
{
|
||||
alpha = i2fl(in) / 255.0f;
|
||||
return alpha;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief retreives unmultiplied 0.0f-1.0f color component.
|
||||
*/
|
||||
float hdr_color::i() const
|
||||
{
|
||||
return intensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sets and returns unmultiplied 0.0f-1.0f color component.
|
||||
*/
|
||||
float hdr_color::i(float in)
|
||||
{
|
||||
intensity = in;
|
||||
return intensity;
|
||||
}
|
||||
/**
|
||||
* @brief Resets to a full alpha, 100% white.
|
||||
*/
|
||||
void hdr_color::reset()
|
||||
{
|
||||
this->red = 1.0f;
|
||||
this->green = 1.0f;
|
||||
this->blue = 1.0f;
|
||||
this->alpha = 1.0f;
|
||||
this->intensity = 1.0f;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include "graphics/2d.h"
|
||||
#include "graphics/color.h"
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/vmallocator.h"
|
||||
|
||||
class hdr_color{
|
||||
private:
|
||||
float red,green,blue,alpha,intensity;
|
||||
public:
|
||||
hdr_color();
|
||||
hdr_color(const float new_r, const float new_g, const float new_b, const float new_a = 1.0f, const float new_i =1.0f);
|
||||
hdr_color(const hdr_color* const source_color);
|
||||
|
||||
void reset();
|
||||
|
||||
void get_v5f(SCP_vector<float>& outvec) const;
|
||||
void set_vecf(const SCP_vector<float>& input);
|
||||
|
||||
void set_rgb(const int new_r,const int new_g,const int new_b);
|
||||
void set_rgb(const color* const new_rgb);
|
||||
void set_rgb(const int* const new_rgb);
|
||||
|
||||
float r() const;
|
||||
float r(const float in);
|
||||
float r(const int in);
|
||||
|
||||
float g() const;
|
||||
float g(const float in);
|
||||
float g(const int in);
|
||||
|
||||
float b() const;
|
||||
float b(const float in);
|
||||
float b(const int in);
|
||||
|
||||
float a() const;
|
||||
float a(const float in);
|
||||
float a(const int in);
|
||||
|
||||
float i() const;
|
||||
float i(const float in);
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "debug_sphere.h"
|
||||
|
||||
#include "globalincs/vmallocator.h"
|
||||
|
||||
#include "render/3d.h"
|
||||
|
||||
SCP_vector<debug_sphere> debug_sphere::Spheres;
|
||||
|
||||
debug_sphere::debug_sphere(const vec3d& pos_in, float rad_in)
|
||||
{
|
||||
pos = pos_in;
|
||||
radius = rad_in;
|
||||
}
|
||||
void debug_sphere::add(const vec3d& pos, float rad)
|
||||
{
|
||||
Spheres.emplace_back(pos, rad);
|
||||
}
|
||||
|
||||
void debug_sphere::clear()
|
||||
{
|
||||
Spheres.clear();
|
||||
}
|
||||
|
||||
void debug_sphere::render()
|
||||
{
|
||||
color uicolor = gr_screen.current_color;
|
||||
color render_color;
|
||||
render_color.red = 255;
|
||||
render_color.green = 255;
|
||||
render_color.blue = 255;
|
||||
render_color.alpha = 64;
|
||||
gr_set_color_fast(&render_color);
|
||||
for (auto& s : Spheres) {
|
||||
g3_draw_sphere_ez(&(s.pos), s.radius);
|
||||
}
|
||||
gr_set_color_fast(&uicolor);
|
||||
clear();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "globalincs/pstypes.h"
|
||||
#include "globalincs/vmallocator.h"
|
||||
class debug_sphere {
|
||||
public:
|
||||
static void add(const vec3d& pos, float radius);
|
||||
static void render();
|
||||
debug_sphere(const vec3d& pos, float radius);
|
||||
|
||||
private:
|
||||
static SCP_vector<debug_sphere> Spheres;
|
||||
static void clear();
|
||||
vec3d pos;
|
||||
float radius;
|
||||
};
|
||||
@@ -205,6 +205,10 @@ static void pre_render_init_lights() {
|
||||
}
|
||||
|
||||
void gr_set_light(light* fs_light) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
// init the light
|
||||
gr_light grl;
|
||||
FSLight2GLLight(fs_light, &grl);
|
||||
@@ -213,6 +217,10 @@ void gr_set_light(light* fs_light) {
|
||||
}
|
||||
|
||||
void gr_set_center_alpha(int type) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
@@ -275,11 +283,19 @@ void gr_set_center_alpha(int type) {
|
||||
}
|
||||
|
||||
void gr_reset_lighting() {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
gr_lights.clear();
|
||||
Num_active_gr_lights = 0;
|
||||
}
|
||||
|
||||
void gr_calculate_ambient_factor(int ambient_factor) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
gr_user_ambient = (float) ((ambient_factor * 2) - 255) / 255.0f;
|
||||
}
|
||||
|
||||
@@ -288,6 +304,10 @@ void gr_light_shutdown() {
|
||||
}
|
||||
|
||||
void gr_light_init() {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
gr_calculate_ambient_factor();
|
||||
|
||||
// allocate memory for enabled lights
|
||||
@@ -295,6 +315,9 @@ void gr_light_init() {
|
||||
}
|
||||
|
||||
void gr_set_lighting() {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Valathil: Sort lights by priority
|
||||
extern bool Deferred_lighting;
|
||||
@@ -326,6 +349,10 @@ void gr_set_lighting() {
|
||||
}
|
||||
|
||||
void gr_set_ambient_light(int red, int green, int blue) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
gr_light_ambient[0] = i2fl(red) / 255.0f;
|
||||
gr_light_ambient[1] = i2fl(green) / 255.0f;
|
||||
gr_light_ambient[2] = i2fl(blue) / 255.0f;
|
||||
@@ -335,6 +362,10 @@ void gr_set_ambient_light(int red, int green, int blue) {
|
||||
}
|
||||
|
||||
void gr_lighting_fill_uniforms(void* data_out, size_t buffer_size) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assertion(sizeof(gr_light_uniforms) <= buffer_size, "Insufficient buffer supplied.");
|
||||
|
||||
memcpy(reinterpret_cast<graphics::model_light*>(data_out), gr_light_uniforms, sizeof(gr_light_uniforms));
|
||||
|
||||
@@ -53,6 +53,26 @@ void material_set_unlit(material* mat_info, int texture, float alpha, bool blend
|
||||
}
|
||||
}
|
||||
|
||||
void material_set_unlit_opaque(material* mat_info, int texture, bool depth_testing)
|
||||
{
|
||||
mat_info->set_texture_map(TM_BASE_TYPE, texture);
|
||||
|
||||
gr_alpha_blend blend_mode = ALPHA_BLEND_NONE;
|
||||
gr_zbuffer_type depth_mode = material_determine_depth_mode(depth_testing, false);
|
||||
|
||||
mat_info->set_blend_mode(blend_mode);
|
||||
mat_info->set_depth_mode(depth_mode);
|
||||
mat_info->set_cull_mode(false);
|
||||
|
||||
|
||||
mat_info->set_color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
|
||||
if ( texture >= 0 && bm_has_alpha_channel(texture) ) {
|
||||
mat_info->set_texture_type(material::TEX_TYPE_XPARENT);
|
||||
}
|
||||
}
|
||||
|
||||
void material_set_unlit_emissive(material* mat_info, int texture, float alpha, float color_scale)
|
||||
{
|
||||
material_set_unlit(mat_info, texture, alpha, true, true);
|
||||
@@ -161,6 +181,13 @@ void material_set_batched_bitmap(batched_bitmap_material* mat_info, int base_tex
|
||||
|
||||
mat_info->set_color_scale(color_scale);
|
||||
}
|
||||
|
||||
void material_set_batched_opaque_bitmap(batched_bitmap_material* mat_info, int base_tex, float color_scale) {
|
||||
material_set_unlit_opaque(mat_info, base_tex, true);
|
||||
|
||||
mat_info->set_color_scale(color_scale);
|
||||
}
|
||||
|
||||
void material_set_nanovg(nanovg_material* mat_info, int base_tex) {
|
||||
material_set_unlit(mat_info, base_tex, 1.0f, true, false);
|
||||
|
||||
|
||||
@@ -373,6 +373,7 @@ void material_set_unlit_volume(particle_material* mat_info, int texture, bool po
|
||||
void material_set_distortion(distortion_material *mat_info, int texture, bool thruster);
|
||||
void material_set_movie(movie_material *mat_info, int y_bm, int u_bm, int v_bm);
|
||||
void material_set_batched_bitmap(batched_bitmap_material* mat_info, int base_tex, float alpha, float color_scale);
|
||||
void material_set_batched_opaque_bitmap(batched_bitmap_material* mat_info, int base_tex, float color_scale);
|
||||
void material_set_nanovg(nanovg_material* mat_info, int base_tex);
|
||||
void material_set_decal(material* mat_info, int diffuse_tex, int glow_tex, int normal_tex);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#endif
|
||||
|
||||
#include "gropengl.h"
|
||||
|
||||
#include "ShaderProgram.h"
|
||||
#include "gropenglbmpman.h"
|
||||
#include "gropengldeferred.h"
|
||||
#include "gropengldraw.h"
|
||||
@@ -290,6 +290,58 @@ void gr_opengl_print_screen(const char *filename)
|
||||
}
|
||||
}
|
||||
|
||||
void gr_opengl_dump_envmap(const char* filename)
|
||||
{
|
||||
char tmp[MAX_PATH_LEN];
|
||||
GLubyte* pixels = nullptr;
|
||||
|
||||
_mkdir(os_get_config_path("envmaps").c_str());
|
||||
|
||||
auto width = 512; // These are the hardcoded envmap dimensions
|
||||
auto height = 512; // in future envmap resolution should be dynamic, or at least extracted from envmap_render_target
|
||||
auto sphere_width = 4 * width;
|
||||
auto sphere_height = 2 * height;
|
||||
auto env_tex = bm_get_gr_info<tcache_slot_opengl>(gr_screen.envmap_render_target);
|
||||
glBindTexture(env_tex->texture_target, env_tex->texture_id);
|
||||
|
||||
// Save the previous render target so we can reset it once we are done here
|
||||
auto previous_target = gr_screen.rendering_to_texture;
|
||||
|
||||
// New render target
|
||||
int sm_flags = (BMP_FLAG_RENDER_TARGET_STATIC);
|
||||
int spheremap_render_target = bm_make_render_target(sphere_width, sphere_height, sm_flags);
|
||||
bm_set_render_target(spheremap_render_target);
|
||||
|
||||
// Set up cubemap to spherical shader and draw
|
||||
int env_shader = gr_opengl_maybe_create_shader(SDR_TYPE_ENVMAP_SPHERE_WARP, 0);
|
||||
opengl_shader_set_current(env_shader);
|
||||
GL_state.Texture.Enable(0, GL_TEXTURE_CUBE_MAP, env_tex->texture_id);
|
||||
Current_shader->program->Uniforms.setTextureUniform("envmap", 0);
|
||||
gr_clear();
|
||||
opengl_draw_full_screen_textured(0.0f, 0.0f, Scene_texture_u_scale, Scene_texture_v_scale);
|
||||
|
||||
bm_set_render_target(previous_target);
|
||||
|
||||
// load texture info and write to file
|
||||
auto sphere_tex = bm_get_gr_info<tcache_slot_opengl>(spheremap_render_target);
|
||||
glBindTexture(sphere_tex->texture_target, sphere_tex->texture_id);
|
||||
pixels = (GLubyte*)vm_malloc(sphere_width * sphere_height * 4, memory::quiet_alloc);
|
||||
glGetTexImage(sphere_tex->texture_target, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
snprintf(tmp, MAX_PATH_LEN - 1, "envmaps/%s.png", filename);
|
||||
if (!png_write_bitmap(os_get_config_path(tmp).c_str(), 4 * width, 2 * height, true, pixels)) {
|
||||
ReleaseWarning(LOCATION, "Failed to write envmap to \"%s\".", os_get_config_path(tmp).c_str());
|
||||
}
|
||||
|
||||
// cleanup
|
||||
if (!bm_release(spheremap_render_target, 1)) {
|
||||
Warning(LOCATION, "Unable to release environment map render target.");
|
||||
}
|
||||
|
||||
if (pixels != nullptr) {
|
||||
vm_free(pixels);
|
||||
}
|
||||
}
|
||||
|
||||
void gr_opengl_shutdown()
|
||||
{
|
||||
opengl_tcache_shutdown();
|
||||
@@ -807,6 +859,7 @@ void opengl_setup_function_pointers()
|
||||
// gr_screen.gf_rect = gr_opengl_rect;
|
||||
|
||||
gr_screen.gf_print_screen = gr_opengl_print_screen;
|
||||
gr_screen.gf_dump_envmap = gr_opengl_dump_envmap;
|
||||
|
||||
gr_screen.gf_zbuffer_get = gr_opengl_zbuffer_get;
|
||||
gr_screen.gf_zbuffer_set = gr_opengl_zbuffer_set;
|
||||
|
||||
@@ -211,13 +211,14 @@ void gr_opengl_deferred_lighting_finish()
|
||||
case Light_Type::Point:
|
||||
vm_vec_scale(&light_data->specLightColor, static_point_factor);
|
||||
|
||||
light_data->lightRadius = MAX(l.rada, l.radb) * 1.25f;
|
||||
light_data->scale.xyz.x = MAX(l.rada, l.radb) * 1.28f;
|
||||
light_data->scale.xyz.y = MAX(l.rada, l.radb) * 1.28f;
|
||||
light_data->scale.xyz.z = MAX(l.rada, l.radb) * 1.28f;
|
||||
light_data->lightRadius = MAX(l.rada, l.radb);
|
||||
//A small padding factor is added to guard against potentially clipping the edges of the light with facets of the volume mesh.
|
||||
light_data->scale.xyz.x = MAX(l.rada, l.radb) * 1.05f;
|
||||
light_data->scale.xyz.y = MAX(l.rada, l.radb) * 1.05f;
|
||||
light_data->scale.xyz.z = MAX(l.rada, l.radb) * 1.05f;
|
||||
break;
|
||||
case Light_Type::Tube: {
|
||||
light_data->lightRadius = l.radb * 1.5f;
|
||||
light_data->lightRadius = l.radb;
|
||||
light_data->lightType = LT_TUBE;
|
||||
|
||||
vec3d a;
|
||||
@@ -228,8 +229,9 @@ void gr_opengl_deferred_lighting_finish()
|
||||
//origin we must extend it here. Later the position will be adjusted as well.
|
||||
length += light_data->lightRadius * 2.0f;
|
||||
|
||||
light_data->scale.xyz.x = l.radb * 1.53f;
|
||||
light_data->scale.xyz.y = l.radb * 1.53f;
|
||||
//A small padding factor is added to guard against potentially clipping the edges of the light with facets of the volume mesh.
|
||||
light_data->scale.xyz.x = l.radb * 1.05f;
|
||||
light_data->scale.xyz.y = l.radb * 1.05f;
|
||||
light_data->scale.xyz.z = length;
|
||||
|
||||
vm_vec_scale(&light_data->specLightColor, static_tube_factor);
|
||||
@@ -276,8 +278,7 @@ void gr_opengl_deferred_lighting_finish()
|
||||
//to allow smooth fall-off from all angles. Since the light volume starts at the mesh
|
||||
//origin we must extend it, which has been done above, and then move it backwards one radius.
|
||||
vm_vec_normalize(&dir);
|
||||
//1.5 multiplier matches scaling used earlier to scale light radius for all tubes
|
||||
vm_vec_scale_sub(&newPos, &l.vec2, &dir, l.radb * 1.5f);
|
||||
vm_vec_scale_sub(&newPos, &l.vec2, &dir, l.radb);
|
||||
gr_opengl_draw_deferred_light_cylinder(&newPos, &orient);
|
||||
++element_index;
|
||||
break;
|
||||
@@ -439,13 +440,15 @@ void gr_opengl_deferred_light_cylinder_init(int segments) // Generate a VBO of a
|
||||
if ( opengl_check_for_errors() ) {
|
||||
glDeleteBuffers(1, &deferred_light_cylinder_vbo);
|
||||
deferred_light_cylinder_vbo = 0;
|
||||
|
||||
vm_free(Indices);
|
||||
Indices = nullptr;
|
||||
vm_free(Vertices);
|
||||
Vertices = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
vm_free(Vertices);
|
||||
Vertices = NULL;
|
||||
}
|
||||
|
||||
glGenBuffers(1, &deferred_light_cylinder_ibo);
|
||||
@@ -459,15 +462,21 @@ void gr_opengl_deferred_light_cylinder_init(int segments) // Generate a VBO of a
|
||||
if ( opengl_check_for_errors() ) {
|
||||
glDeleteBuffers(1, &deferred_light_cylinder_ibo);
|
||||
deferred_light_cylinder_ibo = 0;
|
||||
|
||||
vm_free(Indices);
|
||||
Indices = nullptr;
|
||||
vm_free(Vertices);
|
||||
Vertices = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
vm_free(Indices);
|
||||
Indices = NULL;
|
||||
}
|
||||
|
||||
vm_free(Indices);
|
||||
Indices = nullptr;
|
||||
vm_free(Vertices);
|
||||
Vertices = nullptr;
|
||||
}
|
||||
|
||||
static GLuint deferred_light_sphere_vbo = 0;
|
||||
@@ -531,13 +540,15 @@ void gr_opengl_deferred_light_sphere_init(int rings, int segments) // Generate a
|
||||
if ( opengl_check_for_errors() ) {
|
||||
glDeleteBuffers(1, &deferred_light_sphere_vbo);
|
||||
deferred_light_sphere_vbo = 0;
|
||||
|
||||
vm_free(Vertices);
|
||||
Vertices = nullptr;
|
||||
vm_free(Indices);
|
||||
Indices = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
vm_free(Vertices);
|
||||
Vertices = NULL;
|
||||
}
|
||||
|
||||
glGenBuffers(1, &deferred_light_sphere_ibo);
|
||||
@@ -551,14 +562,21 @@ void gr_opengl_deferred_light_sphere_init(int rings, int segments) // Generate a
|
||||
if ( opengl_check_for_errors() ) {
|
||||
glDeleteBuffers(1, &deferred_light_sphere_ibo);
|
||||
deferred_light_sphere_ibo = 0;
|
||||
|
||||
vm_free(Vertices);
|
||||
Vertices = nullptr;
|
||||
vm_free(Indices);
|
||||
Indices = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
vm_free(Indices);
|
||||
Indices = NULL;
|
||||
}
|
||||
|
||||
vm_free(Vertices);
|
||||
Vertices = nullptr;
|
||||
vm_free(Indices);
|
||||
Indices = nullptr;
|
||||
}
|
||||
|
||||
void opengl_draw_sphere()
|
||||
|
||||
@@ -1,82 +1,82 @@
|
||||
|
||||
#include "graphics/opengl/gropenglquery.h"
|
||||
#include "graphics/opengl/gropengl.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct query_object_slot {
|
||||
bool used = false;
|
||||
GLuint name = 0;
|
||||
};
|
||||
|
||||
SCP_vector<query_object_slot> query_objects;
|
||||
|
||||
int get_new_query_slot() {
|
||||
auto end = query_objects.end();
|
||||
for (auto iter = query_objects.begin(); iter != end; ++iter) {
|
||||
if (!iter->used) {
|
||||
return (int) std::distance(query_objects.begin(), iter);
|
||||
}
|
||||
}
|
||||
|
||||
query_objects.emplace_back();
|
||||
return (int) (query_objects.size() - 1);
|
||||
}
|
||||
|
||||
query_object_slot& get_query_slot(int handle) {
|
||||
Assertion(handle >= 0 && handle < (int)query_objects.size(), "Query object index %d is invalid!", handle);
|
||||
return query_objects[handle];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int gr_opengl_create_query_object() {
|
||||
auto idx = get_new_query_slot();
|
||||
|
||||
auto& slot = query_objects[idx];
|
||||
slot.used = true;
|
||||
|
||||
glGenQueries(1, &slot.name);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
void gr_opengl_query_value(int obj, QueryType type) {
|
||||
auto& slot = get_query_slot(obj);
|
||||
|
||||
switch(type) {
|
||||
case QueryType::Timestamp:
|
||||
Assertion(GLAD_GL_ARB_timer_query, "Timestamp queries are not available! Availability must be checked before calling this function!");
|
||||
glQueryCounter(slot.name, GL_TIMESTAMP);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Unhandled enum value!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool gr_opengl_query_value_available(int obj) {
|
||||
auto& slot = get_query_slot(obj);
|
||||
|
||||
GLuint available;
|
||||
glGetQueryObjectuiv(slot.name, GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
|
||||
return available == GL_TRUE;
|
||||
}
|
||||
|
||||
std::uint64_t gr_opengl_get_query_value(int obj) {
|
||||
auto& slot = get_query_slot(obj);
|
||||
|
||||
GLuint64 available;
|
||||
glGetQueryObjectui64v(slot.name, GL_QUERY_RESULT, &available);
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
void gr_opengl_delete_query_object(int obj) {
|
||||
auto& slot = get_query_slot(obj);
|
||||
glDeleteQueries(1, &slot.name);
|
||||
|
||||
slot.name = 0;
|
||||
slot.used = false;
|
||||
}
|
||||
|
||||
#include "graphics/opengl/gropenglquery.h"
|
||||
#include "graphics/opengl/gropengl.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct query_object_slot {
|
||||
bool used = false;
|
||||
GLuint name = 0;
|
||||
};
|
||||
|
||||
SCP_vector<query_object_slot> query_objects;
|
||||
|
||||
int get_new_query_slot() {
|
||||
auto end = query_objects.end();
|
||||
for (auto iter = query_objects.begin(); iter != end; ++iter) {
|
||||
if (!iter->used) {
|
||||
return (int) std::distance(query_objects.begin(), iter);
|
||||
}
|
||||
}
|
||||
|
||||
query_objects.emplace_back();
|
||||
return (int) (query_objects.size() - 1);
|
||||
}
|
||||
|
||||
query_object_slot& get_query_slot(int handle) {
|
||||
Assertion(handle >= 0 && handle < (int)query_objects.size(), "Query object index %d is invalid!", handle);
|
||||
return query_objects[handle];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int gr_opengl_create_query_object() {
|
||||
auto idx = get_new_query_slot();
|
||||
|
||||
auto& slot = query_objects[idx];
|
||||
slot.used = true;
|
||||
|
||||
glGenQueries(1, &slot.name);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
void gr_opengl_query_value(int obj, QueryType type) {
|
||||
auto& slot = get_query_slot(obj);
|
||||
|
||||
switch(type) {
|
||||
case QueryType::Timestamp:
|
||||
Assertion(GLAD_GL_ARB_timer_query, "Timestamp queries are not available! Availability must be checked before calling this function!");
|
||||
glQueryCounter(slot.name, GL_TIMESTAMP);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Unhandled enum value!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool gr_opengl_query_value_available(int obj) {
|
||||
auto& slot = get_query_slot(obj);
|
||||
|
||||
GLuint available;
|
||||
glGetQueryObjectuiv(slot.name, GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
|
||||
return available == GL_TRUE;
|
||||
}
|
||||
|
||||
std::uint64_t gr_opengl_get_query_value(int obj) {
|
||||
auto& slot = get_query_slot(obj);
|
||||
|
||||
GLuint64 available;
|
||||
glGetQueryObjectui64v(slot.name, GL_QUERY_RESULT, &available);
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
void gr_opengl_delete_query_object(int obj) {
|
||||
auto& slot = get_query_slot(obj);
|
||||
glDeleteQueries(1, &slot.name);
|
||||
|
||||
slot.name = 0;
|
||||
slot.used = false;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
#ifndef _GROPENGLQUERY_H
|
||||
#define _GROPENGLQUERY_H
|
||||
#pragma once
|
||||
|
||||
#include "graphics/2d.h"
|
||||
|
||||
#ifndef _GROPENGLQUERY_H
|
||||
#define _GROPENGLQUERY_H
|
||||
#pragma once
|
||||
|
||||
#include "graphics/2d.h"
|
||||
|
||||
int gr_opengl_create_query_object();
|
||||
|
||||
@@ -13,6 +13,6 @@ bool gr_opengl_query_value_available(int obj);
|
||||
|
||||
std::uint64_t gr_opengl_get_query_value(int obj);
|
||||
|
||||
void gr_opengl_delete_query_object(int obj);
|
||||
|
||||
#endif // _GROPENGLQUERY_H
|
||||
void gr_opengl_delete_query_object(int obj);
|
||||
|
||||
#endif // _GROPENGLQUERY_H
|
||||
@@ -156,6 +156,9 @@ static opengl_shader_type_t GL_shader_types[] = {
|
||||
|
||||
{ SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING, "smaa-neighbour-v.sdr", "smaa-neighbour-f.sdr", nullptr,
|
||||
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "SMAA Neighborhood Blending", false },
|
||||
|
||||
{ SDR_TYPE_ENVMAP_SPHERE_WARP, "post-v.sdr", "envmap-sphere-warp-f.sdr", nullptr,
|
||||
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Environment Map Generation", false },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "options/Option.h"
|
||||
#include "parse/parselo.h"
|
||||
#include "ship/ship.h"
|
||||
#include "starfield/supernova.h"
|
||||
|
||||
namespace graphics {
|
||||
namespace {
|
||||
@@ -208,6 +209,14 @@ void PostProcessingManager::setBloomShadersOk(bool ok)
|
||||
|
||||
bool gr_lightshafts_enabled()
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// supernova glare should disable lightshafts
|
||||
if (supernova_stage() >= SUPERNOVA_STAGE::CLOSE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!graphics::Post_processing_manager->getLightshaftParams().on) {
|
||||
return false;
|
||||
@@ -222,6 +231,10 @@ bool gr_lightshafts_enabled()
|
||||
|
||||
int gr_bloom_intensity()
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (graphics::Post_processing_manager == nullptr || !graphics::Post_processing_manager->bloomShadersOk()) {
|
||||
return 0;
|
||||
}
|
||||
@@ -235,6 +248,10 @@ int gr_bloom_intensity()
|
||||
|
||||
void gr_set_bloom_intensity(int intensity)
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Using_in_game_options) {
|
||||
graphics::Post_processing_bloom_intensity = intensity;
|
||||
} else {
|
||||
|
||||
@@ -46,6 +46,10 @@ static void gr_flash_internal(int r, int g, int b, int a, bool alpha_flash)
|
||||
}
|
||||
|
||||
void gr_flash(int r, int g, int b) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(r || g || b)) {
|
||||
return;
|
||||
}
|
||||
@@ -54,6 +58,10 @@ void gr_flash(int r, int g, int b) {
|
||||
}
|
||||
|
||||
void gr_flash_alpha(int r, int g, int b, int a) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(r || g || b || a)) {
|
||||
return;
|
||||
}
|
||||
@@ -152,6 +160,10 @@ static void bitmap_ex_internal(int x,
|
||||
}
|
||||
|
||||
void gr_aabitmap(int x, int y, int resize_mode, bool mirror) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
GR_DEBUG_SCOPE("Draw AA-bitmap");
|
||||
|
||||
int w, h, do_resize;
|
||||
@@ -222,6 +234,10 @@ void gr_aabitmap(int x, int y, int resize_mode, bool mirror) {
|
||||
&gr_screen.current_color);
|
||||
}
|
||||
void gr_aabitmap_ex(int x, int y, int w, int h, int sx, int sy, int resize_mode, bool mirror) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int reclip;
|
||||
#ifndef NDEBUG
|
||||
@@ -349,6 +365,10 @@ void gr_aabitmap_ex(int x, int y, int w, int h, int sx, int sy, int resize_mode,
|
||||
}
|
||||
//these are penguins bitmap functions
|
||||
void gr_bitmap_ex(int x, int y, int w, int h, int sx, int sy, int resize_mode) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
int reclip;
|
||||
#ifndef NDEBUG
|
||||
int count = 0;
|
||||
@@ -748,6 +768,10 @@ void endDrawing(graphics::paths::PathRenderer* path) {
|
||||
}
|
||||
|
||||
void gr_string(float sx, float sy, const char* s, int resize_mode, int in_length) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
GR_DEBUG_SCOPE("Render string");
|
||||
|
||||
using namespace font;
|
||||
@@ -917,10 +941,18 @@ static void gr_line(float x1, float y1, float x2, float y2, int resize_mode) {
|
||||
}
|
||||
|
||||
void gr_line(int x1, int y1, int x2, int y2, int resize_mode) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
gr_line(i2fl(x1), i2fl(y1), i2fl(x2), i2fl(y2), resize_mode);
|
||||
}
|
||||
|
||||
void gr_aaline(vertex* v1, vertex* v2) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
float x1 = v1->screen.xyw.x;
|
||||
float y1 = v1->screen.xyw.y;
|
||||
float x2 = v2->screen.xyw.x;
|
||||
@@ -931,6 +963,10 @@ void gr_aaline(vertex* v1, vertex* v2) {
|
||||
}
|
||||
|
||||
void gr_gradient(int x1, int y1, int x2, int y2, int resize_mode) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gr_screen.current_color.is_alphacolor) {
|
||||
gr_line(x1, y1, x2, y2, resize_mode);
|
||||
return;
|
||||
@@ -953,10 +989,18 @@ void gr_gradient(int x1, int y1, int x2, int y2, int resize_mode) {
|
||||
endDrawing(path);
|
||||
}
|
||||
void gr_pixel(int x, int y, int resize_mode) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
gr_line(x, y, x, y, resize_mode);
|
||||
}
|
||||
|
||||
void gr_circle(int xc, int yc, int d, int resize_mode) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = beginDrawing(resize_mode);
|
||||
|
||||
path->circle(i2fl(xc), i2fl(yc), d / 2.0f);
|
||||
@@ -966,6 +1010,10 @@ void gr_circle(int xc, int yc, int d, int resize_mode) {
|
||||
endDrawing(path);
|
||||
}
|
||||
void gr_unfilled_circle(int xc, int yc, int d, int resize_mode) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = beginDrawing(resize_mode);
|
||||
|
||||
path->circle(i2fl(xc), i2fl(yc), d / 2.0f);
|
||||
@@ -975,6 +1023,10 @@ void gr_unfilled_circle(int xc, int yc, int d, int resize_mode) {
|
||||
endDrawing(path);
|
||||
}
|
||||
void gr_arc(int xc, int yc, float r, float angle_start, float angle_end, bool fill, int resize_mode) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that angle_start < angle_end
|
||||
if (angle_end < angle_start) {
|
||||
float temp = angle_start;
|
||||
@@ -1001,6 +1053,10 @@ void gr_arc(int xc, int yc, float r, float angle_start, float angle_end, bool fi
|
||||
endDrawing(path);
|
||||
}
|
||||
void gr_curve(int xc, int yc, int r, int direction, int resize_mode) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
using namespace graphics::paths;
|
||||
|
||||
auto path = beginDrawing(resize_mode);
|
||||
@@ -1048,6 +1104,10 @@ void gr_curve(int xc, int yc, int r, int direction, int resize_mode) {
|
||||
}
|
||||
|
||||
void gr_rect(int x, int y, int w, int h, int resize_mode, float angle) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = beginDrawing(resize_mode);
|
||||
if (angle != 0) {
|
||||
// If we don't do this translation before and after rotating, the rotation will use 0,0 as the pivot, flinging the rectangle far away.
|
||||
@@ -1065,6 +1125,10 @@ void gr_rect(int x, int y, int w, int h, int resize_mode, float angle) {
|
||||
}
|
||||
|
||||
void gr_shade(int x, int y, int w, int h, int resize_mode) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto r = (int) gr_screen.current_shader.r;
|
||||
auto g = (int) gr_screen.current_shader.g;
|
||||
auto b = (int) gr_screen.current_shader.b;
|
||||
@@ -1083,6 +1147,10 @@ void gr_shade(int x, int y, int w, int h, int resize_mode) {
|
||||
}
|
||||
|
||||
void gr_2d_start_buffer() {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assertion(!buffering_nanovg, "Tried to enable 2D buffering but it was already enabled!");
|
||||
|
||||
buffering_nanovg = true;
|
||||
@@ -1092,6 +1160,10 @@ void gr_2d_start_buffer() {
|
||||
}
|
||||
|
||||
void gr_2d_stop_buffer() {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assertion(buffering_nanovg, "Tried to stop 2D buffering but it was not enabled!");
|
||||
|
||||
buffering_nanovg = false;
|
||||
@@ -1106,6 +1178,10 @@ static size_t immediate_buffer_size = 0;
|
||||
static const int IMMEDIATE_BUFFER_RESIZE_BLOCK_SIZE = 2048;
|
||||
|
||||
size_t gr_add_to_immediate_buffer(size_t size, void* data) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
GR_DEBUG_SCOPE("Add data to immediate buffer");
|
||||
|
||||
if (!gr_immediate_buffer_handle.isValid()) {
|
||||
@@ -1132,6 +1208,10 @@ size_t gr_add_to_immediate_buffer(size_t size, void* data) {
|
||||
return old_offset;
|
||||
}
|
||||
void gr_reset_immediate_buffer() {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gr_immediate_buffer_handle.isValid()) {
|
||||
// we haven't used the immediate buffer yet
|
||||
return;
|
||||
@@ -1150,6 +1230,10 @@ void gr_render_primitives_immediate(material* material_info,
|
||||
int n_verts,
|
||||
void* data,
|
||||
size_t size) {
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto offset = gr_add_to_immediate_buffer(size, data);
|
||||
|
||||
gr_render_primitives(material_info, prim_type, layout, 0, n_verts, gr_immediate_buffer_handle, offset);
|
||||
@@ -1162,6 +1246,10 @@ void gr_render_primitives_2d_immediate(material* material_info,
|
||||
void* data,
|
||||
size_t size)
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
gr_set_2d_matrix();
|
||||
|
||||
gr_render_primitives_immediate(material_info, prim_type, layout, n_verts, data, size);
|
||||
@@ -1315,6 +1403,10 @@ static void draw_bitmap_list(bitmap_rect_list* list, int n_bm, int resize_mode,
|
||||
|
||||
void gr_bitmap_list(bitmap_rect_list* list, int n_bm, int resize_mode, float angle)
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
material mat_params;
|
||||
material_set_interface(&mat_params,
|
||||
gr_screen.current_bitmap,
|
||||
@@ -1326,6 +1418,10 @@ void gr_bitmap_list(bitmap_rect_list* list, int n_bm, int resize_mode, float ang
|
||||
|
||||
void gr_aabitmap_list(bitmap_rect_list* list, int n_bm, int resize_mode, float angle)
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
material render_mat;
|
||||
render_mat.set_blend_mode(ALPHA_BLEND_ALPHA_BLEND_ALPHA);
|
||||
render_mat.set_depth_mode(ZBUFFER_TYPE_NONE);
|
||||
|
||||
@@ -418,6 +418,10 @@ void shadows_end_render()
|
||||
|
||||
void shadows_render_all(float fov, matrix *eye_orient, vec3d *eye_pos)
|
||||
{
|
||||
if (gr_screen.mode == GR_STUB) {
|
||||
return;
|
||||
}
|
||||
|
||||
GR_DEBUG_SCOPE("Render shadows");
|
||||
TRACE_SCOPE(tracing::BuildShadowMap);
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace font
|
||||
int w, h;
|
||||
|
||||
w = 0;
|
||||
h = 0;
|
||||
h = text == nullptr ? 0 : fl2i(this->getHeight());
|
||||
longest_width = 0;
|
||||
|
||||
// Maintain old behavior where textLen was an int
|
||||
@@ -49,8 +49,8 @@ namespace font
|
||||
|
||||
bool checkLength = textLen >= 0;
|
||||
|
||||
if (text != NULL) {
|
||||
h += fl2i(this->getHeight());
|
||||
if (text != nullptr && textLen != 0)
|
||||
{
|
||||
while (*text)
|
||||
{
|
||||
// Process one or more
|
||||
|
||||
+72
-33
@@ -57,6 +57,7 @@
|
||||
#include "weapon/emp.h"
|
||||
#include "weapon/weapon.h"
|
||||
|
||||
// This contains not only the retail HUD gauges but also any custom HUD gauges that are not specific to a ship.
|
||||
SCP_vector<std::unique_ptr<HudGauge>> default_hud_gauges;
|
||||
|
||||
// new values for HUD alpha
|
||||
@@ -369,12 +370,13 @@ void HudGauge::getPosition(int *x, int *y)
|
||||
*y = position[1];
|
||||
}
|
||||
|
||||
void HudGauge::initBaseResolution(int w, int h)
|
||||
void HudGauge::initBaseResolution(int w, int h, float aq)
|
||||
{
|
||||
Assert(w >= 640 && h >= 480);
|
||||
|
||||
base_w = w;
|
||||
base_h = h;
|
||||
aspect_quotient = aq;
|
||||
}
|
||||
|
||||
void HudGauge::initSlew(bool slew)
|
||||
@@ -389,6 +391,43 @@ void HudGauge::initFont(int input_font_num)
|
||||
}
|
||||
}
|
||||
|
||||
int HudGauge::getFont()
|
||||
{
|
||||
return font_num;
|
||||
}
|
||||
|
||||
void HudGauge::initOriginAndOffset(float originX, float originY, int offsetX, int offsetY)
|
||||
{
|
||||
tabled_origin[0] = originX;
|
||||
tabled_origin[1] = originY;
|
||||
|
||||
tabled_offset[0] = offsetX;
|
||||
tabled_offset[1] = offsetY;
|
||||
}
|
||||
|
||||
void HudGauge::getOriginAndOffset(float *originX, float *originY, int *offsetX, int *offsetY)
|
||||
{
|
||||
*originX = tabled_origin[0];
|
||||
*originY = tabled_origin[1];
|
||||
|
||||
*offsetX = tabled_offset[0];
|
||||
*offsetY = tabled_offset[1];
|
||||
}
|
||||
|
||||
void HudGauge::initCoords(bool use_coords, int coordsX, int coordsY)
|
||||
{
|
||||
tabled_use_coords = use_coords;
|
||||
tabled_coords[0] = coordsX;
|
||||
tabled_coords[1] = coordsY;
|
||||
}
|
||||
|
||||
void HudGauge::getCoords(bool *use_coords, int *coordsX, int *coordsY)
|
||||
{
|
||||
*use_coords = tabled_use_coords;
|
||||
*coordsX = tabled_coords[0];
|
||||
*coordsY = tabled_coords[1];
|
||||
}
|
||||
|
||||
const char* HudGauge::getCustomGaugeName()
|
||||
{
|
||||
return custom_name;
|
||||
@@ -524,6 +563,11 @@ int HudGauge::getBaseHeight()
|
||||
return base_h;
|
||||
}
|
||||
|
||||
float HudGauge::getAspectQuotient()
|
||||
{
|
||||
return aspect_quotient;
|
||||
}
|
||||
|
||||
int HudGauge::getConfigType()
|
||||
{
|
||||
//return gauge_type;
|
||||
@@ -1377,7 +1421,6 @@ void hud_maybe_popup_weapons_gauge()
|
||||
void hud_update_frame(float /*frametime*/)
|
||||
{
|
||||
object *targetp;
|
||||
vec3d target_pos;
|
||||
int can_target;
|
||||
|
||||
update_throttle_sound();
|
||||
@@ -1463,8 +1506,6 @@ void hud_update_frame(float /*frametime*/)
|
||||
hud_update_closest_turret();
|
||||
}
|
||||
|
||||
hud_target_change_check();
|
||||
|
||||
// check to see if we are in messaging mode. If so, send the key to the code
|
||||
// to deal with the message. hud_sqaudmsg_do_frame will return 0 if the key
|
||||
// wasn't used in messaging mode, otherwise 1. In the event the key was used,
|
||||
@@ -1474,15 +1515,15 @@ void hud_update_frame(float /*frametime*/)
|
||||
}
|
||||
|
||||
if (Player_ai->target_objnum == -1) {
|
||||
if (Target_static_looping.isValid()) {
|
||||
snd_stop(Target_static_looping);
|
||||
}
|
||||
Player->target_is_dying = -1; // according to comments elsewhere in the code, set to -1 when no target
|
||||
hud_target_change_check();
|
||||
return;
|
||||
}
|
||||
|
||||
targetp = &Objects[Player_ai->target_objnum];
|
||||
|
||||
if ( Player_ai->targeted_subsys != NULL ) {
|
||||
vec3d target_pos;
|
||||
get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos);
|
||||
|
||||
// get new distance of current target
|
||||
@@ -1509,11 +1550,12 @@ void hud_update_frame(float /*frametime*/)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
target_pos = targetp->pos;
|
||||
|
||||
Player_ai->current_target_distance = hud_find_target_distance(targetp,Player_obj);
|
||||
}
|
||||
|
||||
// moved past the if() block so that the target tracker has the current frame's target distance
|
||||
hud_target_change_check();
|
||||
|
||||
bool stop_targeting_this_thing = false;
|
||||
|
||||
// check to see if the target is still alive
|
||||
@@ -1623,7 +1665,7 @@ void hud_render_preprocess(float frametime)
|
||||
Player->subsys_in_view = -1;
|
||||
hud_target_clear_display_list();
|
||||
|
||||
if ( (Game_detail_flags & DETAIL_FLAG_HUD) && (supernova_active() >= 3) ) {
|
||||
if ( (Game_detail_flags & DETAIL_FLAG_HUD) && (supernova_stage() >= SUPERNOVA_STAGE::HIT) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1734,17 +1776,16 @@ void HudGaugeMissionTime::render(float /*frametime*/)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Show supernova warning it there's a supernova coming
|
||||
* @brief Show supernova warning if there's a supernova coming
|
||||
*/
|
||||
void hud_maybe_display_supernova()
|
||||
{
|
||||
float time_left;
|
||||
|
||||
time_left = supernova_time_left();
|
||||
if(time_left < 0.0f){
|
||||
if (!supernova_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto time_left = supernova_hud_time_left();
|
||||
|
||||
gr_set_color_fast(&Color_bright_red);
|
||||
if (Lcl_pl) {
|
||||
gr_printf(Supernova_coords[gr_screen.res][0], Supernova_coords[gr_screen.res][1], "Wybuch supernowej: %.2f s", time_left);
|
||||
@@ -1794,7 +1835,7 @@ void hud_render_gauges(int cockpit_display_num)
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if( supernova_active() >= 3 ) {
|
||||
if( supernova_stage() >= SUPERNOVA_STAGE::HIT) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1908,10 +1949,8 @@ void update_throttle_sound()
|
||||
|
||||
if ( object_get_gliding(Player_obj) ) { // Backslash
|
||||
percent_throttle = Player_obj->phys_info.forward_thrust;
|
||||
} else if ( Ships[Player_obj->instance].current_max_speed == 0 ) {
|
||||
percent_throttle = Player_obj->phys_info.fspeed / Ship_info[Ships[Player_obj->instance].ship_info_index].max_speed;
|
||||
} else {
|
||||
percent_throttle = Player_obj->phys_info.fspeed / Ships[Player_obj->instance].current_max_speed;
|
||||
percent_throttle = Player_obj->phys_info.fspeed / Ship_info[Ships[Player_obj->instance].ship_info_index].max_speed;
|
||||
}
|
||||
|
||||
if (percent_throttle != last_percent_throttle || !Player_engine_snd_loop.isValid()) {
|
||||
@@ -3869,11 +3908,6 @@ HudGauge* hud_get_custom_gauge(const char* name, bool check_all_gauges)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &gauge : default_hud_gauges) {
|
||||
if (!stricmp(name, gauge->getCustomGaugeName())) {
|
||||
return gauge.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
// if the player is flying a ship with a custom set of gauges, check those
|
||||
else if (player_sip && !player_sip->hud_gauges.empty()) {
|
||||
@@ -3884,12 +3918,19 @@ HudGauge* hud_get_custom_gauge(const char* name, bool check_all_gauges)
|
||||
}
|
||||
}
|
||||
|
||||
// fall back to checking the custom gauges in the default list
|
||||
for (auto &gauge : default_hud_gauges) {
|
||||
if (gauge->isCustom() && !stricmp(name, gauge->getCustomGaugeName())) {
|
||||
return gauge.get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int hud_get_default_gauge_index(const char *name)
|
||||
{
|
||||
int config_type = -1;
|
||||
int config_type = -1, object_type = -1;
|
||||
|
||||
// default gauges had two different lists
|
||||
for (int i = 0; i < NUM_HUD_GAUGES; i++) {
|
||||
@@ -3902,15 +3943,15 @@ int hud_get_default_gauge_index(const char *name)
|
||||
if (config_type < 0) {
|
||||
for (int i = 0; i < Num_hud_gauge_types; i++) {
|
||||
if (!stricmp(name, Hud_gauge_types[i].name)) {
|
||||
config_type = Hud_gauge_types[i].def;
|
||||
object_type = Hud_gauge_types[i].def;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config_type >= 0) {
|
||||
if (config_type >= 0 || object_type >= 0) {
|
||||
for (int i = 0; i < (int)default_hud_gauges.size(); i++) {
|
||||
if (default_hud_gauges[i]->getConfigType() == config_type) {
|
||||
if (default_hud_gauges[i]->getConfigType() == config_type || default_hud_gauges[i]->getObjectType() == object_type) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -4026,14 +4067,12 @@ HudGauge(HUD_OBJECT_SUPERNOVA, HUD_DIRECTIVES_VIEW, false, false, 0, 255, 255, 2
|
||||
|
||||
void HudGaugeSupernova::render(float /*frametime*/)
|
||||
{
|
||||
float time_left;
|
||||
|
||||
// if there's a supernova coming
|
||||
time_left = supernova_time_left();
|
||||
if(time_left < 0.0f){
|
||||
if (!supernova_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto time_left = supernova_hud_time_left();
|
||||
|
||||
gr_set_color_fast(&Color_bright_red);
|
||||
if (Lcl_pl) {
|
||||
renderPrintf(position[0], position[1], "Wybuch supernowej: %.2f s", time_left);
|
||||
|
||||
+17
-2
@@ -220,6 +220,13 @@ protected:
|
||||
|
||||
int font_num;
|
||||
|
||||
// useful information to keep around for scripts
|
||||
float tabled_origin[2];
|
||||
int tabled_offset[2];
|
||||
bool tabled_use_coords;
|
||||
int tabled_coords[2];
|
||||
float aspect_quotient;
|
||||
|
||||
bool lock_color;
|
||||
bool sexp_lock_color;
|
||||
bool reticle_follow;
|
||||
@@ -262,21 +269,29 @@ public:
|
||||
virtual ~HudGauge();
|
||||
|
||||
void initPosition(int x, int y);
|
||||
void initBaseResolution(int w, int h);
|
||||
void initSlew(bool slew);
|
||||
void initBaseResolution(int w, int h, float aq);
|
||||
void initFont(int input_font_num);
|
||||
void initOriginAndOffset(float originX, float originY, int offsetX, int offsetY);
|
||||
void initCoords(bool use_coords, int coordsX, int coordsY);
|
||||
|
||||
void initSlew(bool slew);
|
||||
void initCockpitTarget(const char* display_name, int _target_x, int _target_y, int _target_w, int _target_h, int _canvas_w, int _canvas_h);
|
||||
void initRenderStatus(bool render);
|
||||
|
||||
bool isCustom();
|
||||
int getBaseWidth();
|
||||
int getBaseHeight();
|
||||
float getAspectQuotient();
|
||||
|
||||
int getConfigType();
|
||||
int getObjectType();
|
||||
void getPosition(int *x, int *y);
|
||||
bool isOffbyDefault();
|
||||
bool isActive();
|
||||
|
||||
int getFont();
|
||||
void getOriginAndOffset(float *originX, float *originY, int *offsetX, int *offsetY);
|
||||
void getCoords(bool* use_coords, int* coordsX, int* coordsY);
|
||||
|
||||
void updateColor(int r, int g, int b, int a = 255);
|
||||
const color& getColor();
|
||||
|
||||
@@ -57,9 +57,9 @@ void draw_brackets_square(graphics::line_draw_list* draw_list, int x1, int y1, i
|
||||
}
|
||||
|
||||
width = x2 - x1;
|
||||
Assert( width > 0);
|
||||
height = y2 - y1;
|
||||
Assert( height > 0);
|
||||
if (width < 1 || height < 1)
|
||||
return;
|
||||
|
||||
// make the brackets extend 25% of the way along the width or height
|
||||
int bracket_width = width/4;
|
||||
@@ -395,7 +395,12 @@ void HudGaugeBrackets::renderObjectBrackets(object *targetp, color *clr, int w_c
|
||||
int bound_rc;
|
||||
SCP_list<CJumpNode>::iterator jnp;
|
||||
|
||||
if ( Player->target_is_dying <= 0 || (Player_ai->target_objnum >= 0 && targetp != &Objects[Player_ai->target_objnum])) {
|
||||
|
||||
bool not_player_target = (Player_ai->target_objnum < 0 || targetp != &Objects[Player_ai->target_objnum]);
|
||||
|
||||
|
||||
// target_is_dying is set to zero when the target is not dying, -1 when no target. Only 1, aka "TRUE", is invalid.
|
||||
if ( not_player_target || Player->target_is_dying <= 0) {
|
||||
int modelnum;
|
||||
|
||||
switch ( targetp->type ) {
|
||||
@@ -475,7 +480,13 @@ void HudGaugeBrackets::renderObjectBrackets(object *targetp, color *clr, int w_c
|
||||
int target_objnum = -1;
|
||||
|
||||
if(flags & TARGET_DISPLAY_DIST) {
|
||||
distance = hud_find_target_distance(targetp, Player_obj);
|
||||
// since we can have either the target, or rogue asteroids bracketed,
|
||||
// check which case we are in, and then do the correct distance.
|
||||
if (not_player_target) {
|
||||
distance = hud_find_target_distance(targetp, &Player_obj->pos);
|
||||
} else {
|
||||
distance = Player_ai->current_target_distance;
|
||||
}
|
||||
}
|
||||
|
||||
if(flags & TARGET_DISPLAY_DOTS) {
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
#include "graphics/line_draw_list.h"
|
||||
|
||||
void hud_init_brackets();
|
||||
|
||||
// requires that x2 > x1 and y2 > y1
|
||||
void draw_brackets_square(graphics::line_draw_list* draw_list, int x1, int y1, int x2, int y2, int resize_mode = GR_RESIZE_FULL);
|
||||
|
||||
void draw_brackets_square_quick(graphics::line_draw_list* draw_list, int x1, int y1, int x2, int y2, int thick=0);
|
||||
void draw_brackets_diamond_quick(graphics::line_draw_list* draw_list, int x1, int y1, int x2, int y2);
|
||||
|
||||
|
||||
+12
-6
@@ -1613,6 +1613,7 @@ void hud_config_color_load(const char *name)
|
||||
int idx;
|
||||
char str[1024];
|
||||
char *fname;
|
||||
ubyte r, g, b, a;
|
||||
|
||||
fname = cf_add_ext(name, ".hcf");
|
||||
|
||||
@@ -1630,13 +1631,18 @@ void hud_config_color_load(const char *name)
|
||||
while (optional_string("+Gauge:")) {
|
||||
stuff_string(str, F_NAME, sizeof(str));
|
||||
|
||||
required_string("+RGBA:");
|
||||
stuff_ubyte(&r);
|
||||
stuff_ubyte(&g);
|
||||
stuff_ubyte(&b);
|
||||
stuff_ubyte(&a);
|
||||
|
||||
for (idx = 0; idx < NUM_HUD_GAUGES; idx++) {
|
||||
if (!stricmp(str, Hud_Gauge_Names[idx])) {
|
||||
required_string("+RGBA:");
|
||||
stuff_ubyte(&HUD_config.clr[idx].red);
|
||||
stuff_ubyte(&HUD_config.clr[idx].green);
|
||||
stuff_ubyte(&HUD_config.clr[idx].blue);
|
||||
stuff_ubyte(&HUD_config.clr[idx].alpha);
|
||||
if (!stricmp(str, HC_gauge_descriptions(idx))) {
|
||||
HUD_config.clr[idx].red = r;
|
||||
HUD_config.clr[idx].green = g;
|
||||
HUD_config.clr[idx].blue = b;
|
||||
HUD_config.clr[idx].alpha = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +194,8 @@ void HudGaugeLock::renderOld(float frametime)
|
||||
return;
|
||||
}
|
||||
|
||||
if (Player->target_is_dying) {
|
||||
// 1 is the only value for which your target is actually dying
|
||||
if (Player->target_is_dying == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -603,8 +603,10 @@ void hud_sourced_print(int source, const char *msg)
|
||||
HUD_msg_buffer.push_back(new_msg);
|
||||
|
||||
// Invoke the scripting hook
|
||||
OnHudMessageReceivedHook->run(scripting::hook_param_list(scripting::hook_param("Text", 's', msg),
|
||||
scripting::hook_param("SourceType", 'i', source)));
|
||||
if (OnHudMessageReceivedHook->isActive()) {
|
||||
OnHudMessageReceivedHook->run(scripting::hook_param_list(scripting::hook_param("Text", 's', msg),
|
||||
scripting::hook_param("SourceType", 'i', source)));
|
||||
}
|
||||
}
|
||||
|
||||
int hud_query_scrollback_size()
|
||||
|
||||
@@ -40,10 +40,8 @@ void hud_draw_navigation()
|
||||
|
||||
unsigned int alpha = HUD_COLOR_ALPHA_MAX * 16;
|
||||
|
||||
if (Navs[CurrentNav].flags & NP_VISITED)
|
||||
gr_init_alphacolor( &NavColor, 0xFF, 0xFF, 0x00, alpha);
|
||||
else
|
||||
gr_init_alphacolor( &NavColor, 0x80, 0x80, 0xff, alpha);
|
||||
auto rgb = (Navs[CurrentNav].flags & NP_VISITED) ? Navs[CurrentNav].visited_color : Navs[CurrentNav].normal_color;
|
||||
gr_init_alphacolor( &NavColor, rgb[0], rgb[1], rgb[2], alpha );
|
||||
|
||||
g3_rotate_vertex(&target_point, target_pos);
|
||||
g3_project_vertex(&target_point);
|
||||
|
||||
+26
-15
@@ -1168,7 +1168,7 @@ void check_color(int *colorp)
|
||||
}
|
||||
}
|
||||
|
||||
void adjust_base_res(int *base_res, int *force_scaling_above_res, bool scaling = true)
|
||||
void adjust_base_res(int *base_res, const int *force_scaling_above_res, float *aspect_quotient, bool scaling = true)
|
||||
{
|
||||
// Don't scale gauge if:
|
||||
// no scaling is set and current res is between base res and "force scaling above res"
|
||||
@@ -1178,6 +1178,7 @@ void adjust_base_res(int *base_res, int *force_scaling_above_res, bool scaling =
|
||||
gr_screen.center_w <= force_scaling_above_res[0] || gr_screen.center_h <= force_scaling_above_res[1])) ||
|
||||
(gr_screen.center_w >= base_res[0] && gr_screen.center_h == base_res[1]) ||
|
||||
(gr_screen.center_w == base_res[0] && gr_screen.center_h >= base_res[1])) {
|
||||
*aspect_quotient = 1.0f;
|
||||
base_res[0] = gr_screen.center_w;
|
||||
base_res[1] = gr_screen.center_h;
|
||||
return;
|
||||
@@ -1189,11 +1190,11 @@ void adjust_base_res(int *base_res, int *force_scaling_above_res, bool scaling =
|
||||
base_res[1] = force_scaling_above_res[1];
|
||||
}
|
||||
|
||||
float aspect_quotient = ((float)gr_screen.center_w / (float)gr_screen.center_h) / ((float)base_res[0] / (float)base_res[1]);
|
||||
if (aspect_quotient >= 1.0) {
|
||||
base_res[0] = (int)(base_res[0] * aspect_quotient);
|
||||
*aspect_quotient = ((float)gr_screen.center_w / (float)gr_screen.center_h) / ((float)base_res[0] / (float)base_res[1]);
|
||||
if (*aspect_quotient >= 1.0f) {
|
||||
base_res[0] = (int)(base_res[0] * *aspect_quotient);
|
||||
} else {
|
||||
base_res[1] = (int)(base_res[1] / aspect_quotient);
|
||||
base_res[1] = (int)(base_res[1] / *aspect_quotient);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1237,7 +1238,7 @@ std::unique_ptr<T> gauge_load_common(gauge_settings* settings, T* preAllocated =
|
||||
stuff_int_list(settings->force_scaling_above_res, 2);
|
||||
}
|
||||
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, settings->scale_gauge);
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, &settings->aspect_quotient, settings->scale_gauge);
|
||||
|
||||
// If no positioning information is specified, use the default position
|
||||
bool use_default_pos = true;
|
||||
@@ -1261,7 +1262,7 @@ std::unique_ptr<T> gauge_load_common(gauge_settings* settings, T* preAllocated =
|
||||
}
|
||||
}
|
||||
} else {
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, settings->scale_gauge);
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, &settings->aspect_quotient, settings->scale_gauge);
|
||||
}
|
||||
} else {
|
||||
if(gr_screen.res == GR_640) {
|
||||
@@ -1272,7 +1273,7 @@ std::unique_ptr<T> gauge_load_common(gauge_settings* settings, T* preAllocated =
|
||||
settings->base_res[1] = 768;
|
||||
}
|
||||
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, settings->scale_gauge);
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, &settings->aspect_quotient, settings->scale_gauge);
|
||||
|
||||
if (settings->set_position && !settings->use_coords) {
|
||||
settings->coords[0] = (int)(settings->base_res[0] * settings->origin[0]) + settings->offset[0];
|
||||
@@ -1344,8 +1345,11 @@ std::unique_ptr<T> gauge_load_common(gauge_settings* settings, T* preAllocated =
|
||||
instance.reset(new T());
|
||||
}
|
||||
|
||||
instance->initBaseResolution(settings->base_res[0], settings->base_res[1]);
|
||||
instance->initBaseResolution(settings->base_res[0], settings->base_res[1], settings->aspect_quotient);
|
||||
instance->initFont(settings->font_num);
|
||||
instance->initOriginAndOffset(settings->origin[0], settings->origin[1], settings->offset[0], settings->offset[1]);
|
||||
instance->initCoords(settings->use_coords, settings->coords[0], settings->coords[1]);
|
||||
|
||||
instance->initChase_view_only(settings->chase_view_only);
|
||||
if (settings->set_position) {
|
||||
instance->initPosition(settings->coords[0], settings->coords[1]);
|
||||
@@ -1407,7 +1411,7 @@ void load_gauge_custom(gauge_settings* settings)
|
||||
stuff_int_list(settings->force_scaling_above_res, 2);
|
||||
}
|
||||
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, settings->scale_gauge);
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, &settings->aspect_quotient, settings->scale_gauge);
|
||||
|
||||
// If no positioning information is specified, use the default position
|
||||
bool use_default_pos = true;
|
||||
@@ -1519,9 +1523,13 @@ void load_gauge_custom(gauge_settings* settings)
|
||||
}
|
||||
|
||||
std::unique_ptr<HudGauge> hud_gauge(new HudGauge(gauge_type, settings->slew, r, g, b, name, text, filename, txtoffset_x, txtoffset_y));
|
||||
hud_gauge->initBaseResolution(settings->base_res[0], settings->base_res[1]);
|
||||
|
||||
hud_gauge->initBaseResolution(settings->base_res[0], settings->base_res[1], settings->aspect_quotient);
|
||||
hud_gauge->initPosition(settings->coords[0], settings->coords[1]);
|
||||
hud_gauge->initFont(settings->font_num);
|
||||
hud_gauge->initOriginAndOffset(settings->origin[0], settings->origin[1], settings->offset[0], settings->offset[1]);
|
||||
hud_gauge->initCoords(settings->use_coords, settings->coords[0], settings->coords[1]);
|
||||
|
||||
hud_gauge->initRenderStatus(active_by_default);
|
||||
hud_gauge->updateColor(colors[0], colors[1], colors[2]);
|
||||
hud_gauge->lockConfigColor(lock_color);
|
||||
@@ -2941,7 +2949,7 @@ void load_gauge_radar_dradis(gauge_settings* settings)
|
||||
stuff_int_list(settings->force_scaling_above_res, 2);
|
||||
}
|
||||
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, settings->scale_gauge);
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, &settings->aspect_quotient, settings->scale_gauge);
|
||||
|
||||
if(optional_string("Origin:")) {
|
||||
stuff_float_list(settings->origin, 2);
|
||||
@@ -2967,7 +2975,7 @@ void load_gauge_radar_dradis(gauge_settings* settings)
|
||||
settings->base_res[1] = 768;
|
||||
}
|
||||
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, settings->scale_gauge);
|
||||
adjust_base_res(settings->base_res, settings->force_scaling_above_res, &settings->aspect_quotient, settings->scale_gauge);
|
||||
|
||||
settings->coords[0] = (int)(settings->base_res[0] * settings->origin[0]) + settings->offset[0];
|
||||
settings->coords[1] = (int)(settings->base_res[1] * settings->origin[1]) + settings->offset[1];
|
||||
@@ -3060,12 +3068,15 @@ void load_gauge_radar_dradis(gauge_settings* settings)
|
||||
}
|
||||
|
||||
std::unique_ptr<HudGaugeRadarDradis> hud_gauge(new HudGaugeRadarDradis());
|
||||
hud_gauge->initBaseResolution(settings->base_res[0], settings->base_res[1]);
|
||||
hud_gauge->initBaseResolution(settings->base_res[0], settings->base_res[1], settings->aspect_quotient);
|
||||
hud_gauge->initPosition(settings->coords[0], settings->coords[1]);
|
||||
hud_gauge->initFont(settings->font_num);
|
||||
hud_gauge->initOriginAndOffset(settings->origin[0], settings->origin[1], settings->offset[0], settings->offset[1]);
|
||||
hud_gauge->initCoords(settings->use_coords, settings->coords[0], settings->coords[1]);
|
||||
|
||||
hud_gauge->initRadius(Radar_radius[0], Radar_radius[1]);
|
||||
hud_gauge->initBitmaps(xy_fname, xz_yz_fname, sweep_fname, target_fname, unknown_fname);
|
||||
hud_gauge->initCockpitTarget(display_name, display_offset[0], display_offset[1], display_size[0], display_size[1], canvas_size[0], canvas_size[1]);
|
||||
hud_gauge->initFont(settings->font_num);
|
||||
hud_gauge->initSound(loop_snd, loop_snd_volume, arrival_beep_snd, departure_beep_snd, stealth_arrival_snd, stealth_departure_snd, arrival_beep_delay, departure_beep_delay);
|
||||
hud_gauge->initChase_view_only(settings->chase_view_only);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ typedef struct gauge_settings {
|
||||
int font_num;
|
||||
bool scale_gauge;
|
||||
int force_scaling_above_res[2];
|
||||
float aspect_quotient;
|
||||
SCP_vector<int>* ship_idx;
|
||||
color *use_clr;
|
||||
float origin[2];
|
||||
@@ -45,6 +46,7 @@ typedef struct gauge_settings {
|
||||
base_res[0] = -1;
|
||||
base_res[1] = -1;
|
||||
memcpy(force_scaling_above_res, Force_scaling_above_res_global, sizeof(force_scaling_above_res));
|
||||
aspect_quotient = 1.0f;
|
||||
origin[0] = 0.0f;
|
||||
origin[1] = 0.0f;
|
||||
offset[0] = 0;
|
||||
|
||||
+20
-9
@@ -309,12 +309,21 @@ ship_info *sip = &Ship_info[Player_ship->ship_info_index];
|
||||
}
|
||||
|
||||
void HudGaugeReticle::getFirepointStatus() {
|
||||
|
||||
// allow the firepoint status to be empty when a multiplayer observer
|
||||
// this is not a bug, the observer will simply *not* have any firepoints.
|
||||
if (Objects[Player->objnum].type == OBJ_OBSERVER) {
|
||||
// only multiplayer instances should be getting here!
|
||||
Assertion((Game_mode & GM_MULTIPLAYER), "Somehow FSO thinks its player object is an observer even though it's not in Multiplayer. Please report!");
|
||||
return;
|
||||
}
|
||||
|
||||
//First, get the player ship
|
||||
ship_info* sip;
|
||||
ship* shipp;
|
||||
polymodel* pm;
|
||||
|
||||
Assert(Objects[Player->objnum].type == OBJ_SHIP);
|
||||
Assertion(Objects[Player->objnum].type == OBJ_SHIP, "HudGaugeReticle::getFirepointStatus was passed an invalid object type of %d. Please report!", Objects[Player->objnum].type);
|
||||
|
||||
if (Objects[Player->objnum].type == OBJ_SHIP) {
|
||||
shipp = &Ships[Objects[Player->objnum].instance];
|
||||
@@ -459,8 +468,9 @@ void HudGaugeThrottle::initMatchSpeedOffsets(int x, int y, bool custom)
|
||||
Match_speed_offsets[1] = y;
|
||||
Use_custom_match_speed = custom;
|
||||
|
||||
font::FSFont* fsFont = font::get_font(font_num);
|
||||
auto match_speed_str = XSTR("m", 1667);
|
||||
|
||||
font::FSFont* fsFont = font::get_font(font_num);
|
||||
if (fsFont->getType() == font::VFNT_FONT)
|
||||
{
|
||||
ubyte sc = lcl_get_font_index(font_num);
|
||||
@@ -468,7 +478,7 @@ void HudGaugeThrottle::initMatchSpeedOffsets(int x, int y, bool custom)
|
||||
// a) the german font has no special m (its an a)
|
||||
// b) the font has no special characters
|
||||
if (sc == 0 || Lcl_gr) {
|
||||
Match_speed_icon = 'm';
|
||||
Match_speed_icon = match_speed_str[0];
|
||||
}
|
||||
else {
|
||||
Match_speed_icon = sc + 3;
|
||||
@@ -478,13 +488,12 @@ void HudGaugeThrottle::initMatchSpeedOffsets(int x, int y, bool custom)
|
||||
else
|
||||
{
|
||||
// Default version for other fonts, draw a black character on a rectangle
|
||||
Match_speed_icon = 'm';
|
||||
Match_speed_icon = match_speed_str[0];
|
||||
Match_speed_draw_background = true;
|
||||
}
|
||||
SCP_stringstream stream;
|
||||
|
||||
SCP_stringstream stream;
|
||||
stream << static_cast<char>(Match_speed_icon);
|
||||
|
||||
const SCP_string& iconStr = stream.str();
|
||||
|
||||
gr_get_string_size(&Match_speed_icon_width, nullptr, iconStr.c_str());
|
||||
@@ -584,7 +593,7 @@ void HudGaugeThrottle::render(float /*frametime*/)
|
||||
|
||||
if ( Show_percent ) {
|
||||
if ( Player_obj->phys_info.flags & PF_AFTERBURNER_ON ) {
|
||||
strcpy_s(buf, "A/B");
|
||||
strcpy_s(buf, XSTR( "A/B", 1669 ));
|
||||
} else {
|
||||
sprintf(buf, XSTR( "%d%%", 326), (int)std::lround( (desired_speed/max_speed)*100 ));
|
||||
}
|
||||
@@ -641,8 +650,10 @@ void HudGaugeThrottle::renderThrottleSpeed(float current_speed, int y_end)
|
||||
renderPrintf(sx, sy, "%s", buf);
|
||||
|
||||
if ( object_get_gliding(Player_obj) ) {
|
||||
auto glide_str = XSTR("GLIDE", 1668);
|
||||
|
||||
if ( Use_custom_glide ) {
|
||||
renderString(position[0] + Glide_offsets[0], position[1] + Glide_offsets[1], "GLIDE");
|
||||
renderString(position[0] + Glide_offsets[0], position[1] + Glide_offsets[1], glide_str);
|
||||
} else {
|
||||
int offset;
|
||||
if ( current_speed <= 9.5 ) {
|
||||
@@ -653,7 +664,7 @@ void HudGaugeThrottle::renderThrottleSpeed(float current_speed, int y_end)
|
||||
offset = -13;
|
||||
}
|
||||
|
||||
renderString(sx+offset, sy + h, "GLIDE");
|
||||
renderString(sx+offset, sy + h, glide_str);
|
||||
}
|
||||
} else if ( Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET ) {
|
||||
if ( Use_custom_match_speed ) {
|
||||
|
||||
+129
-119
@@ -12,6 +12,7 @@
|
||||
|
||||
|
||||
#include "ai/aigoals.h"
|
||||
#include "ai/ailua.h"
|
||||
#include "gamesnd/gamesnd.h"
|
||||
#include "globalincs/linklist.h"
|
||||
#include "hud/hudmessage.h"
|
||||
@@ -33,7 +34,6 @@
|
||||
#include "weapon/emp.h"
|
||||
#include "weapon/weapon.h"
|
||||
|
||||
|
||||
// defines for different modes in the squad messaging system
|
||||
|
||||
#define DEFAULT_MSG_TIMEOUT (8 * 1000) // number of seconds * 1000 to get milliseconds
|
||||
@@ -156,25 +156,38 @@ int keys_used[] = { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_
|
||||
|
||||
SCP_string Comm_order_types[NUM_COMM_ORDER_TYPES];
|
||||
|
||||
int player_order::orderingCounter = 0;
|
||||
|
||||
std::vector<player_order> Player_orders = {
|
||||
player_order("attack ship", "Destroy my target", 299, ATTACK_TARGET_ITEM),
|
||||
player_order("disable ship", "Disable my target", 300, DISABLE_TARGET_ITEM ),
|
||||
player_order("disarm ship", "Disarm my target", 301, DISARM_TARGET_ITEM ),
|
||||
player_order("disable subsys","Destroy subsystem", 302, DISABLE_SUBSYSTEM_ITEM ),
|
||||
player_order("guard ship", "Protect my target", 303, PROTECT_TARGET_ITEM ),
|
||||
player_order("ignore ship", "Ignore my target", 304, IGNORE_TARGET_ITEM ),
|
||||
player_order("form on wing","Form on my wing", 305, FORMATION_ITEM ),
|
||||
player_order("cover me", "Cover me", 306, COVER_ME_ITEM ),
|
||||
player_order("attack any", "Engage enemy", 307, ENGAGE_ENEMY_ITEM ),
|
||||
player_order("dock", "Capture my target", 308, CAPTURE_TARGET_ITEM),
|
||||
player_order("rearm me", "Rearm me", 309, REARM_REPAIR_ME_ITEM),
|
||||
player_order("abort rearm", "Abort rearm", 310, ABORT_REARM_REPAIR_ITEM),
|
||||
player_order("depart", "Depart", 311, DEPART_ITEM),
|
||||
player_order("stay near me", "Stay near me", -1, STAY_NEAR_ME_ITEM),
|
||||
player_order("stay near ship", "Stay near my target",-1, STAY_NEAR_TARGET_ITEM),
|
||||
player_order("keep safe dist", "Keep safe distance", -1, KEEP_SAFE_DIST_ITEM)
|
||||
player_order("no order", "No Order", -1, -1, NO_ORDER_ITEM), //Required to keep defines in sync with array indices
|
||||
player_order("attack ship", "Destroy my target", 299, -1, ATTACK_TARGET_ITEM),
|
||||
player_order("disable ship", "Disable my target", 300, -1, DISABLE_TARGET_ITEM),
|
||||
player_order("disarm ship", "Disarm my target", 301, -1, DISARM_TARGET_ITEM),
|
||||
player_order("disable subsys","Destroy subsystem", 302, -1, DISABLE_SUBSYSTEM_ITEM),
|
||||
player_order("guard ship", "Protect my target", 303, -1, PROTECT_TARGET_ITEM),
|
||||
player_order("ignore ship", "Ignore my target", 304, -1, IGNORE_TARGET_ITEM),
|
||||
player_order("form on wing","Form on my wing", 305, -1, FORMATION_ITEM),
|
||||
player_order("cover me", "Cover me", 306, -1, COVER_ME_ITEM),
|
||||
player_order("attack any", "Engage enemy", 307, -1, ENGAGE_ENEMY_ITEM),
|
||||
player_order("dock", "Capture my target", 308, -1, CAPTURE_TARGET_ITEM),
|
||||
player_order("rearm me", "Rearm me", 309, -1, REARM_REPAIR_ME_ITEM),
|
||||
player_order("abort rearm", "Abort rearm", 310, -1, ABORT_REARM_REPAIR_ITEM),
|
||||
player_order("depart", "Depart", 311, -1, DEPART_ITEM),
|
||||
player_order("stay near me", "Stay near me", -1, -1, STAY_NEAR_ME_ITEM),
|
||||
player_order("stay near ship", "Stay near my target",-1, -1, STAY_NEAR_TARGET_ITEM),
|
||||
player_order("keep safe dist", "Keep safe distance", -1, -1, KEEP_SAFE_DIST_ITEM)
|
||||
};
|
||||
|
||||
const std::set<size_t> default_messages{ ATTACK_TARGET_ITEM , DISABLE_TARGET_ITEM , DISARM_TARGET_ITEM , PROTECT_TARGET_ITEM , IGNORE_TARGET_ITEM , FORMATION_ITEM , COVER_ME_ITEM , ENGAGE_ENEMY_ITEM , DEPART_ITEM , DISABLE_SUBSYSTEM_ITEM };
|
||||
const std::set<size_t> enemy_target_messages{ ATTACK_TARGET_ITEM , DISABLE_TARGET_ITEM , DISARM_TARGET_ITEM , IGNORE_TARGET_ITEM , STAY_NEAR_TARGET_ITEM , CAPTURE_TARGET_ITEM , DISABLE_SUBSYSTEM_ITEM };
|
||||
const std::set<size_t> friendly_target_messages{ PROTECT_TARGET_ITEM };
|
||||
const std::set<size_t> target_messages = []() {
|
||||
std::set<size_t> setunion;
|
||||
std::set_union(enemy_target_messages.cbegin(), enemy_target_messages.cend(), friendly_target_messages.cbegin(), friendly_target_messages.cend(), std::inserter(setunion, setunion.end()));
|
||||
return setunion;
|
||||
}();
|
||||
|
||||
|
||||
void hud_init_comm_orders()
|
||||
{
|
||||
int i;
|
||||
@@ -194,22 +207,21 @@ void hud_init_comm_orders()
|
||||
Comm_order_types[i] = temp_comm_order_types[i];
|
||||
}
|
||||
|
||||
for(auto& order : Player_orders) {
|
||||
order.localized_name = XSTR(order.hud_name.c_str(), order.hud_xstr);
|
||||
}
|
||||
for (auto& order : Player_orders)
|
||||
order.localize();
|
||||
|
||||
std::sort(Player_orders.begin(), Player_orders.end(), [](const player_order& o1, const player_order& o2) {
|
||||
return o1.legacy_id < o2.legacy_id;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Text to display on the messaging menu when using the shortcut keys
|
||||
const char *comm_order_get_text(int item)
|
||||
{
|
||||
for(const auto& order : Player_orders){
|
||||
if(order.id == item)
|
||||
return order.localized_name.c_str();
|
||||
}
|
||||
Assertion(item < (int)Player_orders.size(), "Did not find order with index %d!", item);
|
||||
|
||||
UNREACHABLE("Did not find order with id %d!", item);
|
||||
|
||||
return nullptr;
|
||||
return Player_orders[item].localized_name.c_str();
|
||||
}
|
||||
|
||||
SCP_vector<squadmsg_history> Squadmsg_history;
|
||||
@@ -217,7 +229,7 @@ SCP_vector<squadmsg_history> Squadmsg_history;
|
||||
// forward declarations
|
||||
void hud_add_issued_order(const char *name, int order);
|
||||
void hud_update_last_order(const char *target, int order_source, int special_index);
|
||||
bool hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip = nullptr);
|
||||
bool hud_squadmsg_is_target_order_valid(size_t order, ai_info *aip = nullptr);
|
||||
bool hud_squadmsg_ship_valid(ship *shipp, object *objp = nullptr);
|
||||
bool hud_squadmsg_ship_order_valid( int shipnum, int order );
|
||||
|
||||
@@ -332,10 +344,7 @@ bool hud_squadmsg_ship_valid(ship *shipp, object *objp)
|
||||
|
||||
// if a messaging shortcut, be sure this ship can process the order
|
||||
if ( Msg_shortcut_command != -1 ) {
|
||||
bool accepts_any = false;
|
||||
for(size_t order : shipp->orders_accepted)
|
||||
accepts_any |= (bool)(Player_orders[order].id & Msg_shortcut_command);
|
||||
if ( !accepts_any )
|
||||
if (shipp->orders_accepted.count((size_t) Msg_shortcut_command) == 0)
|
||||
return false;
|
||||
|
||||
else if ( !hud_squadmsg_ship_order_valid(objp->instance, Msg_shortcut_command) )
|
||||
@@ -586,11 +595,13 @@ int hud_squadmsg_get_key()
|
||||
return i;
|
||||
|
||||
// play general fail sound if inactive item hit.
|
||||
else if ( (i < Num_menu_items) && !(MsgItems[i].active) )
|
||||
else if ((i + First_menu_item < Num_menu_items) && !(MsgItems[i + First_menu_item].active)) {
|
||||
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
|
||||
}
|
||||
|
||||
else if ( (i < Num_menu_items) && (MsgItems[i].active) ) // only return keys that are associated with menu items
|
||||
return i;
|
||||
else if ((i + First_menu_item < Num_menu_items) && (MsgItems[i + First_menu_item].active)) { // only return keys that are associated with menu items
|
||||
return i + First_menu_item;
|
||||
}
|
||||
|
||||
else {
|
||||
Msg_key_used = 0; // if no #-key pressed for visible item, break and allow timer to
|
||||
@@ -723,7 +734,7 @@ bool hud_squadmsg_ship_order_valid( int shipnum, int order )
|
||||
Assert( shipnum >= 0 && shipnum < MAX_SHIPS );
|
||||
ship *shipp = &Ships[shipnum];
|
||||
|
||||
switch ( order )
|
||||
switch ( order )
|
||||
{
|
||||
case DEPART_ITEM:
|
||||
// disabled ships can't depart.
|
||||
@@ -745,31 +756,27 @@ bool hud_squadmsg_ship_order_valid( int shipnum, int order )
|
||||
// returns true or false if the Players target is valid for the given order
|
||||
// find_order is true when we need to search the comm_orders array for the order entry. We have
|
||||
// to do this action in some cases since all we know is the actual "value" of the order
|
||||
bool hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip )
|
||||
bool hud_squadmsg_is_target_order_valid(size_t order, ai_info *aip )
|
||||
{
|
||||
int target_objnum;
|
||||
size_t i;
|
||||
ship *shipp, *ordering_shipp;
|
||||
object *objp;
|
||||
|
||||
if ( aip == NULL )
|
||||
aip = Player_ai;
|
||||
|
||||
// find the comm_menu item for this command
|
||||
if ( find_order ) {
|
||||
for (i = 0; i < Player_orders.size(); i++ ) {
|
||||
if ( Player_orders[i].id == order )
|
||||
break;
|
||||
}
|
||||
Assert( i < Player_orders.size() );
|
||||
order = (int)i;
|
||||
target_objnum = aip->target_objnum;
|
||||
ordering_shipp = &Ships[aip->shipnum];
|
||||
|
||||
//If it's a lua order, defer to luaai
|
||||
if (Player_orders[order].lua_id != -1) {
|
||||
return ai_lua_is_valid_target(Player_orders[order].lua_id, target_objnum, ordering_shipp);
|
||||
}
|
||||
|
||||
// orders which don't operate on targets are always valid
|
||||
if ( !(Player_orders[order].id & TARGET_MESSAGES) )
|
||||
if ( target_messages.count(order) == 0 )
|
||||
return true;
|
||||
|
||||
target_objnum = aip->target_objnum;
|
||||
|
||||
// order isn't valid if there is no player target
|
||||
if ( target_objnum == -1 ) {
|
||||
@@ -778,7 +785,6 @@ bool hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip
|
||||
|
||||
objp = &Objects[target_objnum];
|
||||
|
||||
ordering_shipp = &Ships[aip->shipnum];
|
||||
|
||||
// target isn't a ship, then return false
|
||||
if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_WEAPON) )
|
||||
@@ -793,7 +799,7 @@ bool hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( (Player_orders[order].id == ATTACK_TARGET_ITEM )
|
||||
if ( order == ATTACK_TARGET_ITEM
|
||||
&& ((Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Bomb]) || (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags[Weapon::Info_Flags::Fighter_Interceptable]))
|
||||
&& (Weapons[objp->instance].team != ordering_shipp->team) ) {
|
||||
return true;
|
||||
@@ -817,24 +823,24 @@ bool hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip
|
||||
}
|
||||
|
||||
// if the order is a disable order or depart, and the ship is disabled, order isn't active
|
||||
if ( (Player_orders[order].id == DISABLE_TARGET_ITEM) && (shipp->flags[Ship::Ship_Flags::Disabled]) ){
|
||||
if ( (order == DISABLE_TARGET_ITEM) && (shipp->flags[Ship::Ship_Flags::Disabled]) ){
|
||||
return false;
|
||||
}
|
||||
|
||||
// same as above except for disarmed.
|
||||
if ( (Player_orders[order].id == DISARM_TARGET_ITEM) && ((shipp->subsys_info[SUBSYSTEM_TURRET].type_count == 0) || (shipp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f)) ){
|
||||
if ( (order == DISARM_TARGET_ITEM) && ((shipp->subsys_info[SUBSYSTEM_TURRET].type_count == 0) || (shipp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f)) ){
|
||||
return false;
|
||||
}
|
||||
|
||||
// if order is disable subsystem, and no subsystem targeted or no hits, then order not valid
|
||||
if ( (Player_orders[order].id == DISABLE_SUBSYSTEM_ITEM) && ((aip->targeted_subsys == nullptr) || (aip->targeted_subsys->current_hits <= 0.0f)) ){
|
||||
if ( (order == DISABLE_SUBSYSTEM_ITEM) && ((aip->targeted_subsys == nullptr) || (aip->targeted_subsys->current_hits <= 0.0f)) ){
|
||||
return false;
|
||||
}
|
||||
|
||||
// check based on target's and player's team
|
||||
if ( (shipp->team == ordering_shipp->team) && (FRIENDLY_TARGET_MESSAGES & Player_orders[order].id) ){
|
||||
if ( (shipp->team == ordering_shipp->team) && (friendly_target_messages.count(order) > 0) ){
|
||||
return true;
|
||||
} else if ( (shipp->team != ordering_shipp->team) && (ENEMY_TARGET_MESSAGES & Player_orders[order].id) ){
|
||||
} else if ( (shipp->team != ordering_shipp->team) && (enemy_target_messages.count(order) > 0) ){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -911,11 +917,7 @@ void hud_squadmsg_send_to_all_fighters( int command, int player_num )
|
||||
|
||||
// don't send the command if the "wing" won't accept the command. We do this by looking at
|
||||
// the set of orders accepted for the wing leader
|
||||
bool accepts_current = false;
|
||||
for(size_t order : shipp->orders_accepted)
|
||||
accepts_current |= (bool)(Player_orders[order].id & command);
|
||||
|
||||
if ( !accepts_current )
|
||||
if (shipp->orders_accepted.count((size_t)command) == 0)
|
||||
continue;
|
||||
|
||||
// send the command to the wing
|
||||
@@ -951,11 +953,7 @@ void hud_squadmsg_send_to_all_fighters( int command, int player_num )
|
||||
continue;
|
||||
|
||||
// don't send command if ship won't accept if
|
||||
bool accepts_current = false;
|
||||
for(size_t order : shipp->orders_accepted)
|
||||
accepts_current |= (bool)(Player_orders[order].id & command);
|
||||
|
||||
if ( !accepts_current )
|
||||
if (shipp->orders_accepted.count((size_t)command) == 0)
|
||||
continue;
|
||||
|
||||
if (send_message) {
|
||||
@@ -1009,6 +1007,7 @@ int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message,
|
||||
ai_info *ainfo;
|
||||
int ai_mode, ai_submode; // ai mode and submode needed for ship commands
|
||||
char *target_shipname; // ship number of possible targets
|
||||
object_ship_wing_point_team lua_target;
|
||||
int message;
|
||||
int target_team, ship_team; // team id's for the ship getting message and any target the player has
|
||||
ship *ordering_shipp;
|
||||
@@ -1045,10 +1044,10 @@ int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message,
|
||||
// a shortcut to save on repetitive coding. If the order is a 'target' order, make the default
|
||||
// mesage be "no target"
|
||||
message = MESSAGE_NOSIR;
|
||||
if ( (command & TARGET_MESSAGES) && (ainfo->target_objnum == -1) )
|
||||
if ( target_messages.count((size_t)command) > 0 && (ainfo->target_objnum == -1) )
|
||||
message = MESSAGE_NO_TARGET;
|
||||
|
||||
if ( hud_squadmsg_is_target_order_valid(command, 1, ainfo) ) {
|
||||
if ( hud_squadmsg_is_target_order_valid((size_t) command, ainfo) ) {
|
||||
|
||||
target_shipname = NULL;
|
||||
target_team = -1;
|
||||
@@ -1126,7 +1125,7 @@ int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message,
|
||||
}
|
||||
|
||||
ai_mode = AI_GOAL_DESTROY_SUBSYSTEM;
|
||||
ai_submode = ship_get_subsys_index( &Ships[Objects[ainfo->target_objnum].instance], ainfo->targeted_subsys->system_info->subobj_name );
|
||||
ai_submode = ship_find_subsys( &Ships[Objects[ainfo->target_objnum].instance], ainfo->targeted_subsys->system_info->subobj_name );
|
||||
special_index = ai_submode;
|
||||
message = MESSAGE_ATTACK_TARGET;
|
||||
break;
|
||||
@@ -1244,16 +1243,24 @@ int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message,
|
||||
message = MESSAGE_YESSIR;
|
||||
break;
|
||||
|
||||
default:
|
||||
Int3(); // get Allender -- illegal message
|
||||
default: {
|
||||
Assert(command < (int) Player_orders.size()); // get Allender -- illegal message
|
||||
|
||||
ai_mode = AI_GOAL_LUA;
|
||||
ai_submode = Player_orders[command].lua_id;
|
||||
auto lua_porder = ai_lua_find_player_order(Player_orders[command].lua_id);
|
||||
message = lua_porder->ai_message;
|
||||
if (ainfo->target_objnum != -1)
|
||||
lua_target = object_ship_wing_point_team(&Ships[Objects[ainfo->target_objnum].instance]);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// handle case of messaging one ship. Deal with messaging all fighters next.
|
||||
if ( ai_mode != AI_GOAL_NONE ) {
|
||||
Assert(ai_submode != -1234567);
|
||||
ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, ai_mode, ai_submode, target_shipname, &Ai_info[Ships[shipnum].ai_index] );
|
||||
ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, ai_mode, ai_submode, target_shipname, &Ai_info[Ships[shipnum].ai_index], lua_target );
|
||||
if( update_history == SQUADMSG_HISTORY_ADD_ENTRY ) {
|
||||
hud_add_issued_order(Ships[shipnum].ship_name, command);
|
||||
hud_update_last_order(target_shipname, player_num, special_index);
|
||||
@@ -1293,6 +1300,7 @@ int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message,
|
||||
ai_info *ainfo;
|
||||
int ai_mode, ai_submode; // ai mode and submode needed for ship commands
|
||||
char *target_shipname; // ship number of possible targets
|
||||
object_ship_wing_point_team lua_target;
|
||||
int message_sent, message;
|
||||
int target_team, wing_team; // team for the wing and the player's target
|
||||
ship *ordering_shipp;
|
||||
@@ -1331,10 +1339,10 @@ int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message,
|
||||
|
||||
// a shortcut to save on repetative coding
|
||||
message = MESSAGE_NOSIR;
|
||||
if ( (command & TARGET_MESSAGES) && (ainfo->target_objnum == -1) )
|
||||
if ( (target_messages.count((size_t)command) > 0) && (ainfo->target_objnum == -1) )
|
||||
message = MESSAGE_NO_TARGET;
|
||||
|
||||
if ( hud_squadmsg_is_target_order_valid(command, 1, ainfo) ) {
|
||||
if ( hud_squadmsg_is_target_order_valid((size_t)command, ainfo) ) {
|
||||
|
||||
target_shipname = NULL;
|
||||
target_team = -1;
|
||||
@@ -1399,7 +1407,7 @@ int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message,
|
||||
Assert( ainfo->targeted_subsys->current_hits > 0.0f);
|
||||
|
||||
ai_mode = AI_GOAL_DESTROY_SUBSYSTEM;
|
||||
ai_submode = ship_get_subsys_index( &Ships[Objects[ainfo->target_objnum].instance], ainfo->targeted_subsys->system_info->subobj_name );
|
||||
ai_submode = ship_find_subsys( &Ships[Objects[ainfo->target_objnum].instance], ainfo->targeted_subsys->system_info->subobj_name );
|
||||
special_index = ai_submode;
|
||||
message = MESSAGE_ATTACK_TARGET;
|
||||
break;
|
||||
@@ -1462,15 +1470,23 @@ int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message,
|
||||
case KEEP_SAFE_DIST_ITEM:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
Int3(); // get Allender -- illegal message
|
||||
default: {
|
||||
Assert(command < (int) Player_orders.size()); // get Allender -- illegal message
|
||||
|
||||
ai_mode = AI_GOAL_LUA;
|
||||
ai_submode = Player_orders[command].lua_id;
|
||||
auto lua_porder = ai_lua_find_player_order(Player_orders[command].lua_id);
|
||||
message = lua_porder->ai_message;
|
||||
if(ainfo->target_objnum != -1)
|
||||
lua_target = object_ship_wing_point_team(&Ships[Objects[ainfo->target_objnum].instance]);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ( ai_mode != AI_GOAL_NONE ) {
|
||||
Assert(ai_submode != -1234567);
|
||||
ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, ai_mode, ai_submode, target_shipname, wingnum );
|
||||
ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, ai_mode, ai_submode, target_shipname, wingnum, lua_target );
|
||||
|
||||
if( update_history == SQUADMSG_HISTORY_ADD_ENTRY ) {
|
||||
hud_add_issued_order(Wings[wingnum].name, command);
|
||||
@@ -1572,8 +1588,6 @@ void hud_squadmsg_type_select( )
|
||||
{
|
||||
int k, i;
|
||||
|
||||
First_menu_item = 0;
|
||||
|
||||
// Add the items
|
||||
for (i = 0; i < NUM_COMM_ORDER_TYPES; i++)
|
||||
{
|
||||
@@ -1697,13 +1711,13 @@ void hud_squadmsg_ship_select()
|
||||
k = hud_squadmsg_get_key();
|
||||
if ( k != -1 ) { // if true, we have selected a ship.
|
||||
if ( Msg_shortcut_command == -1 ) {
|
||||
Msg_instance = MsgItems[First_menu_item + k].instance; // store the instance id in a global
|
||||
Msg_instance = MsgItems[k].instance; // store the instance id in a global
|
||||
hud_squadmsg_do_mode( SM_MODE_SHIP_COMMAND ); // and move to a new mode
|
||||
} else {
|
||||
// we must convert the Msg_shortcut_command value to a value that the message
|
||||
// system normally uses to select a command. Since the menu
|
||||
//Assert( Msg_shortcut_command != IGNORE_TARGET_ITEM );
|
||||
hud_squadmsg_send_ship_command( MsgItems[First_menu_item+k].instance, Msg_shortcut_command, 1, SQUADMSG_HISTORY_ADD_ENTRY);
|
||||
hud_squadmsg_send_ship_command( MsgItems[k].instance, Msg_shortcut_command, 1, SQUADMSG_HISTORY_ADD_ENTRY);
|
||||
hud_squadmsg_toggle();
|
||||
}
|
||||
}
|
||||
@@ -1724,11 +1738,11 @@ void hud_squadmsg_wing_select()
|
||||
k = hud_squadmsg_get_key();
|
||||
if ( k != -1 ) { // if true, we have selected a ship.
|
||||
if ( Msg_shortcut_command == -1 ) { // do normal menu stuff when no hoykey active
|
||||
Msg_instance = MsgItems[First_menu_item + k].instance; // store the instance id in a global
|
||||
Msg_instance = MsgItems[k].instance; // store the instance id in a global
|
||||
hud_squadmsg_do_mode( SM_MODE_WING_COMMAND ); // and move to a new mode
|
||||
} else {
|
||||
//Assert( Msg_shortcut_command != IGNORE_TARGET_ITEM );
|
||||
hud_squadmsg_send_wing_command( MsgItems[First_menu_item+k].instance, Msg_shortcut_command, 1, SQUADMSG_HISTORY_ADD_ENTRY );
|
||||
hud_squadmsg_send_wing_command( MsgItems[k].instance, Msg_shortcut_command, 1, SQUADMSG_HISTORY_ADD_ENTRY );
|
||||
hud_squadmsg_toggle();
|
||||
}
|
||||
}
|
||||
@@ -1893,11 +1907,11 @@ void hud_squadmsg_reinforcement_select()
|
||||
|
||||
hud_squadmsg_toggle(); // take us out of message mode
|
||||
|
||||
rnum = MsgItems[First_menu_item + k].instance;
|
||||
rnum = MsgItems[k].instance;
|
||||
|
||||
// check to see if trying to call a reinforcement not yet available. If so, maybe play message, but
|
||||
// definately bail
|
||||
if ( MsgItems[First_menu_item + k].active == 0 ) {
|
||||
if ( MsgItems[k].active == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1912,12 +1926,17 @@ void hud_squadmsg_reinforcement_select()
|
||||
}
|
||||
}
|
||||
|
||||
bool hud_order_comparator(size_t a, size_t b) {
|
||||
return Player_orders[a].ordering < Player_orders[b].ordering;
|
||||
}
|
||||
|
||||
// function to display list of commands for a ship
|
||||
|
||||
void hud_squadmsg_ship_command()
|
||||
{
|
||||
int k;
|
||||
std::set<size_t> orders, default_orders;
|
||||
std::set<size_t> orders;
|
||||
std::set<size_t, decltype(&hud_order_comparator)> default_orders(&hud_order_comparator);
|
||||
|
||||
// when adding ship commands, we must look at the type of ship, and what messages that
|
||||
// ship allows. First, place all messages that are possible onto the menu, then
|
||||
@@ -1926,33 +1945,31 @@ void hud_squadmsg_ship_command()
|
||||
// show on comm menu.
|
||||
if ( Msg_instance != MESSAGE_ALL_FIGHTERS ) {
|
||||
orders = Ships[Msg_instance].orders_accepted;
|
||||
default_orders = ship_get_default_orders_accepted( &Ship_info[Ships[Msg_instance].ship_info_index] );
|
||||
const auto& default_orders_accepted = ship_get_default_orders_accepted(&Ship_info[Ships[Msg_instance].ship_info_index]);
|
||||
default_orders.insert(default_orders_accepted.cbegin(), default_orders_accepted.cend());
|
||||
} else {
|
||||
for(size_t i = 0; i < Player_orders.size(); i++){
|
||||
if(Player_orders[i].id & DEFAULT_MESSAGES)
|
||||
default_orders.insert(i);
|
||||
}
|
||||
orders = default_orders;
|
||||
orders = default_messages;
|
||||
default_orders.insert(default_messages.cbegin(), default_messages.cend());
|
||||
}
|
||||
|
||||
First_menu_item = 0;
|
||||
Num_menu_items = 0;
|
||||
for(size_t order_id : default_orders) {
|
||||
Assert (Num_menu_items < MAX_MENU_ITEMS);
|
||||
MsgItems[Num_menu_items].text = Player_orders[order_id].localized_name;
|
||||
MsgItems[Num_menu_items].instance = Player_orders[order_id].id;
|
||||
MsgItems[Num_menu_items].instance = (int)order_id;
|
||||
MsgItems[Num_menu_items].active = 0;
|
||||
|
||||
// check the bit to see if the command is active
|
||||
if (orders.find(order_id) != orders.end())
|
||||
MsgItems[Num_menu_items].active = 1;
|
||||
|
||||
// if the order cannot be carried out by the ship, then item should be inactive
|
||||
if ((Msg_instance != MESSAGE_ALL_FIGHTERS) && !hud_squadmsg_ship_order_valid(Msg_instance, Player_orders[order_id].id))
|
||||
if ((Msg_instance != MESSAGE_ALL_FIGHTERS) && !hud_squadmsg_ship_order_valid(Msg_instance, (int)order_id))
|
||||
MsgItems[Num_menu_items].active = 0;
|
||||
|
||||
// do some other checks to possibly gray out other items.
|
||||
// if no target, remove any items which are associated with the players target
|
||||
if (!hud_squadmsg_is_target_order_valid((int)order_id, 0))
|
||||
if (!hud_squadmsg_is_target_order_valid(order_id, nullptr))
|
||||
MsgItems[Num_menu_items].active = 0;
|
||||
|
||||
// if messaging all fighters, see if we should gray out the order if no one will accept it,
|
||||
@@ -1960,10 +1977,10 @@ void hud_squadmsg_ship_command()
|
||||
if (Msg_instance == MESSAGE_ALL_FIGHTERS) {
|
||||
ship_obj* so;
|
||||
ship* shipp;
|
||||
int partial_accept, all_accept; // value which tells us what to do with menu item
|
||||
bool partial_accept, all_accept; // value which tells us what to do with menu item
|
||||
|
||||
all_accept = Player_orders[order_id].id;
|
||||
partial_accept = 0;
|
||||
all_accept = true;
|
||||
partial_accept = false;
|
||||
for (so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so)) {
|
||||
|
||||
// don't send messge to ships not on player's team, or that are in a wing.
|
||||
@@ -1975,11 +1992,9 @@ void hud_squadmsg_ship_command()
|
||||
if (!(Ship_info[shipp->ship_info_index].is_fighter_bomber()))
|
||||
continue;
|
||||
|
||||
int local_accepted = 0;
|
||||
for(size_t accepted_order : shipp->orders_accepted)
|
||||
local_accepted |= Player_orders[accepted_order].id;
|
||||
bool local_accepted = shipp->orders_accepted.count(order_id) > 0;
|
||||
all_accept &= local_accepted; // 'and'ing will either keep this bit set or zero it properly
|
||||
partial_accept |= (local_accepted & Player_orders[order_id].id); // 'or'ing will tell us if at least one accepts
|
||||
partial_accept |= local_accepted; // 'or'ing will tell us if at least one accepts
|
||||
}
|
||||
|
||||
if (!all_accept) {
|
||||
@@ -2021,7 +2036,8 @@ void hud_squadmsg_wing_command()
|
||||
wing *wingp;
|
||||
int shipnum;
|
||||
|
||||
std::set<size_t> default_orders, orders;
|
||||
std::set<size_t> orders;
|
||||
std::set<size_t, decltype(&hud_order_comparator)> default_orders(&hud_order_comparator);
|
||||
|
||||
// when adding commands for wings, we will look at all of the ships in the wing, and create
|
||||
// the order list from that set of ships. In the future, we may want to do something else....
|
||||
@@ -2034,10 +2050,7 @@ void hud_squadmsg_wing_command()
|
||||
default_orders.insert(setAdd.begin(), setAdd.end());
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < Player_orders.size(); i++){
|
||||
if (Player_orders[i].id & CAPTURE_TARGET_ITEM) // we cannot capture any target with a wing.
|
||||
default_orders.erase(i);
|
||||
}
|
||||
default_orders.erase(CAPTURE_TARGET_ITEM); // we cannot capture any target with a wing.
|
||||
|
||||
Num_menu_items = 0;
|
||||
shipnum = wingp->ship_index[wingp->special_ship];
|
||||
@@ -2049,7 +2062,7 @@ void hud_squadmsg_wing_command()
|
||||
// to be available in the wing.
|
||||
Assert ( Num_menu_items < MAX_MENU_ITEMS );
|
||||
MsgItems[Num_menu_items].text = Player_orders[order_id].localized_name;
|
||||
MsgItems[Num_menu_items].instance = Player_orders[order_id].id;
|
||||
MsgItems[Num_menu_items].instance = (int)order_id;
|
||||
MsgItems[Num_menu_items].active = 0;
|
||||
|
||||
// possibly grey out the menu item depending on whether or not the "wing" will accept this order
|
||||
@@ -2242,11 +2255,7 @@ int hud_squadmsg_hotkey_select( int k )
|
||||
continue;
|
||||
|
||||
// be sure that this ship can accept this command
|
||||
bool accepts_current = false;
|
||||
for(size_t order : Ships[objp->instance].orders_accepted)
|
||||
accepts_current |= (bool)(Player_orders[order].id & Msg_shortcut_command);
|
||||
|
||||
if ( !accepts_current )
|
||||
if ( Ships[objp->instance].orders_accepted.count(Msg_shortcut_command) == 0)
|
||||
continue;
|
||||
|
||||
hud_squadmsg_send_ship_command( objp->instance, Msg_shortcut_command, send_message, SQUADMSG_HISTORY_ADD_ENTRY );
|
||||
@@ -2412,16 +2421,17 @@ int hud_query_order_issued(const char *to, const char *order_name, const char *t
|
||||
|
||||
|
||||
for (i = 0; i < (int) Player_orders.size(); i++)
|
||||
if (!stricmp(order_name, Player_orders[i].hud_name.c_str()))
|
||||
order = Player_orders[i].id;
|
||||
if (!stricmp(order_name, Player_orders[i].hud_name.c_str())) {
|
||||
order = i;
|
||||
break;
|
||||
}
|
||||
|
||||
// Goober5000 - if not found, check compatibility
|
||||
if (order == -1)
|
||||
{
|
||||
if (!stricmp(order_name, "Attack my target"))
|
||||
{
|
||||
i = 0; // it maps to option 0, "Destroy my target"
|
||||
order = Player_orders[i].id;
|
||||
order = ATTACK_TARGET_ITEM;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2450,7 +2460,7 @@ int hud_query_order_issued(const char *to, const char *order_name, const char *t
|
||||
continue;
|
||||
}
|
||||
|
||||
int subsys_index = ship_get_subsys_index(&Ships[target_ship], special_argument);
|
||||
int subsys_index = ship_find_subsys(&Ships[target_ship], special_argument);
|
||||
// if the order is for s different subsystem
|
||||
if (Squadmsg_history[i].special_index != subsys_index) {
|
||||
continue;
|
||||
|
||||
+30
-25
@@ -30,42 +30,52 @@
|
||||
|
||||
class object;
|
||||
|
||||
// defines for messages that can be sent from the player. Defined at bitfields so that we can enable
|
||||
// and disable messages on a message by message basis
|
||||
// defines for messages that can be sent from the player. Indexes into Player_orders
|
||||
|
||||
#define ATTACK_TARGET_ITEM (1<<0)
|
||||
#define DISABLE_TARGET_ITEM (1<<1)
|
||||
#define DISARM_TARGET_ITEM (1<<2)
|
||||
#define PROTECT_TARGET_ITEM (1<<3)
|
||||
#define IGNORE_TARGET_ITEM (1<<4)
|
||||
#define FORMATION_ITEM (1<<5)
|
||||
#define COVER_ME_ITEM (1<<6)
|
||||
#define ENGAGE_ENEMY_ITEM (1<<7)
|
||||
#define CAPTURE_TARGET_ITEM (1<<8)
|
||||
#define NO_ORDER_ITEM 0
|
||||
|
||||
#define ATTACK_TARGET_ITEM 1
|
||||
#define DISABLE_TARGET_ITEM 2
|
||||
#define DISARM_TARGET_ITEM 3
|
||||
#define PROTECT_TARGET_ITEM 4
|
||||
#define IGNORE_TARGET_ITEM 5
|
||||
#define FORMATION_ITEM 6
|
||||
#define COVER_ME_ITEM 7
|
||||
#define ENGAGE_ENEMY_ITEM 8
|
||||
#define CAPTURE_TARGET_ITEM 9
|
||||
|
||||
// the next are for the support ship only
|
||||
#define REARM_REPAIR_ME_ITEM (1<<9)
|
||||
#define ABORT_REARM_REPAIR_ITEM (1<<10)
|
||||
#define STAY_NEAR_ME_ITEM (1<<11)
|
||||
#define STAY_NEAR_TARGET_ITEM (1<<12)
|
||||
#define KEEP_SAFE_DIST_ITEM (1<<13)
|
||||
#define REARM_REPAIR_ME_ITEM 10
|
||||
#define ABORT_REARM_REPAIR_ITEM 11
|
||||
#define STAY_NEAR_ME_ITEM 12
|
||||
#define STAY_NEAR_TARGET_ITEM 13
|
||||
#define KEEP_SAFE_DIST_ITEM 14
|
||||
|
||||
// next item for all ships again -- to try to preserve relative order within the message menu
|
||||
#define DEPART_ITEM (1<<14)
|
||||
#define DEPART_ITEM 15
|
||||
|
||||
// out of order, but it was this way in the original source
|
||||
#define DISABLE_SUBSYSTEM_ITEM (1<<15)
|
||||
#define DISABLE_SUBSYSTEM_ITEM 16
|
||||
|
||||
// used for Message box gauge
|
||||
#define NUM_MBOX_FRAMES 3
|
||||
|
||||
typedef struct player_order {
|
||||
private:
|
||||
//Needed, because legacy order-id's were not assigned in order
|
||||
static int orderingCounter;
|
||||
//Used to ensure that built-in order indexing is always correct
|
||||
int legacy_id;
|
||||
friend void hud_init_comm_orders();
|
||||
public:
|
||||
SCP_string parse_name;
|
||||
SCP_string hud_name;
|
||||
int hud_xstr;
|
||||
int id;
|
||||
SCP_string localized_name;
|
||||
player_order(SCP_string parsename, SCP_string hudname, int hudxstr, int orderid = 0) : parse_name(std::move(parsename)), hud_name(std::move(hudname)), hud_xstr(hudxstr), id(orderid), localized_name("") { }
|
||||
int lua_id;
|
||||
int ordering;
|
||||
player_order(SCP_string parsename, SCP_string hudname, int hudxstr, int luaid = -1, int legacyid = 999999) : legacy_id(legacyid), parse_name(std::move(parsename)), hud_name(std::move(hudname)), hud_xstr(hudxstr), localized_name(""), lua_id(luaid), ordering(orderingCounter++) { }
|
||||
inline void localize() { localized_name = XSTR(hud_name.c_str(), hud_xstr); }
|
||||
} player_order;
|
||||
|
||||
extern std::vector<player_order> Player_orders;
|
||||
@@ -74,7 +84,6 @@ extern std::vector<player_order> Player_orders;
|
||||
// might not allow some messages
|
||||
|
||||
//WMC - Formerly FIGHTER_MESSAGES
|
||||
#define DEFAULT_MESSAGES (ATTACK_TARGET_ITEM | DISABLE_TARGET_ITEM | DISARM_TARGET_ITEM | PROTECT_TARGET_ITEM | IGNORE_TARGET_ITEM | FORMATION_ITEM | COVER_ME_ITEM | ENGAGE_ENEMY_ITEM | DEPART_ITEM | DISABLE_SUBSYSTEM_ITEM)
|
||||
/*
|
||||
#define BOMBER_MESSAGES FIGHTER_MESSAGES // bombers can do the same things as fighters
|
||||
|
||||
@@ -92,10 +101,6 @@ extern std::vector<player_order> Player_orders;
|
||||
// these messages require an active target. They are also the set of messages
|
||||
// which cannot be given to a ship when the target is on the same team, or the target
|
||||
// is not a ship.
|
||||
#define ENEMY_TARGET_MESSAGES (ATTACK_TARGET_ITEM | DISABLE_TARGET_ITEM | DISARM_TARGET_ITEM | IGNORE_TARGET_ITEM | STAY_NEAR_TARGET_ITEM | CAPTURE_TARGET_ITEM | DISABLE_SUBSYSTEM_ITEM )
|
||||
#define FRIENDLY_TARGET_MESSAGES (PROTECT_TARGET_ITEM)
|
||||
|
||||
#define TARGET_MESSAGES (ENEMY_TARGET_MESSAGES | FRIENDLY_TARGET_MESSAGES)
|
||||
|
||||
|
||||
|
||||
|
||||
+19
-6
@@ -1530,6 +1530,8 @@ void hud_target_missile(object *source_obj, int next_flag)
|
||||
continue;
|
||||
}
|
||||
|
||||
Assertion(Objects[so->objnum].type == OBJ_SHIP, "hud_target_missile was about to call obj_team with a non-ship obejct with type %d. Please report!", Objects[so->objnum].type);
|
||||
|
||||
// only allow targeting of hostile bombs
|
||||
if (!iff_x_attacks_y(Player_ship->team, obj_team(A))) {
|
||||
continue;
|
||||
@@ -1716,11 +1718,8 @@ typedef struct eval_next_turret {
|
||||
float dist;
|
||||
} eval_next_turret;
|
||||
|
||||
int turret_compare_func(const void *e1, const void *e2)
|
||||
int turret_compare_func(const eval_next_turret *p1, const eval_next_turret *p2)
|
||||
{
|
||||
eval_next_turret *p1 = (eval_next_turret*)e1;
|
||||
eval_next_turret *p2 = (eval_next_turret*)e2;
|
||||
|
||||
Assert(p1->type != TYPE_NONE);
|
||||
Assert(p2->type != TYPE_NONE);
|
||||
|
||||
@@ -1850,7 +1849,7 @@ void hud_target_live_turret(int next_flag, int auto_advance, int only_player_tar
|
||||
|
||||
// sort the list if we're not using turret straight ahead of us
|
||||
if (!use_straight_ahead_turret) {
|
||||
insertion_sort(ent, num_live_turrets, sizeof(eval_next_turret), turret_compare_func);
|
||||
insertion_sort(ent, num_live_turrets, turret_compare_func);
|
||||
}
|
||||
|
||||
if (use_straight_ahead_turret) {
|
||||
@@ -2124,6 +2123,8 @@ bool evaluate_ship_as_closest_target(esct *esct_p)
|
||||
|
||||
// consider the ship alone if there are no attacking turrets
|
||||
if ( !turret_is_attacking ) {
|
||||
// Volition switched distance evaluation methods here... presumably this is to prioritize small fighters when dogfighting next to large ships...
|
||||
// Since this is used specifically for finding an ordering of targets and does not involve any visual displays, distance-to-center is reasonable.
|
||||
//new_distance = hud_find_target_distance(objp, Player_obj);
|
||||
new_distance = vm_vec_dist_quick(&objp->pos, &Player_obj->pos);
|
||||
|
||||
@@ -4407,9 +4408,16 @@ void hud_target_change_check()
|
||||
// check if the main target has changed
|
||||
if (Player_ai->last_target != Player_ai->target_objnum) {
|
||||
|
||||
// acquired target
|
||||
if ( Player_ai->target_objnum != -1){
|
||||
snd_play( gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::TARGET_ACQUIRE)), 0.0f );
|
||||
}
|
||||
// cleared target
|
||||
else {
|
||||
if (Target_static_looping.isValid()) {
|
||||
snd_stop(Target_static_looping);
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a hotkey set active, see if new target is in set. If not in
|
||||
// set, deselect the current hotkey set.
|
||||
@@ -4517,6 +4525,7 @@ void HudGaugeTargetTriangle::render(float /*frametime*/)
|
||||
object *targetp = &Objects[Player_ai->target_objnum];
|
||||
|
||||
// draw the targeting triangle that orbits the outside of the outer circle of the reticle
|
||||
// checking !target_is_dying is correct here, since you do not want -1 (no target) or 1(is actually dying)
|
||||
if (!Player->target_is_dying && maybeFlashSexp() != 1) {
|
||||
|
||||
hud_set_iff_color(targetp, 1);
|
||||
@@ -6311,7 +6320,11 @@ void HudGaugeOffscreen::render(float /*frametime*/)
|
||||
int dir;
|
||||
|
||||
if(target_display_list[i].objp) {
|
||||
dist = hud_find_target_distance( target_display_list[i].objp, Player_obj );
|
||||
if (OBJ_INDEX(target_display_list[i].objp) == Player_ai->target_objnum) {
|
||||
dist = Player_ai->current_target_distance;
|
||||
} else {
|
||||
dist = hud_find_target_distance( target_display_list[i].objp, Player_obj );
|
||||
}
|
||||
} else {
|
||||
// if we don't have a corresponding object, use given position to figure out distance
|
||||
dist = vm_vec_dist_quick(&Player_obj->pos, &target_display_list[i].target_pos);
|
||||
|
||||
@@ -1566,6 +1566,23 @@ void get_turret_subsys_name(ship_weapon *swp, char *outstr)
|
||||
|
||||
//WMC - find the first weapon, if there is one
|
||||
if (swp->num_primary_banks || swp->num_secondary_banks) {
|
||||
// allow the first weapon on the turret to specify the name
|
||||
for (int i = 0; i < swp->num_primary_banks; ++i) {
|
||||
auto wip = &Weapon_info[swp->primary_bank_weapons[i]];
|
||||
if (*(wip->altSubsysName) != '\0') {
|
||||
sprintf(outstr, "%s", wip->altSubsysName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < swp->num_secondary_banks; ++i) {
|
||||
auto wip = &Weapon_info[swp->secondary_bank_weapons[i]];
|
||||
if (*(wip->altSubsysName) != '\0') {
|
||||
sprintf(outstr, "%s", wip->altSubsysName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise use a general name based on the type of weapon(s) on the turret
|
||||
auto flags = turret_weapon_aggregate_flags(swp);
|
||||
|
||||
// check if beam or flak using weapon flags
|
||||
@@ -1578,17 +1595,13 @@ void get_turret_subsys_name(ship_weapon *swp, char *outstr)
|
||||
sprintf(outstr, "%s", XSTR("Missile lnchr", 1569));
|
||||
} else if (turret_weapon_has_subtype(swp, WP_LASER)) {
|
||||
// ballistic too! - Goober5000
|
||||
if (flags[Weapon::Info_Flags::Ballistic])
|
||||
{
|
||||
if (flags[Weapon::Info_Flags::Ballistic]) {
|
||||
sprintf(outstr, "%s", XSTR("Turret", 1487));
|
||||
}
|
||||
// the TVWP has some primaries flagged as bombs
|
||||
else if (flags[Weapon::Info_Flags::Bomb])
|
||||
{
|
||||
else if (flags[Weapon::Info_Flags::Bomb]) {
|
||||
sprintf(outstr, "%s", XSTR("Missile lnchr", 1569));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
sprintf(outstr, "%s", XSTR("Laser turret", 1568));
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -372,7 +372,7 @@ bool CFtpGet::IssuePasv()
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&dataaddr), sizeof(dataaddr)) == SOCKET_ERROR ) {
|
||||
if ( connect(m_DataSock, reinterpret_cast<LPSOCKADDR>(&dataaddr), psnet_get_sockaddr_len(&dataaddr)) == SOCKET_ERROR ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -392,7 +392,7 @@ int CFtpGet::ConnectControlSocket()
|
||||
}
|
||||
|
||||
// Now we will connect to the host
|
||||
if ( connect(m_ControlSock, reinterpret_cast<LPSOCKADDR>(&m_ServerAddr), sizeof(m_ServerAddr)) ) {
|
||||
if ( connect(m_ControlSock, reinterpret_cast<LPSOCKADDR>(&m_ServerAddr), psnet_get_sockaddr_len(&m_ServerAddr)) ) {
|
||||
m_State = FTP_STATE_CANT_CONNECT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loaded 100 of 411 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user