Compare commits
505 Commits
Author | SHA1 | Date |
---|---|---|
Duncan | a01bd32132 | |
Aaron van Geffen | 070ec04888 | |
Marijn van der Werf | d793467167 | |
Aaron van Geffen | 2d1b9944b7 | |
rdrdrdrd95 | ce8f6bca9f | |
Aaron van Geffen | 7d0f7f9dba | |
Duncan | 65895bcab1 | |
Duncan | 18422de137 | |
Aaron van Geffen | f4d0d9215c | |
Duncan | a48d0c7c74 | |
Duncan | 055def4215 | |
Duncan | 6e74345b08 | |
Duncan | ef420ec6e6 | |
Duncan | 191e6d567c | |
Duncan | f4f3d0d72c | |
Aaron van Geffen | 51789bd329 | |
Margen67 | 14edc842b6 | |
Duncan | 3c4937e7ca | |
duncanspumpkin | 29b405007e | |
duncanspumpkin | 7aecfa55ef | |
Marijn van der Werf | 9c24c21227 | |
Duncan | 22cf266773 | |
Duncan | f2f9719b75 | |
Duncan | cba3bd06d1 | |
Aaron van Geffen | 44f5210c2f | |
Duncan | 8026daa22c | |
Duncan | 904abdecce | |
Duncan | 498911fd81 | |
Aaron van Geffen | f7fcba0e6a | |
Aaron van Geffen | ae294ad80e | |
Duncan | 8461ad721b | |
Duncan | 80952a1c99 | |
Duncan | 99049e5fab | |
Duncan | b08815aeaf | |
Duncan | 8333ece44e | |
Duncan | bd794b21dc | |
Duncan | 4e3f77a1d9 | |
Duncan | e698c13357 | |
Aaron van Geffen | 139eebf1c0 | |
Duncan | f5494630c6 | |
Duncan | b427523621 | |
Duncan | 33c28eff12 | |
Duncan | 43b670507d | |
Aaron van Geffen | a57af0e1f1 | |
Duncan | 4c54152737 | |
Duncan | 3616ec0f7d | |
Duncan | da39ecf082 | |
Duncan | b756c2e440 | |
Duncan | c82281540f | |
Duncan | 17bd0e605c | |
Duncan | 375d8571cc | |
Duncan | 63b8813db8 | |
Aaron van Geffen | 06e6c34671 | |
Aaron van Geffen | 4baeabc5d2 | |
Aaron van Geffen | ecae932561 | |
Marijn van der Werf | f6f1bd7b2e | |
Duncan | a3221a8df2 | |
Duncan | 33dd39e760 | |
Duncan | 3b062132c8 | |
Duncan | f85b375063 | |
Duncan | 7e46462f87 | |
Duncan | d4c49e39e4 | |
Duncan | 26256545ba | |
Ted John | 0ee44427e9 | |
Duncan | 1d5b041df3 | |
Duncan | d70a3dd7fc | |
Duncan | 82adf56d87 | |
Duncan | 92b781cf48 | |
Duncan | cbdbded197 | |
Duncan | 4186081736 | |
Duncan | 8a0c0fa68a | |
Duncan | b7ede286a2 | |
Marijn van der Werf | 0a0d9f985b | |
duncanspumpkin | 70c9d8635c | |
duncanspumpkin | 7ec445f7f7 | |
Duncan | f280941bc5 | |
Duncan | f57a13a7da | |
Duncan | 29938544e2 | |
Duncan | 46637fb5c2 | |
Aaron van Geffen | 57166baefd | |
Duncan | ee83e9f53c | |
Duncan | 6a4e022af8 | |
Duncan | 65d905d2c1 | |
Duncan | 79f1d00674 | |
Aaron van Geffen | 1444685591 | |
Aaron van Geffen | 6b0c50f77f | |
Aaron van Geffen | 2f3b9ef696 | |
Aaron van Geffen | e9fd958b23 | |
Aaron van Geffen | fbc145a5bf | |
Michał Janiszewski | d10b16e404 | |
rdrdrdrd95 | 17f1d1f5b1 | |
Michał Janiszewski | cace3b9148 | |
Duncan | 4863d0e617 | |
Aaron van Geffen | f1adeb7568 | |
Duncan | 004f0c520e | |
Duncan | b7c0cf5f9e | |
Duncan | 7bdc32d8b2 | |
Alexander | 641898d977 | |
Aaron van Geffen | 6a10da15ca | |
Aaron van Geffen | d6a0b7a0ff | |
Duncan | 6eaf015902 | |
Duncan | 9a5e879960 | |
Duncan | 62f4fe2cfb | |
Duncan | 3e33a46600 | |
Michał Janiszewski | 475e9075b9 | |
Michał Janiszewski | 31e800c58c | |
Duncan | bfd99e849d | |
Duncan | dfdc1c5d9f | |
Michał Janiszewski | 69318d9a10 | |
Michał Janiszewski | 0dd240a503 | |
Michał Janiszewski | 155845d299 | |
Michał Janiszewski | 324b97b6fb | |
Michał Janiszewski | 228e12a6bd | |
Michał Janiszewski | 4c06a785f1 | |
Duncan | 74995014e6 | |
Michał Janiszewski | 68938e075e | |
Aaron van Geffen | e836285789 | |
Michał Janiszewski | a15e1421f1 | |
Duncan | 121d0be7b8 | |
Michał Janiszewski | 936a379d91 | |
Michał Janiszewski | 18b24c7716 | |
Michał Janiszewski | 4d58d913fd | |
Michał Janiszewski | 4e03c48680 | |
Michał Janiszewski | 46aa213cca | |
Michał Janiszewski | e2be89f114 | |
Duncan | 92543a09a7 | |
Duncan | c762c82caa | |
Duncan | 31fb6a5f83 | |
duncanspumpkin | 4c48a82c4f | |
duncanspumpkin | 31cbe380c0 | |
duncanspumpkin | 7bb91b9b0a | |
duncanspumpkin | 9c9f2ae579 | |
duncanspumpkin | 29a5d155b5 | |
duncanspumpkin | e0e4e79559 | |
Ted John | 00cdd64fef | |
Duncan | 1a3e1902f4 | |
Michał Janiszewski | 6dfa662a43 | |
duncanspumpkin | 2ff86cbf0f | |
Duncan | c1cc552b4b | |
Aaron van Geffen | f9e8e460a4 | |
Hielke Morsink | 65b03b9588 | |
TransshipmentEnvoy | c483a03dbd | |
TELK | 1acfbd9a11 | |
Pedro Ortiz Bledow | f9358b25bc | |
Duncan | 8607b871b8 | |
Aaron van Geffen | cacb426f6f | |
Aaron van Geffen | 8e54f42a41 | |
Duncan | 7b5bc50fad | |
Duncan | bb5cccd7e8 | |
Duncan | b2273923ef | |
Duncan | bd6936c9f6 | |
guKing | 3ff62d5d46 | |
Duncan | a7d62ed539 | |
Duncan | 33b087fc3d | |
Aaron van Geffen | 4914cbd2e3 | |
Aaron van Geffen | 1726df1351 | |
Duncan | b3bdd47c04 | |
Duncan | 30ff8376f2 | |
Aaron van Geffen | 0dc126f9d6 | |
Michał Janiszewski | df7f69c25e | |
Aaron van Geffen | fe4fd1be42 | |
Aaron van Geffen | 36589cef44 | |
Aaron van Geffen | b0b6ac8325 | |
Aaron van Geffen | 8d7f1fce54 | |
Aaron van Geffen | 86268bd3d2 | |
Aaron van Geffen | a026d75b13 | |
ζeh Matt | 8ebeea350b | |
ζeh Matt | 2bfbb3208c | |
Aaron van Geffen | 2613e9b15a | |
Aaron van Geffen | 96c87a57b4 | |
Aaron van Geffen | 99dcaa9900 | |
Aaron van Geffen | 30c29061a7 | |
Aaron van Geffen | 74b0669d2e | |
Duncan | 61b14fc78a | |
Duncan | a432bfcc1a | |
Duncan | 6b83fa40d9 | |
Duncan | 0425ebf524 | |
Duncan | 49610c3121 | |
Peter Gaal | 392a81551e | |
Duncan | c8b3232eb4 | |
Duncan | b85cf7c254 | |
Duncan | 893f552956 | |
Duncan | 061c60d6c7 | |
Aaron van Geffen | 115a785c55 | |
Duncan | 8702c86e3e | |
Duncan | 9b3b434e31 | |
ζeh Matt | 7c0ff0116b | |
Aaron van Geffen | a1ac80f8d8 | |
Aaron van Geffen | 55aff692a2 | |
Duncan | 4b2757bd3b | |
Aaron van Geffen | c774323ce3 | |
Aaron van Geffen | 75d95b7917 | |
duncanspumpkin | a92668a329 | |
duncanspumpkin | aed6fff2a4 | |
Duncan | 596f38b7ff | |
Duncan | f5924dc177 | |
Duncan | 292e2bf4fc | |
Duncan | b20bdd2fef | |
duncanspumpkin | 7e9528eb6c | |
duncanspumpkin | 753b60ea0f | |
Duncan | f59a373168 | |
Duncan | d668605fda | |
Aaron van Geffen | dfdb813f67 | |
Aaron van Geffen | 84090672d7 | |
Duncan | 4b551cff23 | |
Duncan | 329e60f1fb | |
Duncan | 7b279f3be5 | |
Aaron van Geffen | 999c37e1b6 | |
Duncan | 2bf5d70c78 | |
Duncan | 4e56aa84c7 | |
Duncan | 963cb9d785 | |
Aaron van Geffen | dc86bddbf4 | |
Duncan | d170de22f0 | |
Aaron van Geffen | 8b642c07a4 | |
Duncan | 2545e8050a | |
Duncan | e8f001f3db | |
Duncan | 9d180574e5 | |
Peter Gaal | 3e23caad85 | |
Duncan | e6843199bc | |
Aaron van Geffen | c9b5d5148f | |
Duncan | ee3172afae | |
Duncan | 6d3d05a3e5 | |
Aaron van Geffen | 73b504d21b | |
Aaron van Geffen | a245793271 | |
Aaron van Geffen | 701b18364a | |
Peter Gaal | 2876fc6e2f | |
Aaron van Geffen | d6e13ceed3 | |
Aaron van Geffen | 6ea518d149 | |
ζeh Matt | 6af0b6e6b1 | |
Aaron van Geffen | e665e9b32d | |
Aaron van Geffen | 7ca7e1433c | |
Aaron van Geffen | c010ef6c7d | |
Aaron van Geffen | 7552704846 | |
Aaron van Geffen | 7dafa196df | |
Aaron van Geffen | b0c1ba2fda | |
Aaron van Geffen | 810ffbfeeb | |
ζeh Matt | fbcaae5f1a | |
Duncan | b44c0921f2 | |
Duncan | a7c0800ec2 | |
Aaron van Geffen | abaee38a8a | |
Duncan | ded2ea28d4 | |
ζeh Matt | 92ca3ac490 | |
Duncan | 8abc9679b1 | |
Duncan | 839d7f34bf | |
Duncan | 83779ba719 | |
Duncan | cbfca1475b | |
ζeh Matt | 2b87ecbb34 | |
ζeh Matt | f45c89672a | |
Duncan | d710ee10e5 | |
Aaron van Geffen | 19cf4d082a | |
Duncan | 71805f12fa | |
Duncan | 3b22b194f9 | |
Aaron van Geffen | b757d72b0e | |
Duncan | 71f538c0bf | |
ζeh Matt | 4d1a26937d | |
Aaron van Geffen | 920ffa942a | |
ζeh Matt | 7ae3a54a34 | |
Aaron van Geffen | c49e71d4da | |
Aaron van Geffen | fea95bf09d | |
Aaron van Geffen | 8f26e263a8 | |
Aaron van Geffen | 86de7364ac | |
Aaron van Geffen | 6d5b481b61 | |
Aaron van Geffen | 1fbbad01b0 | |
ζeh Matt | c6aa340fb8 | |
Aaron van Geffen | 4f1d6320b2 | |
ζeh Matt | 7b23ab4da9 | |
Michał Janiszewski | 86f104d41e | |
Duncan | f7be97293f | |
Duncan | 138e57e5b3 | |
Duncan | db898ca8ac | |
Duncan | 3205138374 | |
Duncan | c646e8a708 | |
Duncan | a75b041a8e | |
Duncan | 35fcae711c | |
Aaron van Geffen | d831911c4f | |
Aaron van Geffen | 58f70d6942 | |
Duncan | c8f412feb3 | |
Duncan | 7839ebc37a | |
Duncan | 4c55ac8af9 | |
Duncan | a9f48c83d0 | |
Duncan | 0bc0d2a0f2 | |
Duncan | 3247589ec0 | |
Aaron van Geffen | 3e2d49a2d0 | |
Aaron van Geffen | de1f9e9142 | |
Matt | 6d97f455be | |
Matt | ce79c56a50 | |
ζeh Matt | 223048ac51 | |
Aaron van Geffen | b95eb5d78b | |
Aaron van Geffen | b8d6549084 | |
Aaron van Geffen | 7c5863c29b | |
Aaron van Geffen | 7ab2849696 | |
Aaron van Geffen | f41d043d99 | |
Aaron van Geffen | 43dfb5e2a0 | |
Aaron van Geffen | e4121f9e8b | |
Aaron van Geffen | ea680e574c | |
Aaron van Geffen | adadb1879a | |
Aaron van Geffen | 10692610b9 | |
Aaron van Geffen | af53b4b20b | |
Aaron van Geffen | 3b15883b91 | |
Duncan | 2963fd4475 | |
Duncan | d4e72ec04e | |
Aaron van Geffen | 58edc20a3d | |
Aaron van Geffen | 70745c3328 | |
Aaron van Geffen | 59fa24ca1d | |
Ted John | 0853232e19 | |
Aaron van Geffen | 7b8b845f6b | |
Aaron van Geffen | 3b9a6e6bf7 | |
Duncan | b1371d6ace | |
Aaron van Geffen | 84adf81a5b | |
YetiWiz | d05b492f83 | |
Duncan | 67f2c05e0a | |
Aaron van Geffen | b3a979db46 | |
ζeh Matt | a44a9e3d89 | |
Aaron van Geffen | 5c90cdb535 | |
Duncan | f5fd3c3819 | |
Duncan | 7518897238 | |
Duncan | b3d68cf491 | |
Peter Gaal | c9f144414b | |
Duncan | ba54e2fbca | |
Duncan | 36ada16694 | |
Duncan | 2350fb9aac | |
TELK | 2331b5cb7e | |
Aaron van Geffen | 411d5d8a7d | |
Duncan | ad70c289a8 | |
Aaron van Geffen | f9791aa82e | |
Aaron van Geffen | 8d65a8749a | |
Aaron van Geffen | ceb74c0b87 | |
Aaron van Geffen | 7301077b3e | |
Aaron van Geffen | fe60b59d0d | |
Aaron van Geffen | aff8b2dfe6 | |
Duncan | 80f57924b1 | |
Aaron van Geffen | 40b97c7670 | |
Aaron van Geffen | 461647ebd7 | |
Ted John | 66ccc387db | |
Aaron van Geffen | 93a348dd34 | |
duncanspumpkin | 6150ecd87d | |
Aaron van Geffen | d19059bf01 | |
Aaron van Geffen | c51192a455 | |
Ted John | a02b431874 | |
Duncan | 9128b897f5 | |
Aaron van Geffen | c273913ca8 | |
Ted John | a6ec81ea89 | |
Ted John | ca991b2da0 | |
Ted John | b533ee2ab0 | |
Ted John | 77b46cb1d7 | |
Ted John | 76439332cd | |
Ted John | ccb87b13bb | |
Ted John | d7248868a1 | |
Ted John | 9ac134a24b | |
Ted John | 1b068db716 | |
Ted John | f70cba24cd | |
Ted John | 489bb7683c | |
Ted John | 4c5c3de677 | |
Duncan | f5f75f8210 | |
Duncan | 9b0b14467e | |
Marijn van der Werf | 57fd312765 | |
Duncan | 69b5e7f207 | |
ζeh Matt | 7042e9a6ac | |
ζeh Matt | 596804a75a | |
duncanspumpkin | a5ad3dde0c | |
duncanspumpkin | 3cc862b0ad | |
duncanspumpkin | 7fd45795a7 | |
Aaron van Geffen | b53bfdbab7 | |
Aaron van Geffen | 3605a287da | |
Aaron van Geffen | b8174c700f | |
Aaron van Geffen | a8ed7b65bd | |
Duncan | e769ab749a | |
Aaron van Geffen | 7c10816dbe | |
Aaron van Geffen | 6553872549 | |
Duncan | 9b88734361 | |
Duncan | d45977a8e0 | |
duncanspumpkin | e95ea114e8 | |
Duncan | 475478c4e0 | |
Matt | ea5da89344 | |
Marijn van der Werf | 173bab52de | |
Duncan | 71fc5057a5 | |
Duncan | cb191561b4 | |
Duncan | d0560264c2 | |
Aaron van Geffen | f4c77663b8 | |
Aaron van Geffen | 913d420356 | |
Aaron van Geffen | 2d57920631 | |
Aaron van Geffen | 64f2ccc36e | |
Aaron van Geffen | 873a20669a | |
Aaron van Geffen | 72e93d0c29 | |
duncanspumpkin | af3588ef6b | |
duncanspumpkin | e9aa64527a | |
Duncan | 295b516a4a | |
duncanspumpkin | 8b8fe71c88 | |
duncanspumpkin | 20bcd4df23 | |
Aaron van Geffen | 61e11fdc4f | |
Aaron van Geffen | ed6056d3fd | |
duncanspumpkin | 341c30aebe | |
Ted John | d8a4458283 | |
Ted John | 3855dee811 | |
Ted John | ffbb700baa | |
Ted John | 9acb72e085 | |
Ted John | 62da8ad4c1 | |
duncanspumpkin | c5849c08d9 | |
Duncan | 0ee80c5d2a | |
Aaron van Geffen | daaf16b0b8 | |
Duncan | 2f22e1c4a5 | |
Duncan | cec3274640 | |
duncanspumpkin | cc00654c8a | |
duncanspumpkin | 10543435dd | |
duncanspumpkin | 40633cc455 | |
Duncan | 64478b4295 | |
Peter Gaal | 2e2c4147f7 | |
Peter Gaal | 218e4d728f | |
Aaron van Geffen | c6b1f9c0e4 | |
Peter Gaal | 0443a33bf7 | |
Peter Gaal | adc0450e3c | |
Aaron van Geffen | 3e5acf7430 | |
Aaron van Geffen | 9003696e73 | |
duncanspumpkin | a2a414c5d6 | |
Marijn van der Werf | e086988be0 | |
Duncan | ba7b9efd2d | |
Marijn van der Werf | 4ff3e86143 | |
Aaron van Geffen | 6cc76039e1 | |
Aaron van Geffen | 9a30d8e6d5 | |
Duncan | 1756518b0a | |
Duncan | 53215fcb4f | |
Duncan | 80fb580ec0 | |
Duncan | c9d217433a | |
duncanspumpkin | 2cd00005a3 | |
duncanspumpkin | d6fd2ccbaf | |
duncanspumpkin | 07f5272cb2 | |
duncanspumpkin | bde1b7781f | |
duncanspumpkin | 25ef0e70ef | |
duncanspumpkin | 35194d3ee0 | |
duncanspumpkin | b395294ea1 | |
duncanspumpkin | 74ad424104 | |
duncanspumpkin | 8b1a097418 | |
duncanspumpkin | 1e8e3ae973 | |
duncanspumpkin | 3d68d72fab | |
duncanspumpkin | 7ad6c61c8d | |
duncanspumpkin | 6fc63d9039 | |
duncanspumpkin | 9516b0f112 | |
Duncan | e8fdae3786 | |
Aaron van Geffen | 4739db6159 | |
Duncan | b5356a4b10 | |
Duncan | 8f3edd0006 | |
Duncan | ee21e784a6 | |
Aaron van Geffen | 94aba0a016 | |
Aaron van Geffen | 7a537915e1 | |
Duncan | 1908b6d022 | |
Duncan | b02a324e12 | |
Duncan | d1f0f0b24c | |
Duncan | 293ec5bc4b | |
Ted John | 1508ace99b | |
Duncan | 07274cfc37 | |
Duncan | 9fd780a1f5 | |
Duncan | 53050d2290 | |
Duncan | b66f81cc24 | |
Aaron van Geffen | 9d7de77427 | |
Ted John | 9632037436 | |
Ted John | fae40bc22b | |
Ted John | 57e4018671 | |
Ted John | 1646f637c9 | |
Ted John | 8d641cb8f2 | |
Ted John | 145774be18 | |
Ted John | c652fa97c2 | |
Ted John | 94b1b3d924 | |
Ted John | 28a0f2c942 | |
Ted John | 9f790371fe | |
Ted John | 21b1f9a3eb | |
Ted John | 01d4b5b552 | |
Ted John | dfecf42cb4 | |
Ted John | 49865e1495 | |
Ted John | 0e271b13ea | |
Aaron van Geffen | e7a0eae66e | |
Duncan | cc4964a2cc | |
Duncan | ee151a2781 | |
Duncan | 582d866daf | |
Aaron van Geffen | 90cbea50ae | |
Aaron van Geffen | 3e36285045 | |
Aaron van Geffen | 93ec0aa2ef | |
Michael Steenbeek | c0a64265e6 | |
Duncan | 1e0901f920 | |
Ted John | e06e013c6b | |
Aaron van Geffen | 8c7208782b | |
Duncan | 7f4e01ab51 | |
Duncan | fbb028061f | |
Duncan | 95c2386940 | |
Duncan | d93bdb9cd2 | |
Marijn van der Werf | cf6888e0e3 | |
Duncan | 852d0029ae | |
Duncan | 4b79f67e98 | |
Duncan | 277908e92e | |
Duncan | 0d83cf1c13 | |
Duncan | c4d6d5087e | |
Duncan | ae28b292df | |
Marijn van der Werf | 5e596588cf | |
Duncan | dd3174ea8c | |
Marijn van der Werf | 977c1e3114 | |
Marijn van der Werf | f1aeef2685 | |
Marijn van der Werf | 4178cbeddb | |
Marijn van der Werf | 95f9f17664 | |
Aaron van Geffen | eb39c1a7f8 | |
Aaron van Geffen | 81cf28d864 | |
Aaron van Geffen | ea1215cd0d | |
Aaron van Geffen | 854a8267a3 | |
Aaron van Geffen | a3edba7481 | |
ceeac | 6bc1794660 | |
Aaron van Geffen | 0b0cdb8dda | |
Aaron van Geffen | ba1339616c |
|
@ -0,0 +1,12 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
|
||||
[*.txt]
|
||||
trim_trailing_whitespace = false
|
|
@ -1,23 +0,0 @@
|
|||
<!--
|
||||
Please fill out the form below by replacing the placeholders.
|
||||
Delete any headings and placeholders that you do not fill out.
|
||||
-->
|
||||
**OS:** e.g. Windows 10
|
||||
**Version:** e.g. 20.10
|
||||
**Commit/Build:** e.g. e6e665a
|
||||
|
||||
<!-- Explanation of the issue -->
|
||||
|
||||
|
||||
**Steps to reproduce:**
|
||||
1.
|
||||
2.
|
||||
|
||||
**Is the issue reproducible in Locomotion (vanilla)?**
|
||||
<!-- Yes / No, and to what extent? -->
|
||||
|
||||
**Screenshots / Video:**
|
||||
<!-- Drag & drop screenshots here. You can use e.g. YouTube to upload video. -->
|
||||
|
||||
**Save game:**
|
||||
<!-- Change the file extension to .txt or package to a .zip so that it can be drag & dropped here... -->
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Version information:**
|
||||
- Platform: [e.g. Windows 10]
|
||||
- Version: [e.g. 21.08]
|
||||
- Build/commit: [e.g. 9c24c21]
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behaviour:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behaviour**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
|
@ -1,8 +1,8 @@
|
|||
name: CI
|
||||
on: [push, pull_request]
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
env:
|
||||
OPENLOCO_BUILD_SERVER: GitHub
|
||||
OPENLOCO_VERSION: 20.10
|
||||
OPENLOCO_VERSION: 21.08
|
||||
jobs:
|
||||
check-code-formatting:
|
||||
name: Check code formatting
|
||||
|
@ -17,17 +17,19 @@ jobs:
|
|||
windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
needs: check-code-formatting
|
||||
env:
|
||||
CONFIGURATION: Release
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
- name: Build OpenRCT2
|
||||
shell: pwsh
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Build OpenLoco
|
||||
run: |
|
||||
$ErrorView = 'NormalView'
|
||||
$installPath = &"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -version 16.0 -property installationpath
|
||||
$instanceId = &"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -version 16.0 -property instanceid
|
||||
$installPath = vswhere -latest -property installationpath
|
||||
$instanceId = vswhere -latest -property instanceid
|
||||
Import-Module "$installPath\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
|
||||
Enter-VsDevShell $instanceId
|
||||
if (-not $env:GITHUB_REF.StartsWith("refs/tags/"))
|
||||
|
@ -38,21 +40,89 @@ jobs:
|
|||
$env:OPENLOCO_SHA1_SHORT=$env:GITHUB_SHA.Substring(0, 7)
|
||||
$env:GIT_DESCRIBE = (git describe HEAD | sed -E "s/-g.+$//")
|
||||
Write-Host "%GIT_DESCRIBE% = $env:GIT_DESCRIBE"
|
||||
msbuild openloco.sln /t:restore
|
||||
msbuild openloco.sln
|
||||
- name: Build artefacts
|
||||
shell: pwsh
|
||||
msbuild openloco.sln -m -t:restore
|
||||
msbuild openloco.sln -m
|
||||
- name: Build artifacts
|
||||
run: |
|
||||
$ErrorView = 'NormalView'
|
||||
New-Item -ItemType Directory artefacts | Out-Null
|
||||
Copy-Item CHANGELOG.md,CONTRIBUTORS.md,LICENSE,bin\*.dll artefacts
|
||||
Copy-Item data\language -Destination artefacts\data\language -Recurse
|
||||
Copy-Item loco.exe artefacts\openloco.exe
|
||||
Push-Location artefacts
|
||||
7z a -tzip -mx9 -mtc=off -r openloco-v${env:OPENLOCO_VERSION}-win32.zip *
|
||||
Pop-Location
|
||||
- name: Upload artefacts (CI)
|
||||
uses: actions/upload-artifact@v2-preview
|
||||
mkdir artifacts | Out-Null
|
||||
Copy-Item CHANGELOG.md,CONTRIBUTORS.md,LICENSE,loco.exe,bin\*.dll,bin\*.pdb artifacts
|
||||
Copy-Item data\language artifacts\data\language -Recurse
|
||||
Rename-Item artifacts\loco.exe openloco.exe
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "OpenLoco-Windows-Win32"
|
||||
path: artefacts/openloco-*-win32.zip
|
||||
name: OpenLoco-${{ runner.os }}-Win32
|
||||
path: artifacts
|
||||
if-no-files-found: error
|
||||
ubuntu:
|
||||
name: Ubuntu i686
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-code-formatting
|
||||
container: openloco/openloco:ubuntu-i686
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler: [g++, clang++]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Build OpenLoco
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G Ninja -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_BUILD_TYPE=release -DOPENLOCO_USE_CCACHE=OFF -DSDL2_DIR=/usr/lib/i386-linux-gnu/cmake/SDL2 -DSDL2_MIXER_PATH=/usr/lib/i386-linux-gnu -Dyaml-cpp_DIR=/usr/lib/i386-linux-gnu/cmake/yaml-cpp -DPNG_LIBRARY=/usr/lib/i386-linux-gnu/libpng16.so -DPNG_PNG_INCLUDE_DIR=/usr/include -DZLIB_LIBRARY=/usr/lib/i386-linux-gnu/libz.so
|
||||
ninja -k0
|
||||
fedora:
|
||||
name: Fedora i686 MinGW32
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-code-formatting
|
||||
container: openloco/openloco:fedora-mingw32
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Build OpenLoco
|
||||
run: |
|
||||
cmake -B build -G Ninja -DCMAKE_TOOLCHAIN_FILE=../CMakeLists_mingw.txt -DCMAKE_BUILD_TYPE=release -DOPENLOCO_USE_CCACHE=OFF -DSDL2_DIR=/usr/i686-w64-mingw32/sys-root/mingw/lib/cmake/SDL2/ -DSDL2_MIXER_PATH=/usr/i686-w64-mingw32/sys-root/mingw/ -Dyaml-cpp_DIR=/usr/i686-w64-mingw32/sys-root/mingw/CMake/ -DPNG_LIBRARY=/usr/i686-w64-mingw32/sys-root/mingw/bin/libpng16-16.dll -DPNG_PNG_INCLUDE_DIR=/usr/i686-w64-mingw32/sys-root/mingw/include
|
||||
cd build
|
||||
ninja -k0
|
||||
mac:
|
||||
name: macOS i686 osxcross
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-code-formatting
|
||||
container: openloco/osxcross:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install dependencies
|
||||
env:
|
||||
dependency_ver: 1.3.0
|
||||
run: |
|
||||
ln -s /usr/osxcross/SDK/MacOSX10.13.sdk/System /System
|
||||
curl -LfO "https://github.com/OpenLoco/Dependencies/releases/download/v${dependency_ver}/openloco.dependencies.macos.${dependency_ver}.zip"
|
||||
unzip openloco.dependencies.macos.${dependency_ver}.zip -d vcpkg
|
||||
- name: Build
|
||||
env:
|
||||
OSXCROSS_HOST: i386-apple-darwin17
|
||||
TOOLCHAIN1: ${{ github.workspace }}/osxcross/tools/toolchain.cmake
|
||||
TOOLCHAIN2: ${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.13
|
||||
run: |
|
||||
/usr/osxcross/bin/i386-apple-darwin17-osxcross-conf
|
||||
eval $(/usr/osxcross/bin/i386-apple-darwin17-osxcross-conf)
|
||||
mkdir build
|
||||
cd build
|
||||
export LD_LIBRARY_PATH=/usr/osxcross/lib:$LD_LIBRARY_PATH
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/osxcross_toolchain.cmake -DVCPKG_TARGET_TRIPLET=x86-osx
|
||||
make -j$(getconf _NPROCESSORS_ONLN)
|
||||
tar -cvzf ../openloco.tar.gz openloco.app
|
||||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: OpenLoco-macOS
|
||||
path: openloco.tar.gz
|
||||
if-no-files-found: error
|
||||
|
|
63
.travis.yml
63
.travis.yml
|
@ -1,63 +0,0 @@
|
|||
language: c
|
||||
|
||||
before_install:
|
||||
- docker pull openloco/openloco:$DOCKERIMG
|
||||
|
||||
install:
|
||||
- git config remote.origin.fetch +refs/heads/*:refs/remotes/origin/*
|
||||
- git config remote.origin.fetch +refs/tags/*:refs/tags/*
|
||||
- git fetch --tags
|
||||
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
name: Ubuntu i686 GCC
|
||||
services:
|
||||
- docker
|
||||
env:
|
||||
- OPENLOCO_CMAKE_OPTS="-G Ninja -DCMAKE_BUILD_TYPE=release -DSDL2_DIR=/usr/lib/i386-linux-gnu/cmake/SDL2 -DSDL2_MIXER_PATH=/usr/lib/i386-linux-gnu -Dyaml-cpp_DIR=/usr/lib/i386-linux-gnu/cmake/yaml-cpp -DPNG_LIBRARY=/usr/lib/i386-linux-gnu/libpng16.so -DPNG_PNG_INCLUDE_DIR=/usr/include -DZLIB_LIBRARY=/usr/lib/i386-linux-gnu/libz.so"
|
||||
- DOCKERIMG=ubuntu-i686
|
||||
- os: linux
|
||||
name: Ubuntu i686 Clang
|
||||
services:
|
||||
- docker
|
||||
env:
|
||||
- OPENLOCO_CMAKE_OPTS="-G Ninja -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=release -DSDL2_DIR=/usr/lib/i386-linux-gnu/cmake/SDL2 -DSDL2_MIXER_PATH=/usr/lib/i386-linux-gnu -Dyaml-cpp_DIR=/usr/lib/i386-linux-gnu/cmake/yaml-cpp -DPNG_LIBRARY=/usr/lib/i386-linux-gnu/libpng16.so -DPNG_PNG_INCLUDE_DIR=/usr/include -DZLIB_LIBRARY=/usr/lib/i386-linux-gnu/libz.so"
|
||||
- DOCKERIMG=ubuntu-i686
|
||||
- os: linux
|
||||
name: Fedora i686 MinGW32
|
||||
services:
|
||||
- docker
|
||||
env:
|
||||
- OPENLOCO_CMAKE_OPTS="-G Ninja -DCMAKE_TOOLCHAIN_FILE=../CMakeLists_mingw.txt -DCMAKE_BUILD_TYPE=release -DBOOST_ROOT=/usr/i686-w64-mingw32/sys-root/mingw/ -DSDL2_DIR=/usr/i686-w64-mingw32/sys-root/mingw/lib/cmake/SDL2/ -DSDL2_MIXER_PATH=/usr/i686-w64-mingw32/sys-root/mingw/ -Dyaml-cpp_DIR=/usr/i686-w64-mingw32/sys-root/mingw/CMake/ -DPNG_LIBRARY=/usr/i686-w64-mingw32/sys-root/mingw/bin/libpng16-16.dll -DPNG_PNG_INCLUDE_DIR=/usr/i686-w64-mingw32/sys-root/mingw/include"
|
||||
- DOCKERIMG=fedora-mingw32
|
||||
- os: osx
|
||||
name: macOS 10.13 Clang (Xcode 9.3)
|
||||
osx_image: xcode9.3 # macOS 10.13
|
||||
language: cpp
|
||||
before_install:
|
||||
- export HOMEBREW_NO_AUTO_UPDATE=1
|
||||
script:
|
||||
- curl -L https://github.com/OpenLoco/Dependencies/releases/download/v1.2.0/openloco.dependencies.macos.1.2.0.zip -o dependencies.zip
|
||||
- unzip -q dependencies.zip -d vcpkg/
|
||||
- mkdir build && cd build
|
||||
- cmake .. "-DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x86-osx
|
||||
- make -j2
|
||||
- zip -r openloco-macos.zip openloco.app
|
||||
|
||||
script:
|
||||
- mkdir build
|
||||
- docker run -v $(pwd):/openloco -w /openloco/build -i -t openloco/openloco:$DOCKERIMG bash -c "cmake ../ ${OPENLOCO_CMAKE_OPTS} && ninja -k0"
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: Kuzqa3+lCSDyGu3HE4k/fRmqoBTs6DYiVnO7olAnvaqJxzE+BNAlgPbZmO+mw83xJY7u6mKrrM2y7chzbsmUqVFgRig9OC2K7aCVTRkxiXwDFGjksedXnYc35kGE+p9wv7sk8JLcoqjrqkhpLAqjdgsT8V+VvlSVWjp4J0DYbR7M4COBSMGxdvmeQwG6VrXjdRy90c4FEffLuWG79J879hqYVkXNW4GnYan6YW7sX/YkUmXTbbrT618Whb90jZwu1njn+qTWRyIb2EQaPdAhjGBBDt9QIhauv6AkyZOgL9C79ltizr1l24pnYexcQVv7QZ5ipBPk4weAzbSeJRaMS15qG7w+qOwwlduBB8YHO3DKKlyvAlXtVdeOj4LeaTIGctGKIO/2320rZeIzAIA59uyveKVyIqJpYlay5AMdoNvy//a4+huSdu9gHnNZJOACMRKUvRW+7T3clW8rBw4d7aO9siabps45Usbu+U1s/XKxVWMR65fXhxQwVh4u+NACUMK5/01L7SNKztw/x3eKf15ekxu+I2yBmWQRNWk0TRl4MV+qHt09hcJUU3rThaeJfHAM+/asgVDUn1e5rzktSGw8xxi1G3vjsBEAKts27+3AdDQRWVWPS9zfr+FNAG9W1C56uDQsnEMHbBXOLQ0Q7b7jxqdnS4kuZIjYPugkYTw=
|
||||
file: build/openloco-macos.zip
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
repo: OpenLoco/OpenLoco
|
92
CHANGELOG.md
92
CHANGELOG.md
|
@ -1,3 +1,95 @@
|
|||
21.08+ (???)
|
||||
------------------------------------------------------------------------
|
||||
- Fix: [#1108] Road selection not being remembered.
|
||||
- Fix: [#1124] Confirmation prompt captions are not rendered correctly.
|
||||
- Change: [#1104] Exceptions now trigger a message box popup, instead of only being written to the console.
|
||||
|
||||
21.08 (2021-08-12)
|
||||
------------------------------------------------------------------------
|
||||
- Fix: [#366] Original Bug. People and mail cargo incorrectly delivered to far away stations.
|
||||
- Fix: [#1035] Incorrect colour selection when building buildings.
|
||||
- Fix: [#1070] Crash when naming stations after exhausting natural names.
|
||||
- Fix: [#1094] Repeated clicking on construction window not always working.
|
||||
- Fix: [#1095] Individual expenses are drawn in red, not just the expenditure sums.
|
||||
- Fix: [#1102] Invalid file error when clicking empty space in file browser.
|
||||
- Change: [#298] Planting clusters of trees now costs money and influences ratings outside of editor mode.
|
||||
- Change: [#1079] Allow rotating buildings in town list by keyboard shortcut.
|
||||
|
||||
21.07 (2021-07-18)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: [#856] Allow filtering the vehicle list by station or cargo type.
|
||||
- Fix: [#982] Incorrect rating calculation for cargo causing penalty for fast vehicles.
|
||||
- Fix: [#984] Unable to reset/regenerate station names by using an empty name.
|
||||
- Fix: [#1008] Inability to decrease max altitude for trees in landscape editor.
|
||||
- Fix: [#1016] Incorrect detection of station causing incorrect smoke sounds.
|
||||
- Fix: [#1044] Incorrect rotation of headquarters when placing. No scaffolding when placing headquarters.
|
||||
- Technical: [#986] Stack misalignment in GCC builds caused unexplained crashes on Linux and Mac during interop hooks with loco.exe.
|
||||
- Technical: [#993] Retry hook installation to fix incompatibles with older wine versions.
|
||||
- Technical: [#1006] Add breakpad-based dumping for MSVC builds
|
||||
|
||||
21.05 (2021-05-11)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: [#184] Implement cheats window with financial, company, vehicle, and town cheats.
|
||||
- Feature: [#857] Remember last save directory in configuration variable.
|
||||
- Feature: [#923] Tween (linear interpolate) entities when frame limiter is uncapped for smoother movement.
|
||||
- Fix: [#914] Boats get stuck in approaching dock mode when water is above a certain height. This was incorrectly fixed in 21.04.1.
|
||||
- Fix: [#923] Decouple viewport updates from game ticks for smoother panning and zooming.
|
||||
- Fix: [#927] Some available industries are missing in the 'Fund new industries' tab.
|
||||
- Fix: [#945] Station construction preview image is using wrong colours.
|
||||
- Fix: [#957] Element name is not shown when inspecting track elements using the Tile Inspector.
|
||||
- Change: [#975] The multiplayer toggle button on the title screen has been hidden, as multiplayer has not been reimplemented yet.
|
||||
|
||||
21.04.1 (2021-04-14)
|
||||
------------------------------------------------------------------------
|
||||
- Fix: [#914] Boats get stuck in approaching dock mode when water is above a certain height.
|
||||
- Fix: [#915] Money subtractions with large values incorrectly calculated causing negative money.
|
||||
|
||||
21.04 (2021-04-10)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: [#451] Optionally show an FPS counter at the top of the screen.
|
||||
- Feature: [#831] Add a tile inspector, allowing inspection of tile element data (read-only).
|
||||
- Feature: [#853] Allow unlocking FPS by detaching game logic from rendering.
|
||||
- Fix: [#391] Access violation in windows after exiting games.
|
||||
- Fix: [#804] Enter key not confirming save prompt.
|
||||
- Fix: [#809] Audio calculation not using the z axis.
|
||||
- Fix: [#825] Potential crash when opening town rename prompt.
|
||||
- Fix: [#838] Escape key doesn't work in confirmation windows.
|
||||
- Fix: [#845] Town growth incorrectly calculated causing more aggressive growth than should be possible.
|
||||
- Fix: [#853] The game run slightly, but noticeably, slower than vanilla Locomotion.
|
||||
- Fix: [#860] Incorrect capacity information for vehicles that do not carry cargo (e.g. is a train engine).
|
||||
|
||||
21.03 (2021-03-06)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: [#125] Allow construction while paused using a new optional cheats/debugging menu.
|
||||
- Feature: [#796] Allow users to toggle sandbox mode in-game using the cheats menu.
|
||||
- Fix: [#294] Crash when setting company name twice.
|
||||
- Fix: [#697] Ghost elements are not removed in autosaves.
|
||||
- Fix: [#794] Game does not stay paused while in construction mode.
|
||||
- Fix: [#798] Setting waypoints on multitile track/road elements corrupts the position.
|
||||
- Fix: [#801] Initial save path does not contain a trailing slash.
|
||||
- Fix: [#807] Incorrect vehicle animation for speed based animations like hydrofoils when at max speed.
|
||||
- Change: [#361] The game now allows scenarios to start from 1800, with adjusted inflation.
|
||||
- Change: [#787] Scenery and building interaction is now disabled when see-through.
|
||||
|
||||
21.02 (2021-02-20)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: [#122] Allow vehicles to be cloned from the vehicle window.
|
||||
- Feature: [#690] Automatically save the game at regular intervals.
|
||||
- Feature: [#702] Optional new map generator (experimental).
|
||||
- Fix: [#151] Mouse moves out of window when looking around.
|
||||
- Fix: [#588] 'Cancel or Show Last Announcement' shortcut doesn't close announcements.
|
||||
- Fix: [#679] Crash when changing ground texture.
|
||||
- Fix: [#694] Selecting a song to play is guaranteed to not play it.
|
||||
- Fix: [#712] Load / save window tries to show preview for item after last.
|
||||
- Fix: [#721] Incorrect catchment area for airports.
|
||||
- Fix: [#725] Company value graph does not display correctly.
|
||||
- Fix: [#744] Rendering issues ('Z-fighting') with vehicles over bridges.
|
||||
- Fix: [#766] Performance index is off by a factor of 10 in scenario options window.
|
||||
- Fix: [#769] Waypoints for road vehicles could not be set.
|
||||
- Fix: [#779] Town list displays the wrong amount of stations.
|
||||
- Change: [#690] Default saved game directory is now in OpenLoco user directory.
|
||||
- Change: [#762] The vehicle window now uses buttons for local/express mode.
|
||||
|
||||
20.10 (2020-10-25)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: [#569] Option/cheat to disable AI companies entirely.
|
||||
|
|
|
@ -3,6 +3,21 @@ cmake_policy(VERSION 3.9)
|
|||
|
||||
set (PROJECT openloco)
|
||||
|
||||
# Note: Searching for CCache must be before project() so project() can use CCache too
|
||||
# if it is available
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
find_package(CCache)
|
||||
|
||||
if (CCache_FOUND)
|
||||
option(OPENLOCO_USE_CCACHE "Use CCache to improve recompilation speed (optional)" ON)
|
||||
if (OPENLOCO_USE_CCACHE)
|
||||
# Use e.g. "ccache clang++" instead of "clang++"
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCache_EXECUTABLE}")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCache_EXECUTABLE}")
|
||||
endif (OPENLOCO_USE_CCACHE)
|
||||
endif (CCache_FOUND)
|
||||
|
||||
project(${PROJECT} CXX)
|
||||
|
||||
if (APPLE)
|
||||
|
@ -148,16 +163,13 @@ set(DEBUG_LEVEL 0 CACHE STRING "Select debug level for compilation. Use value in
|
|||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEBUG=${DEBUG_LEVEL}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG=${DEBUG_LEVEL}")
|
||||
|
||||
# include lib
|
||||
include_directories("lib/")
|
||||
# add source files
|
||||
file(GLOB_RECURSE OLOCO_SOURCES "src/*.cpp")
|
||||
file(GLOB_RECURSE OLOCO_HEADERS "src/*.h" "src/*.hpp")
|
||||
|
||||
|
||||
if (APPLE)
|
||||
file(GLOB_RECURSE OLOCO_MM_SOURCES "src/*.mm")
|
||||
set_source_files_properties(${OLOCO_MM_SOURCES} PROPERTIES COMPILE_FLAGS "-x objective-c++ -fmodules")
|
||||
set_source_files_properties(${OLOCO_MM_SOURCES} PROPERTIES COMPILE_FLAGS "-x objective-c++")
|
||||
endif ()
|
||||
|
||||
set(PIE_FLAG "-fno-pie")
|
||||
|
@ -205,17 +217,12 @@ find_package(SDL2_mixer REQUIRED)
|
|||
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
# The hint provided here is targetting Arch Linux, a distro of choice for many contributors
|
||||
if ("${CMAKE_SYSTEM_NAME}" MATCHES "(Free|Net|Open|DragonFly)BSD")
|
||||
find_package(yaml-cpp REQUIRED)
|
||||
include_directories(${YAML_CPP_INCLUDE_DIR})
|
||||
else()
|
||||
find_package(yaml-cpp REQUIRED HINTS /usr/lib32/cmake/yaml-cpp)
|
||||
endif()
|
||||
find_package(yaml-cpp REQUIRED HINTS /usr/lib32/cmake/yaml-cpp)
|
||||
include_directories(${YAML_CPP_INCLUDE_DIR})
|
||||
|
||||
# Disable optimizations for interop.cpp for all compilers, to allow optimized
|
||||
# builds without need for -fno-omit-frame-pointer
|
||||
set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/src/openloco/interop/interop.cpp" PROPERTIES COMPILE_FLAGS -fno-omit-frame-pointer)
|
||||
set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/src/OpenLoco/Interop/Interop.cpp" PROPERTIES COMPILE_FLAGS "-fno-omit-frame-pointer -O0")
|
||||
|
||||
if (NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
@ -268,26 +275,30 @@ if (APPLE)
|
|||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${OPENLOCO_VERSION_TAG} ${OPENLOCO_BRANCH}")
|
||||
endif()
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/distribution/macos/AppIcon.iconset DESTINATION ${CMAKE_BINARY_DIR})
|
||||
set(ICON_TARGET "${CMAKE_BINARY_DIR}/AppIcon.iconset")
|
||||
set(ICON_OUTPUT "${CMAKE_BINARY_DIR}/AppIcon.icns")
|
||||
set(SOURCE_ICON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/resources/logo")
|
||||
set(BUNDLE_RESOURCES "")
|
||||
|
||||
add_custom_command(OUTPUT ${ICON_OUTPUT}
|
||||
COMMAND cp icon_x16.png ${ICON_TARGET}/icon_16x16.png
|
||||
COMMAND cp icon_x32.png ${ICON_TARGET}/icon_16x16@2x.png
|
||||
COMMAND cp icon_x32.png ${ICON_TARGET}/icon_32x32.png
|
||||
COMMAND cp icon_x64.png ${ICON_TARGET}/icon_32x32@2x.png
|
||||
COMMAND cp icon_x128.png ${ICON_TARGET}/icon_128x128.png
|
||||
COMMAND cp icon_x256.png ${ICON_TARGET}/icon_128x128@2x.png
|
||||
COMMAND cp icon_x256.png ${ICON_TARGET}/icon_256x256.png
|
||||
COMMAND cp icon_x512.png ${ICON_TARGET}/icon_256x256@2x.png
|
||||
COMMAND cp icon_x512.png ${ICON_TARGET}/icon_512x512.png
|
||||
COMMAND cp icon_x1024.png ${ICON_TARGET}/icon_512x512@2x.png
|
||||
COMMAND iconutil -c icns ${ICON_TARGET}
|
||||
WORKING_DIRECTORY ${SOURCE_ICON_DIR})
|
||||
find_program(ICONUTIL iconutil)
|
||||
if (ICONUTIL)
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/distribution/macos/AppIcon.iconset DESTINATION ${CMAKE_BINARY_DIR})
|
||||
set(ICON_TARGET "${CMAKE_BINARY_DIR}/AppIcon.iconset")
|
||||
set(ICON_OUTPUT "${CMAKE_BINARY_DIR}/AppIcon.icns")
|
||||
set(SOURCE_ICON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/resources/logo")
|
||||
add_custom_command(OUTPUT ${ICON_OUTPUT}
|
||||
COMMAND cp icon_x16.png ${ICON_TARGET}/icon_16x16.png
|
||||
COMMAND cp icon_x32.png ${ICON_TARGET}/icon_16x16@2x.png
|
||||
COMMAND cp icon_x32.png ${ICON_TARGET}/icon_32x32.png
|
||||
COMMAND cp icon_x64.png ${ICON_TARGET}/icon_32x32@2x.png
|
||||
COMMAND cp icon_x128.png ${ICON_TARGET}/icon_128x128.png
|
||||
COMMAND cp icon_x256.png ${ICON_TARGET}/icon_128x128@2x.png
|
||||
COMMAND cp icon_x256.png ${ICON_TARGET}/icon_256x256.png
|
||||
COMMAND cp icon_x512.png ${ICON_TARGET}/icon_256x256@2x.png
|
||||
COMMAND cp icon_x512.png ${ICON_TARGET}/icon_512x512.png
|
||||
COMMAND cp icon_x1024.png ${ICON_TARGET}/icon_512x512@2x.png
|
||||
COMMAND ${ICONUTIL} --convert icns --output ${ICON_OUTPUT} ${ICON_TARGET}
|
||||
WORKING_DIRECTORY ${SOURCE_ICON_DIR})
|
||||
list(APPEND BUNDLE_RESOURCES ${ICON_OUTPUT})
|
||||
endif ()
|
||||
|
||||
set(BUNDLE_RESOURCES ${ICON_OUTPUT})
|
||||
list(APPEND BUNDLE_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CHANGELOG.md)
|
||||
list(APPEND BUNDLE_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CONTRIBUTORS.MD)
|
||||
set_target_properties(${PROJECT} PROPERTIES RESOURCE "${BUNDLE_RESOURCES}")
|
||||
|
@ -310,9 +321,16 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
|||
set_target_properties(${PROJECT}-headers-check PROPERTIES LINKER_LANGUAGE CXX)
|
||||
set_source_files_properties(${OLOCO_HEADERS} PROPERTIES LANGUAGE CXX)
|
||||
add_definitions("-x c++ -Wno-pragma-once-outside-header -Wno-unused-const-variable")
|
||||
get_target_property(OPENLOCO_INCLUDE_DIRS ${PROJECT} INCLUDE_DIRECTORIES)
|
||||
set_target_properties(${PROJECT}-headers-check PROPERTIES INCLUDE_DIRECTORIES "${OPENLOCO_INCLUDE_DIRS}")
|
||||
else ()
|
||||
# Dummy target to ease invocation
|
||||
add_custom_target(${PROJECT}-headers-check)
|
||||
endif ()
|
||||
|
||||
install(FILES "distribution/linux/openloco.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications")
|
||||
install(FILES "resources/logo/icon_x16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "openloco.png")
|
||||
install(FILES "resources/logo/icon_x32.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME "openloco.png")
|
||||
install(FILES "resources/logo/icon_x64.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/64x64/apps" RENAME "openloco.png")
|
||||
install(FILES "resources/logo/icon_x128.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps" RENAME "openloco.png")
|
||||
install(FILES "resources/logo/icon_x256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "openloco.png")
|
||||
install(FILES "resources/logo/icon_x512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "openloco.png")
|
||||
install(FILES "resources/logo/icon_steam.svg" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps" RENAME "openloco.svg")
|
||||
|
|
|
@ -27,9 +27,10 @@ Includes all git commit authors. Aliases are GitHub user names.
|
|||
* Italian - extracted from original game
|
||||
* Korean - (telk5093)
|
||||
* Polish - (Zcooger)
|
||||
* Portuguese (BR) - (Kynake)
|
||||
* Portuguese (BR) - Pedro Bledow (Kynake), Gustavo Fernandes (guKing)
|
||||
* Slovak - Peter Gaál (petergaal)
|
||||
* Spanish - extracted from original game
|
||||
* Chinese (Simplified) - (TransshipmentEnvoy)
|
||||
|
||||
## Graphics
|
||||
* OpenLoco Logo - Zcooger (zcooger)
|
||||
|
|
52
README.md
52
README.md
|
@ -8,18 +8,19 @@ An open source re-implementation of Chris Sawyer's Locomotion. A construction an
|
|||
# Contents
|
||||
- 1 - [Introduction](#1-introduction)
|
||||
- 2 - [Downloading the game (pre-built)](#2-downloading-the-game-pre-built)
|
||||
- 3 - [Building the game](#3-building-the-game)
|
||||
- 3.1 - [Building prerequisites](#31-building-prerequisites)
|
||||
- 3.2 - [Compiling and running](#32-compiling-and-running)
|
||||
- 4 - [Licence](#4-licence)
|
||||
- 5 - [More information](#5-more-information)
|
||||
- 3 - [Contributing](#3-contributing)
|
||||
- 4 - [Compiling the game](#4-compiling-the-game)
|
||||
- 4.1 - [Building prerequisites](#41-building-prerequisites)
|
||||
- 4.2 - [Compiling and running](#42-compiling-and-running)
|
||||
- 5 - [Licence](#5-licence)
|
||||
- 6 - [More information](#6-more-information)
|
||||
|
||||
---
|
||||
|
||||
### Build Status
|
||||
| | Windows | Linux / Mac | Download |
|
||||
|-------------|---------|-------------|----------|
|
||||
| **master** | ![CI](https://github.com/OpenLoco/OpenLoco/workflows/CI/badge.svg) | [![Travis CI](https://travis-ci.org/OpenLoco/OpenLoco.svg?branch=master)](https://travis-ci.org/OpenLoco/OpenLoco) | [![GitHub release](https://img.shields.io/github/release/OpenLoco/OpenLoco.svg)](https://github.com/OpenLoco/OpenLoco/releases) |
|
||||
| | Windows / Linux | Download |
|
||||
|-------------|-----------------|----------|
|
||||
| **master** | ![CI](https://github.com/OpenLoco/OpenLoco/workflows/CI/badge.svg) | [![GitHub release](https://img.shields.io/github/release/OpenLoco/OpenLoco.svg)](https://github.com/OpenLoco/OpenLoco/releases) |
|
||||
|
||||
### Chat
|
||||
|
||||
|
@ -40,17 +41,33 @@ Recent implementation efforts have focussed on re-implementing the UI, so that t
|
|||
|
||||
# 2 Downloading the game (pre-built)
|
||||
|
||||
OpenLoco requires original files of Chris Sawyer's Locomotion to play. It can be bought at either [Steam](https://store.steampowered.com/app/356430/) or [GOG.com](https://www.gog.com/game/chris_sawyers_locomotion).
|
||||
The latest releases can be [downloaded from GitHub](https://github.com/OpenLoco/OpenLoco/releases). Releases are currently provided for Windows and macOS (32-bit only).
|
||||
For Linux and BSD distributions, we currently do not provide any builds. Please refer to the next section to compile the game manually.
|
||||
|
||||
The latest release can be found on [GitHub](https://github.com/OpenLoco/OpenLoco/releases).
|
||||
Please note that OpenLoco requires the asset files of the original Chris Sawyer's Locomotion to play the game.
|
||||
It can be bought at e.g. [Steam](https://store.steampowered.com/app/356430/) or [GOG.com](https://www.gog.com/game/chris_sawyers_locomotion).
|
||||
|
||||
---
|
||||
|
||||
# 3 Building the game
|
||||
# 3 Contributing
|
||||
|
||||
## 3.1 Building prerequisites
|
||||
We warmly welcome any contributions to the project, e.g. for C++ code (game implementation, bug fixes, features) or localisation (new translations).
|
||||
Please have a look at our [issues for newcomers](https://github.com/OpenLoco/OpenLoco/labels/good%20first%20issue).
|
||||
|
||||
OpenLoco requires original files of Chris Sawyer's Locomotion to play. It can be bought at either [Steam](https://store.steampowered.com/app/356430/) or [GOG.com](https://www.gog.com/game/chris_sawyers_locomotion).
|
||||
For code contributions, please stick to our [code style](https://github.com/OpenLoco/OpenLoco/wiki/Coding-Style).
|
||||
You can use `clang-format` to apply these guidelines automatically.
|
||||
|
||||
---
|
||||
|
||||
# 4 Compiling the game
|
||||
|
||||
If you would like to contribute code to OpenLoco, please follow the instructions below to get started compiling the game.
|
||||
Alternatively, we have platform-specific guides for [Ubuntu](https://github.com/OpenLoco/OpenLoco/wiki/Building-on-Ubuntu) and [macOS](https://github.com/OpenLoco/OpenLoco/wiki/Building-on-macOS).
|
||||
|
||||
If you just want to play the game, you can just [download the latest release](https://github.com/OpenLoco/OpenLoco/releases) from GitHub.
|
||||
Releases are currently provided for Windows and macOS (32-bit only).
|
||||
|
||||
## 4.1 Building prerequisites
|
||||
|
||||
Regardless of platform, the following libraries/dependencies are required:
|
||||
- [libpng](http://www.libpng.org/pub/png/libpng.html)
|
||||
|
@ -72,13 +89,14 @@ Regardless of platform, the following libraries/dependencies are required:
|
|||
|
||||
---
|
||||
|
||||
## 3.2 Compiling and running
|
||||
## 4.2 Compiling and running
|
||||
### Windows:
|
||||
1. Check out the repository. This can be done using [GitHub Desktop](https://desktop.github.com) or [other tools](https://help.github.com/articles/which-remote-url-should-i-use).
|
||||
2. Open a new Developer Command Prompt for VS 2019, then navigate to the repository (e.g. `cd C:\GitHub\OpenLoco`).
|
||||
3. Run `msbuild openloco.sln /t:restore;build`
|
||||
4. Run `mklink /D bin\data ..\data` or `xcopy data bin\data /EIY`
|
||||
5. Run the game, `bin\openloco`
|
||||
5. Run `mklink openloco.exe bin\` or `copy openloco.exe bin\`
|
||||
6. Run the game, `bin\openloco`
|
||||
|
||||
### Linux / macOS:
|
||||
The standard CMake build procedure is to install the required libraries, then:
|
||||
|
@ -97,12 +115,12 @@ cp -r ../data ./data
|
|||
```
|
||||
---
|
||||
|
||||
# 4 Licence
|
||||
# 5 Licence
|
||||
**OpenLoco** is licensed under the MIT License.
|
||||
|
||||
---
|
||||
|
||||
# 5 More information
|
||||
# 6 More information
|
||||
- [GitHub](https://github.com/OpenLoco/OpenLoco)
|
||||
- [TT-Forums](https://www.tt-forums.net)
|
||||
- [Locomotion subreddit](https://www.reddit.com/r/locomotion/)
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# To find CCache compiler cache
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_program(CCache_EXECUTABLE ccache)
|
||||
|
||||
if (CCache_EXECUTABLE)
|
||||
execute_process(COMMAND "${CCache_EXECUTABLE}" --version
|
||||
OUTPUT_VARIABLE CCache_VERSION_OUTPUT
|
||||
)
|
||||
|
||||
if (CCache_VERSION_OUTPUT MATCHES "version ([0-9]+\\.[0-9]+\\.[0-9]+)")
|
||||
set(CCache_VERSION "${CMAKE_MATCH_1}")
|
||||
endif ()
|
||||
endif (CCache_EXECUTABLE)
|
||||
|
||||
find_package_handle_standard_args(CCache
|
||||
FOUND_VAR CCache_FOUND
|
||||
REQUIRED_VARS CCache_EXECUTABLE
|
||||
VERSION_VAR CCache_VERSION
|
||||
)
|
||||
|
||||
mark_as_advanced(CCache_EXECUTABLE)
|
|
@ -0,0 +1,37 @@
|
|||
message("HELLO FROM TOOLCHAIN")
|
||||
message("TOOLCHAIN2: $ENV{TOOLCHAIN2}")
|
||||
|
||||
macro(osxcross_getconf VAR)
|
||||
if(NOT ${VAR})
|
||||
set(${VAR} "$ENV{${VAR}}")
|
||||
if(${VAR})
|
||||
set(${VAR} "${${VAR}}" CACHE STRING "${VAR}")
|
||||
message(STATUS "Found ${VAR}: ${${VAR}}")
|
||||
else()
|
||||
message(FATAL_ERROR "Cannot determine \"${VAR}\"")
|
||||
endif()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
osxcross_getconf(OSXCROSS_HOST)
|
||||
osxcross_getconf(OSXCROSS_TARGET_DIR)
|
||||
osxcross_getconf(OSXCROSS_TARGET)
|
||||
osxcross_getconf(OSXCROSS_SDK)
|
||||
|
||||
set(CMAKE_SYSTEM_NAME "Darwin")
|
||||
string(REGEX REPLACE "-.*" "" CMAKE_SYSTEM_PROCESSOR "${OSXCROSS_HOST}")
|
||||
|
||||
# specify the cross compiler
|
||||
set(CMAKE_C_COMPILER "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-clang")
|
||||
set(CMAKE_CXX_COMPILER "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-clang++")
|
||||
|
||||
set(CMAKE_AR "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-ar" CACHE FILEPATH "ar")
|
||||
set(CMAKE_RANLIB "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-ranlib" CACHE FILEPATH "ranlib")
|
||||
set(CMAKE_INSTALL_NAME_TOOL "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-install_name_tool" CACHE FILEPATH "install_name_tool")
|
||||
|
||||
set(ENV{PKG_CONFIG_LIBDIR} "${OSXCROSS_TARGET_DIR}/macports/pkgs/opt/local/lib/pkgconfig")
|
||||
set(ENV{PKG_CONFIG_SYSROOT_DIR} "${OSXCROSS_TARGET_DIR}/macports/pkgs")
|
||||
|
||||
set(TOOLCHAIN2 "$ENV{TOOLCHAIN2}")
|
||||
include("${TOOLCHAIN2}")
|
||||
message("CMAKE_FIND_ROOT_PATH: ${CMAKE_FIND_ROOT_PATH}")
|
|
@ -1243,8 +1243,8 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: Es können keine Aufträge für Fahrzeuge mehr angenommen werden!
|
||||
1215: Für dieses Fahrzeug liegen zu viele Aufträge vor!
|
||||
1216: "- - Lokal - -"
|
||||
1217: "- - Express - -"
|
||||
1216: "Lokal"
|
||||
1217: "Express"
|
||||
1218: "{COLOUR BLACK}- - Keine Strecke definiert - -"
|
||||
1219: "{COLOUR BLACK}- - Ende der Streckenliste - -"
|
||||
1220: Halt bei {STRINGID}
|
||||
|
|
|
@ -1243,8 +1243,8 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: No space for more vehicle orders!
|
||||
1215: Too many orders for this vehicle!
|
||||
1216: "- - Local - -"
|
||||
1217: "- - Express - -"
|
||||
1216: "Local"
|
||||
1217: "Express"
|
||||
1218: "{COLOUR BLACK}- - No route defined - -"
|
||||
1219: "{COLOUR BLACK}- - End of route list - -"
|
||||
1220: Stop at {STRINGID}
|
||||
|
@ -2151,7 +2151,7 @@ strings:
|
|||
2096: New objects installed successfully
|
||||
2097: At least one industry must be selected
|
||||
2098: At least one town building must be selected
|
||||
2099: An company headquarters building type must be selected
|
||||
2099: A company headquarters building type must be selected
|
||||
2100: Only one company headquarters building type must be selected
|
||||
2101: An interface type must be selected
|
||||
2102: At least one vehicle type must be selected
|
||||
|
@ -2195,3 +2195,78 @@ strings:
|
|||
2140: "Exit OpenLoco"
|
||||
2141: "{COLOUR WINDOW_2}Disable AI companies"
|
||||
2142: "{SMALLFONT}{COLOUR BLACK}This disables AI from 'thinking', rendering them ineffective.{NEWLINE}In new games, this also prevents new AI companies from forming."
|
||||
2143: "{COLOUR WINDOW_2}Autosave amount:"
|
||||
2144: "{COLOUR WINDOW_2}Autosave frequency:"
|
||||
2145: "Never"
|
||||
2146: "Every month"
|
||||
2147: "Every {INT32} months"
|
||||
2148: "{COLOUR WINDOW_2}Generator:"
|
||||
2149: Original
|
||||
2150: Improved
|
||||
2151: "Modify vehicle"
|
||||
2152: "Clone vehicle"
|
||||
2153: "Can't clone vehicle..."
|
||||
2154: "Locate vehicle on main view"
|
||||
2155: "Follow vehicle on main view"
|
||||
2156: "Enable cheats/debugging toolbar menu"
|
||||
2157: "{SMALLFONT}{COLOUR BLACK}This adds an extra button to the game's toolbar, allowing easy access to certain cheats and debugging options."
|
||||
2158: "Enable sandbox mode"
|
||||
2159: "Allow manual driving"
|
||||
2160: "Allow building while paused"
|
||||
2161: "Display fps counter"
|
||||
2162: "{SMALLFONT}{COLOUR BLACK}This shows a counter at the top of the screen, indicating the number of frames drawn per second."
|
||||
2163: "Uncap frame limiter"
|
||||
2164: "{SMALLFONT}{COLOUR BLACK}Removes the restriction of rendering at 40Hz."
|
||||
2165: "Hardware"
|
||||
2166: "Map rendering"
|
||||
2167: "Tile inspector"
|
||||
2168: "{SMALLFONT}{COLOUR BLACK}Activate to select a tile on the map to inspect."
|
||||
2169: "Surface"
|
||||
2170: "Track"
|
||||
2171: "Station"
|
||||
2172: "Signal"
|
||||
2173: "Building"
|
||||
2174: "Tree"
|
||||
2175: "Wall"
|
||||
2176: "Road"
|
||||
2177: "Industry"
|
||||
2178: "{COLOUR WINDOW_2} {STRINGID} ({STRINGID})"
|
||||
2179: "{COLOUR WINDOW_2} {STRINGID} - {STRINGID} ({STRINGID})"
|
||||
2180: "X:"
|
||||
2181: "Y:"
|
||||
2182: "{COLOUR WINDOW_2}{INT16}"
|
||||
2183: "Tile element data"
|
||||
2184: "Cheats"
|
||||
2185: "Financial cheats"
|
||||
2186: "Company cheats"
|
||||
2187: "Vehicle cheats"
|
||||
2188: "Town cheats"
|
||||
2189: "Clear loan"
|
||||
2190: "Clear"
|
||||
2191: "{COLOUR BLACK}{CURRENCY32}"
|
||||
2192: "Select target company"
|
||||
2193: "Select cheat to apply"
|
||||
2194: "Switch control to this company"
|
||||
2195: "Acquire all company assets"
|
||||
2196: "Toggle bankruptcy"
|
||||
2197: "Toggle jail status"
|
||||
2198: "Increase funds"
|
||||
2199: "{COLOUR WINDOW_2}Amount:"
|
||||
2200: "Add"
|
||||
2201: "Vehicle reliability"
|
||||
2202: "Set all to zero (unlimited)"
|
||||
2203: "Set all to hundred (full renewal)"
|
||||
2204: "Set company ratings"
|
||||
2205: "-10% everywhere"
|
||||
2206: "+10% everywhere"
|
||||
2207: "Min everywhere"
|
||||
2208: "Max everywhere"
|
||||
2209: "All vehicles"
|
||||
2210: "Stopping at station"
|
||||
2211: "Transporting cargo"
|
||||
2212: "{MOVE_X 10}Carrying {STRINGID} {SPRITE}"
|
||||
2213: "»{MOVE_X 10}Carrying {STRINGID} {SPRITE}"
|
||||
2214: "No station selected"
|
||||
2215: "No cargo type selected"
|
||||
2216: "{SMALLFONT}{COLOUR BLACK}Open a station window to filter by station"
|
||||
2217: "{SMALLFONT}{COLOUR BLACK}Select a cargo type from the list of available cargo"
|
||||
|
|
|
@ -1244,8 +1244,8 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: No space for more vehicle orders!
|
||||
1215: Too many orders for this vehicle!
|
||||
1216: "- - Local - -"
|
||||
1217: "- - Express - -"
|
||||
1216: "Local"
|
||||
1217: "Express"
|
||||
1218: "{COLOUR BLACK}- - No route defined - -"
|
||||
1219: "{COLOUR BLACK}- - End of route list - -"
|
||||
1220: Stop at {STRINGID}
|
||||
|
@ -2152,7 +2152,7 @@ strings:
|
|||
2096: New objects installed successfully
|
||||
2097: At least one industry must be selected
|
||||
2098: At least one town building must be selected
|
||||
2099: An company headquarters building type must be selected
|
||||
2099: A company headquarters building type must be selected
|
||||
2100: Only one company headquarters building type must be selected
|
||||
2101: An interface type must be selected
|
||||
2102: At least one vehicle type must be selected
|
||||
|
|
|
@ -1243,8 +1243,8 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: ¡No hay sitio para más pedidos de vehículos!
|
||||
1215: ¡Hay demasiados pedidos para este vehículo!
|
||||
1216: "- - Local - -"
|
||||
1217: "- - Expreso - -"
|
||||
1216: "Local"
|
||||
1217: "Expreso"
|
||||
1218: "{COLOUR BLACK}- - Sin ruta definida - -"
|
||||
1219: "{COLOUR BLACK}- - Fin de la lista de rutas - -"
|
||||
1220: Parada en {STRINGID}
|
||||
|
|
|
@ -1243,8 +1243,8 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: Pas assez de place pour d'autres ordres !
|
||||
1215: Trop d'ordres pour ce véhicule !
|
||||
1216: "- - Local - -"
|
||||
1217: "- - Express - -"
|
||||
1216: "Local"
|
||||
1217: "Express"
|
||||
1218: "{COLOUR BLACK}- - Aucun itinéraire défini - -"
|
||||
1219: "{COLOUR BLACK}- - Fin de la liste des itinéraires - -"
|
||||
1220: Arrêt à {STRINGID}
|
||||
|
|
|
@ -1243,8 +1243,8 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: Non c'è spazio per altri ordini!
|
||||
1215: Troppi ordini per questo veicolo!
|
||||
1216: "- - Locale - -"
|
||||
1217: "- - Espresso - -"
|
||||
1216: "Locale"
|
||||
1217: "Espresso"
|
||||
1218: "{COLOUR BLACK}- - Percorso non definito - -"
|
||||
1219: "{COLOUR BLACK}- - Fine del percorso - -"
|
||||
1220: Ferma a {STRINGID}
|
||||
|
|
|
@ -1243,8 +1243,8 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: No space for more vehicle orders!
|
||||
1215: Too many orders for this vehicle!
|
||||
1216: "- - Local - -"
|
||||
1217: "- - Express - -"
|
||||
1216: "Local"
|
||||
1217: "Express"
|
||||
1218: "{COLOUR BLACK}- - No route defined - -"
|
||||
1219: "{COLOUR BLACK}- - End of route list - -"
|
||||
1220: Stop at {STRINGID}
|
||||
|
|
|
@ -1243,8 +1243,8 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: 차량 경로를 지정할 공간이 더 이상 없습니다!
|
||||
1215: 이 차량에 경로가 너무 많습니다!
|
||||
1216: "- - 완행 - -"
|
||||
1217: "- - 급행 - -"
|
||||
1216: "완행"
|
||||
1217: "급행"
|
||||
1218: "{COLOUR BLACK}- - 경로 없음 - -"
|
||||
1219: "{COLOUR BLACK}- - 경로 목록 끝 - -"
|
||||
1220: Stop at {STRINGID}
|
||||
|
@ -2193,3 +2193,71 @@ strings:
|
|||
2138: "{COLOUR WINDOW_2}타이틀 스크린 음악 재생"
|
||||
2139: "메뉴로 나가기"
|
||||
2140: "OpenLoco 종료"
|
||||
2141: "{COLOUR WINDOW_2}AI 회사 비활성화"
|
||||
2142: "{SMALLFONT}{COLOUR BLACK}이 설정을 켜면 AI 회사가 '생각'하는 것을 멈춰서 무력화시킵니다.{NEWLINE}새로운 게임에서는, 새로운 AI 회사가 나타나는 것을 막아주기도 합니다."
|
||||
2143: "{COLOUR WINDOW_2}자동 저장 개수:"
|
||||
2144: "{COLOUR WINDOW_2}자동 저장 주기:"
|
||||
2145: "안 함"
|
||||
2146: "매월마다"
|
||||
2147: "매 {INT32}개월마다"
|
||||
2148: "{COLOUR WINDOW_2}생성 알고리즘:"
|
||||
2149: 오리지널
|
||||
2150: 개선
|
||||
2151: "차량 수정"
|
||||
2152: "차량 복제"
|
||||
2153: "차량을 복제할 수 없습니다..."
|
||||
2154: "이 차량의 위치로 주 화면을 옮깁니다"
|
||||
2155: "주 화면이 이 차량을 따라가게 만듭니다"
|
||||
2156: "치트/디버그 툴바 메뉴 켜기"
|
||||
2157: "{SMALLFONT}{COLOUR BLACK}이 설정을 켜면 치트와 디버그 설정을 할 수 있는 추가 버튼이 게임 툴바에 추가됩니다."
|
||||
2158: "모래상자 모드 켜기"
|
||||
2159: "수동 운전 허용"
|
||||
2160: "일시 정지 상태에서 건설 허용"
|
||||
2161: "FPS 표시"
|
||||
2162: "{SMALLFONT}{COLOUR BLACK}1초에 그려지는 프레임의 수를 나타내는 수를 화면 상단에 표시합니다."
|
||||
2163: "프레임 제한 해제"
|
||||
2164: "{SMALLFONT}{COLOUR BLACK}40Hz 이상으로 화면을 표시합니다."
|
||||
2165: "하드웨어"
|
||||
2166: "지도 렌더링"
|
||||
2167: "칸 조사 창"
|
||||
2168: "{SMALLFONT}{COLOUR BLACK}지도 상에서 조사할 칸을 클릭해서 활성화합니다."
|
||||
2169: "지표면"
|
||||
2170: "선로"
|
||||
2171: "역"
|
||||
2172: "신호기"
|
||||
2173: "건물"
|
||||
2174: "나무"
|
||||
2175: "벽"
|
||||
2176: "도로"
|
||||
2177: "산업시설"
|
||||
2178: "{COLOUR WINDOW_2} {STRINGID} ({STRINGID})"
|
||||
2179: "{COLOUR WINDOW_2} {STRINGID} - {STRINGID} ({STRINGID})"
|
||||
2180: "X:"
|
||||
2181: "Y:"
|
||||
2182: "{COLOUR WINDOW_2}{INT16}"
|
||||
2183: "칸 요소 데이터"
|
||||
2184: "치트"
|
||||
2185: "재정 치트"
|
||||
2186: "회사 치트"
|
||||
2187: "차량 치트"
|
||||
2188: "도시 치트"
|
||||
2189: "대출 갚기"
|
||||
2190: "모두 갚기"
|
||||
2191: "{COLOUR BLACK}{CURRENCY32}"
|
||||
2192: "회사를 선택하세요"
|
||||
2193: "적용할 치트를 선택하세요"
|
||||
2194: "이 회사로 이동합니다"
|
||||
2195: "모든 회사 자산 획득"
|
||||
2196: "파산 켜기/끄기"
|
||||
2197: "감옥에 가두기"
|
||||
2198: "돈 추가"
|
||||
2199: "{COLOUR WINDOW_2}금액:"
|
||||
2200: "추가"
|
||||
2201: "차량 신뢰도"
|
||||
2202: "모두 0으로 (무제한)"
|
||||
2203: "모두 100으로 (새 것으로)"
|
||||
2204: "회사 평판 설정"
|
||||
2205: "모든 곳을 -10%"
|
||||
2206: "모든 곳을 +10%"
|
||||
2207: "모든 곳을 최소로"
|
||||
2208: "모든 곳을 최대로"
|
||||
|
|
|
@ -1243,8 +1243,8 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: No space for more vehicle orders!
|
||||
1215: Too many orders for this vehicle!
|
||||
1216: "- - Lokaal - -"
|
||||
1217: "- - Express - -"
|
||||
1216: "Lokaal"
|
||||
1217: "Express"
|
||||
1218: "{COLOUR BLACK}- - Geen route gedefinieerd - -"
|
||||
1219: "{COLOUR BLACK}- - Einde van routelijst - -"
|
||||
1220: Stop op {STRINGID}
|
||||
|
|
|
@ -1236,8 +1236,8 @@ strings:
|
|||
1213: '{COLOUR WINDOW_2}{STRINGID})'
|
||||
1214: Brak miejsca na więcej poleceń pojazdu!
|
||||
1215: Zbyt wiele poleceń dla tego pojazdu!
|
||||
1216: '- - Normalny - -'
|
||||
1217: '- - Pospieszny - -'
|
||||
1216: 'Normalny'
|
||||
1217: 'Pospieszny'
|
||||
1218: '{COLOUR BLACK}- - Brak poleceń - -'
|
||||
1219: '{COLOUR BLACK}- - Koniec poleceń - -'
|
||||
1220: Zatrzymaj przy {STRINGID}
|
||||
|
|
|
@ -6,7 +6,7 @@ header:
|
|||
strings:
|
||||
0: ""
|
||||
1: "{POP16}"
|
||||
2: Nova Empresa{POP16}
|
||||
2: Nova Companhia{POP16}
|
||||
3: Sem Nome{POP16}
|
||||
4: Trem {INT16}
|
||||
5: Ônibus {INT16}
|
||||
|
@ -60,8 +60,8 @@ strings:
|
|||
50: Out
|
||||
51: Nov
|
||||
52: Dez
|
||||
53: Impossível acessar o arquivo de gráficos
|
||||
|
||||
53: Impossível acessar o arquivo de gráficos
|
||||
54: Arquivo de dados ausente ou inacessível
|
||||
55: Impossível alocar memória o suficiente
|
||||
56: "{COLOUR BLACK}❌"
|
||||
|
@ -142,7 +142,7 @@ strings:
|
|||
128: "{SMALLFONT}{COLOUR BLACK}Inverter Sentido"
|
||||
129: "{SMALLFONT}{COLOUR BLACK}(Clique na paisagem para começar construção)"
|
||||
130: "{SMALLFONT}{COLOUR BLACK}Construir esta seção de pista"
|
||||
131: "{SMALLFONT}{COLOUR BLACK}Construir esta seção de pista"
|
||||
131: "{SMALLFONT}{COLOUR BLACK}Remover seção de pista anterior"
|
||||
132: "{SMALLFONT}{COLOUR BLACK}Descida íngreme"
|
||||
133: "{SMALLFONT}{COLOUR BLACK}Descida"
|
||||
134: "{SMALLFONT}{COLOUR BLACK}Plano"
|
||||
|
@ -156,7 +156,7 @@ strings:
|
|||
142: Impossível remover {POP16}{POP16}{POP16}{STRINGID}...
|
||||
143: Impossível construir {POP16}{POP16}{POP16}{STRINGID}...
|
||||
144: Aumente ou rebaixe o terreno primeiro
|
||||
145: Subterrâneo/Visão interna
|
||||
145: Visão Subterrânea
|
||||
146: Esconder vias e estradas em primeiro plano
|
||||
147: Esta estação só pode ser contruída no final da estrada
|
||||
148: Tipo errado de estação para {STRINGID}
|
||||
|
@ -184,9 +184,9 @@ strings:
|
|||
170: Combinação de vias não é possível
|
||||
171: Muitos objetos em jogo
|
||||
172: Rotacionar no sentido horário
|
||||
173: rotacionar no sentido anti-horário
|
||||
174: "Locomotion: Iniciando pela primeira vez..."
|
||||
175: "Locomotion: Verificando arquivos de objeto..."
|
||||
173: Rotacionar no sentido anti-horário
|
||||
174: "OpenLoco: Iniciando pela primeira vez..."
|
||||
175: "OpenLoco: Verificando arquivos de objeto..."
|
||||
176: Carregar Jogo
|
||||
177: Sair do Jogo
|
||||
178: Sair do Jogo
|
||||
|
@ -302,26 +302,26 @@ strings:
|
|||
285: "{STRINGID} Oeste"
|
||||
286: "{STRINGID} Central"
|
||||
287: "{STRINGID} Transfer"
|
||||
288: "{STRINGID} Halt"
|
||||
289: "{STRINGID} Valley"
|
||||
290: "{STRINGID} Heights"
|
||||
291: "{STRINGID} Woods"
|
||||
292: "{STRINGID} Lakeside"
|
||||
288: "Parada {STRINGID}"
|
||||
289: "Vale {STRINGID}"
|
||||
290: "Morros de {STRINGID}"
|
||||
291: "Bosque {STRINGID}"
|
||||
292: "Lago de {STRINGID}"
|
||||
293: "{STRINGID} Exchange"
|
||||
294: "{STRINGID} Airport"
|
||||
295: "{STRINGID} Oilfield"
|
||||
296: "{STRINGID} Mines"
|
||||
297: "{STRINGID} Docks"
|
||||
298: "{STRINGID} Annexe"
|
||||
299: "{STRINGID} Sidings"
|
||||
300: "{STRINGID} Branch"
|
||||
301: Upper {STRINGID}
|
||||
302: Lower {STRINGID}
|
||||
303: "{STRINGID} Heliport"
|
||||
304: "{STRINGID} Forest"
|
||||
305: "{STRINGID} Junction"
|
||||
306: "{STRINGID} Cross"
|
||||
307: "{STRINGID} Views"
|
||||
294: "Aeroporto de {STRINGID}"
|
||||
295: "Petroleira de {STRINGID}"
|
||||
296: "Minas de {STRINGID}"
|
||||
297: "Docas de {STRINGID}"
|
||||
298: "Anexo de {STRINGID}"
|
||||
299: "Desvio de {STRINGID}"
|
||||
300: "Filial de {STRINGID}"
|
||||
301: Alto {STRINGID}
|
||||
302: Baixo {STRINGID}
|
||||
303: "Heliporto de {STRINGID}"
|
||||
304: "Floresta {STRINGID}"
|
||||
305: "Junção {STRINGID}"
|
||||
306: "Cruzamento {STRINGID}"
|
||||
307: "Vista {STRINGID}"
|
||||
308: "{STRINGID} 1"
|
||||
309: "{STRINGID} 2"
|
||||
310: "{STRINGID} 3"
|
||||
|
@ -381,14 +381,14 @@ strings:
|
|||
364: Salvar Paisagem
|
||||
365: Salvar Jogo
|
||||
366: Salvar Cenário
|
||||
367: Locomotion Jogo Salvo
|
||||
368: Locomotion Arquivo de Cenário
|
||||
369: Locomotion Arquivo de Paisagem
|
||||
367: Arquivo de Jogo Salvo do OpenLoco
|
||||
368: Arquivo de Cenário do OpenLoco
|
||||
369: Arquivo de Paisagem do OpenLoco
|
||||
370: Falha ao salvar jogo!
|
||||
371: Falha ao carregar jogo salvo...{NEWLINE}Arquivo contém dados inválidos!
|
||||
372: "Esconder cenário e construções em primeiro plano "
|
||||
373: Somente possível construir isto na água!
|
||||
374: Somente possível construir isto se próximo a indústria sobre a água!
|
||||
374: Somente possível construir isto próximo a indústria sobre a água!
|
||||
375: Nome {STRINGID}
|
||||
376: "Escreva o novo nome para este {STRINGID}:"
|
||||
377: Impossível renomear este veículo...
|
||||
|
@ -417,7 +417,7 @@ strings:
|
|||
400: "{STRINGID} está patinando no lugar num declive"
|
||||
401: "{STRINGID} agora aceita {STRINGID}"
|
||||
402: "{STRINGID} não aceita mais {STRINGID}"
|
||||
403: Nova Empresa de Transportes!{NEWLINE}{STRINGID} comça construção perto de {STRINGID}!
|
||||
403: Nova Companhia de Transportes!{NEWLINE}{STRINGID} começa construção perto de {STRINGID}!
|
||||
404: "{STRINGID} não consegue pousar em {STRINGID} devido a inadequação do aeroporto"
|
||||
405: Cidadãos Celebram!{NEWLINE}Primeiro {STRINGID} chega em {STRINGID}!
|
||||
406: Cidadãos Celebram!{NEWLINE}Primeira entrega de {STRINGID} chega em {STRINGID}!
|
||||
|
@ -425,8 +425,8 @@ strings:
|
|||
408: Novo Veículo Inventado -{NEWLINE}“{STRINGID}”!
|
||||
409: "{STRINGID} é promovido de “{STRINGID}” para “{STRINGID}”!"
|
||||
410: Nova {STRINGID} em construção perto de {STRINGID}!
|
||||
411: Parabéns!{NEWLINE}Você completou seu desafio com êxito!
|
||||
412: Fracasso!{NEWLINE}Você falhou seu desafio!
|
||||
411: Parabéns!{NEWLINE}Você completou o desafio com êxito!
|
||||
412: Fracasso!{NEWLINE}Você falhou o desafio!
|
||||
413: Vencido!{NEWLINE}{STRINGID} completou o desafio!
|
||||
414: Alerta de Falência!{NEWLINE}{STRINGID} será fechado em 6 meses a menos que sua performance melhore!
|
||||
415: Alerta Final!{NEWLINE}{STRINGID} será fechado em 3 meses a menos que sua performance melhore!
|
||||
|
@ -444,7 +444,7 @@ strings:
|
|||
424: "{MOVE_X 10}{STRING}"
|
||||
425: »{MOVE_X 10}{STRING}
|
||||
|
||||
426: Marcas de altura nas psitas e estradas
|
||||
426: Marcas de altura nas pistas e estradas
|
||||
427: Marcas de altura no terreno
|
||||
428: Setas direcionais de mão única
|
||||
429: Nomes de municípios exibidos
|
||||
|
@ -491,7 +491,7 @@ strings:
|
|||
470: Taxiando em {STRINGID}
|
||||
471: Decolando de {STRINGID}
|
||||
472: Indo para {STRINGID}
|
||||
473: No position{POP16}{POP16}
|
||||
473: Sem posição{POP16}{POP16}
|
||||
474: Viajando{POP16}{POP16}
|
||||
475: "{STRINGID} - {STRINGID}"
|
||||
476: "{POP16}{POP16}{STRINGID}"
|
||||
|
@ -603,7 +603,7 @@ strings:
|
|||
582: "{CURRENCY32}"
|
||||
583: Impossível construir isto aqui...
|
||||
584: "{DATE MY}"
|
||||
585: Chris Sawyer's Locomotion
|
||||
585: OpenLoco
|
||||
586: Por favor insira seu CD de Locomotion na seguinte unidade de disco:-
|
||||
587: "{COLOUR WINDOW_2}Despesa/Renda"
|
||||
588: Renda de Trens
|
||||
|
@ -651,7 +651,7 @@ strings:
|
|||
630: "{COLOUR WINDOW_2}Dinheiro: {MOVE_X 81}{COLOUR RED}Falido!"
|
||||
631: "{COLOUR WINDOW_2}Dinheiro: {MOVE_X 81}{COLOUR BLACK}{CURRENCY48}"
|
||||
632: "{COLOUR WINDOW_2}Dinheiro: {MOVE_X 81}{COLOUR RED}{CURRENCY48}"
|
||||
633: "{COLOUR WINDOW_2}Valor da Empresa: {COLOUR BLACK}{CURRENCY48}"
|
||||
633: "{COLOUR WINDOW_2}Valor da Companhia: {COLOUR BLACK}{CURRENCY48}"
|
||||
634: "{COLOUR WINDOW_2}Lucro de Veículos: {COLOUR BLACK}{CURRENCY48} por mês"
|
||||
|
||||
635: Janeiro
|
||||
|
@ -718,7 +718,7 @@ strings:
|
|||
695: "{NEWLINE}Desafio fracassado!"
|
||||
696: "{NEWLINE}Desafio completo!"
|
||||
697: "{SMALLFONT}{COLOUR BLACK}Índice de Performance: {INT16_1DP}% “{STRINGID}”"
|
||||
698: "{SMALLFONT}{COLOUR BLACK}Valor da Empresa: {CURRENCY48}{NEWLINE}Lucro de veículos: {CURRENCY48}/mês"
|
||||
698: "{SMALLFONT}{COLOUR BLACK}Valor da Companhia: {CURRENCY48}{NEWLINE}Lucro de veículos: {CURRENCY48}/mês"
|
||||
699: "{NEWLINE}Progresso do desafio: {INT16}%{STRINGID}"
|
||||
700: "{NEWLINE}Tempo restante: {COLOUR BLACK}{INT16} anos {INT16} meses"
|
||||
701: Customizar Teclas...
|
||||
|
@ -753,8 +753,8 @@ strings:
|
|||
730: Mostrar Lista de Municípios
|
||||
731: Mostrar Lista de Indústrias
|
||||
732: Mostrar Mapa
|
||||
733: Mostrar Lista de Empresas
|
||||
734: Mostrar Informações da Empresa
|
||||
733: Mostrar Lista de Companhias
|
||||
734: Mostrar Informações da Companhia
|
||||
735: Mostrar Finanças
|
||||
736: Mostrar Lista de Anúncios
|
||||
737: Captura de Tela
|
||||
|
@ -840,7 +840,7 @@ strings:
|
|||
815: K
|
||||
816: L
|
||||
817: M
|
||||
818: "N"
|
||||
818: N
|
||||
819: O
|
||||
820: P
|
||||
821: Q
|
||||
|
@ -851,7 +851,7 @@ strings:
|
|||
826: V
|
||||
827: W
|
||||
828: X
|
||||
829: "Y"
|
||||
829: Y
|
||||
830: Z
|
||||
831: ???
|
||||
832: ???
|
||||
|
@ -1032,7 +1032,7 @@ strings:
|
|||
1006: Mapa - Veículos
|
||||
1007: Mapa - Indústrias
|
||||
1008: Mapa - Rotas de Transporte
|
||||
1009: Mapa - Empresas
|
||||
1009: Mapa - Companhias
|
||||
1010: Forçar Software Buffer Mixing
|
||||
1011: "{SMALLFONT}{COLOUR BLACK}Selecione esta opção para melhorar a performance se o jogo levemente pausa quando sons começam ou escuta-se interferência"
|
||||
1012: Cenário instalado com sucesso
|
||||
|
@ -1067,15 +1067,15 @@ strings:
|
|||
1040: "{COLOUR WINDOW_2}Everlasting High-Rise"
|
||||
1041: "{COLOUR BLACK}Allister Brimble"
|
||||
1042: "{COLOUR WINDOW_2}Solace"
|
||||
1043: "{COLOUR BLACK}Scott Joplin (Executado por Peter James Adcock)"
|
||||
1043: "{COLOUR BLACK}Scott Joplin (Performado por Peter James Adcock)"
|
||||
1044: "{COLOUR WINDOW_2}Chrysanthemum"
|
||||
1045: "{COLOUR BLACK}Scott Joplin (Executado por Peter James Adcock)"
|
||||
1045: "{COLOUR BLACK}Scott Joplin (Performado por Peter James Adcock)"
|
||||
1046: "{COLOUR WINDOW_2}Eugenia"
|
||||
1047: "{COLOUR BLACK}Scott Joplin (Executado por Peter James Adcock)"
|
||||
1047: "{COLOUR BLACK}Scott Joplin (Performado por Peter James Adcock)"
|
||||
1048: "{COLOUR WINDOW_2}The Ragtime Dance"
|
||||
1049: "{COLOUR BLACK}Scott Joplin (Executado por Peter James Adcock)"
|
||||
1049: "{COLOUR BLACK}Scott Joplin (Performado por Peter James Adcock)"
|
||||
1050: "{COLOUR WINDOW_2}Easy Winners"
|
||||
1051: "{COLOUR BLACK}Scott Joplin (Executado por Peter James Adcock)"
|
||||
1051: "{COLOUR BLACK}Scott Joplin (Performado por Peter James Adcock)"
|
||||
1052: "{COLOUR WINDOW_2}Setting Off"
|
||||
1053: "{COLOUR BLACK}Allister Brimble (Trombeta tocada por Brian Moore, Clarinete tocado por John Glanfield)"
|
||||
1054: "{COLOUR WINDOW_2}A Traveller's Seranade"
|
||||
|
@ -1114,7 +1114,7 @@ strings:
|
|||
1086: Por Favor, Aguarde...
|
||||
1087: Inicializando...
|
||||
1088: Carregando...
|
||||
1089: "instalando novos dados: "
|
||||
1089: "Instalando novos dados: "
|
||||
|
||||
1090: Branco
|
||||
1091: Translúcido
|
||||
|
@ -1139,12 +1139,12 @@ strings:
|
|||
1109: "{COLOUR BLACK}Caminhões:"
|
||||
1110: "{COLOUR BLACK}Aeronaves:"
|
||||
1111: "{COLOUR BLACK}Navios:"
|
||||
1112: "{SMALLFONT}{COLOUR BLACK}Sede e detalhes da empresa"
|
||||
1113: "{SMALLFONT}{COLOUR BLACK}Proprietário e status da empresa"
|
||||
1114: "{SMALLFONT}{COLOUR BLACK}Finaças da empresa"
|
||||
1112: "{SMALLFONT}{COLOUR BLACK}Sede e detalhes da companhia"
|
||||
1113: "{SMALLFONT}{COLOUR BLACK}Proprietário e status da companhia"
|
||||
1114: "{SMALLFONT}{COLOUR BLACK}Finaças da companhia"
|
||||
1115: "{SMALLFONT}{COLOUR BLACK}Carga entregue"
|
||||
1116: "{SMALLFONT}{COLOUR BLACK}Esquema de cores da empresa"
|
||||
1117: "{SMALLFONT}{COLOUR BLACK}Desafio da empresa para este jogo"
|
||||
1116: "{SMALLFONT}{COLOUR BLACK}Esquema de cores da companhia"
|
||||
1117: "{SMALLFONT}{COLOUR BLACK}Desafio da companhia para este jogo"
|
||||
1118: "{COLOUR WINDOW_2}Esquema de cores especiais usados para:"
|
||||
1119: "{SMALLFONT}{COLOUR BLACK}Selecione a cor principal"
|
||||
1120: "{SMALLFONT}{COLOUR BLACK}Selecione a cor secundária (utilizada apenas em alguns tipos de veículos)"
|
||||
|
@ -1209,7 +1209,7 @@ strings:
|
|||
1179: "{SMALLFONT}{COLOUR BLACK}Mostrar tipos de veículo no mapa"
|
||||
1180: "{SMALLFONT}{COLOUR BLACK}Mostrar tipos de indústria no mapa"
|
||||
1181: "{SMALLFONT}{COLOUR BLACK}Mostrar rotas de veículos no mapa"
|
||||
1182: "{SMALLFONT}{COLOUR BLACK}Mostrar propriedade da empresa no mapa"
|
||||
1182: "{SMALLFONT}{COLOUR BLACK}Mostrar propriedade da companhia no mapa"
|
||||
1183: Estação muito espalhada!
|
||||
1184: Impossível acrescentar {POP16}{POP16}{POP16}{POP16}{POP16}{STRINGID} em {STRINGID}...
|
||||
1185: Impossível construir {POP16}{POP16}{POP16}{POP16}{POP16}{STRINGID}...
|
||||
|
@ -1243,28 +1243,28 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: Sem espaço para mais ordens do veículo!
|
||||
1215: Muitas ordens para esse veículo!
|
||||
1216: "- - Local - -"
|
||||
1217: "- - Expresso - -"
|
||||
1216: "Local"
|
||||
1217: "Expresso"
|
||||
1218: "{COLOUR BLACK}- - Sem rota definida - -"
|
||||
1219: "{COLOUR BLACK}- - Fim da lista de rotas - -"
|
||||
1220: Parar em {STRINGID}
|
||||
1221: Passar por {STRINGID}
|
||||
1222: Passar por waypoint
|
||||
1222: Passar por marcação
|
||||
1223: Descarregar quaisquer {STRINGID} {SPRITE}
|
||||
1224: Esperar por carga total de {STRINGID} {SPRITE}
|
||||
1225: Descarregar quaisquer {STRINGID} {SPRITE}
|
||||
1226: Esperar por carga total de {STRINGID} {SPRITE}
|
||||
1227: "{COLOUR WINDOW_2}▶"
|
||||
1228: Impossível inserir ordem...
|
||||
1229: "{COLOUR WINDOW_3}Clique para inserir nova ordem para{NEWLINE}passar pelo waypoint nesta posição"
|
||||
1229: "{COLOUR WINDOW_3}Clique para inserir nova ordem para{NEWLINE}passar pelo marcação nesta posição"
|
||||
1230: "{COLOUR WINDOW_3}Clique para inserir nova ordem para{NEWLINE}parar em {STRINGID}"
|
||||
1231: "{COLOUR WINDOW_3}Clique novamente para mudar a última{NEWLINE}ordem para passar por {STRINGID}"
|
||||
1232: "{SMALLFONT}{COLOUR BLACK}Inserir uma ordem para esperar por carga total"
|
||||
1233: "{SMALLFONT}{COLOUR BLACK}Inserir uma ordem para forçar descarregamento de carga, mesmo que não aceito pela estação"
|
||||
1234: "{SMALLFONT}{COLOUR BLACK}Pular para próxima ordem na lista"
|
||||
1235: "{SMALLFONT}{COLOUR BLACK}Deletar a última ordem ou a selecionada"
|
||||
1236: "{COLOUR BLACK}Clique numa estação ou waypoint para determinar a rota"
|
||||
1237: "{SMALLFONT}{COLOUR BLACK}Clique na ordem para selecioná-la, clique duas vezes para centralizar visão na estação/waypoint"
|
||||
1236: "{COLOUR BLACK}Clique numa estação ou marcação para determinar a rota"
|
||||
1237: "{SMALLFONT}{COLOUR BLACK}Clique na ordem para selecioná-la, clique duas vezes para centralizar visão na estação/marcação"
|
||||
1238: "{SMALLFONT}{COLOUR BLACK}Clique na ordem para copiá-la para o veículo selecionado"
|
||||
1239: "{STRINGID} {STRINGID}{NEWLINE}{COLOUR WINDOW_3}{STRINGID}"
|
||||
1240: Construir Trens
|
||||
|
@ -1336,7 +1336,7 @@ strings:
|
|||
1306: "{COLOUR BLACK}Tipo"
|
||||
1307: "{COLOUR BLACK}Tipo ▼"
|
||||
1308: Nome do Município
|
||||
1309: "Escreva o novo nome para {STRINGID}:"
|
||||
1309: "Escreva um novo nome para {STRINGID}:"
|
||||
1310: "{COLOUR BLACK}{STRINGID} população {INT32}"
|
||||
1311: Impossível renomear município...
|
||||
1312: Nova Estação
|
||||
|
@ -1368,19 +1368,19 @@ strings:
|
|||
1338: Escarlate
|
||||
1339: "{STRINGID}"
|
||||
1340: "{POP16}{STRINGID}"
|
||||
1341: "{STRINGID} Transportes"
|
||||
1341: "{STRINGID} Transportadora"
|
||||
1342: "{STRINGID} Express"
|
||||
1343: "{STRINGID} Lines"
|
||||
1344: "{STRINGID} Tracks"
|
||||
1345: "{STRINGID} Coaches"
|
||||
1343: "{STRINGID} Rodovias"
|
||||
1344: "{STRINGID} Linhas Férreas"
|
||||
1345: "{STRINGID} Ônibus"
|
||||
1346: "{STRINGID} Air"
|
||||
1347: "{STRINGID} Rail"
|
||||
1347: "{STRINGID} Ferroviária"
|
||||
1348: "{STRINGID} Carts"
|
||||
1349: "{STRINGID} Trains"
|
||||
1349: "{STRINGID} Trens"
|
||||
1350: "{STRINGID} Haulage"
|
||||
1351: "{STRINGID} Shipping"
|
||||
1352: "{STRINGID} Freight"
|
||||
1353: "{STRINGID} Trucks"
|
||||
1351: "{STRINGID} Frota"
|
||||
1352: "{STRINGID} Frete"
|
||||
1353: "{STRINGID} Caminhões"
|
||||
1354: "{COLOUR WINDOW_2}Sede"
|
||||
1355: "{COLOUR WINDOW_2}Proprietário"
|
||||
1356: "{COLOUR BLACK}@ {INT16}% juros por ano"
|
||||
|
@ -1415,7 +1415,7 @@ strings:
|
|||
1385: "{COLOUR BLACK}Nenhuma indústria disponível"
|
||||
1386: "{SMALLFONT}{COLOUR BLACK}Município"
|
||||
1387: "{SMALLFONT}{COLOUR BLACK}Gráfico populacional"
|
||||
1388: "{SMALLFONT}{COLOUR BLACK}Classificação de cada empresa no município"
|
||||
1388: "{SMALLFONT}{COLOUR BLACK}Classificação de cada companhia no município"
|
||||
1389: "{SMALLFONT}{COLOUR BLACK}Indústria"
|
||||
1390: "{SMALLFONT}{COLOUR BLACK}Gráfico de produção"
|
||||
1391: "{COLOUR BLACK}{SMALLFONT}({STRINGID})"
|
||||
|
@ -1453,9 +1453,9 @@ strings:
|
|||
1423: "{COLOUR BLACK}{INT16}%"
|
||||
1424: "{COLOUR BLACK}{TINYFONT}{DATE DMY}"
|
||||
1425: Mensagens
|
||||
1426: "{SMALLFONT}{COLOUR BLACK}Exibir mensangens recentes"
|
||||
1427: "{SMALLFONT}{COLOUR BLACK}Opções de mensangens"
|
||||
1428: Opções de mensangens
|
||||
1426: "{SMALLFONT}{COLOUR BLACK}Exibir mensagens recentes"
|
||||
1427: "{SMALLFONT}{COLOUR BLACK}Opções de mensagens"
|
||||
1428: Opções de mensagens
|
||||
1429: "{TINYFONT}{DATE DMY}"
|
||||
1430: " + "
|
||||
1431: " em espera"
|
||||
|
@ -1478,27 +1478,27 @@ strings:
|
|||
1448: "{SMALLFONT}{COLOUR BLACK}Carga em espera e aceita"
|
||||
1449: "{SMALLFONT}{COLOUR BLACK}Classificações de carga"
|
||||
1450: Demolição não permitida
|
||||
1451: Outra empresa está prestes a construir aqui
|
||||
1451: Outra companhia está prestes a construir aqui
|
||||
1452: Muito longo!
|
||||
1453: "{SMALLFONT}{COLOUR BLACK}Construir ou mover sede"
|
||||
1454: "{SMALLFONT}{COLOUR BLACK}Mudar nome do proprietário"
|
||||
1455: "{COLOUR BLACK}(ainda não construído)"
|
||||
1456: Nomear Empresa
|
||||
1457: "Escreva o novo nome da empresa:"
|
||||
1458: Impossível renomear empresa...
|
||||
1456: Nomear Companhia
|
||||
1457: "Escreva o novo nome da companhia:"
|
||||
1458: Impossível renomear companhia...
|
||||
1459: Nomear Proprietário
|
||||
1460: "Escreva o novo nome do proprietário:"
|
||||
1461: Impossível renomear proprietário...
|
||||
1462: Sede
|
||||
1463: "{STRINGID} Sede"
|
||||
1464: "{STRINGID} autoridade local não permite a remoção de estradas atualmente em uso"
|
||||
1465: "{SMALLFONT}{COLOUR BLACK}Selecionar empresa"
|
||||
1465: "{SMALLFONT}{COLOUR BLACK}Selecionar companhia"
|
||||
1466: Jogo de Dois Jogadores
|
||||
1467: Jogo de Dois Jogadores - Opções
|
||||
1468: "{COLOUR BLACK}para criar um jogo de dois jogadores, um computador deve ser definido como host e o outro deve conectar-se a ele.{NEWLINE}{NEWLINE}Por favor selecione:"
|
||||
1469: ""
|
||||
1470: "{COLOUR BLACK}Definindo este computador como host..."
|
||||
1471: "{COLOUR BLACK}Erro: Falha ao definr este computador como host"
|
||||
1471: "{COLOUR BLACK}Erro: Falha ao definir este computador como host"
|
||||
1472: "{COLOUR BLACK}Configuração do host completa - Esperando pela conexão do outro computador..."
|
||||
1473: ""
|
||||
1474: ""
|
||||
|
@ -1506,9 +1506,9 @@ strings:
|
|||
1476: "{COLOUR BLACK}Tentando se conectar..."
|
||||
1477: "{COLOUR BLACK}Erro: Falha ao localizar ou se conectar com host"
|
||||
1478: "{COLOUR BLACK}Successo!{NEWLINE}Você está conectado a: {STRINGID}"
|
||||
1479: "{COLOUR BLACK}Atualmente conectado a: {STRINGID}{NEWLINE}{NEWLINE}Clique abaixo para desconectar e retornas ao modo de um jogador"
|
||||
1479: "{COLOUR BLACK}Atualmente conectado a: {STRINGID}{NEWLINE}{NEWLINE}Clique abaixo para desconectar e retornar ao modo de um jogador"
|
||||
1480: "{COLOUR WINDOW_2}Configurar este computador como host"
|
||||
1481: "{COLOUR WINDOW_2}Conectar ao hostar"
|
||||
1481: "{COLOUR WINDOW_2}Conectar-se ao host"
|
||||
1482: "{COLOUR WINDOW_2}Desconectar"
|
||||
1483: Digite o Endereço do Host
|
||||
1484: "Digite o endereço de IP ou nome do computador do host a se conectar, ou deixe em branco para procurar na rede local:"
|
||||
|
@ -1539,10 +1539,10 @@ strings:
|
|||
1505: "{COLOUR WINDOW_2}Moeda preferida:"
|
||||
1506: ""
|
||||
|
||||
1507: "Notícias principais da minha empresa:"
|
||||
1508: "Notícias principais de outras empresas:"
|
||||
1509: "Notícias secundárias da minha empresa:"
|
||||
1510: "Notícias secundárias de outras empresas:"
|
||||
1507: "Notícias principais da minha companhia:"
|
||||
1508: "Notícias principais de outras companhias:"
|
||||
1509: "Notícias secundárias da minha companhia:"
|
||||
1510: "Notícias secundárias de outras companhias:"
|
||||
1511: "Notícias gerais:"
|
||||
1512: "Conselho:"
|
||||
1513: Desligado
|
||||
|
@ -1560,7 +1560,7 @@ strings:
|
|||
1525: Porto
|
||||
1526: Tipo de ordem inválido para aeronaves
|
||||
1527: Tipo de ordem inválido para navios
|
||||
1528: Estação pertence a outra empresa
|
||||
1528: Estação pertence a outra companhia
|
||||
1529: Sem água!
|
||||
1530: Hidrovia atualmente em uso por navios
|
||||
1531: Atualmente em uso por pelo menos um veículos
|
||||
|
@ -1572,7 +1572,7 @@ strings:
|
|||
1536: "{SMALLFONT}{COLOUR BLACK}Parar música"
|
||||
1537: "{SMALLFONT}{COLOUR BLACK}Tocar música"
|
||||
1538: "{SMALLFONT}{COLOUR BLACK}Tocar pŕoxima música"
|
||||
1539: Tocar música somente da era atual
|
||||
1539: Tocar somente músicas da era atual
|
||||
1540: Tocar todas as músicas
|
||||
1541: Tocar seleção customizada de músicas
|
||||
1542: Editar seleção...
|
||||
|
@ -1584,8 +1584,8 @@ strings:
|
|||
1548: "{SMALLFONT}{COLOUR BLACK}Definir volume da música"
|
||||
1549: Opções de Música
|
||||
1550: Selecionar aparência do proprietário de {STRINGID}
|
||||
1551: "{SMALLFONT}{COLOUR BLACK}Clique para selecionar a aparência do proprietário/empresa"
|
||||
1552: Já selecionado por outra empresa
|
||||
1551: "{SMALLFONT}{COLOUR BLACK}Clique para selecionar a aparência do proprietário/companhia"
|
||||
1552: Já selecionado por outra companhia
|
||||
1553: Impossível selecionar aparência...
|
||||
1554: aeroportos
|
||||
1555: portos
|
||||
|
@ -1606,9 +1606,9 @@ strings:
|
|||
1570: Principiante
|
||||
1571: Fácil
|
||||
1572: Médio
|
||||
1573: Díficil
|
||||
1573: Desafiador
|
||||
1574: Especialista
|
||||
1575: Seleção de Região/Objetos
|
||||
1575: Seleção de Região / Objetos
|
||||
1576: Editor de Paisagens
|
||||
1577: Opções de Cenário
|
||||
1578: Salvar Cenário
|
||||
|
@ -1701,7 +1701,7 @@ strings:
|
|||
1663: "{POP16}{POP16}{INT16}%"
|
||||
1664: "{COLOUR WINDOW_2}N° de municípios:"
|
||||
1665: "{INT16}"
|
||||
1666: "{COLOUR WINDOW_2}Tamanho máximo de um município:"
|
||||
1666: "{COLOUR WINDOW_2}Tamanho máximo de município:"
|
||||
1667: Baixo
|
||||
1668: Médio
|
||||
1669: Alto
|
||||
|
@ -1711,12 +1711,12 @@ strings:
|
|||
1673: Impossível salvar o cenário ainda...
|
||||
1674: "{SMALLFONT}{COLOUR BLACK}Opções do cenário"
|
||||
1675: "{SMALLFONT}{COLOUR BLACK}Desafio do cenário"
|
||||
1676: "{SMALLFONT}{COLOUR BLACK}Opções da empresa"
|
||||
1677: "{SMALLFONT}{COLOUR BLACK}Opções finaceiras"
|
||||
1676: "{SMALLFONT}{COLOUR BLACK}Opções da companhia"
|
||||
1677: "{SMALLFONT}{COLOUR BLACK}Opções financeiras"
|
||||
1678: Opções do cenário
|
||||
1679: Desafio do cenário
|
||||
1680: Opções da empresa
|
||||
1681: Opções finaceiras
|
||||
1680: Opções da companhia
|
||||
1681: Opções financeiras
|
||||
1682: "{COLOUR WINDOW_2}Número máximo de competidores:"
|
||||
1683: "{INT16}"
|
||||
1684: "{COLOUR WINDOW_2}Atraso antes da criação dos competidores:"
|
||||
|
@ -1752,7 +1752,7 @@ strings:
|
|||
1714: Por favor espere - O outro jogador está salvando a partida...
|
||||
1715: Por favor espere - O outro jogador está carregando a partida...
|
||||
1716: Enviar Mensagem
|
||||
1717: Envira Mensagem
|
||||
1717: Enviar Mensagem
|
||||
1718: "Escreva a mensagem a ser enviada para {STRINGID}:"
|
||||
1719: ""
|
||||
1720: "{COLOUR WINDOW_2}Tempo de conexão esgotado:"
|
||||
|
@ -1762,7 +1762,7 @@ strings:
|
|||
1724: "{COLOUR WINDOW_2} Obsoleto desde: {COLOUR BLACK}{UINT16 RAW}"
|
||||
1725: "{COLOUR WINDOW_2} Potência: {COLOUR BLACK}{POWER}"
|
||||
1726: "{COLOUR WINDOW_2} Peso: {COLOUR BLACK}{INT16}t"
|
||||
1727: "{COLOUR WINDOW_2} Vel. máxima: {COLOUR BLACK}{VELOCITY}"
|
||||
1727: "{COLOUR WINDOW_2} Vel. Máxima: {COLOUR BLACK}{VELOCITY}"
|
||||
1728: "{COLOUR WINDOW_2} Capacidade: {COLOUR BLACK}{STRINGID}"
|
||||
1729: Estabeleçendo conexão...
|
||||
1730: Em todo lugar
|
||||
|
@ -1778,30 +1778,30 @@ strings:
|
|||
1740: "{SMALLFONT}{COLOUR BLACK}Editor de Cenário"
|
||||
1741: "{STRINGID} Transporte"
|
||||
1742: Mapa
|
||||
1743: "{NEWLINE 1 2}{SPRITE}{NEWLINE 33 8}Lista de Empresas"
|
||||
1743: "{NEWLINE 1 2}{SPRITE}{NEWLINE 33 8}Lista de Companhias"
|
||||
1744: "{NEWLINE 0 8}{STRINGID}{NEWLINE 25 2}{SPRITE}{NEWLINE 51 2}{STRINGID}{NEWLINE 51 12}{COLOUR WINDOW_1}{INT16_1DP}% “{STRINGID}”"
|
||||
1745: Empresas
|
||||
1746: Índices de Performance das Empresas
|
||||
1745: Companhias
|
||||
1746: Índices de Performance das Companhias
|
||||
1747: Unidades de Carga Entregues por Mês
|
||||
1748: Valores da Empresas
|
||||
1748: Valores da Companhias
|
||||
1749: Taxa de Pagamento por Carga
|
||||
1750: "{SMALLFONT}{COLOUR BLACK}Comparar empresas"
|
||||
1751: "{SMALLFONT}{COLOUR BLACK}Gráficos de índices de performance das empresas"
|
||||
1752: "{SMALLFONT}{COLOUR BLACK}Gráficos de carga entregue por cada empresa"
|
||||
1753: "{SMALLFONT}{COLOUR BLACK}Gráficos de valor das empresas"
|
||||
1750: "{SMALLFONT}{COLOUR BLACK}Comparar companhias"
|
||||
1751: "{SMALLFONT}{COLOUR BLACK}Gráficos de índices de performance das companhias"
|
||||
1752: "{SMALLFONT}{COLOUR BLACK}Gráficos de carga entregue por cada companhia"
|
||||
1753: "{SMALLFONT}{COLOUR BLACK}Gráficos de valor das companhias"
|
||||
1754: "{SMALLFONT}{COLOUR BLACK}Taxa de pagamento por carga"
|
||||
1755: "{SMALLFONT}{COLOUR BLACK}Ordenar lista pelo nome da empresa"
|
||||
1756: "{SMALLFONT}{COLOUR BLACK}Ordenar lista pelo status da empresa"
|
||||
1755: "{SMALLFONT}{COLOUR BLACK}Ordenar lista pelo nome da companhia"
|
||||
1756: "{SMALLFONT}{COLOUR BLACK}Ordenar lista pelo status da companhia"
|
||||
1757: "{SMALLFONT}{COLOUR BLACK}Ordenar lista pelo índice de performance"
|
||||
1758: "{SMALLFONT}{COLOUR BLACK}Ordenar lista pelo valor da empresa"
|
||||
1759: "{COLOUR BLACK}Empresa"
|
||||
1760: "{COLOUR BLACK}Empresa ▼"
|
||||
1758: "{SMALLFONT}{COLOUR BLACK}Ordenar lista pelo valor da companhia"
|
||||
1759: "{COLOUR BLACK}Companhia"
|
||||
1760: "{COLOUR BLACK}Companhia ▼"
|
||||
1761: "{COLOUR BLACK}Status"
|
||||
1762: "{COLOUR BLACK}Status ▼"
|
||||
1763: "{COLOUR BLACK}Índice de Performance"
|
||||
1764: "{COLOUR BLACK}Índice de Performance ▼"
|
||||
1765: "{COLOUR BLACK}Valor da Empresa"
|
||||
1766: "{COLOUR BLACK}Valor da Empresa ▼"
|
||||
1765: "{COLOUR BLACK}Valor da Companhia"
|
||||
1766: "{COLOUR BLACK}Valor da Companhia ▼"
|
||||
1767: "{NEWLINE 1 2}{SPRITE}{NEWLINE 27 8}{STRINGID}"
|
||||
1768: "{NEWLINE 0 3}{INT16_1DP}% {NEWLINE 0 13}“{STRINGID}”"
|
||||
1769: "{NEWLINE 0 3}{INT16_1DP}%{SPRITE 2325}{NEWLINE 0 13}“{STRINGID}”"
|
||||
|
@ -1813,12 +1813,12 @@ strings:
|
|||
1775: Coordenador de Transporte
|
||||
1776: Supervisor de Rota
|
||||
1777: Diretor
|
||||
1778: Diretor Executivo
|
||||
1779: CEO
|
||||
1778: Chefe Executivo
|
||||
1779: Chairman
|
||||
1780: Presidente
|
||||
1781: Magnata
|
||||
1782: "{INT16} empresa"
|
||||
1783: "{INT16} empresas"
|
||||
1782: "{INT16} companhia"
|
||||
1783: "{INT16} companhias"
|
||||
1784: "{RAWDATE MY SHORT}"
|
||||
1785: "{INT16_1DP}%"
|
||||
1786: "{SMALLFONT}{COLOUR BLACK}{STRINGID}"
|
||||
|
@ -1861,35 +1861,35 @@ strings:
|
|||
1823: "{COLOUR WINDOW_2}Competidores: {COLOUR BLACK}Nenhum"
|
||||
1824: "{COLOUR WINDOW_2}Competidores: {COLOUR BLACK}Até {INT16}"
|
||||
1825: "{COLOUR BLACK}{MOVE_X 10}(não começam antes de {INT16} mês)"
|
||||
1825: "{COLOUR BLACK}{MOVE_X 10}(não começam antes de {INT16} meses)"
|
||||
1826: "{COLOUR BLACK}{MOVE_X 10}(não começam antes de {INT16} meses)"
|
||||
1827: "{COLOUR WINDOW_2}Valor de venda do veículo: {COLOUR BLACK}{CURRENCY32}"
|
||||
1828: "{COLOUR WINDOW_2}Última renda: {COLOUR BLACK}N/A"
|
||||
1829: "{COLOUR WINDOW_2}Última renda em: {COLOUR BLACK}{DATE DMY}"
|
||||
1830: "{COLOUR BLACK}{STRINGID} transportou {INT16} blocos em {INT16} dias = {CURRENCY32}"
|
||||
1831: "{COLOUR WINDOW_2} Primeiro ano de construção: {COLOUR BLACK}{UINT16 RAW}"
|
||||
1832: "{COLOUR WINDOW_2} Último ano de construção: {COLOUR BLACK}{UINT16 RAW}"
|
||||
1833: "{COLOUR WINDOW_2}Classificação das empresas segundo a autoridade local:"
|
||||
1833: "{COLOUR WINDOW_2}Classificação das companhias segundo a autoridade local:"
|
||||
1834: Terrível
|
||||
1835: Ruim
|
||||
1836: Normal
|
||||
1837: Boa
|
||||
1838: Excelente
|
||||
1839: "{COLOUR WINDOW_2}{STRINGID}: {COLOUR BLACK}{INT16}% ({STRINGID})"
|
||||
1840: Alcançe um valor da empresa de {CURRENCY32}{STRINGID}{STRINGID}
|
||||
1840: Alcançe um valor da companhia de {CURRENCY32}{STRINGID}{STRINGID}
|
||||
1841: Alcançe uma renda mensal de veículos de {CURRENCY32}{STRINGID}{STRINGID}
|
||||
1842: Alcançe um índice de performance de {INT16_1DP}% (“{STRINGID}”){STRINGID}{STRINGID}
|
||||
1843: Entregue {STRINGID}{STRINGID}{STRINGID}
|
||||
1844: " e seja a empresa de melhor performance"
|
||||
1845: " e esteja estre uma das três empresas de melhor performance"
|
||||
1844: " e seja a companhia de melhor performance"
|
||||
1845: " e esteja estre uma das três companhias de melhor performance"
|
||||
1846: " em menos {INT16} anos"
|
||||
1847: " ao final de {UINT16 RAW}"
|
||||
1848: "...e seja a melhor empresa"
|
||||
1849: "...e esteja entre as três melhores empresas"
|
||||
1848: "...e seja a melhor companhia"
|
||||
1849: "...e esteja entre as três melhores companhias"
|
||||
1850: "...com um limite de tempo de:"
|
||||
1851: "{COLOUR WINDOW_2}Desafio:"
|
||||
1852: "{COLOUR BLACK} {STRINGID}"
|
||||
1853: Alcançe um certo valor de empresa
|
||||
1854: Alcançe umacerta renda mensal de veículos
|
||||
1853: Alcançe um certo valor de companhia
|
||||
1854: Alcançe uma certa renda mensal de veículos
|
||||
1855: Alcançe um certo índice de performance
|
||||
1856: Entregue uma certa quantidade de carga
|
||||
1857: "{CURRENCY32}"
|
||||
|
@ -1907,37 +1907,37 @@ strings:
|
|||
|
||||
1869: "{OUTLINE}{COLOUR WINDOW_2}Sair do{NEWLINE}Jogo"
|
||||
|
||||
1870: Empresa está falida!
|
||||
1870: Companhia está falida!
|
||||
1871: Permitir que indústrias fechem durante a partida
|
||||
1872: Permitir que indústrias novas sejam criadas durante a partida
|
||||
1873: Compartilhar aparências das empresas/propritário adicionais
|
||||
1874: "{SMALLFONT}{COLOUR BLACK}Selecione esta opção para enviar quaisquer arquivos de dados personalizados da aparência de empresas/proprietário para o outro computador durante o processo de conexão"
|
||||
1873: Compartilhar aparências das companhias/proprietário adicionais
|
||||
1874: "{SMALLFONT}{COLOUR BLACK}Selecione esta opção para enviar quaisquer arquivos de dados personalizados da aparência de companhias/proprietário para o outro computador durante o processo de conexão"
|
||||
1875: "{COLOUR WINDOW_2}Proibir que os competidores utilizem:"
|
||||
1876: "{COLOUR WINDOW_2}Proibir que empresas de jogadores utilizem:"
|
||||
1876: "{COLOUR WINDOW_2}Proibir que companhias de jogadores utilizem:"
|
||||
1877: "{NEWLINE}Para em: {STRINGID}"
|
||||
1878: ", {STRINGID}"
|
||||
|
||||
1879: "Tutorial 1: Construindo um serviço de ônibus em um município"
|
||||
1879: "Tutorial 1: Iniciando um serviço de ônibus em um município"
|
||||
1880: "Tutorial 2: Construindo um serviço de ônibus entre dois municípios"
|
||||
1881: "Tutorial 3: Construindo um serviço ferroviário entre dois municípios"
|
||||
|
||||
# Tutorial 1
|
||||
1882: "{SMALLFONT}{COLOUR BLACK}Construiremos um serviço de ônibus para transportar passageiros em um município, o primeiro passo é escolher um município..."
|
||||
1883: "{SMALLFONT}{COLOUR BLACK}Mova o mouse com o botão direito pressionado to para mover a vista para dar uma olhada nos municípios..."
|
||||
1883: "{SMALLFONT}{COLOUR BLACK}Mova o mouse com o botão direito pressionado para mover a vista e dar uma olhada nos municípios..."
|
||||
1884: "{SMALLFONT}{COLOUR BLACK}Boulders Bay é o maior município, portanto produzirá o maior número de passageiros. Utilize a roda do mouse ou o ícone de zoom para aproximar a vista..."
|
||||
1885: "{SMALLFONT}{COLOUR BLACK}Toda construção relacionada a estradas é feita utilizando a janela de construção de estradas..."
|
||||
1886: "{SMALLFONT}{COLOUR BLACK}Não precisamos de novas estradas no município para nosso serviço de ônibus, apenas de novas estações de ônibus..."
|
||||
1887: "{SMALLFONT}{COLOUR BLACK}Nosso serviço de ônibus irá de um a lado ao outro do município..."
|
||||
1887: "{SMALLFONT}{COLOUR BLACK}Nosso serviço de ônibus irá de um a lado do município a outro..."
|
||||
1888: "{SMALLFONT}{COLOUR BLACK}A área realçada em azul é a área de captação da estação. Quanto mais edifícios na área de captação mais passageiro utilizarão a estação..."
|
||||
1889: "{SMALLFONT}{COLOUR BLACK}Verique-se de a posição da estação aceita passageiros, caso contrário você não poderá descarregá-los e não será pago. Observe o texto “Área de Captação” na parte inferior da janela de construção..."
|
||||
1889: "{SMALLFONT}{COLOUR BLACK}Verique-se de que a posição da estação aceita passageiros, caso contrário você não poderá descarregá-los e não será pago. Observe o texto “Área de Captação” na parte inferior da janela de construção..."
|
||||
1890: "{SMALLFONT}{COLOUR BLACK}Quando estiver contente com a localização, clique o botão esquerdo do mouse para construir a estação..."
|
||||
1891: "{SMALLFONT}{COLOUR BLACK}Construiremos a segunda estação no lado oposto do município..."
|
||||
1892: "{SMALLFONT}{COLOUR BLACK}Agora temos que comprar um ônibus..."
|
||||
1893: "{SMALLFONT}{COLOUR BLACK}Temos a escolha de dois tipos diferentes de ônibus. Vamos escolher o desigh mais novo que carrega mais passageiros..."
|
||||
1893: "{SMALLFONT}{COLOUR BLACK}Temos a escolha de dois tipos diferentes de ônibus. Vamos escolher o modelo mais novo que carrega mais passageiros..."
|
||||
1894: "{SMALLFONT}{COLOUR BLACK}Mova o cursor do mouse para posicionar o ônibus numa estrada e clique o botão esquerdo para colocá-lo..."
|
||||
1895: "{SMALLFONT}{COLOUR BLACK}Antes de ligarmos o ônibus devemos informá-lo aonde ir. Selecione a aba de detalhes da rota na janela do veículo..."
|
||||
1896: "{SMALLFONT}{COLOUR BLACK}Para determinar a rota, simplesmente clique na estação na vista..."
|
||||
1897: "{SMALLFONT}{COLOUR BLACK}Agora podemos ligar o ônibus..."
|
||||
1895: "{SMALLFONT}{COLOUR BLACK}Antes de colocarmos o ônibus em movimento devemos informá-lo aonde ir. Selecione a aba de detalhes da rota na janela do veículo..."
|
||||
1896: "{SMALLFONT}{COLOUR BLACK}Para determinar a rota, apenas clique na estação na vista..."
|
||||
1897: "{SMALLFONT}{COLOUR BLACK}Agora podemos por o ônibus em movimento..."
|
||||
1898: "{SMALLFONT}{COLOUR BLACK}Vamos observá-lo pegando seus primeiros passageiros..."
|
||||
|
||||
# Tutorial 2
|
||||
|
@ -1945,28 +1945,28 @@ strings:
|
|||
1900: "{SMALLFONT}{COLOUR BLACK}Selecione a orientação para a nova seção de estrada, e clique na vista para iniciar a construção..."
|
||||
1901: "{SMALLFONT}{COLOUR BLACK}Clicar no ícone 'construir isto' irá acrescentar mais seções de estrada do mesmo tipo..."
|
||||
1902: "{SMALLFONT}{COLOUR BLACK}Antes de prosseguirmos, existe um método mais rápido de iniciar construção de estradas..."
|
||||
1903: "{SMALLFONT}{COLOUR BLACK}Simplesmente pressione o botão direito do mouse no final da estrada que você quer extender..."
|
||||
1903: "{SMALLFONT}{COLOUR BLACK}Apenas pressione o botão direito do mouse no final da estrada que você quer extender..."
|
||||
1904: "{SMALLFONT}{COLOUR BLACK}Precisamos dobrar à esquerda aqui, portanto selecione uma curva à esquerda..."
|
||||
1905: "{SMALLFONT}{COLOUR BLACK}E agora de volta a construção de retas novamente..."
|
||||
1906: "{SMALLFONT}{COLOUR BLACK}Com a estrada construída, contruiremos as estações..."
|
||||
1907: "{SMALLFONT}{COLOUR BLACK}E finalmente, Vamos comprar um ônibus e o colocaremos para rodar..."
|
||||
1906: "{SMALLFONT}{COLOUR BLACK}Com a estrada construída, construiremos as estações..."
|
||||
1907: "{SMALLFONT}{COLOUR BLACK}E finalmente, Vamos comprar um ônibus e o colocaremos em movimento..."
|
||||
1908: "{SMALLFONT}{COLOUR BLACK}Essa rota pode ser bem movimentada, então vamos comprar um segundo ônibus..."
|
||||
1909: "{SMALLFONT}{COLOUR BLACK}Para copiar rapidamente toda a rota do Ônibus 1 para o Ônibus 2, CLique no texto 'Fim da lista de rotas' do Ônibus 1..."
|
||||
1909: "{SMALLFONT}{COLOUR BLACK}Para copiar rapidamente toda a rota do Ônibus 1 para o Ônibus 2, Clique no texto 'Fim da lista de rotas' do Ônibus 1..."
|
||||
|
||||
# Tutorial 3
|
||||
1910: "{SMALLFONT}{COLOUR BLACK}Vamos começar construindo um serviço ferroviário de passageiros entre as duas maiores cidades..."
|
||||
1911: "{SMALLFONT}{COLOUR BLACK}Estações de trem só podem ser construidas em trilhos retos e precisam ser longas o suficiente para nosso trem - Aqui é onde construiremos uma estação em alguns minutos..."
|
||||
1912: "{SMALLFONT}{COLOUR BLACK}Vamos construir as estações..."
|
||||
1913: "{SMALLFONT}{COLOUR BLACK}Vamos construir o trem. Precisaremos de uma locomotiva para puxar o trem e vagões para carregar os passageiros..."
|
||||
1914: "{SMALLFONT}{COLOUR BLACK}Como a via é uma só linha, não precisamos configurar uma rota - Apenas começar o trem..."
|
||||
1914: "{SMALLFONT}{COLOUR BLACK}Como a via é de uma só linha, não precisamos configurar uma rota - Apenas por o trem para andar..."
|
||||
1915: "{SMALLFONT}{COLOUR BLACK}Vamos extender a via para criar uma junção e uma extensão para o terceiro município. Clicar com o botão direito na via abre a janela de construção de vias pronta para construir a junção..."
|
||||
1916: "{SMALLFONT}{COLOUR BLACK}Já que o traçado da via não é mais simples Precisaremos dar ao nosso trem uma rota..."
|
||||
1917: "{SMALLFONT}{COLOUR BLACK}Poderíamos agora adicionar um segundo trem, mas primeiro precisamos construir semáforos para prevenir colisões entre trens..."
|
||||
1918: "{SMALLFONT}{COLOUR BLACK}Os semáforos diveidem a via em três diferentes 'Seções de bloco' e apenas um trem por vez poderá entrar em cada seção..."
|
||||
1916: "{SMALLFONT}{COLOUR BLACK}Já que o traçado da via não é mais simples precisaremos dar ao nosso trem uma rota..."
|
||||
1917: "{SMALLFONT}{COLOUR BLACK}Poderíamos agora adicionar um segundo trem, mas primeiro precisamos construir semáforos para evitar colisões entre trens..."
|
||||
1918: "{SMALLFONT}{COLOUR BLACK}Os semáforos dividem a via em três diferentes 'Seções de bloco' e apenas um trem por vez poderá entrar em cada seção..."
|
||||
|
||||
# Options/Misc
|
||||
1919: Usar nome do proprietário preferido no início de cada partida
|
||||
1920: "{SMALLFONT}{COLOUR BLACK}Selecione esta opção para utilizar o mesmo nome do proprietário toda vez que ocê começar uma nova partida"
|
||||
1920: "{SMALLFONT}{COLOUR BLACK}Selecione esta opção para utilizar o mesmo nome do proprietário toda vez que você começar uma nova partida"
|
||||
1921: "{COLOUR WINDOW_2}Nome do proprietário preferido: {COLOUR BLACK}{STRINGID}"
|
||||
1922: Nome do Proprietário Preferido
|
||||
1923: "Escreva o nome do proprietário preferido:"
|
||||
|
@ -2035,8 +2035,8 @@ strings:
|
|||
1981: "{COLOUR WINDOW_2}"
|
||||
1982: "{COLOUR WINDOW_2}"
|
||||
|
||||
1983: Distâcia por Unidades de Carga Entregues por Mês
|
||||
1984: "{SMALLFONT}{COLOUR BLACK}Cráficos de carga por distância entregues por cada empresa"
|
||||
1983: Distância por Unidades de Carga Entregues por Mês
|
||||
1984: "{SMALLFONT}{COLOUR BLACK}Cráficos de carga por distância entregues por cada companhia"
|
||||
1985: "{COLOUR WINDOW_2}Velocidade média do último trajeto: {COLOUR BLACK}{VELOCITY}"
|
||||
1986: terra
|
||||
1987: ar
|
||||
|
@ -2054,17 +2054,17 @@ strings:
|
|||
1999: "{COLOUR WINDOW_2}Recorde de velocidade de um serviço aquático: {COLOUR BLACK}{VELOCITY}"
|
||||
2000: "{COLOUR BLACK}Realizado por {STRINGID} em {DATE DMY}"
|
||||
2001: Confirmar troca do modo de visualização
|
||||
2002: "{COLOUR WINDOW_2}Resolução aerada para {UINT16 RAW} x {UINT16 RAW}"
|
||||
2002: "{COLOUR WINDOW_2}Resolução da tela mudou para {UINT16 RAW} x {UINT16 RAW}"
|
||||
2003: "{COLOUR WINDOW_2}_"
|
||||
2004: "{COLOUR WINDOW_2}Nome do arquivo:"
|
||||
2005: "{COLOUR WINDOW_2}Diretório: {COLOUR BLACK}{STRINGID}"
|
||||
2006: "{SMALLFONT}{COLOUR BLACK}Subir um nível para a pasta principal"
|
||||
2007: "{COLOUR WINDOW_2}Empresa: {COLOUR BLACK}{STRINGID}"
|
||||
2007: "{COLOUR WINDOW_2}Companhia: {COLOUR BLACK}{STRINGID}"
|
||||
2008: "{COLOUR WINDOW_2}Data: {COLOUR BLACK}{DATE DMY}"
|
||||
2009: "{COLOUR WINDOW_2}Progresso do Desafio: {COLOUR BLACK}{INT16}%"
|
||||
2010: "{COLOUR WINDOW_2}Desafio: {COLOUR BLACK}Completed"
|
||||
2011: "{COLOUR WINDOW_2}Desafio: {COLOUR BLACK}Failed"
|
||||
2012: "Sobrescrever arquivo existente: “{STRINGID}”?"
|
||||
2012: "Substituir arquivo existente: “{STRINGID}”?"
|
||||
2013: Substituir
|
||||
2014: "Excluir arquivo: “{STRINGID}”?"
|
||||
2015: Excluir
|
||||
|
@ -2072,7 +2072,7 @@ strings:
|
|||
2017: Nome da Indústria
|
||||
2018: "Escreva um novo nome para {STRINGID}:"
|
||||
2019: Impossível renomear indústria...
|
||||
2020: "{NEWLINE 0 1}{SPRITE}{SMALLFONT}{NEWLINE 45 1}Max. speed: {STRINGID}{NEWLINE 45 11}Height limit: {HEIGHT}"
|
||||
2020: "{NEWLINE 0 1}{SPRITE}{SMALLFONT}{NEWLINE 45 1}Vel. máxima: {STRINGID}{NEWLINE 45 11}Limite de altura: {HEIGHT}"
|
||||
2021: Códigos seriais duplicados - Falha na conexão!
|
||||
2022: "{COLOUR BLACK}{SMALLFONT}(Nome do Computador = {STRINGID})"
|
||||
2023: "1"
|
||||
|
@ -2099,7 +2099,7 @@ strings:
|
|||
2044: Muitos objetos deste tipo selecionados
|
||||
2045: "O seguinte objeto deve ser selecionado primeiro: "
|
||||
2046: Este objeto está atualmente em uso
|
||||
2047: Este objeto e necessitado por outro objeto
|
||||
2047: Este objeto e requerido por outro objeto
|
||||
2048: Este objeto é sempre necessário
|
||||
2049: Impossível selecionar este objeto
|
||||
2050: Impossível cancelar a seleção deste objeto
|
||||
|
@ -2109,7 +2109,7 @@ strings:
|
|||
2054: Sons
|
||||
2055: Moeda
|
||||
2056: Efeitos de Animação
|
||||
2057: Peredes Verticais do Terreno
|
||||
2057: Faces Verticais do Terreno
|
||||
2058: Água
|
||||
2059: Terreno
|
||||
2060: Nomes de Municípios
|
||||
|
@ -2133,24 +2133,24 @@ strings:
|
|||
2078: Neve
|
||||
2079: Clima
|
||||
2080: Dados da Geração de Mapas
|
||||
2081: Edifícios
|
||||
2082: Andaimes
|
||||
2081: Construções
|
||||
2082: Suportes
|
||||
2083: Indústrias
|
||||
2084: Região do Mundo
|
||||
2085: Proprietários da Empresa
|
||||
2085: Proprietários da Companhia
|
||||
2086: Descrições dos Cenários
|
||||
2087: lista de objetos
|
||||
2088: "Faltam dados de objeto, ID: "
|
||||
2089: Exportar objetos de plug-in com os jogos salvos
|
||||
2090: "{SMALLFONT}{COLOUR BLACK}Selecione se deseja salvar os dados de objetos de plug-in adicionais (dados adicionais não proporcionados com o produto principal) nos arquivos de jogos salvos ou cenários, possibilitando que alguém sem os dados de objeto os carregue"
|
||||
2091: Pelo menos uma estrada de mão dupla deve ser selecionada
|
||||
2092: Um tipo de andaime deve ser selecionado
|
||||
2092: Um tipo de suporte deve ser selecionado
|
||||
2093: Avançado
|
||||
2094: "{SMALLFONT}{COLOUR BLACK}Permite uma seleção de itens individuais mais avançada"
|
||||
2095: "{COLOUR WHITE}{BIGFONT}£"
|
||||
2096: Novos objetos instalados com sucesso
|
||||
2097: Pelo menos uma indústria deve ser selecionada
|
||||
2098: Pelo menos um edifício municipal deve ser selecionado
|
||||
2098: Pelo menos uma construção municipal deve ser selecionada
|
||||
2099: Um tipo de sede deve ser selecionado
|
||||
2100: Apenas um tipo de sede deve ser selecionado
|
||||
2101: Um tipo de interface deve ser selecionado
|
||||
|
@ -2167,19 +2167,97 @@ strings:
|
|||
2112: Um tipo de região deve ser selecionado
|
||||
2113: mph
|
||||
2114: kmh⁻¹
|
||||
2115: "hour:"
|
||||
2116: "hours:"
|
||||
2115: "hora:"
|
||||
2116: "horas:"
|
||||
2117: mins
|
||||
2118: "min:"
|
||||
2119: secs
|
||||
2120: " units"
|
||||
2119: segs
|
||||
2120: " unidades"
|
||||
2121: ft
|
||||
2122: m
|
||||
2123: hp
|
||||
2124: kW
|
||||
2125: "{COLOUR WINDOW_2}Idioma:"
|
||||
|
||||
2126: "{COLOUR WINDOW_2}Desabilitar quebra de veículos"
|
||||
2127: "{COLOUR WINDOW_2}Modo de tela:"
|
||||
2128: Janela
|
||||
2129: Tela Cheia
|
||||
2130: Tela Cheia Sem Borda
|
||||
2131: Dispositivo padrão de aúdio
|
||||
2132: "{COLOUR WINDOW_2}Fator de escala da janela:"
|
||||
2133: "{COLOUR BLACK}+"
|
||||
2134: "{COLOUR BLACK}-"
|
||||
2135: "{COLOUR BLACK}{INT32_1DP}"
|
||||
2136: "{COLOUR WINDOW_2}Zoom para posição do cursor"
|
||||
2137: "{SMALLFONT}{COLOUR BLACK}Quando ativado, o zoom centralizará no cursor ao invés do centro da tela"
|
||||
2138: "{COLOUR WINDOW_2}Tocar música do menu"
|
||||
2139: "Sair para o menu"
|
||||
2140: "Sair do OpenLoco"
|
||||
2141: "{COLOUR WINDOW_2}Desabilitar companhias de IA"
|
||||
2142: "{SMALLFONT}{COLOUR BLACK}Isso desabilita o 'pensamento' da IA, tornando-as inefetivas.{NEWLINE}Em novos jogos, isso também previne que novas companhias de IA se formem."
|
||||
2143: "{COLOUR WINDOW_2}Quantidade de autosaves:"
|
||||
2144: "{COLOUR WINDOW_2}Frequência de autosaves:"
|
||||
2145: "Nunca"
|
||||
2146: "Todo mês"
|
||||
2147: "Cada {INT32} meses"
|
||||
2148: "{COLOUR WINDOW_2}Gerador:"
|
||||
2149: Original
|
||||
2150: Melhorado
|
||||
2151: "Modificar veículo"
|
||||
2152: "Clonar veículo"
|
||||
2153: "Impossível clonar veículo..."
|
||||
2154: "Localizar veículo na visão principal"
|
||||
2155: "Seguir veículo na visão principal"
|
||||
2156: "Habilitar menu de trapaças/debugging"
|
||||
2157: "{SMALLFONT}{COLOUR BLACK}Isso adiciona um botão extra na barra de tarefas do jogo, permitindo fácil acesso a certas trapaças e opções de debug."
|
||||
2158: "Habilitar modo sandbox"
|
||||
2159: "Permitir direção manual"
|
||||
2160: "Permitir construir enquanto pausado"
|
||||
2161: "Mostrar contador de FPS"
|
||||
2162: "{SMALLFONT}{COLOUR BLACK}Isso mostrará um contato no topo da tela, indicando o número de frames desenhados por segundo."
|
||||
2163: "Tirar limite de FPS"
|
||||
2164: "{SMALLFONT}{COLOUR BLACK}Remove a restrição de renderização a 40Hz."
|
||||
2165: "Hardware"
|
||||
2166: "Renderização de mapa"
|
||||
2167: "Inspetor de Tiles"
|
||||
2168: "{SMALLFONT}{COLOUR BLACK}Ative para selecionar um tile para inspecionar."
|
||||
2169: "Superfíce"
|
||||
2170: "Via"
|
||||
2171: "Estação"
|
||||
2172: "Semáforo"
|
||||
2173: "Construção"
|
||||
2174: "Árvore"
|
||||
2175: "Parede"
|
||||
2176: "Estrada"
|
||||
2177: "Indústria"
|
||||
2178: "{COLOUR WINDOW_2} {STRINGID} ({STRINGID})"
|
||||
2179: "{COLOUR WINDOW_2} {STRINGID} - {STRINGID} ({STRINGID})"
|
||||
2180: "X:"
|
||||
2181: "Y:"
|
||||
2182: "{COLOUR WINDOW_2}{INT16}"
|
||||
2183: "Dados dos elementos do tile"
|
||||
2184: "Trapaças"
|
||||
2185: "Trapaças financeiras"
|
||||
2186: "Trapaças de companhia"
|
||||
2187: "Trapaças de veículos"
|
||||
2188: "Trapaças de municípios"
|
||||
2189: "Limpar empréstimo"
|
||||
2190: "Limpar"
|
||||
2191: "{COLOUR BLACK}{CURRENCY32}"
|
||||
2192: "Selecionar companhia alvo"
|
||||
2193: "Selecionar trapaça para aplicar"
|
||||
2194: "Mudar controle para essa companhia"
|
||||
2195: "Adquirir todas as posses da companhia"
|
||||
2196: "Ativar falência"
|
||||
2197: "Ativar status da cadeia"
|
||||
2198: "Aumentar fundos"
|
||||
2199: "{COLOUR WINDOW_2}Quantidade:"
|
||||
2200: "Adicionar"
|
||||
2201: "Confiabilidade de veículo"
|
||||
2202: "Mudar todas para 0 (ilimitada)"
|
||||
2203: "Mudar todas para 100 (renovação completa)"
|
||||
2204: "Mudar avaliação da companhia"
|
||||
2205: "-10% em tudo"
|
||||
2206: "+10% em tudo"
|
||||
2207: "Min em tudo"
|
||||
2208: "Max em tudo"
|
||||
|
|
|
@ -1220,8 +1220,8 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: "Нет места для новых распоряжений!"
|
||||
1215: "Слишком много распоряжений для данного транспортного средства!"
|
||||
1216: "- - Местный - -"
|
||||
1217: "- - Экспресс - -"
|
||||
1216: "Местный"
|
||||
1217: "Экспресс"
|
||||
1218: "{COLOUR BLACK}- - Маршрут не определен - -"
|
||||
1219: "{COLOUR BLACK}- - Конец списка маршрутов - -"
|
||||
1220: "Остановка: {STRINGID}"
|
||||
|
|
|
@ -1244,8 +1244,8 @@ strings:
|
|||
1213: "{COLOUR WINDOW_2}{STRINGID})"
|
||||
1214: Nie je priestor pre viac príkazov vozidiel!
|
||||
1215: Príliš veľa príkazov pre toto vozidlo!
|
||||
1216: "- - Normálny - -"
|
||||
1217: "- - Rýchlik - -"
|
||||
1216: "Normálny"
|
||||
1217: "Rýchlik"
|
||||
1218: "{COLOUR BLACK}- - Nie je definovaná žiadna trasa - -"
|
||||
1219: "{COLOUR BLACK}- - Koniec zoznamu trás - -"
|
||||
1220: "Zastávka v: {STRINGID}"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
|||
# http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Version=1.0
|
||||
Exec=openloco %u
|
||||
Icon=openloco
|
||||
Name=OpenLoco
|
||||
GenericName=Transport management game
|
||||
GenericName[nl]=Transportbedrijfsimulator
|
||||
Comment=Open Source re-implementation of Chris Sawyer's Locomotion.
|
||||
Comment[ca]=Reimplementació de codi obert del Chris Sawyer's Locomotion.
|
||||
Comment[cs]=Open Source remake hry Chris Sawyer's Locomotion.
|
||||
Comment[de]=Quelloffene Neuimplementierung von Chris Sawyer's Locomotion.
|
||||
Comment[es]=Re-implementación de código abierto de Chris Sawyer's Locomotion.
|
||||
Comment[fr]=Ré-implémentation libre (Open Source) de Chris Sawyer's Locomotion.
|
||||
Comment[it]=Versione Open Source di Chris Sawyer's Locomotion.
|
||||
Comment[nb]=Open Source re-implementasjon av Chris Sawyer's Locomotion.
|
||||
Comment[nl]=Een opensource-versie van Chris Sawyer's Locomotion.
|
||||
Comment[pt]=Reimplementação em código aberto do Chris Sawyer's Locomotion.
|
||||
Comment[ru]=Ремейк с открытым исходным кодом игры Chris Sawyer's Locomotion.
|
||||
Comment[sv]=Open Source om-implementation av Chris Sawyer's Locomotion.
|
||||
Categories=Game;Simulation;
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
||||
<clear />
|
||||
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
|
@ -50,7 +50,7 @@
|
|||
C4549: 'operator': operator before comma has no effect; did you intend 'operator'?
|
||||
C4555: expression has no effect; expected expression with side-effect
|
||||
-->
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_USE_MATH_DEFINES;SDL_MAIN_HANDLED;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_USE_MATH_DEFINES;SDL_MAIN_HANDLED;_WINSOCK_DEPRECATED_NO_WARNINGS;USE_BREAKPAD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<AdditionalOptions>/utf-8 /std:c++17 /permissive-</AdditionalOptions>
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
#include "../Config.h"
|
||||
#include "../Console.h"
|
||||
#include "../Date.h"
|
||||
#include "../Entities/EntityManager.h"
|
||||
#include "../Environment.h"
|
||||
#include "../Interop/Interop.hpp"
|
||||
#include "../Localisation/StringIds.h"
|
||||
#include "../Map/TileManager.h"
|
||||
#include "../Objects/ObjectManager.h"
|
||||
#include "../Objects/SoundObject.h"
|
||||
#include "../Things/ThingManager.h"
|
||||
#include "../Things/Vehicle.h"
|
||||
#include "../Ui/WindowManager.h"
|
||||
#include "../Utility/Stream.hpp"
|
||||
#include "../Vehicles/Vehicle.h"
|
||||
#include "Channel.h"
|
||||
#include "MusicChannel.h"
|
||||
#include "VehicleChannel.h"
|
||||
|
@ -45,7 +45,7 @@ namespace OpenLoco::Audio
|
|||
int16_t cbSize;
|
||||
};
|
||||
|
||||
struct sound_object_data
|
||||
struct SoundObjectData
|
||||
{
|
||||
int32_t var_00;
|
||||
int32_t offset;
|
||||
|
@ -54,12 +54,12 @@ namespace OpenLoco::Audio
|
|||
|
||||
const void* pcm()
|
||||
{
|
||||
return (void*)((uintptr_t)this + sizeof(sound_object_data));
|
||||
return (void*)((uintptr_t)this + sizeof(SoundObjectData));
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct audio_format
|
||||
struct AudioFormat
|
||||
{
|
||||
int32_t frequency{};
|
||||
int32_t format{};
|
||||
|
@ -80,20 +80,20 @@ namespace OpenLoco::Audio
|
|||
|
||||
static uint8_t _numActiveVehicleSounds; // 0x0112C666
|
||||
static std::vector<std::string> _devices;
|
||||
static audio_format _outputFormat;
|
||||
static std::array<channel, 4> _channels;
|
||||
static std::array<vehicle_channel, 10> _vehicle_channels;
|
||||
static music_channel _music_channel;
|
||||
static channel_id _music_current_channel = channel_id::bgm;
|
||||
static AudioFormat _outputFormat;
|
||||
static std::array<Channel, 4> _channels;
|
||||
static std::array<VehicleChannel, 10> _vehicle_channels;
|
||||
static MusicChannel _music_channel;
|
||||
static ChannelId _music_current_channel = ChannelId::bgm;
|
||||
|
||||
static std::vector<sample> _samples;
|
||||
static std::unordered_map<uint16_t, sample> _object_samples;
|
||||
static std::vector<Sample> _samples;
|
||||
static std::unordered_map<uint16_t, Sample> _object_samples;
|
||||
|
||||
static void playSound(sound_id id, loc16 loc, int32_t volume, int32_t pan, int32_t frequency);
|
||||
static void mixSound(sound_id id, bool loop, int32_t volume, int32_t pan, int32_t freq);
|
||||
static void playSound(SoundId id, const Map::Pos3& loc, int32_t volume, int32_t pan, int32_t frequency);
|
||||
static void mixSound(SoundId id, bool loop, int32_t volume, int32_t pan, int32_t freq);
|
||||
|
||||
// 0x004FE910
|
||||
static const music_info MusicInfo[] = {
|
||||
static const MusicInfo _musicInfo[] = {
|
||||
{ path_id::music_20s1, StringIds::music_chuggin_along, 1925, 1933 },
|
||||
{ path_id::music_20s2, StringIds::music_long_dusty_road, 1927, 1935 },
|
||||
{ path_id::music_20s4, StringIds::music_flying_high, 1932, 1940 },
|
||||
|
@ -125,9 +125,9 @@ namespace OpenLoco::Audio
|
|||
{ path_id::music_20s6, StringIds::music_sandy_track_blues, 1921, 1929 }
|
||||
};
|
||||
|
||||
static constexpr bool isMusicChannel(channel_id id)
|
||||
static constexpr bool isMusicChannel(ChannelId id)
|
||||
{
|
||||
return (id == channel_id::bgm || id == channel_id::title);
|
||||
return (id == ChannelId::bgm || id == ChannelId::title);
|
||||
}
|
||||
|
||||
int32_t volumeLocoToSDL(int32_t loco)
|
||||
|
@ -135,7 +135,7 @@ namespace OpenLoco::Audio
|
|||
return (int)(SDL_MIX_MAXVOLUME * (SDL_pow(10, (float)loco / 2000)));
|
||||
}
|
||||
|
||||
static channel* getChannel(channel_id id)
|
||||
static Channel* getChannel(ChannelId id)
|
||||
{
|
||||
auto index = (size_t)id;
|
||||
if (index < _channels.size())
|
||||
|
@ -145,7 +145,7 @@ namespace OpenLoco::Audio
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static sample loadSoundFromWaveMemory(const WAVEFORMATEX& format, const void* pcm, size_t pcmLen)
|
||||
static Sample loadSoundFromWaveMemory(const WAVEFORMATEX& format, const void* pcm, size_t pcmLen)
|
||||
{
|
||||
// Build a CVT to convert the audio
|
||||
const auto& dstFormat = _outputFormat;
|
||||
|
@ -167,7 +167,7 @@ namespace OpenLoco::Audio
|
|||
else if (cr == 0)
|
||||
{
|
||||
// No conversion necessary
|
||||
sample s;
|
||||
Sample s;
|
||||
s.pcm = std::malloc(pcmLen);
|
||||
if (s.pcm == nullptr)
|
||||
{
|
||||
|
@ -201,7 +201,7 @@ namespace OpenLoco::Audio
|
|||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
sample s;
|
||||
Sample s;
|
||||
s.pcm = cvt.buf;
|
||||
s.len = cvt.len_cvt;
|
||||
s.chunk = Mix_QuickLoad_RAW(cvt.buf, cvt.len_cvt);
|
||||
|
@ -209,10 +209,10 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
}
|
||||
|
||||
static std::vector<sample> loadSoundsFromCSS(const fs::path& path)
|
||||
static std::vector<Sample> loadSoundsFromCSS(const fs::path& path)
|
||||
{
|
||||
Console::logVerbose("loadSoundsFromCSS(%s)", path.string().c_str());
|
||||
std::vector<sample> results;
|
||||
std::vector<Sample> results;
|
||||
std::ifstream fs(path, std::ios::in | std::ios::binary);
|
||||
|
||||
if (fs.is_open())
|
||||
|
@ -254,8 +254,8 @@ namespace OpenLoco::Audio
|
|||
|
||||
static void disposeChannels()
|
||||
{
|
||||
std::generate(_channels.begin(), _channels.end(), []() { return channel(); });
|
||||
std::generate(_vehicle_channels.begin(), _vehicle_channels.end(), []() { return vehicle_channel(); });
|
||||
std::generate(_channels.begin(), _channels.end(), []() { return Channel(); });
|
||||
std::generate(_vehicle_channels.begin(), _vehicle_channels.end(), []() { return VehicleChannel(); });
|
||||
}
|
||||
|
||||
static void reinitialise()
|
||||
|
@ -330,11 +330,11 @@ namespace OpenLoco::Audio
|
|||
|
||||
for (size_t i = 0; i < _channels.size(); i++)
|
||||
{
|
||||
_channels[i] = channel(i);
|
||||
_channels[i] = Channel(i);
|
||||
}
|
||||
for (size_t i = 0; i < _vehicle_channels.size(); i++)
|
||||
{
|
||||
_vehicle_channels[i] = vehicle_channel(channel(4 + i));
|
||||
_vehicle_channels[i] = VehicleChannel(Channel(4 + i));
|
||||
}
|
||||
|
||||
auto css1path = Environment::getPath(Environment::path_id::css1);
|
||||
|
@ -348,11 +348,17 @@ namespace OpenLoco::Audio
|
|||
disposeSamples();
|
||||
disposeChannels();
|
||||
_music_channel = {};
|
||||
_music_current_channel = (channel_id)-1;
|
||||
_music_current_channel = (ChannelId)-1;
|
||||
Mix_CloseAudio();
|
||||
_audio_initialised = 0;
|
||||
}
|
||||
|
||||
// 0x00489BA1
|
||||
void close()
|
||||
{
|
||||
call(0x00489BA1);
|
||||
}
|
||||
|
||||
#ifdef __HAS_DEFAULT_DEVICE__
|
||||
static const char* getDefaultDeviceName()
|
||||
{
|
||||
|
@ -461,13 +467,13 @@ namespace OpenLoco::Audio
|
|||
_audioIsPaused = false;
|
||||
}
|
||||
|
||||
static sound_object* getSoundObject(sound_id id)
|
||||
static SoundObject* getSoundObject(SoundId id)
|
||||
{
|
||||
auto idx = (int32_t)id & ~0x8000;
|
||||
return ObjectManager::get<sound_object>(idx);
|
||||
return ObjectManager::get<SoundObject>(idx);
|
||||
}
|
||||
|
||||
static viewport* findBestViewportForSound(viewport_pos vpos)
|
||||
static Viewport* findBestViewportForSound(viewport_pos vpos)
|
||||
{
|
||||
auto w = WindowManager::find(WindowType::main, 0);
|
||||
if (w != nullptr)
|
||||
|
@ -495,7 +501,7 @@ namespace OpenLoco::Audio
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static int32_t getVolumeForSoundId(sound_id id)
|
||||
static int32_t getVolumeForSoundId(SoundId id)
|
||||
{
|
||||
if (isObjectSoundId(id))
|
||||
{
|
||||
|
@ -513,7 +519,7 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
}
|
||||
|
||||
static int32_t calculateVolumeFromViewport(sound_id id, const Map::map_pos3& mpos, const viewport& viewport)
|
||||
static int32_t calculateVolumeFromViewport(SoundId id, const Map::Pos3& mpos, const Viewport& viewport)
|
||||
{
|
||||
auto volume = 0;
|
||||
auto zVol = 0;
|
||||
|
@ -533,17 +539,17 @@ namespace OpenLoco::Audio
|
|||
return volume;
|
||||
}
|
||||
|
||||
void playSound(sound_id id, loc16 loc)
|
||||
void playSound(SoundId id, const Map::Pos3& loc)
|
||||
{
|
||||
playSound(id, loc, play_at_location);
|
||||
}
|
||||
|
||||
void playSound(sound_id id, int32_t pan)
|
||||
void playSound(SoundId id, int32_t pan)
|
||||
{
|
||||
playSound(id, {}, pan);
|
||||
}
|
||||
|
||||
static vehicle_channel* getFreeVehicleChannel()
|
||||
static VehicleChannel* getFreeVehicleChannel()
|
||||
{
|
||||
for (auto& vc : _vehicle_channels)
|
||||
{
|
||||
|
@ -555,7 +561,7 @@ namespace OpenLoco::Audio
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool shouldSoundLoop(sound_id id)
|
||||
bool shouldSoundLoop(SoundId id)
|
||||
{
|
||||
loco_global<uint8_t[64], 0x0050D514> unk_50D514;
|
||||
if (isObjectSoundId(id))
|
||||
|
@ -570,7 +576,7 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
|
||||
// 0x0048A4BF
|
||||
void playSound(vehicle_26* v)
|
||||
void playSound(Vehicles::Vehicle2or6* v)
|
||||
{
|
||||
if (v->var_4A & 1)
|
||||
{
|
||||
|
@ -584,20 +590,20 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
|
||||
// 0x00489F1B
|
||||
void playSound(sound_id id, loc16 loc, int32_t volume, int32_t frequency)
|
||||
void playSound(SoundId id, const Map::Pos3& loc, int32_t volume, int32_t frequency)
|
||||
{
|
||||
playSound(id, loc, volume, play_at_location, frequency);
|
||||
}
|
||||
|
||||
// 0x00489CB5
|
||||
void playSound(sound_id id, loc16 loc, int32_t pan)
|
||||
void playSound(SoundId id, const Map::Pos3& loc, int32_t pan)
|
||||
{
|
||||
playSound(id, loc, 0, pan, 0);
|
||||
}
|
||||
|
||||
// 0x00489CB5 / 0x00489F1B
|
||||
// pan is in UI pixels or known constant
|
||||
void playSound(sound_id id, loc16 loc, int32_t volume, int32_t pan, int32_t frequency)
|
||||
void playSound(SoundId id, const Map::Pos3& loc, int32_t volume, int32_t pan, int32_t frequency)
|
||||
{
|
||||
loco_global<int32_t, 0x00e3f0b8> current_rotation;
|
||||
|
||||
|
@ -606,15 +612,15 @@ namespace OpenLoco::Audio
|
|||
volume += getVolumeForSoundId(id);
|
||||
if (pan == play_at_location)
|
||||
{
|
||||
auto vpos = viewport::mapFrom3d(loc, current_rotation);
|
||||
auto vpos = Map::gameToScreen(loc, current_rotation);
|
||||
auto viewport = findBestViewportForSound(vpos);
|
||||
if (viewport == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
volume += calculateVolumeFromViewport(id, { loc.x, loc.y }, *viewport);
|
||||
pan = viewport->mapToUi(vpos).x;
|
||||
volume += calculateVolumeFromViewport(id, loc, *viewport);
|
||||
pan = viewport->viewportToScreen(vpos).x;
|
||||
if (volume < -10000)
|
||||
{
|
||||
return;
|
||||
|
@ -640,7 +646,7 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
}
|
||||
|
||||
sample* getSoundSample(sound_id id)
|
||||
Sample* getSoundSample(SoundId id)
|
||||
{
|
||||
if (isObjectSoundId(id))
|
||||
{
|
||||
|
@ -651,7 +657,7 @@ namespace OpenLoco::Audio
|
|||
auto obj = getSoundObject(id);
|
||||
if (obj != nullptr)
|
||||
{
|
||||
auto data = (sound_object_data*)obj->data;
|
||||
auto data = (SoundObjectData*)obj->data;
|
||||
assert(data->offset == 8);
|
||||
auto sample = loadSoundFromWaveMemory(data->pcm_header, data->pcm(), data->length);
|
||||
_object_samples[static_cast<size_t>(id)] = sample;
|
||||
|
@ -670,7 +676,7 @@ namespace OpenLoco::Audio
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static void mixSound(sound_id id, bool loop, int32_t volume, int32_t pan, int32_t freq)
|
||||
static void mixSound(SoundId id, bool loop, int32_t volume, int32_t pan, int32_t freq)
|
||||
{
|
||||
Console::logVerbose("mixSound(%d, %s, %d, %d, %d)", (int32_t)id, loop ? "true" : "false", volume, pan, freq);
|
||||
auto sample = getSoundSample(id);
|
||||
|
@ -691,7 +697,7 @@ namespace OpenLoco::Audio
|
|||
{
|
||||
}
|
||||
|
||||
static bool loadChannel(channel_id id, const fs::path& path, int32_t c)
|
||||
static bool loadChannel(ChannelId id, const fs::path& path, int32_t c)
|
||||
{
|
||||
Console::logVerbose("loadChannel(%d, %s, %d)", id, path.string().c_str(), c);
|
||||
if (isMusicChannel(id))
|
||||
|
@ -715,13 +721,13 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
|
||||
// 0x0040194E
|
||||
bool loadChannel(channel_id id, const char* path, int32_t c)
|
||||
bool loadChannel(ChannelId id, const char* path, int32_t c)
|
||||
{
|
||||
return loadChannel(id, fs::path(path), c);
|
||||
return loadChannel(id, fs::u8path(path), c);
|
||||
}
|
||||
|
||||
// 0x00401999
|
||||
bool playChannel(channel_id id, int32_t loop, int32_t volume, int32_t d, int32_t freq)
|
||||
bool playChannel(ChannelId id, int32_t loop, int32_t volume, int32_t d, int32_t freq)
|
||||
{
|
||||
Console::logVerbose("playChannel(%d, %d, %d, %d, %d)", id, loop, volume, d, freq);
|
||||
if (isMusicChannel(id))
|
||||
|
@ -745,7 +751,7 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
|
||||
// 0x00401A05
|
||||
void stopChannel(channel_id id)
|
||||
void stopChannel(ChannelId id)
|
||||
{
|
||||
Console::logVerbose("stopChannel(%d)", id);
|
||||
if (isMusicChannel(id))
|
||||
|
@ -766,7 +772,7 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
|
||||
// 0x00401AD3
|
||||
void setChannelVolume(channel_id id, int32_t volume)
|
||||
void setChannelVolume(ChannelId id, int32_t volume)
|
||||
{
|
||||
Console::logVerbose("setChannelVolume(%d, %d)", id, volume);
|
||||
if (isMusicChannel(id))
|
||||
|
@ -787,7 +793,7 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
|
||||
// 0x00401B10
|
||||
bool isChannelPlaying(channel_id id)
|
||||
bool isChannelPlaying(ChannelId id)
|
||||
{
|
||||
if (isMusicChannel(id))
|
||||
{
|
||||
|
@ -804,12 +810,12 @@ namespace OpenLoco::Audio
|
|||
return false;
|
||||
}
|
||||
|
||||
static void sub_48A274(vehicle_26* v)
|
||||
static void sub_48A274(Vehicles::Vehicle2or6* v)
|
||||
{
|
||||
if (v == nullptr)
|
||||
return;
|
||||
|
||||
if (v->sound_id == SoundObjectId::null)
|
||||
if (v->drivingSoundId == SoundObjectId::null)
|
||||
return;
|
||||
|
||||
// TODO: left or top?
|
||||
|
@ -873,7 +879,7 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
}
|
||||
|
||||
static void off_4FEB58(vehicle_26* v, int32_t x)
|
||||
static void off_4FEB58(Vehicles::Vehicle2or6* v, int32_t x)
|
||||
{
|
||||
switch (x)
|
||||
{
|
||||
|
@ -905,11 +911,11 @@ namespace OpenLoco::Audio
|
|||
_numActiveVehicleSounds = 0;
|
||||
}
|
||||
|
||||
for (auto v : ThingManager::VehicleList())
|
||||
for (auto v : EntityManager::VehicleList())
|
||||
{
|
||||
Things::Vehicle::Vehicle train(v);
|
||||
off_4FEB58(reinterpret_cast<vehicle_26*>(train.veh2), x);
|
||||
off_4FEB58(reinterpret_cast<vehicle_26*>(train.tail), x);
|
||||
Vehicles::Vehicle train(v);
|
||||
off_4FEB58(reinterpret_cast<Vehicles::Vehicle2or6*>(train.veh2), x);
|
||||
off_4FEB58(reinterpret_cast<Vehicles::Vehicle2or6*>(train.tail), x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -956,7 +962,7 @@ namespace OpenLoco::Audio
|
|||
loco_global<uint32_t, 0x0050D5AC> _50D5AC;
|
||||
if (_audio_initialised && _50D5AC != 1)
|
||||
{
|
||||
stopChannel(channel_id::ambient);
|
||||
stopChannel(ChannelId::ambient);
|
||||
_50D5AC = 1;
|
||||
}
|
||||
}
|
||||
|
@ -964,7 +970,7 @@ namespace OpenLoco::Audio
|
|||
// 0x0048AA0C
|
||||
void revalidateCurrentTrack()
|
||||
{
|
||||
using music_playlist_type = Config::music_playlist_type;
|
||||
using MusicPlaylistType = Config::MusicPlaylistType;
|
||||
auto cfg = Config::get();
|
||||
|
||||
if (_currentSong == no_song)
|
||||
|
@ -973,19 +979,19 @@ namespace OpenLoco::Audio
|
|||
bool trackStillApplies = true;
|
||||
switch (cfg.music_playlist)
|
||||
{
|
||||
case music_playlist_type::current_era:
|
||||
case MusicPlaylistType::currentEra:
|
||||
{
|
||||
auto currentYear = getCurrentYear();
|
||||
auto info = MusicInfo[_currentSong];
|
||||
auto info = _musicInfo[_currentSong];
|
||||
if (currentYear < info.start_year || currentYear > info.end_year)
|
||||
trackStillApplies = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case music_playlist_type::all:
|
||||
case MusicPlaylistType::all:
|
||||
return;
|
||||
|
||||
case music_playlist_type::custom:
|
||||
case MusicPlaylistType::custom:
|
||||
if (!cfg.enabled_music[_currentSong])
|
||||
trackStillApplies = false;
|
||||
break;
|
||||
|
@ -1001,7 +1007,7 @@ namespace OpenLoco::Audio
|
|||
|
||||
static int32_t chooseNextMusicTrack(int32_t excludeTrack)
|
||||
{
|
||||
using music_playlist_type = Config::music_playlist_type;
|
||||
using MusicPlaylistType = Config::MusicPlaylistType;
|
||||
|
||||
static std::vector<uint8_t> playlist;
|
||||
playlist.clear();
|
||||
|
@ -1009,12 +1015,12 @@ namespace OpenLoco::Audio
|
|||
auto cfg = Config::get();
|
||||
switch (cfg.music_playlist)
|
||||
{
|
||||
case music_playlist_type::current_era:
|
||||
case MusicPlaylistType::currentEra:
|
||||
{
|
||||
auto currentYear = getCurrentYear();
|
||||
for (auto i = 0; i < num_music_tracks; i++)
|
||||
{
|
||||
const auto& mi = MusicInfo[i];
|
||||
const auto& mi = _musicInfo[i];
|
||||
if (currentYear >= mi.start_year && currentYear <= mi.end_year)
|
||||
{
|
||||
if (i != excludeTrack)
|
||||
|
@ -1025,7 +1031,7 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
break;
|
||||
}
|
||||
case music_playlist_type::all:
|
||||
case MusicPlaylistType::all:
|
||||
for (auto i = 0; i < num_music_tracks; i++)
|
||||
{
|
||||
if (i != excludeTrack)
|
||||
|
@ -1034,7 +1040,7 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
}
|
||||
break;
|
||||
case music_playlist_type::custom:
|
||||
case MusicPlaylistType::custom:
|
||||
for (auto i = 0; i < num_music_tracks; i++)
|
||||
{
|
||||
if (i != excludeTrack && (cfg.enabled_music[i] & 1))
|
||||
|
@ -1059,7 +1065,7 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
else
|
||||
{
|
||||
const auto& mi = MusicInfo[excludeTrack];
|
||||
const auto& mi = _musicInfo[excludeTrack];
|
||||
auto currentYear = getCurrentYear();
|
||||
if (currentYear >= mi.start_year && currentYear <= mi.end_year)
|
||||
{
|
||||
|
@ -1089,14 +1095,28 @@ namespace OpenLoco::Audio
|
|||
|
||||
if (!_music_channel.isPlaying())
|
||||
{
|
||||
_currentSong = chooseNextMusicTrack(_lastSong);
|
||||
_lastSong = _currentSong;
|
||||
// Not playing, but the 'current song' is last song? It's been requested manually!
|
||||
bool requestedSong = _lastSong != no_song && _lastSong == _currentSong;
|
||||
|
||||
const auto& mi = MusicInfo[_currentSong];
|
||||
// Choose a track to play, unless we have requested one track in particular.
|
||||
if (_currentSong == no_song || !requestedSong)
|
||||
{
|
||||
auto trackToExclude = _lastSong;
|
||||
_lastSong = _currentSong;
|
||||
_currentSong = chooseNextMusicTrack(trackToExclude);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're choosing this one, but the next one should be decided automatically again.
|
||||
_lastSong = no_song;
|
||||
}
|
||||
|
||||
// Load info on the song to play.
|
||||
const auto& mi = _musicInfo[_currentSong];
|
||||
auto path = Environment::getPath((path_id)mi.path_id);
|
||||
if (_music_channel.load(path))
|
||||
{
|
||||
_music_current_channel = channel_id::bgm;
|
||||
_music_current_channel = ChannelId::bgm;
|
||||
if (!_music_channel.play(false))
|
||||
{
|
||||
cfg.music_playing = 0;
|
||||
|
@ -1111,6 +1131,14 @@ namespace OpenLoco::Audio
|
|||
}
|
||||
}
|
||||
|
||||
// 0x0048AAD2
|
||||
void resetMusic()
|
||||
{
|
||||
stopBackgroundMusic();
|
||||
_currentSong = no_song;
|
||||
_lastSong = no_song;
|
||||
}
|
||||
|
||||
// 0x0048AAE8
|
||||
void stopBackgroundMusic()
|
||||
{
|
||||
|
@ -1125,20 +1153,20 @@ namespace OpenLoco::Audio
|
|||
{
|
||||
if (isTitleMode() && _audio_initialised && _audioIsEnabled && Config::getNew().audio.play_title_music)
|
||||
{
|
||||
if (!isChannelPlaying(channel_id::title))
|
||||
if (!isChannelPlaying(ChannelId::title))
|
||||
{
|
||||
auto path = Environment::getPath(path_id::css5);
|
||||
if (loadChannel(channel_id::title, path, 0))
|
||||
if (loadChannel(ChannelId::title, path, 0))
|
||||
{
|
||||
playChannel(channel_id::title, 1, -500, 0, 0);
|
||||
playChannel(ChannelId::title, 1, -500, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isChannelPlaying(channel_id::title))
|
||||
if (isChannelPlaying(ChannelId::title))
|
||||
{
|
||||
stopChannel(channel_id::title);
|
||||
stopChannel(ChannelId::title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1146,9 +1174,9 @@ namespace OpenLoco::Audio
|
|||
// 0x0048AC2B
|
||||
void stopTitleMusic()
|
||||
{
|
||||
if (isChannelPlaying(channel_id::title))
|
||||
if (isChannelPlaying(ChannelId::title))
|
||||
{
|
||||
stopChannel(channel_id::title);
|
||||
stopChannel(ChannelId::title);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1157,8 +1185,26 @@ namespace OpenLoco::Audio
|
|||
return _audioIsEnabled;
|
||||
}
|
||||
|
||||
const music_info* getMusicInfo(music_id track)
|
||||
const MusicInfo* getMusicInfo(MusicId track)
|
||||
{
|
||||
return &MusicInfo[track];
|
||||
return &_musicInfo[track];
|
||||
}
|
||||
|
||||
// 0x0048AA67
|
||||
void setBgmVolume(int32_t volume)
|
||||
{
|
||||
if (Config::get().volume == volume)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto& cfg = Config::get();
|
||||
cfg.volume = volume;
|
||||
Config::write();
|
||||
|
||||
if (_audio_initialised && _currentSong != no_song && isChannelPlaying(ChannelId::bgm))
|
||||
{
|
||||
setChannelVolume(ChannelId::bgm, volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Environment.h"
|
||||
#include "../Location.hpp"
|
||||
#include "../Map/Map.hpp"
|
||||
#include "../Types.hpp"
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
@ -8,14 +10,14 @@
|
|||
|
||||
struct Mix_Chunk;
|
||||
|
||||
namespace OpenLoco
|
||||
namespace OpenLoco::Vehicles
|
||||
{
|
||||
struct vehicle_26;
|
||||
struct Vehicle2or6;
|
||||
}
|
||||
|
||||
namespace OpenLoco::Audio
|
||||
{
|
||||
struct sample
|
||||
struct Sample
|
||||
{
|
||||
void* pcm{};
|
||||
size_t len{};
|
||||
|
@ -23,45 +25,45 @@ namespace OpenLoco::Audio
|
|||
};
|
||||
|
||||
// TODO: This should only be a byte needs to be split off from sound object
|
||||
enum class sound_id : uint16_t
|
||||
enum class SoundId : uint16_t
|
||||
{
|
||||
click_down = 0,
|
||||
click_up = 1,
|
||||
click_press = 2,
|
||||
clickDown = 0,
|
||||
clickUp = 1,
|
||||
clickPress = 2,
|
||||
construct = 3,
|
||||
demolish = 4,
|
||||
income = 5,
|
||||
crash = 6,
|
||||
water = 7,
|
||||
splash_1 = 8,
|
||||
splash_2 = 9,
|
||||
splash1 = 8,
|
||||
splash2 = 9,
|
||||
waypoint = 10,
|
||||
notification = 11,
|
||||
open_window = 12,
|
||||
openWindow = 12,
|
||||
applause_1 = 13,
|
||||
error = 14,
|
||||
unk_15 = 15,
|
||||
unk_16 = 16,
|
||||
demolish_tree = 17,
|
||||
demolish_building = 18,
|
||||
demolishTree = 17,
|
||||
demolishBuilding = 18,
|
||||
unk_19 = 19,
|
||||
unk_20 = 20,
|
||||
construct_ship = 21,
|
||||
vehiclePickup = 20,
|
||||
constructShip = 21,
|
||||
ticker = 22,
|
||||
applause_2 = 23,
|
||||
news_oooh = 24,
|
||||
news_awww = 25,
|
||||
breakdown_1 = 26,
|
||||
breakdown_2 = 27,
|
||||
breakdown_3 = 28,
|
||||
breakdown_4 = 29,
|
||||
breakdown_5 = 30,
|
||||
breakdown_6 = 31,
|
||||
applause2 = 23,
|
||||
newsOooh = 24,
|
||||
newsAwww = 25,
|
||||
breakdown1 = 26,
|
||||
breakdown2 = 27,
|
||||
breakdown3 = 28,
|
||||
breakdown4 = 29,
|
||||
breakdown5 = 30,
|
||||
breakdown6 = 31,
|
||||
|
||||
null = 0xFF
|
||||
};
|
||||
|
||||
enum class channel_id
|
||||
enum class ChannelId
|
||||
{
|
||||
bgm,
|
||||
unk_1,
|
||||
|
@ -71,9 +73,9 @@ namespace OpenLoco::Audio
|
|||
};
|
||||
constexpr int32_t num_reserved_channels = 4 + 10;
|
||||
|
||||
using music_id = uint8_t;
|
||||
using MusicId = uint8_t;
|
||||
|
||||
struct music_info
|
||||
struct MusicInfo
|
||||
{
|
||||
Environment::path_id path_id;
|
||||
string_id title_id;
|
||||
|
@ -83,30 +85,32 @@ namespace OpenLoco::Audio
|
|||
|
||||
void initialiseDSound();
|
||||
void disposeDSound();
|
||||
void close();
|
||||
|
||||
const std::vector<std::string>& getDevices();
|
||||
const char* getCurrentDeviceName();
|
||||
size_t getCurrentDevice();
|
||||
void setDevice(size_t index);
|
||||
|
||||
sample* getSoundSample(sound_id id);
|
||||
bool shouldSoundLoop(sound_id id);
|
||||
Sample* getSoundSample(SoundId id);
|
||||
bool shouldSoundLoop(SoundId id);
|
||||
|
||||
void toggleSound();
|
||||
void pauseSound();
|
||||
void unpauseSound();
|
||||
void playSound(vehicle_26* t);
|
||||
void playSound(sound_id id, loc16 loc);
|
||||
void playSound(sound_id id, loc16 loc, int32_t pan);
|
||||
void playSound(sound_id id, int32_t pan);
|
||||
void playSound(sound_id id, loc16 loc, int32_t volume, int32_t frequency);
|
||||
void playSound(Vehicles::Vehicle2or6* t);
|
||||
void playSound(SoundId id, const Map::Pos3& loc);
|
||||
void playSound(SoundId id, const Map::Pos3& loc, int32_t pan);
|
||||
void playSound(SoundId id, int32_t pan);
|
||||
void playSound(SoundId id, const Map::Pos3& loc, int32_t volume, int32_t frequency);
|
||||
void updateSounds();
|
||||
|
||||
bool loadChannel(channel_id id, const char* path, int32_t c);
|
||||
bool playChannel(channel_id id, int32_t loop, int32_t volume, int32_t d, int32_t freq);
|
||||
void stopChannel(channel_id id);
|
||||
void setChannelVolume(channel_id id, int32_t volume);
|
||||
bool isChannelPlaying(channel_id id);
|
||||
bool loadChannel(ChannelId id, const char* path, int32_t c);
|
||||
bool playChannel(ChannelId id, int32_t loop, int32_t volume, int32_t d, int32_t freq);
|
||||
void stopChannel(ChannelId id);
|
||||
void setChannelVolume(ChannelId id, int32_t volume);
|
||||
bool isChannelPlaying(ChannelId id);
|
||||
void setBgmVolume(int32_t volume);
|
||||
|
||||
void updateVehicleNoise();
|
||||
void stopVehicleNoise();
|
||||
|
@ -116,6 +120,7 @@ namespace OpenLoco::Audio
|
|||
|
||||
void revalidateCurrentTrack();
|
||||
|
||||
void resetMusic();
|
||||
void playBackgroundMusic();
|
||||
void stopBackgroundMusic();
|
||||
void playTitleScreenMusic();
|
||||
|
@ -123,7 +128,7 @@ namespace OpenLoco::Audio
|
|||
|
||||
bool isAudioEnabled();
|
||||
|
||||
const music_info* getMusicInfo(music_id track);
|
||||
const MusicInfo* getMusicInfo(MusicId track);
|
||||
constexpr int32_t num_music_tracks = 29;
|
||||
|
||||
/**
|
||||
|
@ -133,35 +138,35 @@ namespace OpenLoco::Audio
|
|||
*/
|
||||
int32_t volumeLocoToSDL(int32_t loco);
|
||||
|
||||
constexpr bool isObjectSoundId(sound_id id)
|
||||
constexpr bool isObjectSoundId(SoundId id)
|
||||
{
|
||||
return ((int32_t)id & 0x8000);
|
||||
}
|
||||
|
||||
constexpr sound_id makeObjectSoundId(sound_object_id_t id)
|
||||
constexpr SoundId makeObjectSoundId(SoundObjectId_t id)
|
||||
{
|
||||
return (sound_id)((int32_t)id | 0x8000);
|
||||
return (SoundId)((int32_t)id | 0x8000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Locomotion pan range to a left and right value for SDL2 mixer.
|
||||
*/
|
||||
constexpr std::tuple<int32_t, int32_t> panLocoToSDL(int32_t pan)
|
||||
constexpr std::pair<int32_t, int32_t> panLocoToSDL(int32_t pan)
|
||||
{
|
||||
constexpr auto range = 2048.0f;
|
||||
if (pan == 0)
|
||||
{
|
||||
return std::make_tuple(0, 0);
|
||||
return { 0, 0 };
|
||||
}
|
||||
else if (pan < 0)
|
||||
{
|
||||
auto r = (int32_t)(255 - ((pan / -range) * 255));
|
||||
return std::make_tuple(255, r);
|
||||
return { 255, r };
|
||||
}
|
||||
else
|
||||
{
|
||||
auto r = (int32_t)(255 - ((pan / range) * 255));
|
||||
return std::make_tuple(r, 255);
|
||||
return { r, 255 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,19 +6,19 @@
|
|||
|
||||
using namespace OpenLoco::Audio;
|
||||
|
||||
channel::channel(int32_t cid)
|
||||
Channel::Channel(int32_t cid)
|
||||
: _id(cid)
|
||||
{
|
||||
}
|
||||
|
||||
channel::channel(channel&& c)
|
||||
Channel::Channel(Channel&& c)
|
||||
: _id(std::exchange(c._id, undefined_id))
|
||||
, _chunk(std::exchange(c._chunk, nullptr))
|
||||
, _chunk_owner(std::exchange(c._chunk_owner, {}))
|
||||
{
|
||||
}
|
||||
|
||||
channel& channel::operator=(channel&& other)
|
||||
Channel& Channel::operator=(Channel&& other)
|
||||
{
|
||||
std::swap(_id, other._id);
|
||||
std::swap(_chunk, other._chunk);
|
||||
|
@ -26,19 +26,19 @@ channel& channel::operator=(channel&& other)
|
|||
return *this;
|
||||
}
|
||||
|
||||
channel::~channel()
|
||||
Channel::~Channel()
|
||||
{
|
||||
disposeChunk();
|
||||
}
|
||||
|
||||
bool channel::load(sample& sample)
|
||||
bool Channel::load(Sample& sample)
|
||||
{
|
||||
disposeChunk();
|
||||
_chunk = sample.chunk;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool channel::load(const fs::path& path)
|
||||
bool Channel::load(const fs::path& path)
|
||||
{
|
||||
disposeChunk();
|
||||
_chunk = Mix_LoadWAV(path.u8string().c_str());
|
||||
|
@ -46,7 +46,7 @@ bool channel::load(const fs::path& path)
|
|||
return _chunk != nullptr;
|
||||
}
|
||||
|
||||
bool channel::play(bool loop)
|
||||
bool Channel::play(bool loop)
|
||||
{
|
||||
if (!isUndefined())
|
||||
{
|
||||
|
@ -61,7 +61,7 @@ bool channel::play(bool loop)
|
|||
return false;
|
||||
}
|
||||
|
||||
void channel::stop()
|
||||
void Channel::stop()
|
||||
{
|
||||
if (!isUndefined())
|
||||
{
|
||||
|
@ -69,7 +69,7 @@ void channel::stop()
|
|||
}
|
||||
}
|
||||
|
||||
void channel::setVolume(int32_t volume)
|
||||
void Channel::setVolume(int32_t volume)
|
||||
{
|
||||
if (!isUndefined())
|
||||
{
|
||||
|
@ -77,7 +77,7 @@ void channel::setVolume(int32_t volume)
|
|||
}
|
||||
}
|
||||
|
||||
void channel::setPan(int32_t pan)
|
||||
void Channel::setPan(int32_t pan)
|
||||
{
|
||||
if (!isUndefined())
|
||||
{
|
||||
|
@ -86,17 +86,17 @@ void channel::setPan(int32_t pan)
|
|||
}
|
||||
}
|
||||
|
||||
void channel::setFrequency(int32_t freq)
|
||||
void Channel::setFrequency(int32_t freq)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
bool channel::isPlaying() const
|
||||
bool Channel::isPlaying() const
|
||||
{
|
||||
return isUndefined() ? false : (Mix_Playing(_id) != 0);
|
||||
}
|
||||
|
||||
void channel::disposeChunk()
|
||||
void Channel::disposeChunk()
|
||||
{
|
||||
if (_chunk_owner)
|
||||
{
|
||||
|
|
|
@ -6,9 +6,9 @@ struct Mix_Chunk;
|
|||
|
||||
namespace OpenLoco::Audio
|
||||
{
|
||||
struct sample;
|
||||
struct Sample;
|
||||
|
||||
class channel
|
||||
class Channel
|
||||
{
|
||||
public:
|
||||
static constexpr int32_t undefined_id = -1;
|
||||
|
@ -19,13 +19,13 @@ namespace OpenLoco::Audio
|
|||
bool _chunk_owner{};
|
||||
|
||||
public:
|
||||
channel() = default;
|
||||
channel(int32_t id);
|
||||
channel(const channel&) = delete;
|
||||
channel(channel&&);
|
||||
channel& operator=(channel&& other);
|
||||
~channel();
|
||||
bool load(sample& sample);
|
||||
Channel() = default;
|
||||
Channel(int32_t id);
|
||||
Channel(const Channel&) = delete;
|
||||
Channel(Channel&&);
|
||||
Channel& operator=(Channel&& other);
|
||||
~Channel();
|
||||
bool load(Sample& sample);
|
||||
bool load(const fs::path& path);
|
||||
bool play(bool loop);
|
||||
void stop();
|
||||
|
|
|
@ -3,17 +3,17 @@
|
|||
|
||||
using namespace OpenLoco::Audio;
|
||||
|
||||
music_channel::~music_channel()
|
||||
MusicChannel::~MusicChannel()
|
||||
{
|
||||
disposeMusic();
|
||||
}
|
||||
|
||||
bool music_channel::isPlaying() const
|
||||
bool MusicChannel::isPlaying() const
|
||||
{
|
||||
return Mix_PlayingMusic() != 0;
|
||||
}
|
||||
|
||||
bool music_channel::load(const fs::path& path)
|
||||
bool MusicChannel::load(const fs::path& path)
|
||||
{
|
||||
auto paths = path.u8string();
|
||||
auto music = Mix_LoadMUS(paths.c_str());
|
||||
|
@ -26,7 +26,7 @@ bool music_channel::load(const fs::path& path)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool music_channel::play(bool loop)
|
||||
bool MusicChannel::play(bool loop)
|
||||
{
|
||||
if (_music_track != nullptr)
|
||||
{
|
||||
|
@ -39,17 +39,17 @@ bool music_channel::play(bool loop)
|
|||
return false;
|
||||
}
|
||||
|
||||
void music_channel::stop()
|
||||
void MusicChannel::stop()
|
||||
{
|
||||
Mix_HaltMusic();
|
||||
}
|
||||
|
||||
void music_channel::setVolume(int32_t volume)
|
||||
void MusicChannel::setVolume(int32_t volume)
|
||||
{
|
||||
Mix_VolumeMusic(volumeLocoToSDL(volume));
|
||||
}
|
||||
|
||||
void music_channel::disposeMusic()
|
||||
void MusicChannel::disposeMusic()
|
||||
{
|
||||
Mix_FreeMusic(_music_track);
|
||||
_music_track = nullptr;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Core/FileSystem.hpp"
|
||||
#include "../Things/Thing.h"
|
||||
#include "../Entities/Entity.h"
|
||||
#include "Audio.h"
|
||||
#include "Channel.h"
|
||||
|
||||
|
@ -10,16 +10,16 @@ typedef struct _Mix_Music Mix_Music;
|
|||
|
||||
namespace OpenLoco::Audio
|
||||
{
|
||||
class music_channel
|
||||
class MusicChannel
|
||||
{
|
||||
private:
|
||||
Mix_Music* _music_track;
|
||||
int32_t _current_music = -1;
|
||||
|
||||
public:
|
||||
music_channel() = default;
|
||||
music_channel(const music_channel&) = delete;
|
||||
~music_channel();
|
||||
MusicChannel() = default;
|
||||
MusicChannel(const MusicChannel&) = delete;
|
||||
~MusicChannel();
|
||||
|
||||
bool isPlaying() const;
|
||||
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
#include "VehicleChannel.h"
|
||||
#include "../Entities/EntityManager.h"
|
||||
#include "../Interop/Interop.hpp"
|
||||
#include "../Things/ThingManager.h"
|
||||
#include "../Things/Vehicle.h"
|
||||
#include "../Vehicles/Vehicle.h"
|
||||
|
||||
using namespace OpenLoco;
|
||||
using namespace OpenLoco::Audio;
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
static std::tuple<sound_id, channel_attributes> sub_48A590(const vehicle* v)
|
||||
static std::pair<SoundId, ChannelAttributes> sub_48A590(const Vehicles::Vehicle2or6* v)
|
||||
{
|
||||
registers regs;
|
||||
regs.esi = (int32_t)v;
|
||||
regs.esi = X86Pointer(v);
|
||||
call(0x0048A590, regs);
|
||||
return std::make_tuple<sound_id, channel_attributes>((sound_id)regs.eax, { regs.ecx, regs.edx, regs.ebx });
|
||||
return { static_cast<SoundId>(regs.eax), { regs.ecx, regs.edx, regs.ebx } };
|
||||
}
|
||||
|
||||
vehicle_channel::vehicle_channel(channel&& c)
|
||||
VehicleChannel::VehicleChannel(Channel&& c)
|
||||
: _channel(std::exchange(c, {}))
|
||||
{
|
||||
}
|
||||
|
||||
vehicle_channel::vehicle_channel(vehicle_channel&& vc)
|
||||
VehicleChannel::VehicleChannel(VehicleChannel&& vc)
|
||||
: _channel(std::exchange(vc._channel, {}))
|
||||
, _vehicle_id(std::exchange(vc._vehicle_id, ThingId::null))
|
||||
, _vehicle_id(std::exchange(vc._vehicle_id, EntityId::null))
|
||||
, _sound_id(std::exchange(vc._sound_id, {}))
|
||||
, _attributes(std::exchange(vc._attributes, {}))
|
||||
{
|
||||
}
|
||||
|
||||
vehicle_channel& vehicle_channel::operator=(vehicle_channel&& other)
|
||||
VehicleChannel& VehicleChannel::operator=(VehicleChannel&& other)
|
||||
{
|
||||
std::swap(_channel, other._channel);
|
||||
std::swap(_vehicle_id, other._vehicle_id);
|
||||
|
@ -37,62 +37,70 @@ vehicle_channel& vehicle_channel::operator=(vehicle_channel&& other)
|
|||
return *this;
|
||||
}
|
||||
|
||||
void vehicle_channel::begin(thing_id_t vid)
|
||||
void VehicleChannel::begin(EntityId_t vid)
|
||||
{
|
||||
auto v = ThingManager::get<vehicle>(vid);
|
||||
if (v != nullptr)
|
||||
auto v = EntityManager::get<Vehicles::VehicleBase>(vid);
|
||||
if (v != nullptr && v->isVehicle2Or6())
|
||||
{
|
||||
auto [sid, sa] = sub_48A590(v);
|
||||
auto loop = Audio::shouldSoundLoop(sid);
|
||||
auto sample = Audio::getSoundSample(sid);
|
||||
if (sample != nullptr)
|
||||
auto* veh26 = v->asVehicle2Or6();
|
||||
if (veh26 != nullptr)
|
||||
{
|
||||
_vehicle_id = vid;
|
||||
_sound_id = sid;
|
||||
_attributes = sa;
|
||||
auto [sid, sa] = sub_48A590(veh26);
|
||||
auto loop = Audio::shouldSoundLoop(sid);
|
||||
auto sample = Audio::getSoundSample(sid);
|
||||
if (sample != nullptr)
|
||||
{
|
||||
_vehicle_id = vid;
|
||||
_sound_id = sid;
|
||||
_attributes = sa;
|
||||
|
||||
_channel.load(*sample);
|
||||
_channel.play(loop);
|
||||
_channel.setVolume(sa.volume);
|
||||
_channel.setPan(sa.pan);
|
||||
_channel.setFrequency(sa.freq);
|
||||
_channel.load(*sample);
|
||||
_channel.play(loop);
|
||||
_channel.setVolume(sa.volume);
|
||||
_channel.setPan(sa.pan);
|
||||
_channel.setFrequency(sa.freq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vehicle_channel::update()
|
||||
void VehicleChannel::update()
|
||||
{
|
||||
if (!isFree())
|
||||
{
|
||||
auto v = ThingManager::get<vehicle>(_vehicle_id);
|
||||
if (v != nullptr && v->base_type == thing_base_type::vehicle && (v->type == VehicleThingType::vehicle_2 || v->type == VehicleThingType::tail) && (v->var_4A & 1))
|
||||
auto v = EntityManager::get<Vehicles::VehicleBase>(_vehicle_id);
|
||||
if (v != nullptr && v->isVehicle2Or6())
|
||||
{
|
||||
auto [sid, sa] = sub_48A590(v);
|
||||
if (_sound_id == sid)
|
||||
auto* veh26 = v->asVehicle2Or6();
|
||||
if (veh26 != nullptr && (veh26->var_4A & 1))
|
||||
{
|
||||
v->var_4A &= ~1;
|
||||
if (_attributes.volume != sa.volume)
|
||||
auto [sid, sa] = sub_48A590(veh26);
|
||||
if (_sound_id == sid)
|
||||
{
|
||||
_channel.setVolume(sa.volume);
|
||||
veh26->var_4A &= ~1;
|
||||
if (_attributes.volume != sa.volume)
|
||||
{
|
||||
_channel.setVolume(sa.volume);
|
||||
}
|
||||
if (_attributes.pan != sa.pan)
|
||||
{
|
||||
_channel.setPan(sa.pan);
|
||||
}
|
||||
if (_attributes.freq != sa.freq)
|
||||
{
|
||||
_channel.setFrequency(sa.freq);
|
||||
}
|
||||
_attributes = sa;
|
||||
return;
|
||||
}
|
||||
if (_attributes.pan != sa.pan)
|
||||
{
|
||||
_channel.setPan(sa.pan);
|
||||
}
|
||||
if (_attributes.freq != sa.freq)
|
||||
{
|
||||
_channel.setFrequency(sa.freq);
|
||||
}
|
||||
_attributes = sa;
|
||||
return;
|
||||
}
|
||||
}
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void vehicle_channel::stop()
|
||||
void VehicleChannel::stop()
|
||||
{
|
||||
_channel.stop();
|
||||
_vehicle_id = ThingId::null;
|
||||
_vehicle_id = EntityId::null;
|
||||
}
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Things/Thing.h"
|
||||
#include "../Entities/Entity.h"
|
||||
#include "Audio.h"
|
||||
#include "Channel.h"
|
||||
|
||||
namespace OpenLoco::Audio
|
||||
{
|
||||
struct channel_attributes
|
||||
struct ChannelAttributes
|
||||
{
|
||||
int32_t volume{};
|
||||
int32_t pan{};
|
||||
int32_t freq{};
|
||||
};
|
||||
|
||||
class vehicle_channel
|
||||
class VehicleChannel
|
||||
{
|
||||
private:
|
||||
channel _channel;
|
||||
thing_id_t _vehicle_id = ThingId::null;
|
||||
sound_id _sound_id{};
|
||||
channel_attributes _attributes;
|
||||
Channel _channel;
|
||||
EntityId_t _vehicle_id = EntityId::null;
|
||||
SoundId _sound_id{};
|
||||
ChannelAttributes _attributes;
|
||||
|
||||
public:
|
||||
vehicle_channel() = default;
|
||||
vehicle_channel(const vehicle_channel& c) = delete;
|
||||
explicit vehicle_channel(channel&& c);
|
||||
vehicle_channel(vehicle_channel&& c);
|
||||
vehicle_channel& operator=(vehicle_channel&& other);
|
||||
VehicleChannel() = default;
|
||||
VehicleChannel(const VehicleChannel& c) = delete;
|
||||
explicit VehicleChannel(Channel&& c);
|
||||
VehicleChannel(VehicleChannel&& c);
|
||||
VehicleChannel& operator=(VehicleChannel&& other);
|
||||
|
||||
bool isFree() const { return _vehicle_id == ThingId::null; }
|
||||
bool isFree() const { return _vehicle_id == EntityId::null; }
|
||||
|
||||
void begin(thing_id_t vid);
|
||||
void begin(EntityId_t vid);
|
||||
void update();
|
||||
void stop();
|
||||
};
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#include "Company.h"
|
||||
#include "Entities/EntityManager.h"
|
||||
#include "Graphics/Gfx.h"
|
||||
#include "Interop/Interop.hpp"
|
||||
#include "Localisation/FormatArguments.hpp"
|
||||
#include "Localisation/StringIds.h"
|
||||
#include "Things/ThingManager.h"
|
||||
#include "Map/TileManager.h"
|
||||
#include "Ui/WindowManager.h"
|
||||
#include "Vehicles/Vehicle.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
@ -12,39 +15,38 @@ using namespace OpenLoco::Interop;
|
|||
|
||||
namespace OpenLoco
|
||||
{
|
||||
static loco_global<company_id_t[2], 0x00525E3C> _player_company[2];
|
||||
static loco_global<CompanyId_t[2], 0x00525E3C> _playerCompanies;
|
||||
|
||||
bool isPlayerCompany(company_id_t id)
|
||||
bool isPlayerCompany(CompanyId_t id)
|
||||
{
|
||||
auto& player_company = *((std::array<company_id_t, 2>*)_player_company->get());
|
||||
auto findResult = std::find(
|
||||
player_company.begin(),
|
||||
player_company.end(),
|
||||
_playerCompanies.begin(),
|
||||
_playerCompanies.end(),
|
||||
id);
|
||||
return findResult != player_company.end();
|
||||
return findResult != _playerCompanies.end();
|
||||
}
|
||||
|
||||
company_id_t company::id() const
|
||||
CompanyId_t Company::id() const
|
||||
{
|
||||
auto first = (company*)0x00531784;
|
||||
return (company_id_t)(this - first);
|
||||
auto first = (Company*)0x00531784;
|
||||
return (CompanyId_t)(this - first);
|
||||
}
|
||||
|
||||
bool company::empty() const
|
||||
bool Company::empty() const
|
||||
{
|
||||
return name == StringIds::empty;
|
||||
}
|
||||
|
||||
// 0x00430762
|
||||
void company::aiThink()
|
||||
void Company::aiThink()
|
||||
{
|
||||
registers regs;
|
||||
regs.esi = (int32_t)this;
|
||||
regs.esi = X86Pointer(this);
|
||||
call(0x00430762, regs);
|
||||
}
|
||||
|
||||
// 0x00437ED0
|
||||
void company::recalculateTransportCounts()
|
||||
void Company::recalculateTransportCounts()
|
||||
{
|
||||
// Reset all counts to 0
|
||||
for (auto& count : transportTypeCount)
|
||||
|
@ -53,7 +55,7 @@ namespace OpenLoco
|
|||
}
|
||||
|
||||
auto companyId = id();
|
||||
for (auto v : ThingManager::VehicleList())
|
||||
for (auto v : EntityManager::VehicleList())
|
||||
{
|
||||
if (v->owner == companyId)
|
||||
{
|
||||
|
@ -104,10 +106,92 @@ namespace OpenLoco
|
|||
args.push(getCorporateRatingAsStringId(performanceToRating(performanceIndex)));
|
||||
}
|
||||
|
||||
bool company::isVehicleIndexUnlocked(const uint8_t vehicleIndex) const
|
||||
bool Company::isVehicleIndexUnlocked(const uint8_t vehicleIndex) const
|
||||
{
|
||||
auto vehicleTypeIndex = vehicleIndex >> 5;
|
||||
|
||||
return (unlocked_vehicles[vehicleTypeIndex] & (1 << (vehicleIndex & 0x1F))) != 0;
|
||||
}
|
||||
|
||||
// 0x00487FCC
|
||||
void Company::updateQuarterly()
|
||||
{
|
||||
for (auto& unk : var_4A8)
|
||||
{
|
||||
if (unk.var_00 == 0xFF)
|
||||
continue;
|
||||
|
||||
unk.var_88 = std::min(0xFF, unk.var_88 + 1);
|
||||
unk.var_84 = unk.var_80;
|
||||
unk.var_80 = 0;
|
||||
currency32_t totalRunCost = 0;
|
||||
for (auto i = 0; i < unk.var_44; ++i)
|
||||
{
|
||||
auto* vehHead = EntityManager::get<Vehicles::VehicleHead>(unk.var_66[i]);
|
||||
totalRunCost += vehHead->calculateRunningCost();
|
||||
}
|
||||
unk.var_7C = totalRunCost;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x004B8ED2
|
||||
void Company::updateVehicleColours()
|
||||
{
|
||||
for (auto v : EntityManager::VehicleList())
|
||||
{
|
||||
if (v->owner != id())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Vehicles::Vehicle train(v);
|
||||
for (auto& car : train.cars)
|
||||
{
|
||||
auto* vehObject = car.body->object();
|
||||
auto colour = mainColours;
|
||||
if (customVehicleColoursSet & (1 << vehObject->colour_type))
|
||||
{
|
||||
colour = vehicleColours[vehObject->colour_type - 1];
|
||||
}
|
||||
for (auto& carComponent : car)
|
||||
{
|
||||
carComponent.front->colour_scheme = colour;
|
||||
carComponent.back->colour_scheme = colour;
|
||||
carComponent.body->colour_scheme = colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
Gfx::invalidateScreen();
|
||||
}
|
||||
|
||||
// 0x0042F0C1
|
||||
static void updateHeadquartersColourAtTile(const Map::TilePos2& pos, uint8_t zPos, Colour_t newColour)
|
||||
{
|
||||
auto tile = Map::TileManager::get(pos);
|
||||
for (auto& element : tile)
|
||||
{
|
||||
if (element.baseZ() != zPos)
|
||||
continue;
|
||||
|
||||
auto building = element.asBuilding();
|
||||
if (building == nullptr)
|
||||
continue;
|
||||
|
||||
building->setColour(newColour);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x0042F07B
|
||||
void Company::updateHeadquartersColour()
|
||||
{
|
||||
if (headquarters_x == -1)
|
||||
return;
|
||||
|
||||
Colour_t colour = mainColours.primary;
|
||||
auto hqPos = Map::TilePos2(Map::Pos2(headquarters_x, headquarters_y));
|
||||
updateHeadquartersColourAtTile(hqPos + Map::TilePos2(0, 0), headquarters_z, colour);
|
||||
updateHeadquartersColourAtTile(hqPos + Map::TilePos2(1, 0), headquarters_z, colour);
|
||||
updateHeadquartersColourAtTile(hqPos + Map::TilePos2(1, 1), headquarters_z, colour);
|
||||
updateHeadquartersColourAtTile(hqPos + Map::TilePos2(0, 1), headquarters_z, colour);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "Localisation/StringManager.h"
|
||||
#include "Management/Expenditures.h"
|
||||
#include "Economy/Currency.h"
|
||||
#include "Economy/Expenditures.h"
|
||||
#include "Types.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
@ -12,20 +12,20 @@ namespace OpenLoco
|
|||
|
||||
namespace CompanyId
|
||||
{
|
||||
constexpr company_id_t neutral = 15;
|
||||
constexpr company_id_t null = std::numeric_limits<company_id_t>::max();
|
||||
constexpr CompanyId_t neutral = 15;
|
||||
constexpr CompanyId_t null = std::numeric_limits<CompanyId_t>::max();
|
||||
}
|
||||
|
||||
enum company_flags
|
||||
namespace CompanyFlags
|
||||
{
|
||||
sorted = (1 << 3), // 0x08
|
||||
increased_performance = (1 << 4), // 0x10
|
||||
decreased_performance = (1 << 5), // 0x20
|
||||
challenge_completed = (1 << 6), // 0x40
|
||||
challenge_failed = (1 << 7), // 0x80
|
||||
challenge_beaten_by_opponent = (1 << 8), // 0x100
|
||||
bankrupt = (1 << 9), // 0x200
|
||||
};
|
||||
constexpr uint32_t sorted = (1 << 3); // 0x08
|
||||
constexpr uint32_t increasedPerformance = (1 << 4); // 0x10
|
||||
constexpr uint32_t decreasedPerformance = (1 << 5); // 0x20
|
||||
constexpr uint32_t challengeCompleted = (1 << 6); // 0x40
|
||||
constexpr uint32_t challengeFailed = (1 << 7); // 0x80
|
||||
constexpr uint32_t challengeBeatenByOpponent = (1 << 8); // 0x100
|
||||
constexpr uint32_t bankrupt = (1 << 9); // 0x200
|
||||
}
|
||||
|
||||
enum class CorporateRating
|
||||
{
|
||||
|
@ -41,19 +41,37 @@ namespace OpenLoco
|
|||
tycoon // 90 - 100%
|
||||
};
|
||||
|
||||
enum ObservationStatus : uint8_t
|
||||
{
|
||||
empty,
|
||||
buildingTrackRoad,
|
||||
buildingAirport,
|
||||
buildingDock,
|
||||
checkingServices,
|
||||
surveyingLandscape,
|
||||
};
|
||||
|
||||
void formatPerformanceIndex(const int16_t performanceIndex, FormatArguments& args);
|
||||
|
||||
constexpr size_t expenditureHistoryCapacity = 16;
|
||||
|
||||
struct ColourScheme
|
||||
{
|
||||
uint8_t primary; // 0x1A
|
||||
uint8_t secondary; // 0x1B
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct company
|
||||
struct Company
|
||||
{
|
||||
struct unk4A8
|
||||
{
|
||||
uint8_t var_00;
|
||||
uint8_t pad_01[0x44 - 0x01];
|
||||
uint8_t var_44; // 0x4EC size of var_66
|
||||
uint8_t pad_45[0x66 - 0x45];
|
||||
EntityId_t var_66[11]; // 0x50E unsure on size
|
||||
currency32_t var_7C; // 0x524
|
||||
uint32_t var_80; // 0x528
|
||||
uint32_t var_84; // 0x52C
|
||||
uint8_t var_88; // 0x530
|
||||
uint8_t pad_89[3];
|
||||
};
|
||||
static_assert(sizeof(unk4A8) == 0x8C);
|
||||
string_id name;
|
||||
string_id owner_name;
|
||||
uint32_t challenge_flags; // 0x04
|
||||
|
@ -66,17 +84,22 @@ namespace OpenLoco
|
|||
ColourScheme mainColours; // 0x1A
|
||||
ColourScheme vehicleColours[10]; // 0x1C
|
||||
uint32_t customVehicleColoursSet; // 0x30
|
||||
uint32_t unlocked_vehicles[7]; // 0x34 (bit field based on vehicle_object index)
|
||||
uint32_t unlocked_vehicles[7]; // 0x34 (bit field based on VehicleObject index)
|
||||
uint16_t available_vehicles; // 0x50
|
||||
uint8_t pad_52[0x57 - 0x52];
|
||||
uint8_t numExpenditureMonths; // 0x57
|
||||
currency32_t expenditures[expenditureHistoryCapacity][ExpenditureType::Count]; // 0x58
|
||||
uint32_t startedDate; // 0x0498
|
||||
uint8_t pad_49C[0x2579 - 0x49C];
|
||||
uint32_t var_49C;
|
||||
uint32_t var_4A0;
|
||||
uint8_t pad_4A4[0x4A8 - 0x4A4];
|
||||
unk4A8 var_4A8[60];
|
||||
uint8_t pad_2578;
|
||||
uint8_t headquarters_z; // 0x2579
|
||||
coord_t headquarters_x; // 0x257A -1 on no headquarter placed
|
||||
coord_t headquarters_y; // 0x257C
|
||||
uint8_t pad_257E[0x85FC - 0x257E];
|
||||
uint8_t pad_257E[0x85F8 - 0x257E];
|
||||
uint32_t cargoUnitsTotalDelivered; // 0x85F8
|
||||
uint32_t cargo_units_delivered_history[120]; // 0x85FC
|
||||
int16_t performance_index_history[120]; // 0x87DC
|
||||
uint16_t history_size; // 0x88CC
|
||||
|
@ -84,34 +107,40 @@ namespace OpenLoco
|
|||
currency48_t vehicleProfit; // 0x8B9E
|
||||
uint16_t transportTypeCount[6]; // 0x8BA4
|
||||
uint8_t var_8BB0[9];
|
||||
uint8_t pad_8BB9[0x8BBC - 0x8BB9];
|
||||
thing_id_t observation_thing; // 0x8BBC;
|
||||
int16_t observation_x; // 0x8BBE;
|
||||
int16_t observation_y; // 0x8BC0;
|
||||
uint8_t pad_8BC2[0x8BCE - 0x8BC2];
|
||||
ObservationStatus observationStatus; // 0x8BB9;
|
||||
TownId_t observationTownId; // 0x8BBA;
|
||||
EntityId_t observation_thing; // 0x8BBC;
|
||||
int16_t observation_x; // 0x8BBE;
|
||||
int16_t observation_y; // 0x8BC0;
|
||||
uint16_t observationObject; // 0x8BC2;
|
||||
uint8_t pad_8BC4[0x8BCE - 0x8BC4];
|
||||
uint32_t cargoDelivered[32]; // 0x8BCE;
|
||||
uint8_t challengeProgress; // 0x8C4E - percent completed on challenge
|
||||
uint8_t pad_8C4F[0x8C54 - 0x8C4F];
|
||||
uint32_t cargo_units_distance_history[120]; // 0x008C54
|
||||
uint8_t pad_8C4F;
|
||||
uint32_t cargoUnitsTotalDistance; // 0x8C50
|
||||
uint32_t cargo_units_distance_history[120]; // 0x8C54
|
||||
uint16_t jail_status; // 0x8E34
|
||||
uint8_t pad_8E36[0x8FA8 - 0x8E36];
|
||||
|
||||
company_id_t id() const;
|
||||
CompanyId_t id() const;
|
||||
bool empty() const;
|
||||
void aiThink();
|
||||
bool isVehicleIndexUnlocked(const uint8_t vehicleIndex) const;
|
||||
void recalculateTransportCounts();
|
||||
void updateQuarterly();
|
||||
void updateVehicleColours();
|
||||
void updateHeadquartersColour();
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(company) == 0x8FA8);
|
||||
static_assert(sizeof(company::expenditures) == 0x440);
|
||||
static_assert(offsetof(company, companyValueHistory[0]) == 0x88CE);
|
||||
static_assert(offsetof(company, vehicleProfit) == 0x8B9E);
|
||||
static_assert(offsetof(company, challengeProgress) == 0x8C4E);
|
||||
static_assert(offsetof(company, var_8BB0) == 0x8BB0);
|
||||
static_assert(sizeof(Company) == 0x8FA8);
|
||||
static_assert(sizeof(Company::expenditures) == 0x440);
|
||||
static_assert(offsetof(Company, companyValueHistory[0]) == 0x88CE);
|
||||
static_assert(offsetof(Company, vehicleProfit) == 0x8B9E);
|
||||
static_assert(offsetof(Company, challengeProgress) == 0x8C4E);
|
||||
static_assert(offsetof(Company, var_8BB0) == 0x8BB0);
|
||||
|
||||
bool isPlayerCompany(company_id_t id);
|
||||
bool isPlayerCompany(CompanyId_t id);
|
||||
constexpr CorporateRating performanceToRating(int16_t performanceIndex);
|
||||
void formatPerformanceIndex(const int16_t performanceIndex, FormatArguments& args);
|
||||
}
|
||||
|
|
|
@ -1,48 +1,85 @@
|
|||
#include "CompanyManager.h"
|
||||
#include "Config.h"
|
||||
#include "GameCommands.h"
|
||||
#include "Entities/EntityManager.h"
|
||||
#include "Entities/Misc.h"
|
||||
#include "GameCommands/GameCommands.h"
|
||||
#include "Graphics/Colour.h"
|
||||
#include "Interop/Interop.hpp"
|
||||
#include "Localisation/FormatArguments.hpp"
|
||||
#include "Map/Tile.h"
|
||||
#include "Map/TileManager.h"
|
||||
#include "Objects/AirportObject.h"
|
||||
#include "Objects/DockObject.h"
|
||||
#include "Objects/RoadObject.h"
|
||||
#include "Objects/TrackObject.h"
|
||||
#include "OpenLoco.h"
|
||||
#include "Things/ThingManager.h"
|
||||
#include "Things/Vehicle.h"
|
||||
#include "TownManager.h"
|
||||
#include "Ui/WindowManager.h"
|
||||
#include "Vehicles/Vehicle.h"
|
||||
#include "Vehicles/VehicleManager.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
using namespace OpenLoco::Ui;
|
||||
|
||||
namespace OpenLoco::CompanyManager
|
||||
{
|
||||
static loco_global<company_id_t[2], 0x00525E3C> _player_company;
|
||||
static loco_global<CompanyId_t[2], 0x00525E3C> _player_company;
|
||||
static loco_global<uint8_t, 0x00525FCB> _byte_525FCB;
|
||||
static loco_global<uint8_t, 0x00526214> _company_competition_delay;
|
||||
static loco_global<uint8_t, 0x00525FB7> _company_max_competing;
|
||||
static loco_global<uint8_t, 0x00525E3C> _byte_525E3C;
|
||||
static loco_global<uint8_t, 0x00525E3D> _byte_525E3D;
|
||||
static loco_global<company[max_companies], 0x00531784> _companies;
|
||||
static loco_global<Company[max_companies], 0x00531784> _companies;
|
||||
static loco_global<uint8_t[max_companies + 1], 0x009C645C> _company_colours;
|
||||
static loco_global<company_id_t, 0x009C68EB> _updating_company_id;
|
||||
static loco_global<CompanyId_t, 0x009C68EB> _updating_company_id;
|
||||
|
||||
static void produceCompanies();
|
||||
|
||||
company_id_t updatingCompanyId()
|
||||
// 0x0042F7F8
|
||||
void reset()
|
||||
{
|
||||
// First, empty all non-empty companies.
|
||||
for (auto& company : companies())
|
||||
company.name = StringIds::empty;
|
||||
|
||||
_byte_525FCB = 0;
|
||||
|
||||
// Reset player companies depending on network mode.
|
||||
if (isNetworkHost())
|
||||
{
|
||||
_player_company[0] = 1;
|
||||
_player_company[1] = 0;
|
||||
}
|
||||
else if (isNetworked())
|
||||
{
|
||||
_player_company[0] = 0;
|
||||
_player_company[1] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_player_company[0] = 0;
|
||||
_player_company[1] = 0xFF;
|
||||
}
|
||||
|
||||
// Reset primary company colours.
|
||||
_companies[0].mainColours.primary = Colour::saturated_green;
|
||||
updateColours();
|
||||
}
|
||||
|
||||
CompanyId_t updatingCompanyId()
|
||||
{
|
||||
return _updating_company_id;
|
||||
}
|
||||
|
||||
void updatingCompanyId(company_id_t id)
|
||||
void updatingCompanyId(CompanyId_t id)
|
||||
{
|
||||
_updating_company_id = id;
|
||||
}
|
||||
|
||||
std::array<company, max_companies>& companies()
|
||||
LocoFixedVector<Company> companies()
|
||||
{
|
||||
auto arr = (std::array<company, max_companies>*)_companies.get();
|
||||
return *arr;
|
||||
return LocoFixedVector<Company>(_companies);
|
||||
}
|
||||
|
||||
company* get(company_id_t id)
|
||||
Company* get(CompanyId_t id)
|
||||
{
|
||||
auto index = id;
|
||||
if (index < _companies.size())
|
||||
|
@ -52,17 +89,32 @@ namespace OpenLoco::CompanyManager
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
company_id_t getControllingId()
|
||||
CompanyId_t getControllingId()
|
||||
{
|
||||
return _player_company[0];
|
||||
}
|
||||
|
||||
company* getPlayerCompany()
|
||||
CompanyId_t getSecondaryPlayerId()
|
||||
{
|
||||
return _player_company[1];
|
||||
}
|
||||
|
||||
void setControllingId(CompanyId_t id)
|
||||
{
|
||||
_player_company[0] = id;
|
||||
}
|
||||
|
||||
void setSecondaryPlayerId(CompanyId_t id)
|
||||
{
|
||||
_player_company[1] = id;
|
||||
}
|
||||
|
||||
Company* getPlayerCompany()
|
||||
{
|
||||
return &_companies[_player_company[0]];
|
||||
}
|
||||
|
||||
uint8_t getCompanyColour(company_id_t id)
|
||||
uint8_t getCompanyColour(CompanyId_t id)
|
||||
{
|
||||
return _company_colours[id];
|
||||
}
|
||||
|
@ -77,7 +129,7 @@ namespace OpenLoco::CompanyManager
|
|||
{
|
||||
if (!isEditorMode() && !Config::getNew().companyAIDisabled)
|
||||
{
|
||||
company_id_t id = scenarioTicks() & 0x0F;
|
||||
CompanyId_t id = scenarioTicks() & 0x0F;
|
||||
auto company = get(id);
|
||||
if (company != nullptr && !isPlayerCompany(id) && !company->empty())
|
||||
{
|
||||
|
@ -94,11 +146,44 @@ namespace OpenLoco::CompanyManager
|
|||
}
|
||||
}
|
||||
|
||||
// 0x00487FC1
|
||||
void updateQuarterly()
|
||||
{
|
||||
for (auto& company : companies())
|
||||
{
|
||||
company.updateQuarterly();
|
||||
}
|
||||
}
|
||||
|
||||
static void sub_42F9AC()
|
||||
{
|
||||
call(0x0042F9AC);
|
||||
}
|
||||
|
||||
// 0x0042F23C
|
||||
currency32_t calculateDeliveredCargoPayment(uint8_t cargoItem, int32_t numUnits, int32_t distance, uint16_t numDays)
|
||||
{
|
||||
registers regs;
|
||||
regs.eax = cargoItem;
|
||||
regs.ebx = numUnits;
|
||||
regs.ecx = distance;
|
||||
regs.edx = numDays;
|
||||
call(0x0042F23C, regs);
|
||||
return regs.eax;
|
||||
}
|
||||
|
||||
// 0x0042FDE2
|
||||
void determineAvailableVehicles()
|
||||
{
|
||||
for (auto& company : _companies)
|
||||
{
|
||||
if (company.empty())
|
||||
continue;
|
||||
|
||||
VehicleManager::determineAvailableVehicles(company);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x004306D1
|
||||
static void produceCompanies()
|
||||
{
|
||||
|
@ -107,15 +192,11 @@ namespace OpenLoco::CompanyManager
|
|||
int32_t companies_active = 0;
|
||||
for (const auto& company : companies())
|
||||
{
|
||||
auto id = company.id();
|
||||
if (!company.empty() && id != _byte_525E3C && id != _byte_525E3D)
|
||||
{
|
||||
if (!isPlayerCompany(company.id()))
|
||||
companies_active++;
|
||||
}
|
||||
}
|
||||
|
||||
auto& prng = gPrng();
|
||||
|
||||
if (prng.randNext(16) == 0)
|
||||
{
|
||||
if (prng.randNext(_company_max_competing) + 1 > companies_active)
|
||||
|
@ -127,37 +208,73 @@ namespace OpenLoco::CompanyManager
|
|||
}
|
||||
}
|
||||
|
||||
company* getOpponent()
|
||||
Company* getOpponent()
|
||||
{
|
||||
return &_companies[_player_company[1]];
|
||||
}
|
||||
|
||||
// 0x00438047
|
||||
// Returns a string between 1810 and 1816 with up to two arguments.
|
||||
string_id getOwnerStatus(company_id_t id, FormatArguments& args)
|
||||
string_id getOwnerStatus(CompanyId_t id, FormatArguments& args)
|
||||
{
|
||||
registers regs;
|
||||
regs.esi = (int32_t)get(id);
|
||||
call(0x00438047, regs);
|
||||
auto& company = _companies[id];
|
||||
if (company.challenge_flags & CompanyFlags::bankrupt)
|
||||
return StringIds::company_status_bankrupt;
|
||||
|
||||
args.push(regs.ecx);
|
||||
args.push(regs.edx);
|
||||
return regs.bx;
|
||||
}
|
||||
const string_id observationStatusStrings[] = {
|
||||
StringIds::company_status_empty,
|
||||
StringIds::company_status_building_track_road,
|
||||
StringIds::company_status_building_airport,
|
||||
StringIds::company_status_building_dock,
|
||||
StringIds::company_status_checking_services,
|
||||
StringIds::company_status_surveying_landscape,
|
||||
};
|
||||
|
||||
owner_status getOwnerStatus(company_id_t id)
|
||||
{
|
||||
registers regs;
|
||||
regs.esi = (int32_t)get(id);
|
||||
call(0x00438047, regs);
|
||||
string_id statusString = observationStatusStrings[company.observationStatus];
|
||||
if (company.observationStatus == ObservationStatus::empty || company.observationTownId == 0xFFFF)
|
||||
return StringIds::company_status_empty;
|
||||
|
||||
owner_status ownerStatus;
|
||||
switch (company.observationStatus)
|
||||
{
|
||||
case ObservationStatus::buildingTrackRoad:
|
||||
if (company.observationObject & 0x80)
|
||||
{
|
||||
auto* obj = ObjectManager::get<RoadObject>(company.observationObject & 0xFF7F);
|
||||
if (obj != nullptr)
|
||||
args.push(obj->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* obj = ObjectManager::get<TrackObject>(company.observationObject);
|
||||
if (obj != nullptr)
|
||||
args.push(obj->name);
|
||||
}
|
||||
break;
|
||||
|
||||
ownerStatus.string = regs.bx;
|
||||
ownerStatus.argument1 = regs.ecx;
|
||||
ownerStatus.argument2 = regs.edx;
|
||||
case ObservationStatus::buildingAirport:
|
||||
{
|
||||
auto* obj = ObjectManager::get<AirportObject>(company.observationObject);
|
||||
if (obj != nullptr)
|
||||
args.push(obj->name);
|
||||
break;
|
||||
}
|
||||
|
||||
return ownerStatus;
|
||||
case ObservationStatus::buildingDock:
|
||||
{
|
||||
auto* obj = ObjectManager::get<DockObject>(company.observationObject);
|
||||
if (obj != nullptr)
|
||||
args.push(obj->name);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
auto* town = TownManager::get(company.observationTownId);
|
||||
args.push(town->name);
|
||||
|
||||
return statusString;
|
||||
}
|
||||
|
||||
// 0x004383ED
|
||||
|
@ -185,8 +302,8 @@ namespace OpenLoco::CompanyManager
|
|||
if (w->type != WindowType::vehicle)
|
||||
continue;
|
||||
|
||||
auto vehicle = ThingManager::get<OpenLoco::vehicle>(w->number);
|
||||
if (vehicle->x == Location::null)
|
||||
auto vehicle = EntityManager::get<Vehicles::VehicleBase>(w->number);
|
||||
if (vehicle->position.x == Location::null)
|
||||
continue;
|
||||
|
||||
if (vehicle->owner != _updating_company_id)
|
||||
|
@ -204,31 +321,139 @@ namespace OpenLoco::CompanyManager
|
|||
if (viewport == nullptr)
|
||||
return;
|
||||
|
||||
Gfx::point_t screenPosition;
|
||||
screenPosition.x = viewport->x + viewport->width / 2;
|
||||
screenPosition.y = viewport->y + viewport->height / 2;
|
||||
auto screenPosition = viewport->getUiCentre();
|
||||
|
||||
registers r1;
|
||||
r1.ax = screenPosition.x;
|
||||
r1.bx = screenPosition.y;
|
||||
call(0x0045F1A7, r1);
|
||||
Ui::viewport* vp = (Ui::viewport*)r1.edi;
|
||||
auto mapPosition = Map::map_pos(r1.ax, r1.bx);
|
||||
auto res = Ui::ViewportInteraction::getSurfaceLocFromUi(screenPosition);
|
||||
|
||||
// Happens if center of viewport is obstructed. Probably estimates the centre location
|
||||
if (mapPosition.x == Location::null || viewport != vp)
|
||||
Map::Pos2 mapPosition{};
|
||||
if (!res || res->second != viewport)
|
||||
{
|
||||
registers r2;
|
||||
|
||||
r2.ax = viewport->view_x + viewport->view_width / 2;
|
||||
r2.bx = viewport->view_y + viewport->view_height / 2;
|
||||
r2.edx = viewport->getRotation();
|
||||
call(0x0045F997, r2);
|
||||
|
||||
mapPosition.x = r2.ax;
|
||||
mapPosition.y = r2.bx;
|
||||
// Happens if center of viewport is obstructed. Probably estimates the centre location
|
||||
mapPosition = viewport->getCentreMapPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
mapPosition = res->first;
|
||||
}
|
||||
|
||||
GameCommands::do_73(mapPosition);
|
||||
}
|
||||
|
||||
// 0x0046DC9F
|
||||
// loc : gGameCommandMapX/Y/Z global
|
||||
// company : updatingCompanyId global
|
||||
// amount : ebx
|
||||
void spendMoneyEffect(const Map::Pos3& loc, const CompanyId_t company, const currency32_t amount)
|
||||
{
|
||||
if (isEditorMode())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Map::Pos3 pos = loc;
|
||||
if (loc.x == Location::null)
|
||||
{
|
||||
auto* view = Ui::WindowManager::getMainViewport();
|
||||
if (view == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto centre = view->getCentreScreenMapPosition();
|
||||
if (!centre)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pos = Map::Pos3(centre->x, centre->y, Map::TileManager::getHeight(*centre).landHeight);
|
||||
}
|
||||
|
||||
pos.z += 10;
|
||||
|
||||
MoneyEffect::create(pos, company, -amount);
|
||||
}
|
||||
|
||||
// 0x0046DE2B
|
||||
// id : updatingCompanyId global var
|
||||
// payment : ebx (subtracted from company balance)
|
||||
// type : gGameCommandExpenditureType global var
|
||||
void applyPaymentToCompany(const CompanyId_t id, const currency32_t payment, const ExpenditureType type)
|
||||
{
|
||||
auto* company = get(id);
|
||||
if (company == nullptr || OpenLoco::isEditorMode())
|
||||
return;
|
||||
|
||||
WindowManager::invalidate(WindowType::company, id);
|
||||
|
||||
// Invalidate the company balance if this is the player company
|
||||
if (getControllingId() == id)
|
||||
{
|
||||
Ui::Windows::PlayerInfoPanel::invalidateFrame();
|
||||
}
|
||||
auto cost = currency48_t{ payment };
|
||||
company->cash -= cost;
|
||||
company->expenditures[0][static_cast<uint8_t>(type)] -= payment;
|
||||
}
|
||||
|
||||
// 0x004302EF
|
||||
void updateColours()
|
||||
{
|
||||
size_t index = 0;
|
||||
for (auto& company : _companies)
|
||||
{
|
||||
_company_colours[index] = company.mainColours.primary;
|
||||
index++;
|
||||
}
|
||||
_company_colours[CompanyId::neutral] = 1;
|
||||
}
|
||||
|
||||
uint32_t competingColourMask(CompanyId_t companyId)
|
||||
{
|
||||
const uint32_t similarColourMask[] = {
|
||||
0b11,
|
||||
0b11,
|
||||
0b100,
|
||||
0b11000,
|
||||
0b11000,
|
||||
0b100000,
|
||||
0b11000000,
|
||||
0b11000000,
|
||||
0b1100000000,
|
||||
0b1100000000,
|
||||
0b11110000000000,
|
||||
0b11110000000000,
|
||||
0b11110000000000,
|
||||
0b11110000000000,
|
||||
0b1100000000000000,
|
||||
0b1100000000000000,
|
||||
0b10110000000000000000,
|
||||
0b10110000000000000000,
|
||||
0b101000000000000000000,
|
||||
0b10110000000000000000,
|
||||
0b101000000000000000000,
|
||||
0b11000000000000000000000,
|
||||
0b11000000000000000000000,
|
||||
0b100000000000000000000000,
|
||||
0b1000000000000000000000000,
|
||||
0b10000000000000000000000000,
|
||||
0b1100000000000000000000000000,
|
||||
0b1100000000000000000000000000,
|
||||
0b110000000000000000000000000000,
|
||||
0b110000000000000000000000000000,
|
||||
0b1000000000000000000000000000000,
|
||||
};
|
||||
|
||||
uint32_t mask = 0;
|
||||
for (auto& company : companies())
|
||||
{
|
||||
if (company.id() == companyId)
|
||||
continue;
|
||||
|
||||
mask |= similarColourMask[company.mainColours.primary];
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
uint32_t competingColourMask()
|
||||
{
|
||||
return competingColourMask(_updating_company_id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "Company.h"
|
||||
#include "Core/LocoFixedVector.hpp"
|
||||
#include "Map/Map.hpp"
|
||||
#include "Types.hpp"
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
@ -9,26 +11,31 @@ namespace OpenLoco::CompanyManager
|
|||
{
|
||||
constexpr size_t max_companies = 15;
|
||||
|
||||
company_id_t updatingCompanyId();
|
||||
void updatingCompanyId(company_id_t id);
|
||||
void reset();
|
||||
CompanyId_t updatingCompanyId();
|
||||
void updatingCompanyId(CompanyId_t id);
|
||||
|
||||
std::array<company, max_companies>& companies();
|
||||
company* get(company_id_t id);
|
||||
company_id_t getControllingId();
|
||||
company* getPlayerCompany();
|
||||
uint8_t getCompanyColour(company_id_t id);
|
||||
LocoFixedVector<Company> companies();
|
||||
Company* get(CompanyId_t id);
|
||||
CompanyId_t getControllingId();
|
||||
CompanyId_t getSecondaryPlayerId();
|
||||
void setControllingId(CompanyId_t id);
|
||||
void setSecondaryPlayerId(CompanyId_t id);
|
||||
Company* getPlayerCompany();
|
||||
uint8_t getCompanyColour(CompanyId_t id);
|
||||
uint8_t getPlayerCompanyColour();
|
||||
void update();
|
||||
void updateQuarterly();
|
||||
void determineAvailableVehicles();
|
||||
currency32_t calculateDeliveredCargoPayment(uint8_t cargoItem, int32_t numUnits, int32_t distance, uint16_t numDays);
|
||||
|
||||
struct owner_status
|
||||
{
|
||||
string_id string;
|
||||
uint32_t argument1;
|
||||
uint32_t argument2;
|
||||
};
|
||||
|
||||
company* getOpponent();
|
||||
string_id getOwnerStatus(company_id_t id, FormatArguments& args);
|
||||
owner_status getOwnerStatus(company_id_t id);
|
||||
Company* getOpponent();
|
||||
string_id getOwnerStatus(CompanyId_t id, FormatArguments& args);
|
||||
void updateOwnerStatus();
|
||||
void updateColours();
|
||||
|
||||
void spendMoneyEffect(const Map::Pos3& loc, const CompanyId_t company, const currency32_t amount);
|
||||
void applyPaymentToCompany(const CompanyId_t id, const currency32_t payment, const ExpenditureType type);
|
||||
uint32_t competingColourMask(CompanyId_t companyId);
|
||||
uint32_t competingColourMask();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#include <fstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -17,22 +16,22 @@ using namespace OpenLoco::Interop;
|
|||
|
||||
namespace OpenLoco::Config
|
||||
{
|
||||
static loco_global<config_t, 0x0050AEB4> _config;
|
||||
static new_config _new_config;
|
||||
static loco_global<LocoConfig, 0x0050AEB4> _config;
|
||||
static NewConfig _new_config;
|
||||
static YAML::Node _config_yaml;
|
||||
|
||||
config_t& get()
|
||||
LocoConfig& get()
|
||||
{
|
||||
return _config;
|
||||
}
|
||||
|
||||
new_config& getNew()
|
||||
NewConfig& getNew()
|
||||
{
|
||||
return _new_config;
|
||||
}
|
||||
|
||||
// 0x00441A6C
|
||||
config_t& read()
|
||||
LocoConfig& read()
|
||||
{
|
||||
call(0x00441A6C);
|
||||
return _config;
|
||||
|
@ -45,9 +44,9 @@ namespace OpenLoco::Config
|
|||
writeNewConfig();
|
||||
}
|
||||
|
||||
new_config& readNewConfig()
|
||||
NewConfig& readNewConfig()
|
||||
{
|
||||
auto configPath = Environment::getPath(Environment::path_id::openloco_yml);
|
||||
auto configPath = Environment::getPathNoWarning(Environment::path_id::openloco_yml);
|
||||
|
||||
if (!fs::exists(configPath))
|
||||
return _new_config;
|
||||
|
@ -60,10 +59,10 @@ namespace OpenLoco::Config
|
|||
if (displayNode && displayNode.IsMap())
|
||||
{
|
||||
auto& displayConfig = _new_config.display;
|
||||
displayConfig.mode = displayNode["mode"].as<screen_mode>(screen_mode::window);
|
||||
displayConfig.mode = displayNode["mode"].as<ScreenMode>(ScreenMode::window);
|
||||
displayConfig.index = displayNode["index"].as<int32_t>(0);
|
||||
displayConfig.window_resolution = displayNode["window_resolution"].as<resolution_t>();
|
||||
displayConfig.fullscreen_resolution = displayNode["fullscreen_resolution"].as<resolution_t>();
|
||||
displayConfig.window_resolution = displayNode["window_resolution"].as<Resolution>();
|
||||
displayConfig.fullscreen_resolution = displayNode["fullscreen_resolution"].as<Resolution>();
|
||||
}
|
||||
|
||||
auto& audioNode = config["audio"];
|
||||
|
@ -77,36 +76,37 @@ namespace OpenLoco::Config
|
|||
|
||||
if (config["loco_install_path"])
|
||||
_new_config.loco_install_path = config["loco_install_path"].as<std::string>();
|
||||
if (config["last_save_path"])
|
||||
_new_config.last_save_path = config["last_save_path"].as<std::string>();
|
||||
if (config["language"])
|
||||
_new_config.language = config["language"].as<std::string>();
|
||||
if (config["breakdowns_disabled"])
|
||||
_new_config.breakdowns_disabled = config["breakdowns_disabled"].as<bool>();
|
||||
if (config["cheats_menu_enabled"])
|
||||
_new_config.cheats_menu_enabled = config["cheats_menu_enabled"].as<bool>();
|
||||
if (config["companyAIDisabled"])
|
||||
_new_config.companyAIDisabled = config["companyAIDisabled"].as<bool>();
|
||||
if (config["scale_factor"])
|
||||
_new_config.scale_factor = config["scale_factor"].as<float>();
|
||||
if (config["zoom_to_cursor"])
|
||||
_new_config.zoom_to_cursor = config["zoom_to_cursor"].as<bool>();
|
||||
if (config["autosave_frequency"])
|
||||
_new_config.autosave_frequency = config["autosave_frequency"].as<int32_t>();
|
||||
if (config["autosave_amount"])
|
||||
_new_config.autosave_amount = config["autosave_amount"].as<int32_t>();
|
||||
if (config["showFPS"])
|
||||
_new_config.showFPS = config["showFPS"].as<bool>();
|
||||
if (config["uncapFPS"])
|
||||
_new_config.uncapFPS = config["uncapFPS"].as<bool>();
|
||||
|
||||
return _new_config;
|
||||
}
|
||||
|
||||
void writeNewConfig()
|
||||
{
|
||||
auto configPath = Environment::getPath(Environment::path_id::openloco_yml);
|
||||
auto configPath = Environment::getPathNoWarning(Environment::path_id::openloco_yml);
|
||||
auto dir = configPath.parent_path();
|
||||
if (!fs::is_directory(dir))
|
||||
{
|
||||
fs::create_directories(dir);
|
||||
// clang-format off
|
||||
fs::permissions(
|
||||
dir,
|
||||
fs::perms::owner_all |
|
||||
fs::perms::group_read | fs::perms::group_exec |
|
||||
fs::perms::others_read | fs::perms::others_exec
|
||||
);
|
||||
// clang-format on
|
||||
}
|
||||
Environment::autoCreateDirectory(dir);
|
||||
|
||||
auto& node = _config_yaml;
|
||||
|
||||
|
@ -138,11 +138,17 @@ namespace OpenLoco::Config
|
|||
node["audio"] = audioNode;
|
||||
|
||||
node["loco_install_path"] = _new_config.loco_install_path;
|
||||
node["last_save_path"] = _new_config.last_save_path;
|
||||
node["language"] = _new_config.language;
|
||||
node["breakdowns_disabled"] = _new_config.breakdowns_disabled;
|
||||
node["cheats_menu_enabled"] = _new_config.cheats_menu_enabled;
|
||||
node["companyAIDisabled"] = _new_config.companyAIDisabled;
|
||||
node["scale_factor"] = _new_config.scale_factor;
|
||||
node["zoom_to_cursor"] = _new_config.zoom_to_cursor;
|
||||
node["autosave_frequency"] = _new_config.autosave_frequency;
|
||||
node["autosave_amount"] = _new_config.autosave_amount;
|
||||
node["showFPS"] = _new_config.showFPS;
|
||||
node["uncapFPS"] = _new_config.uncapFPS;
|
||||
|
||||
std::ofstream stream(configPath);
|
||||
if (stream.is_open())
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
|
@ -7,32 +8,32 @@ namespace OpenLoco::Config
|
|||
{
|
||||
#pragma pack(push, 1)
|
||||
|
||||
enum flags
|
||||
namespace Flags
|
||||
{
|
||||
gridlines_on_landscape = (1 << 0),
|
||||
show_height_as_units = (1 << 1),
|
||||
landscape_smoothing = (1 << 2),
|
||||
export_objects_with_saves = (1 << 3),
|
||||
constexpr uint32_t gridlinesOnLandscape = (1 << 0);
|
||||
constexpr uint32_t showHeightAsUnits = (1 << 1);
|
||||
constexpr uint32_t landscapeSmoothing = (1 << 2);
|
||||
constexpr uint32_t exportObjectsWithSaves = (1 << 3);
|
||||
|
||||
preferred_currency_for_new_games = (1 << 6),
|
||||
preferred_currency_always = (1 << 7),
|
||||
constexpr uint32_t preferredCurrencyForNewGames = (1 << 6);
|
||||
constexpr uint32_t preferredCurrencyAlways = (1 << 7);
|
||||
|
||||
use_preferred_owner_name = (1 << 9),
|
||||
};
|
||||
constexpr uint32_t usePreferredOwnerName = (1 << 9);
|
||||
}
|
||||
|
||||
enum measurement_format
|
||||
enum class MeasurementFormat : uint8_t
|
||||
{
|
||||
imperial = 0,
|
||||
metric = 1,
|
||||
};
|
||||
|
||||
struct keyboard_shortcut_t
|
||||
struct KeyboardShortcut
|
||||
{
|
||||
uint8_t var_0;
|
||||
uint8_t var_1;
|
||||
};
|
||||
|
||||
enum class newsType : uint8_t
|
||||
enum class NewsType : uint8_t
|
||||
{
|
||||
none = 0,
|
||||
ticker,
|
||||
|
@ -41,21 +42,21 @@ namespace OpenLoco::Config
|
|||
|
||||
constexpr auto newsItemSubTypeCount = 6;
|
||||
|
||||
enum class screen_mode
|
||||
enum class ScreenMode
|
||||
{
|
||||
window,
|
||||
fullscreen,
|
||||
fullscreen_borderless
|
||||
fullscreenBorderless
|
||||
};
|
||||
|
||||
enum class music_playlist_type : uint8_t
|
||||
enum class MusicPlaylistType : uint8_t
|
||||
{
|
||||
current_era,
|
||||
currentEra,
|
||||
all,
|
||||
custom,
|
||||
};
|
||||
|
||||
struct config_t
|
||||
struct LocoConfig
|
||||
{
|
||||
uint32_t flags; // 0x50AEB4, 0x00
|
||||
int16_t resolution_width; // 0x50AEB8, 0x04
|
||||
|
@ -72,31 +73,33 @@ namespace OpenLoco::Config
|
|||
uint8_t max_vehicle_sounds; // 0x25
|
||||
uint8_t max_sound_instances; // 0x26
|
||||
uint8_t sound_quality; // 0x27
|
||||
uint8_t measurement_format; // 0x50AEDC, 0x28
|
||||
MeasurementFormat measurement_format; // 0x50AEDC, 0x28
|
||||
uint8_t pad_29; // 0x29
|
||||
keyboard_shortcut_t keyboard_shortcuts[35]; // 0x2A
|
||||
KeyboardShortcut keyboard_shortcuts[35]; // 0x2A
|
||||
uint8_t edge_scrolling; // 0x70
|
||||
uint8_t vehicles_min_scale; // 0x71
|
||||
uint8_t vehicles_min_scale; // 0x50AF25, 0x71
|
||||
uint8_t var_72; // 0x50AF26, 0x72
|
||||
music_playlist_type music_playlist; // 0x50AF27, 0x73
|
||||
MusicPlaylistType music_playlist; // 0x50AF27, 0x73
|
||||
uint16_t height_marker_offset; // 0x50AF28, 0x74
|
||||
newsType news_settings[newsItemSubTypeCount]; // 0x50AF2A, 0x76
|
||||
NewsType news_settings[newsItemSubTypeCount]; // 0x50AF2A, 0x76
|
||||
uint8_t preferred_currency[16]; // 0x7C
|
||||
uint8_t enabled_music[29]; // 0x50AF40, 0x8C
|
||||
uint8_t pad_A9[0xCC - 0xA9]; // 0xA9
|
||||
int32_t volume; // 0xCC
|
||||
int32_t volume; // 0x50AF80, 0xCC
|
||||
uint32_t connection_timeout; // 0xD0
|
||||
char last_host[64]; // 0xD4
|
||||
uint8_t station_names_min_scale; // 0x114
|
||||
uint8_t scenario_selected_tab; // 0x115
|
||||
char preferred_name[256]; // 0x116
|
||||
};
|
||||
static_assert(offsetof(config_t, keyboard_shortcuts) == 0x2A);
|
||||
static_assert(offsetof(config_t, preferred_name) == 0x116);
|
||||
static_assert(offsetof(config_t, last_host) == 0xD4);
|
||||
static_assert(sizeof(config_t) == 0x216);
|
||||
static_assert(offsetof(LocoConfig, keyboard_shortcuts) == 0x2A);
|
||||
static_assert(offsetof(LocoConfig, preferred_name) == 0x116);
|
||||
static_assert(offsetof(LocoConfig, last_host) == 0xD4);
|
||||
static_assert(sizeof(LocoConfig) == 0x216);
|
||||
|
||||
struct resolution_t
|
||||
#pragma pack(pop)
|
||||
|
||||
struct Resolution
|
||||
{
|
||||
int32_t width{};
|
||||
int32_t height{};
|
||||
|
@ -106,22 +109,22 @@ namespace OpenLoco::Config
|
|||
return width > 0 && height > 0;
|
||||
}
|
||||
|
||||
bool operator==(const resolution_t& rhs) const
|
||||
bool operator==(const Resolution& rhs) const
|
||||
{
|
||||
return width == rhs.width && height == rhs.height;
|
||||
}
|
||||
|
||||
bool operator!=(const resolution_t& rhs) const
|
||||
bool operator!=(const Resolution& rhs) const
|
||||
{
|
||||
return width != rhs.width || height != rhs.height;
|
||||
}
|
||||
|
||||
bool operator>(const resolution_t& rhs) const
|
||||
bool operator>(const Resolution& rhs) const
|
||||
{
|
||||
return width > rhs.width || height > rhs.height;
|
||||
}
|
||||
|
||||
resolution_t& operator*=(const float scalar)
|
||||
Resolution& operator*=(const float scalar)
|
||||
{
|
||||
width *= scalar;
|
||||
height *= scalar;
|
||||
|
@ -129,39 +132,43 @@ namespace OpenLoco::Config
|
|||
}
|
||||
};
|
||||
|
||||
struct display_config
|
||||
struct Display
|
||||
{
|
||||
screen_mode mode;
|
||||
ScreenMode mode;
|
||||
int32_t index{};
|
||||
resolution_t window_resolution = { 800, 600 };
|
||||
resolution_t fullscreen_resolution;
|
||||
Resolution window_resolution = { 800, 600 };
|
||||
Resolution fullscreen_resolution;
|
||||
};
|
||||
|
||||
struct audio_config
|
||||
struct Audio
|
||||
{
|
||||
std::string device;
|
||||
bool play_title_music = true;
|
||||
};
|
||||
|
||||
struct new_config
|
||||
struct NewConfig
|
||||
{
|
||||
display_config display;
|
||||
audio_config audio;
|
||||
Display display;
|
||||
Audio audio;
|
||||
std::string loco_install_path;
|
||||
std::string last_save_path;
|
||||
std::string language = "en-GB";
|
||||
bool cheats_menu_enabled = false;
|
||||
bool breakdowns_disabled = false;
|
||||
bool companyAIDisabled = false;
|
||||
float scale_factor = 1.0f;
|
||||
bool zoom_to_cursor = true;
|
||||
int32_t autosave_frequency = 1;
|
||||
int32_t autosave_amount = 12;
|
||||
bool showFPS = false;
|
||||
bool uncapFPS = false;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
LocoConfig& get();
|
||||
NewConfig& getNew();
|
||||
|
||||
config_t& get();
|
||||
new_config& getNew();
|
||||
|
||||
config_t& read();
|
||||
new_config& readNewConfig();
|
||||
LocoConfig& read();
|
||||
NewConfig& readNewConfig();
|
||||
void write();
|
||||
void writeNewConfig();
|
||||
}
|
||||
|
|
|
@ -48,11 +48,11 @@ namespace YAML
|
|||
}
|
||||
};
|
||||
|
||||
// resolution_t
|
||||
// Resolution
|
||||
template<>
|
||||
struct convert<resolution_t>
|
||||
struct convert<Resolution>
|
||||
{
|
||||
static Node encode(const resolution_t& rhs)
|
||||
static Node encode(const Resolution& rhs)
|
||||
{
|
||||
Node node;
|
||||
node["width"] = rhs.width;
|
||||
|
@ -60,7 +60,7 @@ namespace YAML
|
|||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, resolution_t& rhs)
|
||||
static bool decode(const Node& node, Resolution& rhs)
|
||||
{
|
||||
if (node.IsMap())
|
||||
{
|
||||
|
@ -72,16 +72,16 @@ namespace YAML
|
|||
}
|
||||
};
|
||||
|
||||
// screen_mode
|
||||
const convert_pair_vector<screen_mode> screen_mode_entries = {
|
||||
enum_def(screen_mode, window),
|
||||
enum_def(screen_mode, fullscreen),
|
||||
enum_def(screen_mode, fullscreen_borderless),
|
||||
// ScreenMode
|
||||
const convert_pair_vector<ScreenMode> screen_mode_entries = {
|
||||
enum_def(ScreenMode, window),
|
||||
enum_def(ScreenMode, fullscreen),
|
||||
enum_def(ScreenMode, fullscreenBorderless),
|
||||
};
|
||||
template<>
|
||||
struct convert<screen_mode> : convert_enum_base<screen_mode>
|
||||
struct convert<ScreenMode> : convert_enum_base<ScreenMode>
|
||||
{
|
||||
static const convert_pair_vector<screen_mode>& getEntries() { return screen_mode_entries; }
|
||||
static const convert_pair_vector<ScreenMode>& getEntries() { return screen_mode_entries; }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace OpenLoco
|
||||
{
|
||||
template<typename ValueType>
|
||||
class LocoFixedVector
|
||||
{
|
||||
private:
|
||||
ValueType* startAddress = nullptr;
|
||||
ValueType* endAddress = nullptr;
|
||||
|
||||
class Iter
|
||||
{
|
||||
private:
|
||||
ValueType* arr;
|
||||
ValueType* endAdd;
|
||||
|
||||
public:
|
||||
constexpr Iter(ValueType* _arr, ValueType* _endAdd)
|
||||
: arr(_arr)
|
||||
, endAdd(_endAdd)
|
||||
{
|
||||
// finds first valid entry
|
||||
++(*this);
|
||||
}
|
||||
|
||||
constexpr Iter& operator++()
|
||||
{
|
||||
while (arr != endAdd && (++arr)->empty())
|
||||
{
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Iter operator++(int)
|
||||
{
|
||||
Iter retval = *this;
|
||||
++(*this);
|
||||
return retval;
|
||||
}
|
||||
|
||||
constexpr bool operator==(Iter other) const
|
||||
{
|
||||
return arr == other.arr;
|
||||
}
|
||||
constexpr bool operator!=(Iter other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr ValueType& operator*() const
|
||||
{
|
||||
return *arr;
|
||||
}
|
||||
// iterator traits
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = ValueType;
|
||||
using pointer = ValueType*;
|
||||
using reference = ValueType&;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
};
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
LocoFixedVector(T& _arr)
|
||||
: startAddress(reinterpret_cast<ValueType*>(T::address))
|
||||
, endAddress(reinterpret_cast<ValueType*>(T::endAddress))
|
||||
{
|
||||
}
|
||||
|
||||
Iter begin() const
|
||||
{
|
||||
return Iter(startAddress - 1, endAddress);
|
||||
}
|
||||
Iter end() const
|
||||
{
|
||||
return Iter(endAddress, endAddress);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -4,14 +4,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
// Find out where std::optional is:
|
||||
#if defined(__APPLE__) // XCode has the header in experimental namespace
|
||||
#define NORMAL_OPTIONAL 0
|
||||
#else // By default assume supported.
|
||||
#define NORMAL_OPTIONAL 1
|
||||
#endif
|
||||
|
||||
#if NORMAL_OPTIONAL
|
||||
#if defined(__has_include) // For GCC/Clang check if the header exists.
|
||||
#if __has_include(<optional>)
|
||||
#include <optional>
|
||||
#else
|
||||
#include <experimental/optional>
|
||||
|
@ -21,5 +15,6 @@ namespace std
|
|||
using std::experimental::optional;
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef NORMAL_OPTIONAL // Not needed any more, don't make it public.
|
||||
#else
|
||||
#error "__has_include operator is unavailable; <optional> header could not be located"
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/// @file
|
||||
/// This file enables access to std::span as `stdx` namespace
|
||||
|
||||
#pragma once
|
||||
|
||||
#define TCB_SPAN_NAMESPACE_NAME stdx
|
||||
#include "../../Thirdparty/span.hpp"
|
|
@ -0,0 +1,23 @@
|
|||
// This file enables access to stdx::variant stdx::visit
|
||||
|
||||
#pragma once
|
||||
|
||||
// TODO: use a more fine-grained approach to detecting whether <variant>
|
||||
// contains support for std::visit.
|
||||
#if !defined(__APPLE__)
|
||||
|
||||
#include <variant>
|
||||
namespace stdx
|
||||
{
|
||||
using std::variant;
|
||||
using std::visit;
|
||||
}
|
||||
#else
|
||||
|
||||
#include "../../Thirdparty/variant.hpp"
|
||||
namespace stdx
|
||||
{
|
||||
using mpark::variant;
|
||||
using mpark::visit;
|
||||
}
|
||||
#endif
|
|
@ -16,11 +16,21 @@ namespace OpenLoco
|
|||
|
||||
static std::pair<month_id, uint8_t> getMonthDay(int32_t dayOfYear);
|
||||
|
||||
bool isLeapYear(const int year)
|
||||
{
|
||||
return year % 4 == 0;
|
||||
}
|
||||
|
||||
uint32_t getCurrentDay()
|
||||
{
|
||||
return _current_day;
|
||||
}
|
||||
|
||||
void setCurrentDay(const uint32_t day)
|
||||
{
|
||||
_current_day = day;
|
||||
}
|
||||
|
||||
month_id getCurrentMonth()
|
||||
{
|
||||
return static_cast<month_id>(*_current_month);
|
||||
|
@ -48,6 +58,16 @@ namespace OpenLoco
|
|||
_current_year = date.year;
|
||||
}
|
||||
|
||||
uint16_t getDayProgression()
|
||||
{
|
||||
return _day_progression;
|
||||
}
|
||||
|
||||
void setDayProgression(const uint16_t progression)
|
||||
{
|
||||
_day_progression = progression;
|
||||
}
|
||||
|
||||
bool updateDayCounter()
|
||||
{
|
||||
bool result = false;
|
||||
|
|
|
@ -38,7 +38,10 @@ namespace OpenLoco
|
|||
}
|
||||
};
|
||||
|
||||
bool isLeapYear(const int year);
|
||||
|
||||
uint32_t getCurrentDay();
|
||||
void setCurrentDay(const uint32_t day);
|
||||
month_id getCurrentMonth();
|
||||
uint16_t getCurrentYear();
|
||||
void setCurrentYear(const int16_t year);
|
||||
|
@ -46,6 +49,9 @@ namespace OpenLoco
|
|||
date getCurrentDate();
|
||||
void setDate(const date& date);
|
||||
|
||||
uint16_t getDayProgression();
|
||||
void setDayProgression(const uint16_t progression);
|
||||
|
||||
/**
|
||||
* Updates the current day counter.
|
||||
* @returns true if the counter wraps indicating a new day.
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
#include "FPSCounter.h"
|
||||
#include "../Graphics/Colour.h"
|
||||
#include "../Graphics/Gfx.h"
|
||||
#include "../Localisation/StringManager.h"
|
||||
#include "../Ui.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace OpenLoco::Drawing
|
||||
{
|
||||
using Clock_t = std::chrono::high_resolution_clock;
|
||||
using TimePoint_t = Clock_t::time_point;
|
||||
|
||||
static TimePoint_t _referenceTime;
|
||||
static uint32_t _currentFrameCount;
|
||||
static float _currentFPS;
|
||||
|
||||
static float measureFPS()
|
||||
{
|
||||
_currentFrameCount++;
|
||||
|
||||
auto currentTime = Clock_t::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - _referenceTime).count() / 1000.0;
|
||||
|
||||
if (elapsed > 1.0)
|
||||
{
|
||||
_currentFPS = _currentFrameCount / elapsed;
|
||||
_currentFrameCount = 0;
|
||||
_referenceTime = currentTime;
|
||||
}
|
||||
|
||||
return _currentFPS;
|
||||
}
|
||||
|
||||
void drawFPS()
|
||||
{
|
||||
// Measure FPS
|
||||
const float fps = measureFPS();
|
||||
|
||||
// Format string
|
||||
char buffer[64];
|
||||
buffer[0] = ControlCodes::font_bold;
|
||||
buffer[1] = ControlCodes::outline;
|
||||
buffer[2] = ControlCodes::colour_white;
|
||||
|
||||
const char* formatString = (_currentFPS >= 10.0f ? "%.0f" : "%.1f");
|
||||
snprintf(&buffer[3], std::size(buffer) - 3, formatString, fps);
|
||||
|
||||
auto& context = Gfx::screenContext();
|
||||
|
||||
// Draw text
|
||||
const int stringWidth = Gfx::getStringWidth(buffer);
|
||||
const auto x = Ui::width() / 2 - (stringWidth / 2);
|
||||
const auto y = 2;
|
||||
Gfx::drawString(context, x, y, Colour::black, buffer);
|
||||
|
||||
// Make area dirty so the text doesn't get drawn over the last
|
||||
Gfx::setDirtyBlocks(x - 16, y - 4, x + 16, 16);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
namespace OpenLoco::Drawing
|
||||
{
|
||||
void drawFPS();
|
||||
}
|
|
@ -10,12 +10,12 @@ using namespace OpenLoco::Ui;
|
|||
|
||||
namespace OpenLoco::Drawing
|
||||
{
|
||||
static loco_global<Ui::screen_info_t, 0x0050B884> screen_info;
|
||||
static loco_global<Ui::ScreenInfo, 0x0050B884> screen_info;
|
||||
static loco_global<uint8_t[1], 0x00E025C4> _E025C4;
|
||||
|
||||
static void windowDraw(drawpixelinfo_t* dpi, Ui::window* w, Rect rect);
|
||||
static void windowDraw(drawpixelinfo_t* dpi, Ui::window* w, int16_t left, int16_t top, int16_t right, int16_t bottom);
|
||||
static bool windowDrawSplit(Gfx::drawpixelinfo_t* dpi, Ui::window* w, int16_t left, int16_t top, int16_t right, int16_t bottom);
|
||||
static void windowDraw(Context* context, Ui::Window* w, Rect rect);
|
||||
static void windowDraw(Context* context, Ui::Window* w, int16_t left, int16_t top, int16_t right, int16_t bottom);
|
||||
static bool windowDrawSplit(Gfx::Context* context, Ui::Window* w, int16_t left, int16_t top, int16_t right, int16_t bottom);
|
||||
|
||||
// T[m][n]
|
||||
template<typename T>
|
||||
|
@ -113,14 +113,8 @@ namespace OpenLoco::Drawing
|
|||
if (grid[y][x] == 0)
|
||||
continue;
|
||||
|
||||
// Determine columns
|
||||
size_t xx;
|
||||
for (xx = x; xx < columns; xx++)
|
||||
{
|
||||
if (grid[y][xx] == 0)
|
||||
break;
|
||||
}
|
||||
size_t dX = xx - x;
|
||||
// Don't determine columns will cause rendering z fighting issues
|
||||
const size_t dX = 1;
|
||||
|
||||
// Check rows
|
||||
size_t dY = grid.getRows(x, dX, y);
|
||||
|
@ -166,14 +160,14 @@ namespace OpenLoco::Drawing
|
|||
regs.dx = rect.bottom() - 1;
|
||||
call(0x00451D98, regs);
|
||||
|
||||
drawpixelinfo_t windowDPI;
|
||||
windowDPI.width = rect.width();
|
||||
windowDPI.height = rect.height();
|
||||
windowDPI.x = rect.left();
|
||||
windowDPI.y = rect.top();
|
||||
windowDPI.bits = screen_info->dpi.bits + rect.left() + ((screen_info->dpi.width + screen_info->dpi.pitch) * rect.top());
|
||||
windowDPI.pitch = screen_info->dpi.width + screen_info->dpi.pitch - rect.width();
|
||||
windowDPI.zoom_level = 0;
|
||||
Context windowContext;
|
||||
windowContext.width = rect.width();
|
||||
windowContext.height = rect.height();
|
||||
windowContext.x = rect.left();
|
||||
windowContext.y = rect.top();
|
||||
windowContext.bits = screen_info->context.bits + rect.left() + ((screen_info->context.width + screen_info->context.pitch) * rect.top());
|
||||
windowContext.pitch = screen_info->context.width + screen_info->context.pitch - rect.width();
|
||||
windowContext.zoom_level = 0;
|
||||
|
||||
for (size_t i = 0; i < Ui::WindowManager::count(); i++)
|
||||
{
|
||||
|
@ -188,13 +182,13 @@ namespace OpenLoco::Drawing
|
|||
if (rect.left() >= w->x + w->width || rect.top() >= w->y + w->height)
|
||||
continue;
|
||||
|
||||
windowDraw(&windowDPI, w, rect);
|
||||
windowDraw(&windowContext, w, rect);
|
||||
}
|
||||
}
|
||||
|
||||
static void windowDraw(drawpixelinfo_t* dpi, Ui::window* w, Rect rect)
|
||||
static void windowDraw(Context* context, Ui::Window* w, Rect rect)
|
||||
{
|
||||
windowDraw(dpi, w, rect.left(), rect.top(), rect.right(), rect.bottom());
|
||||
windowDraw(context, w, rect.left(), rect.top(), rect.right(), rect.bottom());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -206,13 +200,13 @@ namespace OpenLoco::Drawing
|
|||
* @param right @<dx>
|
||||
* @param bottom @<bp>
|
||||
*/
|
||||
static void windowDraw(drawpixelinfo_t* dpi, Ui::window* w, int16_t left, int16_t top, int16_t right, int16_t bottom)
|
||||
static void windowDraw(Context* context, Ui::Window* w, int16_t left, int16_t top, int16_t right, int16_t bottom)
|
||||
{
|
||||
if (!w->isVisible())
|
||||
return;
|
||||
|
||||
// Split window into only the regions that require drawing
|
||||
if (windowDrawSplit(dpi, w, left, top, right, bottom))
|
||||
if (windowDrawSplit(context, w, left, top, right, bottom))
|
||||
return;
|
||||
|
||||
// Clamp region
|
||||
|
@ -226,7 +220,7 @@ namespace OpenLoco::Drawing
|
|||
return;
|
||||
|
||||
// Draw the window in this region
|
||||
Ui::WindowManager::drawSingle(dpi, w, left, top, right, bottom);
|
||||
Ui::WindowManager::drawSingle(context, w, left, top, right, bottom);
|
||||
|
||||
for (uint32_t index = Ui::WindowManager::indexOf(w) + 1; index < Ui::WindowManager::count(); index++)
|
||||
{
|
||||
|
@ -236,14 +230,14 @@ namespace OpenLoco::Drawing
|
|||
if ((v->flags & Ui::WindowFlags::transparent) == 0)
|
||||
continue;
|
||||
|
||||
Ui::WindowManager::drawSingle(dpi, v, left, top, right, bottom);
|
||||
Ui::WindowManager::drawSingle(context, v, left, top, right, bottom);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 0x004C5EA9
|
||||
*
|
||||
* @param dpi
|
||||
* @param context
|
||||
* @param w @<esi>
|
||||
* @param left @<ax>
|
||||
* @param top @<bx>
|
||||
|
@ -251,7 +245,7 @@ namespace OpenLoco::Drawing
|
|||
* @param bottom @<bp>
|
||||
* @return
|
||||
*/
|
||||
static bool windowDrawSplit(Gfx::drawpixelinfo_t* dpi, Ui::window* w, int16_t left, int16_t top, int16_t right, int16_t bottom)
|
||||
static bool windowDrawSplit(Gfx::Context* context, Ui::Window* w, int16_t left, int16_t top, int16_t right, int16_t bottom)
|
||||
{
|
||||
// Divide the draws up for only the visible regions of the window recursively
|
||||
for (uint32_t index = Ui::WindowManager::indexOf(w) + 1; index < Ui::WindowManager::count(); index++)
|
||||
|
@ -270,26 +264,26 @@ namespace OpenLoco::Drawing
|
|||
if (topwindow->x > left)
|
||||
{
|
||||
// Split draw at topwindow.left
|
||||
windowDraw(dpi, w, left, top, topwindow->x, bottom);
|
||||
windowDraw(dpi, w, topwindow->x, top, right, bottom);
|
||||
windowDraw(context, w, left, top, topwindow->x, bottom);
|
||||
windowDraw(context, w, topwindow->x, top, right, bottom);
|
||||
}
|
||||
else if (topwindow->x + topwindow->width < right)
|
||||
{
|
||||
// Split draw at topwindow.right
|
||||
windowDraw(dpi, w, left, top, topwindow->x + topwindow->width, bottom);
|
||||
windowDraw(dpi, w, topwindow->x + topwindow->width, top, right, bottom);
|
||||
windowDraw(context, w, left, top, topwindow->x + topwindow->width, bottom);
|
||||
windowDraw(context, w, topwindow->x + topwindow->width, top, right, bottom);
|
||||
}
|
||||
else if (topwindow->y > top)
|
||||
{
|
||||
// Split draw at topwindow.top
|
||||
windowDraw(dpi, w, left, top, right, topwindow->y);
|
||||
windowDraw(dpi, w, left, topwindow->y, right, bottom);
|
||||
windowDraw(context, w, left, top, right, topwindow->y);
|
||||
windowDraw(context, w, left, topwindow->y, right, bottom);
|
||||
}
|
||||
else if (topwindow->y + topwindow->height < bottom)
|
||||
{
|
||||
// Split draw at topwindow.bottom
|
||||
windowDraw(dpi, w, left, top, right, topwindow->y + topwindow->height);
|
||||
windowDraw(dpi, w, left, topwindow->y + topwindow->height, right, bottom);
|
||||
windowDraw(context, w, left, top, right, topwindow->y + topwindow->height);
|
||||
windowDraw(context, w, left, topwindow->y + topwindow->height, right, bottom);
|
||||
}
|
||||
|
||||
// Drawing for this region should be done now, exit
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace OpenLoco
|
||||
{
|
||||
using currency32_t = int32_t;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct currency48_t
|
||||
{
|
||||
uint32_t var_00 = 0;
|
||||
int16_t var_04 = 0;
|
||||
|
||||
currency48_t(int32_t currency)
|
||||
: currency48_t(static_cast<int64_t>(currency))
|
||||
{
|
||||
}
|
||||
|
||||
currency48_t(int64_t currency)
|
||||
{
|
||||
var_00 = currency & 0xFFFFFFFF;
|
||||
var_04 = (currency >> 32) & 0xFFFF;
|
||||
}
|
||||
|
||||
int64_t asInt64() const
|
||||
{
|
||||
return var_00 | (static_cast<int64_t>(var_04) << 32);
|
||||
}
|
||||
|
||||
bool operator==(const currency48_t rhs) const
|
||||
{
|
||||
return var_00 == rhs.var_00 && var_04 == rhs.var_04;
|
||||
}
|
||||
|
||||
bool operator!=(const currency48_t rhs) const
|
||||
{
|
||||
return !(var_00 == rhs.var_00 && var_04 == rhs.var_04);
|
||||
}
|
||||
|
||||
currency48_t operator+(currency32_t& rhs)
|
||||
{
|
||||
return currency48_t(asInt64() + rhs);
|
||||
}
|
||||
|
||||
currency48_t operator+(currency48_t& rhs)
|
||||
{
|
||||
return currency48_t(asInt64() + rhs.asInt64());
|
||||
}
|
||||
|
||||
currency48_t& operator+=(currency32_t& rhs)
|
||||
{
|
||||
auto sum = currency48_t(asInt64() + rhs);
|
||||
return *this = sum;
|
||||
}
|
||||
|
||||
currency48_t& operator+=(currency48_t& rhs)
|
||||
{
|
||||
auto sum = currency48_t(asInt64() + rhs.asInt64());
|
||||
return *this = sum;
|
||||
}
|
||||
|
||||
currency48_t operator-(currency32_t& rhs)
|
||||
{
|
||||
return currency48_t(asInt64() - rhs);
|
||||
}
|
||||
|
||||
currency48_t operator-(currency48_t& rhs)
|
||||
{
|
||||
return currency48_t(asInt64() - rhs.asInt64());
|
||||
}
|
||||
|
||||
currency48_t& operator-=(currency32_t& rhs)
|
||||
{
|
||||
auto sum = currency48_t(asInt64() - rhs);
|
||||
return *this = sum;
|
||||
}
|
||||
|
||||
currency48_t& operator-=(currency48_t& rhs)
|
||||
{
|
||||
auto sum = currency48_t(asInt64() - rhs.asInt64());
|
||||
return *this = sum;
|
||||
}
|
||||
|
||||
bool operator<(const currency48_t& rhs) const
|
||||
{
|
||||
return asInt64() < rhs.asInt64();
|
||||
}
|
||||
|
||||
bool operator<(const int64_t rhs) const
|
||||
{
|
||||
return asInt64() < rhs;
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(currency48_t) == 6);
|
||||
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
#include "Economy.h"
|
||||
#include "../CompanyManager.h"
|
||||
#include "../Interop/Interop.hpp"
|
||||
#include "../Ui/WindowManager.h"
|
||||
#include "../Ui/WindowType.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::Economy
|
||||
{
|
||||
static const uint32_t _inflationFactors[32] = {
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
23,
|
||||
20,
|
||||
23,
|
||||
23,
|
||||
20,
|
||||
17,
|
||||
17,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
};
|
||||
|
||||
static loco_global<uint32_t[32], 0x00525E5E> currencyMultiplicationFactor;
|
||||
static loco_global<currency32_t[32][60], 0x009C68F8> _deliveredCargoPayment;
|
||||
|
||||
// NB: This is not used for anything due to a mistake in original inflation calculation
|
||||
// looks as if it was meant to be extra precesion for the currencyMultiplicationFactor
|
||||
// Always 0.
|
||||
static loco_global<uint32_t[32], 0x00525EDE> _525EDE;
|
||||
|
||||
// 0x004375F7
|
||||
void buildDeliveredCargoPaymentsTable()
|
||||
{
|
||||
for (uint8_t cargoItem = 0; cargoItem < ObjectManager::getMaxObjects(ObjectType::cargo); ++cargoItem)
|
||||
{
|
||||
auto* cargoObj = ObjectManager::get<CargoObject>(cargoItem);
|
||||
if (cargoObj == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint16_t numDays = 2; numDays <= 122; ++numDays)
|
||||
{
|
||||
_deliveredCargoPayment[cargoItem][(numDays / 2) - 1] = CompanyManager::calculateDeliveredCargoPayment(cargoItem, 100, 10, numDays);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x0046E239
|
||||
// NB: called in sub_46E2C0 below, as well in openloco::date_tick.
|
||||
void updateMonthly()
|
||||
{
|
||||
for (uint8_t i = 0; i < 32; i++)
|
||||
{
|
||||
currencyMultiplicationFactor[i] += (static_cast<uint64_t>(_inflationFactors[i]) * currencyMultiplicationFactor[i]) >> 12;
|
||||
}
|
||||
|
||||
buildDeliveredCargoPaymentsTable();
|
||||
Ui::WindowManager::invalidate(Ui::WindowType::companyList);
|
||||
Ui::WindowManager::invalidate(Ui::WindowType::buildVehicle);
|
||||
Ui::WindowManager::invalidate(Ui::WindowType::construction);
|
||||
Ui::WindowManager::invalidate(Ui::WindowType::terraform);
|
||||
Ui::WindowManager::invalidate(Ui::WindowType::industryList);
|
||||
}
|
||||
|
||||
// 0x0046E2C0
|
||||
void sub_46E2C0(uint16_t year)
|
||||
{
|
||||
for (uint8_t i = 0; i < 32; i++)
|
||||
{
|
||||
currencyMultiplicationFactor[i] = 1024;
|
||||
_525EDE[i] = 0;
|
||||
}
|
||||
|
||||
// OpenLoco allows 1800 as the minimum year, whereas Locomotion uses 1900.
|
||||
// Treat years before 1900 as though they were 1900 to not change vanilla scenarios.
|
||||
const uint32_t baseYear = std::clamp<uint32_t>(year, 1900, 2030) - 1900;
|
||||
|
||||
for (uint32_t monthCount = baseYear * 12; monthCount > 0; monthCount--)
|
||||
{
|
||||
updateMonthly();
|
||||
}
|
||||
}
|
||||
|
||||
currency32_t getInflationAdjustedCost(uint16_t costFactor, uint8_t costIndex, uint8_t divisor)
|
||||
{
|
||||
return costFactor * currencyMultiplicationFactor[costIndex] / (1 << divisor);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "Currency.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace OpenLoco::Economy
|
||||
{
|
||||
void updateMonthly();
|
||||
void sub_46E2C0(uint16_t year);
|
||||
currency32_t getInflationAdjustedCost(uint16_t costFactor, uint8_t costIndex, uint8_t divisor);
|
||||
void buildDeliveredCargoPaymentsTable();
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Types.hpp"
|
||||
|
||||
namespace OpenLoco
|
||||
{
|
||||
enum ExpenditureType : uint8_t
|
||||
{
|
||||
TrainIncome,
|
||||
TrainRunningCosts,
|
||||
BusIncome,
|
||||
BusRunningCosts,
|
||||
TruckIncome,
|
||||
TruckRunningCosts,
|
||||
TramIncome,
|
||||
TramRunningCosts,
|
||||
AircraftIncome,
|
||||
AircraftRunningCosts,
|
||||
ShipIncome,
|
||||
ShipRunningCosts,
|
||||
Construction,
|
||||
VehiclePurchases,
|
||||
VehicleDisposals,
|
||||
LoanInterest,
|
||||
Miscellaneous,
|
||||
Count
|
||||
};
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
#include "EditorController.h"
|
||||
#include "Ui/WindowManager.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
using namespace OpenLoco::Ui;
|
||||
using namespace OpenLoco::Ui::Windows;
|
||||
|
||||
namespace OpenLoco::EditorController
|
||||
{
|
||||
static loco_global<Step, 0x009C8714> _editorStep;
|
||||
|
||||
// 0x0043D7DC
|
||||
void init()
|
||||
{
|
||||
call(0x0043D7DC);
|
||||
}
|
||||
|
||||
Step getCurrentStep()
|
||||
{
|
||||
return _editorStep;
|
||||
}
|
||||
|
||||
Step getPreviousStep()
|
||||
{
|
||||
return Step(static_cast<uint8_t>(*_editorStep) - 1);
|
||||
}
|
||||
|
||||
Step getNextStep()
|
||||
{
|
||||
return Step(static_cast<uint8_t>(*_editorStep) + 1);
|
||||
}
|
||||
|
||||
bool canGoBack()
|
||||
{
|
||||
return getCurrentStep() != Step::objectSelection;
|
||||
}
|
||||
|
||||
// 0x0043D0FA
|
||||
void goToPreviousStep()
|
||||
{
|
||||
call(0x0043D0FA);
|
||||
}
|
||||
|
||||
// 0x0043D15D
|
||||
void goToNextStep()
|
||||
{
|
||||
call(0x0043D15D);
|
||||
}
|
||||
|
||||
// Part of 0x0043D9D4
|
||||
void tick()
|
||||
{
|
||||
switch (getCurrentStep())
|
||||
{
|
||||
case Step::null:
|
||||
break;
|
||||
|
||||
case Step::objectSelection:
|
||||
if (WindowManager::find(WindowType::objectSelection) == nullptr)
|
||||
Windows::ObjectSelectionWindow::open();
|
||||
break;
|
||||
|
||||
case Step::landscapeEditor:
|
||||
// Scenario/landscape loaded?
|
||||
if ((addr<0x00525E28, uint32_t>() & 1) != 0)
|
||||
return;
|
||||
|
||||
if (WindowManager::find(WindowType::landscapeGeneration) == nullptr)
|
||||
Windows::LandscapeGeneration::open();
|
||||
break;
|
||||
|
||||
case Step::scenarioOptions:
|
||||
if (WindowManager::find(WindowType::scenarioOptions) == nullptr)
|
||||
Windows::ScenarioOptions::open();
|
||||
break;
|
||||
|
||||
case Step::saveScenario:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace OpenLoco::EditorController
|
||||
{
|
||||
|
||||
enum class Step : int8_t
|
||||
{
|
||||
null = -1,
|
||||
objectSelection = 0,
|
||||
landscapeEditor = 1,
|
||||
scenarioOptions = 2,
|
||||
saveScenario = 3,
|
||||
};
|
||||
|
||||
void init();
|
||||
|
||||
Step getCurrentStep();
|
||||
Step getPreviousStep();
|
||||
Step getNextStep();
|
||||
bool canGoBack();
|
||||
void goToPreviousStep();
|
||||
void goToNextStep();
|
||||
|
||||
void tick();
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
#include "Thing.h"
|
||||
#include "Entity.h"
|
||||
#include "../Config.h"
|
||||
#include "../Graphics/Gfx.h"
|
||||
#include "../Interop/Interop.hpp"
|
||||
|
@ -8,19 +8,24 @@
|
|||
using namespace OpenLoco;
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
bool EntityBase::isEmpty() const
|
||||
{
|
||||
return base_type == EntityBaseType::null;
|
||||
}
|
||||
|
||||
// 0x0046FC83
|
||||
void thing_base::moveTo(loc16 loc)
|
||||
void EntityBase::moveTo(const Map::Pos3& loc)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = loc.x;
|
||||
regs.cx = loc.y;
|
||||
regs.dx = loc.z;
|
||||
regs.esi = (int32_t)this;
|
||||
regs.esi = X86Pointer(this);
|
||||
call(0x0046FC83, regs);
|
||||
}
|
||||
|
||||
// 0x004CBB01
|
||||
void OpenLoco::thing_base::invalidateSprite()
|
||||
void OpenLoco::EntityBase::invalidateSprite()
|
||||
{
|
||||
Ui::ViewportManager::invalidate((Thing*)this, ZoomLevel::eighth);
|
||||
Ui::ViewportManager::invalidate(this, ZoomLevel::eighth);
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Location.hpp"
|
||||
#include "../Map/Map.hpp"
|
||||
#include "../Types.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
namespace OpenLoco::Vehicles
|
||||
{
|
||||
struct VehicleBase;
|
||||
}
|
||||
|
||||
namespace OpenLoco
|
||||
{
|
||||
struct MiscBase;
|
||||
|
||||
namespace EntityId
|
||||
{
|
||||
constexpr EntityId_t null = std::numeric_limits<EntityId_t>::max();
|
||||
}
|
||||
|
||||
enum class EntityBaseType : uint8_t
|
||||
{
|
||||
vehicle = 0,
|
||||
misc,
|
||||
null = 0xFF
|
||||
};
|
||||
|
||||
enum class Pitch : uint8_t
|
||||
{
|
||||
// actual angle (for trig)
|
||||
flat = 0,
|
||||
up6deg = 1, // Transition, 5.75 deg
|
||||
up12deg = 2, // Gentle, 11.75 deg
|
||||
up18deg = 3, // Transition, 17 deg
|
||||
up25deg = 4, // Steep, 22.5 deg
|
||||
down6deg = 5,
|
||||
down12deg = 6,
|
||||
down18deg = 7,
|
||||
down25deg = 8,
|
||||
up10deg = 9, // Gentle Curve Up, 10 deg
|
||||
down10deg = 10,
|
||||
up20deg = 11, // Steep Curve Up, 19.25 deg
|
||||
down20deg = 12,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct EntityBase
|
||||
{
|
||||
EntityBaseType base_type;
|
||||
|
||||
private:
|
||||
uint8_t type; // Use type specific getters/setters as this depends on base_type
|
||||
public:
|
||||
EntityId_t nextQuadrantId; // 0x02
|
||||
EntityId_t next_thing_id; // 0x04
|
||||
EntityId_t llPreviousId; // 0x06
|
||||
uint8_t linkedListOffset; // 0x8
|
||||
uint8_t var_09;
|
||||
EntityId_t id; // 0xA
|
||||
uint16_t var_0C;
|
||||
Map::Pos3 position; // 0x0E
|
||||
uint8_t var_14;
|
||||
uint8_t var_15;
|
||||
int16_t sprite_left; // 0x16
|
||||
int16_t sprite_top; // 0x18
|
||||
int16_t sprite_right; // 0x1A
|
||||
int16_t sprite_bottom; // 0x1C
|
||||
uint8_t sprite_yaw; // 0x1E
|
||||
Pitch sprite_pitch; // 0x1F
|
||||
uint8_t pad_20;
|
||||
CompanyId_t owner; // 0x21
|
||||
string_id name; // 0x22, combined with ordinalNumber on vehicles
|
||||
|
||||
void moveTo(const Map::Pos3& loc);
|
||||
void invalidateSprite();
|
||||
|
||||
Vehicles::VehicleBase* asVehicle() const { return asBase<Vehicles::VehicleBase, EntityBaseType::vehicle>(); }
|
||||
MiscBase* asMisc() const { return asBase<MiscBase, EntityBaseType::misc>(); }
|
||||
bool isEmpty() const;
|
||||
|
||||
protected:
|
||||
constexpr uint8_t getSubType() const { return type; }
|
||||
void setSubType(const uint8_t newType) { type = newType; }
|
||||
|
||||
private:
|
||||
template<typename TType, EntityBaseType TClass>
|
||||
TType* asBase() const
|
||||
{
|
||||
return base_type == TClass ? (TType*)this : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// Max size of a Entity. Use when needing to know Entity size
|
||||
struct Entity : EntityBase
|
||||
{
|
||||
private:
|
||||
uint8_t pad_24[0x80 - 0x24];
|
||||
};
|
||||
static_assert(sizeof(Entity) == 0x80);
|
||||
#pragma pack(pop)
|
||||
}
|
|
@ -0,0 +1,383 @@
|
|||
#include "EntityManager.h"
|
||||
#include "../Console.h"
|
||||
#include "../Entities/Misc.h"
|
||||
#include "../GameCommands/GameCommands.h"
|
||||
#include "../Interop/Interop.hpp"
|
||||
#include "../Localisation/StringIds.h"
|
||||
#include "../Map/Tile.h"
|
||||
#include "../OpenLoco.h"
|
||||
#include "../Vehicles/Vehicle.h"
|
||||
#include "EntityTweener.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::EntityManager
|
||||
{
|
||||
loco_global<EntityId_t[numEntityLists], 0x00525E3E> _heads;
|
||||
loco_global<uint16_t[numEntityLists], 0x00525E4C> _listCounts;
|
||||
loco_global<Entity[maxEntities], 0x006DB6DC> _entities;
|
||||
loco_global<EntityId_t[0x40001], 0x01025A8C> _entitySpatialIndex;
|
||||
loco_global<uint32_t, 0x01025A88> _entitySpatialCount;
|
||||
constexpr size_t _entitySpatialIndexNull = 0x40000;
|
||||
|
||||
// 0x0046FDFD
|
||||
void reset()
|
||||
{
|
||||
// Reset all entities to 0
|
||||
std::fill_n(_entities.get(), maxEntities, Entity{});
|
||||
// Reset all entity lists
|
||||
for (auto& count : _listCounts)
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
for (auto& head : _heads)
|
||||
{
|
||||
head = EntityId::null;
|
||||
}
|
||||
|
||||
// Remake null entities (size maxNormalEntities)
|
||||
EntityBase* previous = nullptr;
|
||||
EntityId_t id = 0;
|
||||
for (; id < maxNormalEntities; ++id)
|
||||
{
|
||||
auto& ent = _entities[id];
|
||||
ent.base_type = EntityBaseType::null;
|
||||
ent.id = id;
|
||||
ent.next_thing_id = EntityId::null;
|
||||
ent.linkedListOffset = static_cast<uint8_t>(EntityListType::null) * 2;
|
||||
if (previous == nullptr)
|
||||
{
|
||||
ent.llPreviousId = EntityId::null;
|
||||
_heads[static_cast<uint8_t>(EntityListType::null)] = id;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent.llPreviousId = previous->id;
|
||||
previous->next_thing_id = id;
|
||||
}
|
||||
previous = &ent;
|
||||
}
|
||||
_listCounts[static_cast<uint8_t>(EntityListType::null)] = maxNormalEntities;
|
||||
|
||||
// Remake null money entities (size maxMoneyEntities)
|
||||
previous = nullptr;
|
||||
for (; id < maxEntities; ++id)
|
||||
{
|
||||
auto& ent = _entities[id];
|
||||
ent.base_type = EntityBaseType::null;
|
||||
ent.id = id;
|
||||
ent.next_thing_id = EntityId::null;
|
||||
ent.linkedListOffset = static_cast<uint8_t>(EntityListType::nullMoney) * 2;
|
||||
if (previous == nullptr)
|
||||
{
|
||||
ent.llPreviousId = EntityId::null;
|
||||
_heads[static_cast<uint8_t>(EntityListType::nullMoney)] = id;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent.llPreviousId = previous->id;
|
||||
previous->next_thing_id = id;
|
||||
}
|
||||
previous = &ent;
|
||||
}
|
||||
_listCounts[static_cast<uint8_t>(EntityListType::nullMoney)] = maxMoneyEntities;
|
||||
|
||||
resetSpatialIndex();
|
||||
EntityTweener::get().reset();
|
||||
}
|
||||
|
||||
EntityId_t firstId(EntityListType list)
|
||||
{
|
||||
return _heads[(size_t)list];
|
||||
}
|
||||
|
||||
uint16_t getListCount(const EntityListType list)
|
||||
{
|
||||
return _listCounts[static_cast<size_t>(list)];
|
||||
}
|
||||
|
||||
template<>
|
||||
Vehicles::VehicleHead* first()
|
||||
{
|
||||
return get<Vehicles::VehicleHead>(firstId(EntityListType::vehicleHead));
|
||||
}
|
||||
|
||||
template<>
|
||||
EntityBase* get(EntityId_t id)
|
||||
{
|
||||
EntityBase* result = nullptr;
|
||||
if (id < maxEntities)
|
||||
{
|
||||
return &_entities.get()[id];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr size_t getSpatialIndexOffset(const Map::Pos2& loc)
|
||||
{
|
||||
size_t index = _entitySpatialIndexNull;
|
||||
if (loc.x != Location::null)
|
||||
{
|
||||
uint16_t x = loc.x & 0x3FE0;
|
||||
uint16_t y = loc.y & 0x3FE0;
|
||||
|
||||
index = (x << 4) | (y >> 5);
|
||||
}
|
||||
|
||||
if (index >= 0x40001)
|
||||
{
|
||||
return _entitySpatialIndexNull;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
EntityId_t firstQuadrantId(const Map::Pos2& loc)
|
||||
{
|
||||
auto index = getSpatialIndexOffset(loc);
|
||||
return _entitySpatialIndex[index];
|
||||
}
|
||||
|
||||
// 0x0046FF54
|
||||
void resetSpatialIndex()
|
||||
{
|
||||
call(0x0046FF54);
|
||||
}
|
||||
|
||||
// 0x0046FC57
|
||||
void updateSpatialIndex()
|
||||
{
|
||||
for (auto& ent : _entities)
|
||||
{
|
||||
if (!ent.isEmpty())
|
||||
{
|
||||
ent.moveTo(ent.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static EntityBase* createEntity(EntityId_t id, EntityListType list)
|
||||
{
|
||||
auto* newEntity = get<EntityBase>(id);
|
||||
if (newEntity == nullptr)
|
||||
{
|
||||
Console::error("Tried to create invalid entity!");
|
||||
return nullptr;
|
||||
}
|
||||
moveEntityToList(newEntity, list);
|
||||
|
||||
newEntity->position = { Location::null, Location::null, 0 };
|
||||
auto index = getSpatialIndexOffset(newEntity->position);
|
||||
auto nextSpatialId = _entitySpatialIndex[index];
|
||||
_entitySpatialIndex[index] = newEntity->id;
|
||||
newEntity->nextQuadrantId = nextSpatialId;
|
||||
|
||||
newEntity->name = StringIds::empty_pop;
|
||||
newEntity->var_14 = 16;
|
||||
newEntity->var_09 = 20;
|
||||
newEntity->var_15 = 8;
|
||||
newEntity->var_0C = 0;
|
||||
newEntity->sprite_left = Location::null;
|
||||
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
// 0x004700A5
|
||||
EntityBase* createEntityMisc()
|
||||
{
|
||||
if (getListCount(EntityListType::misc) >= maxMiscEntities)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (getListCount(EntityListType::null) <= 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto newId = _heads[static_cast<uint8_t>(EntityListType::null)];
|
||||
return createEntity(newId, EntityListType::misc);
|
||||
}
|
||||
|
||||
// 0x0047011C
|
||||
EntityBase* createEntityMoney()
|
||||
{
|
||||
if (getListCount(EntityListType::nullMoney) <= 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto newId = _heads[static_cast<uint8_t>(EntityListType::nullMoney)];
|
||||
return createEntity(newId, EntityListType::misc);
|
||||
}
|
||||
|
||||
// 0x00470039
|
||||
EntityBase* createEntityVehicle()
|
||||
{
|
||||
if (getListCount(EntityListType::null) <= 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto newId = _heads[static_cast<uint8_t>(EntityListType::null)];
|
||||
return createEntity(newId, EntityListType::vehicle);
|
||||
}
|
||||
|
||||
// 0x0047024A
|
||||
void freeEntity(EntityBase* const entity)
|
||||
{
|
||||
EntityTweener::get().removeEntity(entity);
|
||||
|
||||
auto list = entity->id < 19800 ? EntityListType::null : EntityListType::nullMoney;
|
||||
moveEntityToList(entity, list);
|
||||
StringManager::emptyUserString(entity->name);
|
||||
entity->base_type = EntityBaseType::null;
|
||||
|
||||
// Remove from spatial lists
|
||||
auto* quadId = &_entitySpatialIndex[getSpatialIndexOffset(entity->position)];
|
||||
_entitySpatialCount = 0;
|
||||
while (*quadId < maxEntities)
|
||||
{
|
||||
auto* quadEnt = get<EntityBase>(*quadId);
|
||||
if (quadEnt == entity)
|
||||
{
|
||||
*quadId = entity->nextQuadrantId;
|
||||
return;
|
||||
}
|
||||
_entitySpatialCount++;
|
||||
if (_entitySpatialCount > maxEntities)
|
||||
{
|
||||
break;
|
||||
}
|
||||
quadId = &quadEnt->nextQuadrantId;
|
||||
}
|
||||
Console::log("Invalid quadrant ids... Reseting spatial index.");
|
||||
resetSpatialIndex();
|
||||
}
|
||||
|
||||
// 0x004A8826
|
||||
void updateVehicles()
|
||||
{
|
||||
if ((addr<0x00525E28, uint32_t>() & 1) && !isEditorMode())
|
||||
{
|
||||
for (auto v : VehicleList())
|
||||
{
|
||||
v->updateVehicle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x004402F4
|
||||
void updateMiscEntities()
|
||||
{
|
||||
if ((addr<0x00525E28, uint32_t>() & 1))
|
||||
{
|
||||
for (auto* misc : EntityList<EntityListIterator<MiscBase>, EntityListType::misc>())
|
||||
{
|
||||
misc->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x0047019F
|
||||
void moveEntityToList(EntityBase* const entity, const EntityListType list)
|
||||
{
|
||||
auto newListOffset = static_cast<uint8_t>(list) * 2;
|
||||
if (entity->linkedListOffset == newListOffset)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto curList = entity->linkedListOffset / 2;
|
||||
auto nextId = entity->next_thing_id;
|
||||
auto previousId = entity->llPreviousId;
|
||||
|
||||
// Unlink previous entity from this entity
|
||||
if (previousId == EntityId::null)
|
||||
{
|
||||
_heads[curList] = nextId;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* previousEntity = get<EntityBase>(previousId);
|
||||
if (previousEntity == nullptr)
|
||||
{
|
||||
Console::error("Invalid previous entity id. Entity linked list corrupted?");
|
||||
}
|
||||
else
|
||||
{
|
||||
previousEntity->next_thing_id = nextId;
|
||||
}
|
||||
}
|
||||
// Unlink next entity from this entity
|
||||
if (nextId != EntityId::null)
|
||||
{
|
||||
auto* nextEntity = get<EntityBase>(nextId);
|
||||
if (nextEntity == nullptr)
|
||||
{
|
||||
Console::error("Invalid next entity id. Entity linked list corrupted?");
|
||||
}
|
||||
else
|
||||
{
|
||||
nextEntity->llPreviousId = previousId;
|
||||
}
|
||||
}
|
||||
|
||||
entity->llPreviousId = EntityId::null;
|
||||
entity->linkedListOffset = newListOffset;
|
||||
entity->next_thing_id = _heads[static_cast<uint8_t>(list)];
|
||||
_heads[static_cast<uint8_t>(list)] = entity->id;
|
||||
// Link next entity to this entity
|
||||
if (entity->next_thing_id != EntityId::null)
|
||||
{
|
||||
auto* nextEntity = get<EntityBase>(entity->next_thing_id);
|
||||
if (nextEntity == nullptr)
|
||||
{
|
||||
Console::error("Invalid next entity id. Entity linked list corrupted?");
|
||||
}
|
||||
else
|
||||
{
|
||||
nextEntity->llPreviousId = entity->id;
|
||||
}
|
||||
}
|
||||
|
||||
_listCounts[curList]--;
|
||||
_listCounts[static_cast<uint8_t>(list)]++;
|
||||
}
|
||||
|
||||
// 0x00470188
|
||||
bool checkNumFreeEntities(const size_t numNewEntities)
|
||||
{
|
||||
if (EntityManager::getListCount(EntityManager::EntityListType::null) <= numNewEntities)
|
||||
{
|
||||
GameCommands::setErrorText(StringIds::too_many_objects_in_game);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void zeroEntity(EntityBase* ent)
|
||||
{
|
||||
auto next = ent->next_thing_id;
|
||||
auto previous = ent->llPreviousId;
|
||||
auto id = ent->id;
|
||||
auto llOffset = ent->linkedListOffset;
|
||||
std::fill_n(reinterpret_cast<uint8_t*>(ent), sizeof(Entity), 0);
|
||||
ent->base_type = EntityBaseType::null;
|
||||
ent->next_thing_id = next;
|
||||
ent->llPreviousId = previous;
|
||||
ent->id = id;
|
||||
ent->linkedListOffset = llOffset;
|
||||
}
|
||||
|
||||
// 0x0046FED5
|
||||
void zeroUnused()
|
||||
{
|
||||
for (auto* ent : EntityList<EntityListIterator<EntityBase>, EntityListType::null>())
|
||||
{
|
||||
zeroEntity(ent);
|
||||
}
|
||||
for (auto* ent : EntityList<EntityListIterator<EntityBase>, EntityListType::nullMoney>())
|
||||
{
|
||||
zeroEntity(ent);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Map/Map.hpp"
|
||||
#include "Entity.h"
|
||||
#include <cstdio>
|
||||
#include <iterator>
|
||||
|
||||
namespace OpenLoco::Vehicles
|
||||
{
|
||||
struct VehicleHead;
|
||||
}
|
||||
|
||||
namespace OpenLoco::EntityManager
|
||||
{
|
||||
constexpr size_t numEntityLists = 7;
|
||||
constexpr size_t maxEntities = 20000;
|
||||
// There is a seperate pool of 200 entities dedicated for money
|
||||
constexpr size_t maxMoneyEntities = 200;
|
||||
// This is the main pool for everything that isn't money
|
||||
constexpr size_t maxNormalEntities = maxEntities - maxMoneyEntities;
|
||||
// Money is not counted in this limit
|
||||
constexpr size_t maxMiscEntities = 4000;
|
||||
|
||||
enum class EntityListType
|
||||
{
|
||||
null, // Used for vehicles and other misc entities (not money)
|
||||
nullMoney, // For some reason money effects have their own pool of entities to use
|
||||
vehicleHead,
|
||||
misc = 4,
|
||||
vehicle = 6,
|
||||
};
|
||||
|
||||
void reset();
|
||||
|
||||
template<typename T>
|
||||
T* get(EntityId_t id);
|
||||
|
||||
template<>
|
||||
EntityBase* get(EntityId_t id);
|
||||
|
||||
template<typename T>
|
||||
T* get(EntityId_t id)
|
||||
{
|
||||
return static_cast<T*>(get<EntityBase>(id));
|
||||
}
|
||||
|
||||
EntityId_t firstId(EntityListType list);
|
||||
|
||||
template<typename T>
|
||||
T* first();
|
||||
|
||||
EntityId_t firstQuadrantId(const Map::Pos2& loc);
|
||||
void resetSpatialIndex();
|
||||
void updateSpatialIndex();
|
||||
|
||||
EntityBase* createEntityMisc();
|
||||
EntityBase* createEntityMoney();
|
||||
EntityBase* createEntityVehicle();
|
||||
void freeEntity(EntityBase* const entity);
|
||||
|
||||
void updateVehicles();
|
||||
void updateMiscEntities();
|
||||
|
||||
uint16_t getListCount(const EntityListType list);
|
||||
void moveEntityToList(EntityBase* const entity, const EntityListType list);
|
||||
bool checkNumFreeEntities(const size_t numNewEntities);
|
||||
void zeroUnused();
|
||||
|
||||
template<typename TEntityType, EntityId_t EntityBase::*nextList>
|
||||
class ListIterator
|
||||
{
|
||||
private:
|
||||
TEntityType* entity = nullptr;
|
||||
EntityId_t nextEntityId = EntityId::null;
|
||||
|
||||
public:
|
||||
ListIterator(const uint16_t _headId)
|
||||
: nextEntityId(_headId)
|
||||
{
|
||||
++(*this);
|
||||
}
|
||||
|
||||
ListIterator& operator++()
|
||||
{
|
||||
entity = get<TEntityType>(nextEntityId);
|
||||
|
||||
if (entity)
|
||||
{
|
||||
nextEntityId = entity->*nextList;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ListIterator operator++(int)
|
||||
{
|
||||
ListIterator retval = *this;
|
||||
++(*this);
|
||||
return retval;
|
||||
}
|
||||
bool operator==(ListIterator& other) const
|
||||
{
|
||||
return entity == other.entity;
|
||||
}
|
||||
bool operator!=(ListIterator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
TEntityType* operator*()
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
// iterator traits
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = TEntityType;
|
||||
using pointer = TEntityType*;
|
||||
using reference = TEntityType&;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using EntityListIterator = ListIterator<T, &EntityBase::next_thing_id>;
|
||||
|
||||
template<typename T, EntityListType list>
|
||||
class EntityList
|
||||
{
|
||||
private:
|
||||
uint16_t firstId = EntityId::null;
|
||||
|
||||
public:
|
||||
EntityList()
|
||||
{
|
||||
firstId = EntityManager::firstId(list);
|
||||
}
|
||||
|
||||
T begin()
|
||||
{
|
||||
return T(firstId);
|
||||
}
|
||||
T end()
|
||||
{
|
||||
return T(EntityId::null);
|
||||
}
|
||||
};
|
||||
|
||||
using VehicleList = EntityList<EntityListIterator<Vehicles::VehicleHead>, EntityListType::vehicleHead>;
|
||||
|
||||
class EntityTileList
|
||||
{
|
||||
private:
|
||||
uint16_t firstId = EntityId::null;
|
||||
using Iterator = ListIterator<EntityBase, &EntityBase::nextQuadrantId>;
|
||||
|
||||
public:
|
||||
EntityTileList(const Map::Pos2& loc)
|
||||
{
|
||||
firstId = EntityManager::firstQuadrantId(loc);
|
||||
}
|
||||
|
||||
Iterator begin()
|
||||
{
|
||||
return Iterator(firstId);
|
||||
}
|
||||
Iterator end()
|
||||
{
|
||||
return Iterator(EntityId::null);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
#include "EntityTweener.h"
|
||||
#include "../OpenLoco.h"
|
||||
#include "../Vehicles/Vehicle.h"
|
||||
#include "Entity.h"
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenLoco
|
||||
{
|
||||
using EntityListType = EntityManager::EntityListType;
|
||||
using EntityListIterator = EntityManager::ListIterator<EntityBase, &EntityBase::next_thing_id>;
|
||||
|
||||
template<EntityListType id, typename Pred>
|
||||
void PopulateEntities(std::vector<EntityBase*>& list, std::vector<Map::Pos3>& posList, const Pred& pred)
|
||||
{
|
||||
auto entsView = EntityManager::EntityList<EntityListIterator, id>();
|
||||
for (auto* ent : entsView)
|
||||
{
|
||||
if (!pred(ent))
|
||||
continue;
|
||||
|
||||
list.push_back(ent);
|
||||
posList.emplace_back(ent->position);
|
||||
}
|
||||
}
|
||||
|
||||
static EntityTweener _tweener;
|
||||
|
||||
EntityTweener& EntityTweener::get()
|
||||
{
|
||||
return _tweener;
|
||||
}
|
||||
|
||||
void EntityTweener::preTick()
|
||||
{
|
||||
restore();
|
||||
reset();
|
||||
PopulateEntities<EntityListType::misc>(_entities, _prePos, [](auto* ent) { return true; });
|
||||
PopulateEntities<EntityListType::vehicle>(_entities, _prePos, [](auto* ent) {
|
||||
const auto* vehicle = ent->asVehicle();
|
||||
if (vehicle == nullptr)
|
||||
{
|
||||
// This can be never null but makes the compiler happy.
|
||||
return false;
|
||||
}
|
||||
return vehicle->isVehicleBody() || vehicle->isVehicleBogie();
|
||||
});
|
||||
}
|
||||
|
||||
void EntityTweener::postTick()
|
||||
{
|
||||
for (auto* ent : _entities)
|
||||
{
|
||||
if (ent == nullptr || ent->id == EntityId::null)
|
||||
{
|
||||
// Sprite was removed, add a dummy position to keep the index aligned.
|
||||
_postPos.emplace_back(0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_postPos.emplace_back(ent->position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTweener::removeEntity(const EntityBase* entity)
|
||||
{
|
||||
auto it = std::find(_entities.begin(), _entities.end(), entity);
|
||||
if (it != _entities.end())
|
||||
{
|
||||
*it = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTweener::tween(float alpha)
|
||||
{
|
||||
const float inv = (1.0f - alpha);
|
||||
|
||||
for (size_t i = 0; i < _entities.size(); ++i)
|
||||
{
|
||||
auto* ent = _entities[i];
|
||||
if (ent == nullptr)
|
||||
continue;
|
||||
|
||||
auto& posA = _prePos[i];
|
||||
auto& posB = _postPos[i];
|
||||
|
||||
if (posA == posB)
|
||||
continue;
|
||||
|
||||
auto newPos = Map::Pos3{ static_cast<int16_t>(std::round(posB.x * alpha + posA.x * inv)),
|
||||
static_cast<int16_t>(std::round(posB.y * alpha + posA.y * inv)),
|
||||
static_cast<int16_t>(std::round(posB.z * alpha + posA.z * inv)) };
|
||||
|
||||
if (ent->position == newPos)
|
||||
continue;
|
||||
|
||||
ent->moveTo(newPos);
|
||||
ent->invalidateSprite();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTweener::restore()
|
||||
{
|
||||
for (size_t i = 0; i < _entities.size(); ++i)
|
||||
{
|
||||
auto* ent = _entities[i];
|
||||
if (ent == nullptr)
|
||||
continue;
|
||||
|
||||
auto& newPos = _postPos[i];
|
||||
|
||||
if (ent->position == newPos)
|
||||
continue;
|
||||
|
||||
ent->moveTo(newPos);
|
||||
ent->invalidateSprite();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTweener::reset()
|
||||
{
|
||||
_entities.clear();
|
||||
_prePos.clear();
|
||||
_postPos.clear();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Map/Map.hpp"
|
||||
#include "EntityManager.h"
|
||||
#include <vector>
|
||||
|
||||
namespace OpenLoco
|
||||
{
|
||||
class EntityTweener
|
||||
{
|
||||
std::vector<EntityBase*> _entities;
|
||||
std::vector<Map::Pos3> _prePos;
|
||||
std::vector<Map::Pos3> _postPos;
|
||||
|
||||
public:
|
||||
static EntityTweener& get();
|
||||
|
||||
void preTick();
|
||||
void postTick();
|
||||
void removeEntity(const EntityBase* entity);
|
||||
void tween(float alpha);
|
||||
void restore();
|
||||
void reset();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
#include "Misc.h"
|
||||
#include "../Localisation/FormatArguments.hpp"
|
||||
#include "../Localisation/StringIds.h"
|
||||
#include "../Map/TileManager.h"
|
||||
#include "../Objects/ObjectManager.h"
|
||||
#include "../Ui/WindowManager.h"
|
||||
#include "EntityManager.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco
|
||||
{
|
||||
// 0x004405CD
|
||||
void MiscBase::update()
|
||||
{
|
||||
registers regs;
|
||||
regs.esi = X86Pointer(this);
|
||||
call(0x004405CD, regs);
|
||||
}
|
||||
|
||||
SteamObject* Exhaust::object() const
|
||||
{
|
||||
return ObjectManager::get<SteamObject>(object_id & 0x7F);
|
||||
}
|
||||
|
||||
// 0x0044080C
|
||||
Exhaust* Exhaust::create(Map::Pos3 loc, uint8_t type)
|
||||
{
|
||||
if ((uint16_t)loc.x > 12287 || (uint16_t)loc.y > 12287)
|
||||
return nullptr;
|
||||
auto surface = Map::TileManager::get(loc.x & 0xFFE0, loc.y & 0xFFE0).surface();
|
||||
|
||||
if (surface == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (loc.z <= surface->baseZ() * 4)
|
||||
return nullptr;
|
||||
|
||||
auto _exhaust = static_cast<Exhaust*>(EntityManager::createEntityMisc());
|
||||
|
||||
if (_exhaust != nullptr)
|
||||
{
|
||||
_exhaust->base_type = EntityBaseType::misc;
|
||||
_exhaust->moveTo(loc);
|
||||
_exhaust->object_id = type;
|
||||
auto obj = _exhaust->object();
|
||||
_exhaust->var_14 = obj->var_05;
|
||||
_exhaust->var_09 = obj->var_06;
|
||||
_exhaust->var_15 = obj->var_07;
|
||||
_exhaust->setSubType(MiscEntityType::exhaust);
|
||||
_exhaust->var_26 = 0;
|
||||
_exhaust->var_28 = 0;
|
||||
_exhaust->var_32 = 0;
|
||||
_exhaust->var_34 = 0;
|
||||
_exhaust->var_36 = 0;
|
||||
}
|
||||
return _exhaust;
|
||||
}
|
||||
|
||||
// 0x00440BEB
|
||||
Smoke* Smoke::create(Map::Pos3 loc)
|
||||
{
|
||||
auto t = static_cast<Smoke*>(EntityManager::createEntityMisc());
|
||||
if (t != nullptr)
|
||||
{
|
||||
t->var_14 = 44;
|
||||
t->var_09 = 32;
|
||||
t->var_15 = 34;
|
||||
t->base_type = EntityBaseType::misc;
|
||||
t->moveTo(loc);
|
||||
t->setSubType(MiscEntityType::smoke);
|
||||
t->frame = 0;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static loco_global<int32_t, 0x112C876> _currentFontSpriteBase;
|
||||
|
||||
// 0x00440A74
|
||||
// company : updatingCompanyId global
|
||||
// loc : ax, cx, dx
|
||||
// amount : ebx
|
||||
MoneyEffect* MoneyEffect::create(const Map::Pos3& loc, const CompanyId_t company, const currency32_t amount)
|
||||
{
|
||||
if (isTitleMode())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* m = static_cast<MoneyEffect*>(EntityManager::createEntityMoney());
|
||||
if (m != nullptr)
|
||||
{
|
||||
m->amount = amount;
|
||||
m->var_14 = 64;
|
||||
m->var_09 = 20;
|
||||
m->var_15 = 30;
|
||||
m->base_type = EntityBaseType::misc;
|
||||
m->var_2E = company;
|
||||
m->moveTo(loc);
|
||||
m->setSubType(MiscEntityType::windowCurrency);
|
||||
m->var_26 = 0;
|
||||
m->var_28 = 0;
|
||||
|
||||
string_id strFormat = (amount < 0) ? StringIds::format_currency_expense_red_negative : StringIds::format_currency_income_green;
|
||||
char buffer[255] = {};
|
||||
auto args = FormatArguments::common(amount);
|
||||
StringManager::formatString(buffer, strFormat, &args);
|
||||
_currentFontSpriteBase = Font::medium_bold;
|
||||
m->offsetX = -Gfx::getStringWidth(buffer) / 2;
|
||||
m->wiggle = 0;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Economy/Currency.h"
|
||||
#include "../Map/Map.hpp"
|
||||
#include "../Objects/SteamObject.h"
|
||||
#include "Entity.h"
|
||||
|
||||
namespace OpenLoco
|
||||
{
|
||||
struct Exhaust;
|
||||
struct MoneyEffect;
|
||||
struct VehicleCrashParticle;
|
||||
struct ExplosionCloud;
|
||||
struct Splash;
|
||||
struct Fireball;
|
||||
struct ExplosionSmoke;
|
||||
struct Smoke;
|
||||
|
||||
enum class MiscEntityType : uint8_t
|
||||
{
|
||||
exhaust = 0, // Steam from the exhaust
|
||||
redGreenCurrency = 1,
|
||||
windowCurrency = 2, // currency which is created in the company's colour when a transaction is made (for example the train arrives with a passengers into the station)
|
||||
vehicleCrashParticle = 3, // parts (particles) of vehicle after crash which they fall to the ground after explosion
|
||||
explosionCloud = 4, // explosion which is created when two trains (or maybe other vehicles) crash to each other
|
||||
splash = 5, // splash when particles after explosion land to water and creates a splash (exploding train on the bridge)
|
||||
fireball = 6,
|
||||
explosionSmoke = 7,
|
||||
smoke = 8 // Smoke from broken down train
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct MiscBase : EntityBase
|
||||
{
|
||||
private:
|
||||
template<typename TType, MiscEntityType TClass>
|
||||
TType* as() const
|
||||
{
|
||||
return getSubType() == TClass ? (TType*)this : nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
MiscEntityType getSubType() const { return MiscEntityType(EntityBase::getSubType()); }
|
||||
void setSubType(const MiscEntityType newType) { EntityBase::setSubType(static_cast<uint8_t>(newType)); }
|
||||
Exhaust* asExhaust() const { return as<Exhaust, MiscEntityType::exhaust>(); }
|
||||
MoneyEffect* asRedGreenCurrency() const { return as<MoneyEffect, MiscEntityType::redGreenCurrency>(); }
|
||||
MoneyEffect* asWindowCurrency() const { return as<MoneyEffect, MiscEntityType::windowCurrency>(); }
|
||||
VehicleCrashParticle* asVehicleCrashParticle() const { return as<VehicleCrashParticle, MiscEntityType::vehicleCrashParticle>(); }
|
||||
ExplosionCloud* asExplosionCloud() const { return as<ExplosionCloud, MiscEntityType::explosionCloud>(); }
|
||||
Splash* asSplash() const { return as<Splash, MiscEntityType::splash>(); }
|
||||
Fireball* asFireball() const { return as<Fireball, MiscEntityType::fireball>(); }
|
||||
ExplosionSmoke* asExplosionSmoke() const { return as<ExplosionSmoke, MiscEntityType::explosionSmoke>(); }
|
||||
Smoke* asSmoke() const { return as<Smoke, MiscEntityType::smoke>(); }
|
||||
void update();
|
||||
};
|
||||
|
||||
struct Exhaust : MiscBase
|
||||
{
|
||||
uint8_t pad_24[0x26 - 0x24];
|
||||
int16_t var_26;
|
||||
int16_t var_28;
|
||||
uint8_t pad_2A[0x32 - 0x2A];
|
||||
int16_t var_32;
|
||||
int16_t var_34;
|
||||
int16_t var_36;
|
||||
uint8_t pad_38[0x49 - 0x38];
|
||||
uint8_t object_id; // 0x49
|
||||
|
||||
SteamObject* object() const;
|
||||
|
||||
static Exhaust* create(Map::Pos3 loc, uint8_t type);
|
||||
};
|
||||
static_assert(sizeof(Exhaust) == 0x4A);
|
||||
|
||||
struct MoneyEffect : MiscBase
|
||||
{
|
||||
uint8_t pad_24[0x26 - 0x24];
|
||||
uint16_t var_26;
|
||||
uint16_t var_28;
|
||||
int32_t amount; // 0x2A - currency amount in British pounds - different currencies are probably getting recalculated
|
||||
int8_t var_2E; // company colour?
|
||||
uint8_t pad_2F[0x44 - 0x2F];
|
||||
int16_t offsetX; // 0x44
|
||||
uint16_t wiggle; // 0x46
|
||||
|
||||
static MoneyEffect* create(const Map::Pos3& loc, const CompanyId_t company, const currency32_t amount);
|
||||
};
|
||||
static_assert(sizeof(MoneyEffect) == 0x48);
|
||||
|
||||
struct VehicleCrashParticle : MiscBase
|
||||
{
|
||||
uint8_t pad_24[0x28 - 0x24];
|
||||
uint16_t frame; // 0x28
|
||||
uint8_t pad_2A[0x2E - 0x2A];
|
||||
ColourScheme colourScheme; // 0x2E
|
||||
uint16_t crashedSpriteBase; // 0x30 crashed_sprite_base
|
||||
};
|
||||
static_assert(sizeof(VehicleCrashParticle) == 0x32);
|
||||
|
||||
struct ExplosionCloud : MiscBase
|
||||
{
|
||||
uint8_t pad_24[0x28 - 0x24];
|
||||
uint16_t frame; // 0x28
|
||||
};
|
||||
static_assert(sizeof(ExplosionCloud) == 0x2A);
|
||||
|
||||
struct Splash : MiscBase
|
||||
{
|
||||
uint8_t pad_24[0x28 - 0x24];
|
||||
uint16_t frame; // 0x28
|
||||
};
|
||||
static_assert(sizeof(Splash) == 0x2A);
|
||||
|
||||
struct Fireball : MiscBase
|
||||
{
|
||||
uint8_t pad_24[0x28 - 0x24];
|
||||
uint16_t frame; // 0x28
|
||||
};
|
||||
static_assert(sizeof(Fireball) == 0x2A);
|
||||
|
||||
struct ExplosionSmoke : MiscBase
|
||||
{
|
||||
uint8_t pad_24[0x28 - 0x24];
|
||||
uint16_t frame; // 0x28
|
||||
};
|
||||
static_assert(sizeof(ExplosionSmoke) == 0x2A);
|
||||
|
||||
struct Smoke : MiscBase
|
||||
{
|
||||
uint8_t pad_24[0x28 - 0x24];
|
||||
uint16_t frame; // 0x28
|
||||
|
||||
static Smoke* create(Map::Pos3 loc);
|
||||
};
|
||||
static_assert(sizeof(Smoke) == 0x2A);
|
||||
#pragma pack(pop)
|
||||
}
|
|
@ -53,6 +53,7 @@ namespace OpenLoco::Environment
|
|||
static constexpr const char* searchPaths[] = {
|
||||
"C:/Program Files (x86)/Atari/Locomotion",
|
||||
"C:/GOG Games/Chris Sawyer's Locomotion",
|
||||
"C:/GOG Games/Locomotion",
|
||||
};
|
||||
|
||||
std::cout << "Searching for Locomotion install path..." << std::endl;
|
||||
|
@ -70,7 +71,7 @@ namespace OpenLoco::Environment
|
|||
static fs::path resolveLocoInstallPath()
|
||||
{
|
||||
auto& cfg = Config::getNew();
|
||||
auto path = fs::path(cfg.loco_install_path);
|
||||
auto path = fs::u8path(cfg.loco_install_path);
|
||||
if (!path.empty())
|
||||
{
|
||||
if (validateLocoInstallPath(path))
|
||||
|
@ -109,7 +110,7 @@ namespace OpenLoco::Environment
|
|||
|
||||
static fs::path getLocoInstallPath()
|
||||
{
|
||||
return _path_install.get();
|
||||
return fs::u8path(_path_install.get());
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
@ -146,7 +147,7 @@ namespace OpenLoco::Environment
|
|||
{
|
||||
auto basePath = getBasePath(id);
|
||||
auto subPath = getSubPath(id);
|
||||
auto result = basePath / subPath;
|
||||
auto result = (basePath / subPath).lexically_normal();
|
||||
if (!fs::exists(result))
|
||||
{
|
||||
#ifndef _WIN32
|
||||
|
@ -166,19 +167,73 @@ namespace OpenLoco::Environment
|
|||
return result;
|
||||
}
|
||||
|
||||
fs::path getPathNoWarning(path_id id)
|
||||
{
|
||||
auto basePath = getBasePath(id);
|
||||
auto subPath = getSubPath(id);
|
||||
auto result = (basePath / subPath).lexically_normal();
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void setDirectory(T& buffer, fs::path path)
|
||||
{
|
||||
Utility::strcpy_safe(buffer, path.make_preferred().u8string().c_str());
|
||||
}
|
||||
|
||||
void autoCreateDirectory(const fs::path& path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!fs::is_directory(path))
|
||||
{
|
||||
auto path8 = path.u8string();
|
||||
std::printf("Creating directory: %s\n", path8.c_str());
|
||||
fs::create_directories(path);
|
||||
// clang-format off
|
||||
fs::permissions(
|
||||
path,
|
||||
fs::perms::owner_all |
|
||||
fs::perms::group_read | fs::perms::group_exec |
|
||||
fs::perms::others_read | fs::perms::others_exec);
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::fprintf(stderr, "Unable to create directory: %s\n", e.what());
|
||||
Ui::showMessageBox("Unable to create directory", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// 0x004412CE
|
||||
void resolvePaths()
|
||||
{
|
||||
auto basePath = resolveLocoInstallPath();
|
||||
setDirectory(_path_install, basePath);
|
||||
setDirectory(_path_saves_single_player, basePath / "Single Player Saved Games/");
|
||||
setDirectory(_path_saves_two_player, basePath / "Two Player Saved Games/");
|
||||
|
||||
// NB: vanilla routines do not use std::filesystem yet, so the trailing slash is still needed.
|
||||
auto saveDirectory = getPathNoWarning(path_id::save) / "";
|
||||
auto& configLastSavePath = Config::getNew().last_save_path;
|
||||
if (!configLastSavePath.empty())
|
||||
{
|
||||
// Getting the directory can fail if config is bad.
|
||||
try
|
||||
{
|
||||
auto directory = fs::u8path(configLastSavePath);
|
||||
if (fs::is_directory(directory))
|
||||
{
|
||||
saveDirectory = directory;
|
||||
}
|
||||
}
|
||||
catch (std::system_error&)
|
||||
{
|
||||
}
|
||||
}
|
||||
setDirectory(_path_saves_single_player, saveDirectory);
|
||||
setDirectory(_path_saves_two_player, saveDirectory);
|
||||
autoCreateDirectory(saveDirectory);
|
||||
|
||||
setDirectory(_path_scenarios, basePath / "Scenarios/*.SC5");
|
||||
setDirectory(_path_landscapes, basePath / "Scenarios/Landscapes/*.SC5");
|
||||
setDirectory(_path_objects, basePath / "ObjData/*.DAT");
|
||||
|
@ -193,6 +248,8 @@ namespace OpenLoco::Environment
|
|||
case path_id::gamecfg:
|
||||
case path_id::scores:
|
||||
case path_id::openloco_yml:
|
||||
case path_id::save:
|
||||
case path_id::autosave:
|
||||
return platform::getUserDirectory();
|
||||
case path_id::language_files:
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
|
@ -258,6 +315,9 @@ namespace OpenLoco::Environment
|
|||
"Data/TUT800_3.DAT",
|
||||
"openloco.yml",
|
||||
"language",
|
||||
"save",
|
||||
"save/autosave",
|
||||
"1.TMP",
|
||||
};
|
||||
|
||||
size_t index = (size_t)id;
|
||||
|
|
|
@ -56,8 +56,13 @@ namespace OpenLoco::Environment
|
|||
tut800_3,
|
||||
openloco_yml,
|
||||
language_files,
|
||||
save,
|
||||
autosave,
|
||||
_1tmp,
|
||||
};
|
||||
|
||||
void autoCreateDirectory(const fs::path& path);
|
||||
fs::path getPath(path_id id);
|
||||
fs::path getPathNoWarning(path_id id);
|
||||
void resolvePaths();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,327 @@
|
|||
#include "Game.h"
|
||||
#include "Audio/Audio.h"
|
||||
#include "CompanyManager.h"
|
||||
#include "Config.h"
|
||||
#include "GameCommands/GameCommands.h"
|
||||
#include "GameException.hpp"
|
||||
#include "Input.h"
|
||||
#include "Interop/Interop.hpp"
|
||||
#include "Localisation/StringIds.h"
|
||||
#include "MultiPlayer.h"
|
||||
#include "S5/S5.h"
|
||||
#include "Title.h"
|
||||
#include "Ui/ProgressBar.h"
|
||||
#include "Ui/WindowManager.h"
|
||||
#include "Ui/WindowType.h"
|
||||
|
||||
namespace OpenLoco::Game
|
||||
{
|
||||
static loco_global<uint8_t, 0x00508F08> _game_command_nest_level;
|
||||
static loco_global<GameCommands::LoadOrQuitMode, 0x0050A002> _savePromptType;
|
||||
|
||||
// TODO: make accessible from Environment
|
||||
static loco_global<char[257], 0x0050B1CF> _path_saves_single_player;
|
||||
static loco_global<char[257], 0x0050B2EC> _path_saves_two_player;
|
||||
static loco_global<char[257], 0x0050B406> _path_scenarios;
|
||||
static loco_global<char[257], 0x0050B518> _path_landscapes;
|
||||
|
||||
static loco_global<char[256], 0x0050B745> _currentScenarioFilename;
|
||||
|
||||
static loco_global<uint32_t, 0x00525E28> _525E28;
|
||||
|
||||
static loco_global<uint16_t, 0x009C871A> _scenarioFlags;
|
||||
static loco_global<char[64], 0x009C873E> _scenarioTitle;
|
||||
|
||||
static loco_global<char[512], 0x0112CE04> _savePath;
|
||||
|
||||
// 0x0046DB4C
|
||||
static void sub_46DB4C()
|
||||
{
|
||||
call(0x0046DB4C);
|
||||
}
|
||||
|
||||
using Ui::Windows::PromptBrowse::browse_type;
|
||||
|
||||
static bool openBrowsePrompt(string_id titleId, browse_type type, const char* filter)
|
||||
{
|
||||
char titleBuffer[256] = {};
|
||||
StringManager::formatString(titleBuffer, std::size(titleBuffer), titleId);
|
||||
|
||||
Audio::pauseSound();
|
||||
setPauseFlag(1 << 2);
|
||||
Gfx::invalidateScreen();
|
||||
Gfx::render();
|
||||
|
||||
bool confirm = Ui::Windows::PromptBrowse::open(type, &_savePath[0], filter, titleBuffer);
|
||||
|
||||
Audio::unpauseSound();
|
||||
Ui::processMessagesMini();
|
||||
unsetPauseFlag(1 << 2);
|
||||
Gfx::invalidateScreen();
|
||||
Gfx::render();
|
||||
|
||||
return confirm;
|
||||
}
|
||||
|
||||
// 0x004416FF
|
||||
bool loadSaveGameOpen()
|
||||
{
|
||||
if (!isNetworked())
|
||||
strncpy(&_savePath[0], &_path_saves_single_player[0], std::size(_savePath));
|
||||
else
|
||||
strncpy(&_savePath[0], &_path_saves_two_player[0], std::size(_savePath));
|
||||
|
||||
return openBrowsePrompt(StringIds::title_prompt_load_game, browse_type::load, S5::filterSV5);
|
||||
}
|
||||
|
||||
// 0x004417A7
|
||||
bool loadLandscapeOpen()
|
||||
{
|
||||
strncpy(&_savePath[0], &_path_landscapes[0], std::size(_savePath));
|
||||
|
||||
return openBrowsePrompt(StringIds::title_prompt_load_landscape, browse_type::load, S5::filterSC5);
|
||||
}
|
||||
|
||||
// 0x00441843
|
||||
bool saveSaveGameOpen()
|
||||
{
|
||||
if (!isNetworked())
|
||||
strncpy(&_savePath[0], &_path_saves_single_player[0], std::size(_savePath));
|
||||
else
|
||||
strncpy(&_savePath[0], &_path_saves_two_player[0], std::size(_savePath));
|
||||
|
||||
return openBrowsePrompt(StringIds::title_prompt_save_game, browse_type::save, S5::filterSV5);
|
||||
}
|
||||
|
||||
// 0x004418DB
|
||||
bool saveScenarioOpen()
|
||||
{
|
||||
strncpy(&_savePath[0], &_path_landscapes[0], std::size(_savePath));
|
||||
strncat(&_savePath[0], &_scenarioTitle[0], std::size(_savePath));
|
||||
strncat(&_savePath[0], S5::extensionSC5, std::size(_savePath));
|
||||
|
||||
return openBrowsePrompt(StringIds::title_prompt_save_game, browse_type::save, S5::filterSC5);
|
||||
}
|
||||
|
||||
// 0x00441993
|
||||
bool saveLandscapeOpen()
|
||||
{
|
||||
*_scenarioFlags &= ~(1 << 0);
|
||||
if (_525E28 & (1 << 0))
|
||||
{
|
||||
*_scenarioFlags |= (1 << 0);
|
||||
sub_46DB4C();
|
||||
}
|
||||
|
||||
strncpy(&_savePath[0], &_path_landscapes[0], std::size(_savePath));
|
||||
strncat(&_savePath[0], &_scenarioTitle[0], std::size(_savePath));
|
||||
strncat(&_savePath[0], S5::extensionSC5, std::size(_savePath));
|
||||
|
||||
return openBrowsePrompt(StringIds::title_prompt_save_landscape, browse_type::save, S5::filterSC5);
|
||||
}
|
||||
|
||||
// 0x004424CE
|
||||
static bool sub_4424CE()
|
||||
{
|
||||
registers regs;
|
||||
call(0x004424CE);
|
||||
return regs.eax != 0;
|
||||
}
|
||||
|
||||
// 0x0043BFF8
|
||||
void loadGame()
|
||||
{
|
||||
GameCommands::do_21(1, 0);
|
||||
Input::toolCancel();
|
||||
|
||||
if (isEditorMode())
|
||||
{
|
||||
if (Game::loadLandscapeOpen())
|
||||
{
|
||||
// 0x0043C087
|
||||
auto path = fs::u8path(&_savePath[0]).replace_extension(S5::extensionSC5);
|
||||
std::strncpy(&_currentScenarioFilename[0], path.u8string().c_str(), std::size(_currentScenarioFilename));
|
||||
|
||||
if (sub_4424CE())
|
||||
{
|
||||
resetScreenAge();
|
||||
throw GameException::Interrupt;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!isNetworked())
|
||||
{
|
||||
if (Game::loadSaveGameOpen())
|
||||
{
|
||||
// 0x0043C033
|
||||
auto path = fs::u8path(&_savePath[0]).replace_extension(S5::extensionSV5);
|
||||
std::strncpy(&_currentScenarioFilename[0], path.u8string().c_str(), std::size(_currentScenarioFilename));
|
||||
|
||||
if (S5::load(path, 0))
|
||||
{
|
||||
resetScreenAge();
|
||||
throw GameException::Interrupt;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isNetworked())
|
||||
{
|
||||
// 0x0043C0DB
|
||||
if (CompanyManager::getControllingId() == CompanyManager::updatingCompanyId())
|
||||
{
|
||||
MultiPlayer::setFlag(MultiPlayer::flags::flag_4);
|
||||
MultiPlayer::setFlag(MultiPlayer::flags::flag_3);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x0043C0D1
|
||||
Gfx::invalidateScreen();
|
||||
}
|
||||
|
||||
// 0x0043C182
|
||||
void quitGame()
|
||||
{
|
||||
_game_command_nest_level = 0;
|
||||
|
||||
// Path for networked games; untested.
|
||||
if (isNetworked())
|
||||
{
|
||||
clearScreenFlag(ScreenFlags::networked);
|
||||
auto playerCompanyId = CompanyManager::getControllingId();
|
||||
auto previousUpdatingId = CompanyManager::updatingCompanyId();
|
||||
CompanyManager::updatingCompanyId(playerCompanyId);
|
||||
|
||||
Ui::WindowManager::closeAllFloatingWindows();
|
||||
|
||||
CompanyManager::updatingCompanyId(previousUpdatingId);
|
||||
setScreenFlag(ScreenFlags::networked);
|
||||
|
||||
// If the other party is leaving the game, go back to the title screen.
|
||||
if (playerCompanyId != previousUpdatingId)
|
||||
{
|
||||
// 0x0043C1CD
|
||||
addr<0x00F25428, uint32_t>() = 0;
|
||||
clearScreenFlag(ScreenFlags::networked);
|
||||
clearScreenFlag(ScreenFlags::networkHost);
|
||||
addr<0x00508F0C, uint32_t>() = 0;
|
||||
CompanyManager::setControllingId(0);
|
||||
CompanyManager::setSecondaryPlayerId(CompanyId::null);
|
||||
|
||||
Gfx::invalidateScreen();
|
||||
ObjectManager::loadIndex();
|
||||
|
||||
Ui::WindowManager::close(Ui::WindowType::options);
|
||||
Ui::WindowManager::close(Ui::WindowType::companyFaceSelection);
|
||||
Ui::WindowManager::close(Ui::WindowType::objectSelection);
|
||||
|
||||
clearScreenFlag(ScreenFlags::editor);
|
||||
Audio::pauseSound();
|
||||
Audio::unpauseSound();
|
||||
|
||||
if (Input::hasFlag(Input::Flags::flag5))
|
||||
{
|
||||
Input::sub_407231();
|
||||
Input::resetFlag(Input::Flags::flag5);
|
||||
}
|
||||
|
||||
Title::start();
|
||||
|
||||
Ui::Windows::Error::open(StringIds::error_the_other_player_has_exited_the_game, StringIds::null);
|
||||
|
||||
throw GameException::Interrupt;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x0043C1C8
|
||||
exitCleanly();
|
||||
}
|
||||
|
||||
// 0x0043C0FD
|
||||
void returnToTitle()
|
||||
{
|
||||
if (isNetworked())
|
||||
{
|
||||
Ui::WindowManager::closeAllFloatingWindows();
|
||||
}
|
||||
|
||||
Ui::WindowManager::close(Ui::WindowType::options);
|
||||
Ui::WindowManager::close(Ui::WindowType::companyFaceSelection);
|
||||
Ui::WindowManager::close(Ui::WindowType::objectSelection);
|
||||
Ui::WindowManager::close(Ui::WindowType::saveGamePrompt);
|
||||
|
||||
clearScreenFlag(ScreenFlags::editor);
|
||||
Audio::pauseSound();
|
||||
Audio::unpauseSound();
|
||||
|
||||
if (Input::hasFlag(Input::Flags::flag5))
|
||||
{
|
||||
Input::sub_407231();
|
||||
Input::resetFlag(Input::Flags::flag5);
|
||||
}
|
||||
|
||||
Title::start();
|
||||
|
||||
throw GameException::Interrupt;
|
||||
}
|
||||
|
||||
// 0x0043C427
|
||||
void confirmSaveGame()
|
||||
{
|
||||
Input::toolCancel();
|
||||
|
||||
if (isEditorMode())
|
||||
{
|
||||
if (Game::saveLandscapeOpen())
|
||||
{
|
||||
// 0x0043C4B3
|
||||
auto path = fs::u8path(&_savePath[0]).replace_extension(S5::extensionSC5);
|
||||
std::strncpy(&_currentScenarioFilename[0], path.u8string().c_str(), std::size(_currentScenarioFilename));
|
||||
|
||||
if (!S5::save(path, S5::SaveFlags::scenario))
|
||||
Ui::Windows::Error::open(StringIds::landscape_save_failed, StringIds::null);
|
||||
else
|
||||
GameCommands::do_21(2, 0);
|
||||
}
|
||||
}
|
||||
else if (!isNetworked())
|
||||
{
|
||||
if (Game::saveSaveGameOpen())
|
||||
{
|
||||
// 0x0043C446
|
||||
auto path = fs::u8path(&_savePath[0]).replace_extension(S5::extensionSV5);
|
||||
std::strncpy(&_currentScenarioFilename[0], path.u8string().c_str(), std::size(_currentScenarioFilename));
|
||||
|
||||
S5::SaveFlags flags = {};
|
||||
if (Config::get().flags & Config::Flags::exportObjectsWithSaves)
|
||||
flags = S5::SaveFlags::packCustomObjects;
|
||||
|
||||
if (!S5::save(path, flags))
|
||||
Ui::Windows::Error::open(StringIds::error_game_save_failed, StringIds::null);
|
||||
else
|
||||
GameCommands::do_21(2, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 0x0043C511
|
||||
GameCommands::do_72();
|
||||
MultiPlayer::setFlag(MultiPlayer::flags::flag_2);
|
||||
|
||||
switch (_savePromptType)
|
||||
{
|
||||
case GameCommands::LoadOrQuitMode::loadGamePrompt:
|
||||
MultiPlayer::setFlag(MultiPlayer::flags::flag_13); // intend to load?
|
||||
break;
|
||||
case GameCommands::LoadOrQuitMode::returnToTitlePrompt:
|
||||
MultiPlayer::setFlag(MultiPlayer::flags::flag_14); // intend to return to title?
|
||||
break;
|
||||
case GameCommands::LoadOrQuitMode::quitGamePrompt:
|
||||
MultiPlayer::setFlag(MultiPlayer::flags::flag_15); // intend to quit game?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x0043C411
|
||||
Gfx::invalidateScreen();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
namespace OpenLoco::Game
|
||||
{
|
||||
bool loadSaveGameOpen();
|
||||
bool loadLandscapeOpen();
|
||||
bool saveSaveGameOpen();
|
||||
bool saveScenarioOpen();
|
||||
bool saveLandscapeOpen();
|
||||
void loadGame();
|
||||
void quitGame();
|
||||
void returnToTitle();
|
||||
void confirmSaveGame();
|
||||
}
|
|
@ -1,326 +0,0 @@
|
|||
#include "GameCommands.h"
|
||||
#include "Audio/Audio.h"
|
||||
#include "Company.h"
|
||||
#include "CompanyManager.h"
|
||||
#include "Map/Tile.h"
|
||||
#include "Objects/ObjectManager.h"
|
||||
#include "Objects/RoadObject.h"
|
||||
#include "Objects/TrackObject.h"
|
||||
#include "StationManager.h"
|
||||
#include "Things/Vehicle.h"
|
||||
#include "Ui/WindowManager.h"
|
||||
#include <cassert>
|
||||
|
||||
using namespace OpenLoco::Ui;
|
||||
using namespace OpenLoco::Map;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
static loco_global<company_id_t, 0x009C68EB> _updating_company_id;
|
||||
static loco_global<uint8_t, 0x00508F08> game_command_nest_level;
|
||||
static loco_global<company_id_t[2], 0x00525E3C> _player_company;
|
||||
static loco_global<uint8_t, 0x00508F17> paused_state;
|
||||
static loco_global<uint8_t, 0x00508F1A> game_speed;
|
||||
static loco_global<uint16_t, 0x0050A004> _50A004;
|
||||
|
||||
static uint16_t _gameCommandFlags;
|
||||
static loco_global<uintptr_t[80], 0x004F9548> _4F9548;
|
||||
static loco_global<uint8_t[80], 0x004F9688> _4F9688;
|
||||
|
||||
static loco_global<tile_element*, 0x009C68D0> _9C68D0;
|
||||
|
||||
static loco_global<coord_t, 0x009C68E4> _game_command_map_z;
|
||||
static loco_global<string_id, 0x009C68E6> gGameCommandErrorText;
|
||||
static loco_global<string_id, 0x009C68E8> gGameCommandErrorTitle;
|
||||
static loco_global<uint8_t, 0x009C68EE> _errorCompanyId;
|
||||
static loco_global<string_id[8], 0x112C826> _commonFormatArgs;
|
||||
|
||||
void registerHooks()
|
||||
{
|
||||
registerHook(
|
||||
0x00431315,
|
||||
[](registers& regs) FORCE_ALIGN_ARG_POINTER -> uint8_t {
|
||||
registers backup = regs;
|
||||
auto ebx = doCommand(regs.esi, backup);
|
||||
|
||||
regs = backup;
|
||||
regs.ebx = ebx;
|
||||
return 0;
|
||||
});
|
||||
|
||||
registerHook(
|
||||
0x004AE5E4,
|
||||
[](registers& regs) FORCE_ALIGN_ARG_POINTER -> uint8_t {
|
||||
registers backup = regs;
|
||||
auto ebx = Things::Vehicle::create(regs.bl, regs.dx, regs.di);
|
||||
|
||||
regs = backup;
|
||||
regs.ebx = ebx;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
static uint32_t loc_4314EA();
|
||||
static uint32_t loc_4313C6(int esi, const registers& regs);
|
||||
|
||||
// 0x00431315
|
||||
uint32_t doCommand(int esi, const registers& regs)
|
||||
{
|
||||
uint16_t flags = regs.bx;
|
||||
|
||||
_gameCommandFlags = regs.bx;
|
||||
if (game_command_nest_level != 0)
|
||||
return loc_4313C6(esi, regs);
|
||||
|
||||
if ((flags & GameCommandFlag::apply) == 0)
|
||||
{
|
||||
return loc_4313C6(esi, regs);
|
||||
}
|
||||
|
||||
if ((flags & (GameCommandFlag::flag_4 | GameCommandFlag::flag_6)) != 0
|
||||
&& _4F9688[esi] == 1
|
||||
&& _updating_company_id == _player_company[0])
|
||||
{
|
||||
if (getPauseFlags() & 1)
|
||||
{
|
||||
paused_state = paused_state ^ 1;
|
||||
WindowManager::invalidate(WindowType::timeToolbar);
|
||||
Audio::unpauseSound();
|
||||
_50A004 = _50A004 | 1;
|
||||
}
|
||||
|
||||
if (game_speed != 0)
|
||||
{
|
||||
game_speed = 0;
|
||||
WindowManager::invalidate(WindowType::timeToolbar);
|
||||
}
|
||||
|
||||
if (isPaused())
|
||||
{
|
||||
gGameCommandErrorText = StringIds::empty;
|
||||
return 0x80000000;
|
||||
}
|
||||
}
|
||||
|
||||
if (_updating_company_id == _player_company[0] && isNetworked())
|
||||
{
|
||||
assert(false);
|
||||
registers fnRegs = regs;
|
||||
call(0x0046E34A, fnRegs); // some network stuff. Untested
|
||||
}
|
||||
|
||||
return loc_4313C6(esi, regs);
|
||||
}
|
||||
|
||||
static uint32_t loc_4313C6(int esi, const registers& regs)
|
||||
{
|
||||
uint16_t flags = regs.bx;
|
||||
gGameCommandErrorText = StringIds::null;
|
||||
game_command_nest_level++;
|
||||
|
||||
auto addr = _4F9548[esi];
|
||||
|
||||
uint16_t flagsBackup = _gameCommandFlags;
|
||||
registers fnRegs1 = regs;
|
||||
fnRegs1.bl &= ~GameCommandFlag::apply;
|
||||
call(addr, fnRegs1);
|
||||
int32_t ebx = fnRegs1.ebx;
|
||||
_gameCommandFlags = flagsBackup;
|
||||
|
||||
if (ebx != static_cast<int32_t>(0x80000000))
|
||||
{
|
||||
if (isEditorMode())
|
||||
ebx = 0;
|
||||
|
||||
if (game_command_nest_level == 1)
|
||||
{
|
||||
if ((_gameCommandFlags & GameCommandFlag::flag_2) == 0
|
||||
&& (_gameCommandFlags & GameCommandFlag::flag_6) == 0
|
||||
&& ebx != 0)
|
||||
{
|
||||
registers regs2;
|
||||
regs2.ebp = ebx;
|
||||
call(0x0046DD06, regs2);
|
||||
ebx = regs2.ebp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ebx == static_cast<int32_t>(0x80000000))
|
||||
{
|
||||
if (flags & GameCommandFlag::apply)
|
||||
{
|
||||
return loc_4314EA();
|
||||
}
|
||||
else
|
||||
{
|
||||
game_command_nest_level--;
|
||||
return ebx;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & 1) == 0)
|
||||
{
|
||||
game_command_nest_level--;
|
||||
return ebx;
|
||||
}
|
||||
|
||||
uint16_t flagsBackup2 = _gameCommandFlags;
|
||||
registers fnRegs2 = regs;
|
||||
call(addr, fnRegs2);
|
||||
int32_t ebx2 = fnRegs2.ebx;
|
||||
_gameCommandFlags = flagsBackup2;
|
||||
|
||||
if (ebx2 == static_cast<int32_t>(0x80000000))
|
||||
{
|
||||
return loc_4314EA();
|
||||
}
|
||||
|
||||
if (isEditorMode())
|
||||
{
|
||||
ebx = 0;
|
||||
}
|
||||
|
||||
if (ebx2 < ebx)
|
||||
{
|
||||
ebx = ebx2;
|
||||
}
|
||||
|
||||
game_command_nest_level--;
|
||||
if (game_command_nest_level != 0)
|
||||
return ebx;
|
||||
|
||||
if ((flagsBackup2 & GameCommandFlag::flag_5) != 0)
|
||||
return ebx;
|
||||
|
||||
{
|
||||
// Apply to company money
|
||||
registers fnRegs;
|
||||
fnRegs.ebx = ebx;
|
||||
call(0x0046DE2B, fnRegs);
|
||||
}
|
||||
|
||||
if (ebx != 0 && _updating_company_id == _player_company[0])
|
||||
{
|
||||
// Add flying cost text
|
||||
registers fnRegs;
|
||||
fnRegs.ebx = ebx;
|
||||
_game_command_map_z = _game_command_map_z + 24;
|
||||
call(0x0046DC9F, fnRegs);
|
||||
_game_command_map_z = _game_command_map_z - 24;
|
||||
}
|
||||
|
||||
return ebx;
|
||||
}
|
||||
|
||||
static uint32_t loc_4314EA()
|
||||
{
|
||||
game_command_nest_level--;
|
||||
if (game_command_nest_level != 0)
|
||||
return 0x80000000;
|
||||
|
||||
if (_updating_company_id != _player_company[0])
|
||||
return 0x80000000;
|
||||
|
||||
if (_gameCommandFlags & GameCommandFlag::flag_3)
|
||||
return 0x80000000;
|
||||
|
||||
if (gGameCommandErrorText != 0xFFFE)
|
||||
{
|
||||
Windows::showError(gGameCommandErrorTitle, gGameCommandErrorText);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
// advanced errors
|
||||
if (_9C68D0 != (void*)-1)
|
||||
{
|
||||
auto tile = (tile_element*)_9C68D0;
|
||||
|
||||
switch (tile->type())
|
||||
{
|
||||
case element_type::track: // 4
|
||||
{
|
||||
auto trackElement = tile->asTrack();
|
||||
if (trackElement == nullptr)
|
||||
break; // throw exception?
|
||||
|
||||
track_object* pObject = ObjectManager::get<track_object>(trackElement->trackObjectId());
|
||||
if (pObject == nullptr)
|
||||
break;
|
||||
|
||||
_commonFormatArgs[0] = pObject->name;
|
||||
_commonFormatArgs[1] = CompanyManager::get(_errorCompanyId)->name;
|
||||
Windows::Error::openWithCompetitor(gGameCommandErrorTitle, StringIds::error_reason_stringid_belongs_to, _errorCompanyId);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
case element_type::road: //0x1C
|
||||
{
|
||||
auto roadElement = tile->asRoad();
|
||||
if (roadElement == nullptr)
|
||||
break; // throw exception?
|
||||
|
||||
road_object* pObject = ObjectManager::get<road_object>(roadElement->roadObjectId());
|
||||
if (pObject == nullptr)
|
||||
break;
|
||||
|
||||
_commonFormatArgs[0] = pObject->name;
|
||||
_commonFormatArgs[1] = CompanyManager::get(_errorCompanyId)->name;
|
||||
Windows::Error::openWithCompetitor(gGameCommandErrorTitle, StringIds::error_reason_stringid_belongs_to, _errorCompanyId);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
case element_type::station: // 8
|
||||
{
|
||||
auto stationElement = tile->asStation();
|
||||
if (stationElement == nullptr)
|
||||
break; // throw exception?
|
||||
|
||||
station* pStation = StationManager::get(stationElement->stationId());
|
||||
if (pStation == nullptr)
|
||||
break;
|
||||
|
||||
_commonFormatArgs[0] = pStation->name;
|
||||
_commonFormatArgs[1] = pStation->town;
|
||||
_commonFormatArgs[2] = CompanyManager::get(_errorCompanyId)->name;
|
||||
Windows::Error::openWithCompetitor(gGameCommandErrorTitle, StringIds::error_reason_stringid_belongs_to, _errorCompanyId);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
case element_type::signal: // 0x0C
|
||||
{
|
||||
_commonFormatArgs[0] = CompanyManager::get(_errorCompanyId)->name;
|
||||
Windows::Error::openWithCompetitor(gGameCommandErrorTitle, StringIds::error_reason_signal_belongs_to, _errorCompanyId);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
_commonFormatArgs[0] = CompanyManager::get(_errorCompanyId)->name;
|
||||
Windows::Error::openWithCompetitor(gGameCommandErrorTitle, StringIds::error_reason_stringid_belongs_to, _errorCompanyId);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
// 0x00431E6A
|
||||
// al : company
|
||||
// esi : tile
|
||||
bool sub_431E6A(const company_id_t company, Map::tile_element* const tile /*= nullptr*/)
|
||||
{
|
||||
if (company == CompanyId::neutral)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (_updating_company_id == company || _updating_company_id == CompanyId::neutral)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
gGameCommandErrorText = -2;
|
||||
_errorCompanyId = company;
|
||||
_9C68D0 = tile == nullptr ? reinterpret_cast<Map::tile_element*>(-1) : tile;
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,371 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "Interop/Interop.hpp"
|
||||
#include "Map/Tile.h"
|
||||
#include "Objects/ObjectManager.h"
|
||||
#include "Things/Thing.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
enum GameCommandFlag : uint8_t
|
||||
{
|
||||
apply = 1 << 0, // 0x01
|
||||
flag_1 = 1 << 1, // 0x02
|
||||
flag_2 = 1 << 2, // 0x04
|
||||
flag_3 = 1 << 3, // 0x08
|
||||
flag_4 = 1 << 4, // 0x10
|
||||
flag_5 = 1 << 5, // 0x20
|
||||
flag_6 = 1 << 6, // 0x40
|
||||
};
|
||||
|
||||
enum class GameCommand : uint8_t
|
||||
{
|
||||
vehicle_rearange = 0,
|
||||
vehicle_place = 1,
|
||||
vehicle_pickup = 2,
|
||||
vehicle_create = 5,
|
||||
vehicle_sell = 6,
|
||||
build_vehicle = 9,
|
||||
change_station_name = 11,
|
||||
change_company_colour_scheme = 19,
|
||||
pause_game = 20,
|
||||
load_save_quit_game = 21,
|
||||
change_land_material = 24,
|
||||
raise_land = 25,
|
||||
lower_land = 26,
|
||||
lower_raise_land_mountain = 27,
|
||||
raise_water = 28,
|
||||
lower_water = 29,
|
||||
change_company_name = 46,
|
||||
remove_industry = 54,
|
||||
build_company_headquarters = 55,
|
||||
change_company_face = 66,
|
||||
load_multiplayer_map = 67,
|
||||
send_chat_message = 71,
|
||||
update_owner_status = 73,
|
||||
rename_industry = 79,
|
||||
};
|
||||
|
||||
constexpr uint32_t FAILURE = 0x80000000;
|
||||
|
||||
void registerHooks();
|
||||
uint32_t doCommand(int esi, const registers& registers);
|
||||
bool sub_431E6A(const company_id_t company, Map::tile_element* const tile = nullptr);
|
||||
|
||||
// Build vehicle
|
||||
inline bool do_5(uint16_t vehicle_type, uint16_t vehicle_id = 0xFFFF)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.di = vehicle_id;
|
||||
regs.edx = vehicle_type;
|
||||
|
||||
return doCommand(5, regs) != FAILURE;
|
||||
}
|
||||
|
||||
// Change loan
|
||||
inline void do_9(currency32_t newLoan)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.edx = newLoan;
|
||||
doCommand(9, regs);
|
||||
}
|
||||
|
||||
// Change station name
|
||||
inline void do_11(uint16_t cx, uint16_t ax, uint32_t edx, uint32_t ebp, uint32_t edi)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.cx = cx; // station number or 0
|
||||
regs.ax = ax; // [ 0, 1, 2]
|
||||
regs.edx = edx; // part of name buffer
|
||||
regs.ebp = ebp; // part of name buffer
|
||||
regs.edi = edi; // part of name buffer
|
||||
doCommand(11, regs);
|
||||
}
|
||||
|
||||
// Change company colour scheme
|
||||
inline void do_19(int8_t isPrimary, int8_t value, int8_t colourType, int8_t setColourMode, uint8_t companyId)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.cl = colourType; // vehicle type or main
|
||||
regs.dh = setColourMode; // [ 0, 1 ] -- 0 = set colour, 1 = toggle enabled/disabled;
|
||||
regs.dl = companyId; // company id
|
||||
|
||||
if (setColourMode == 0)
|
||||
{
|
||||
// cl is divided by 2 when used
|
||||
regs.ah = isPrimary; // [ 0, 1 ] -- primary or secondary palette
|
||||
regs.al = value; // new colour
|
||||
}
|
||||
else if (setColourMode == 1)
|
||||
{
|
||||
regs.al = value; // [ 0, 1 ] -- off or on
|
||||
}
|
||||
|
||||
doCommand(19, regs);
|
||||
}
|
||||
|
||||
// Pause game
|
||||
inline void do_20()
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
doCommand(20, regs);
|
||||
}
|
||||
|
||||
// Load/save/quit game
|
||||
inline void do_21(uint8_t dl, uint8_t di)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.dl = dl; // [ 0, 2 ]
|
||||
regs.di = di; // [ 0 = load game, 1 = return to title screen, 2 = quit to desktop ]
|
||||
doCommand(21, regs);
|
||||
}
|
||||
|
||||
// Change Land Material
|
||||
inline void do_24(Map::map_pos pointA, Map::map_pos pointB, uint8_t landType, uint8_t flags)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = pointA.x;
|
||||
regs.cx = pointA.y;
|
||||
regs.di = pointB.x;
|
||||
regs.bp = pointB.y;
|
||||
regs.dl = landType;
|
||||
regs.bl = flags;
|
||||
doCommand(24, regs);
|
||||
}
|
||||
|
||||
// Raise Land
|
||||
inline uint32_t do_25(Map::map_pos centre, Map::map_pos pointA, Map::map_pos pointB, uint16_t di, uint8_t flags)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = centre.x;
|
||||
regs.cx = centre.y;
|
||||
regs.edx = pointB.x << 16 | pointA.x;
|
||||
regs.ebp = pointB.y << 16 | pointA.y;
|
||||
regs.bl = flags;
|
||||
regs.di = di;
|
||||
return doCommand(25, regs);
|
||||
}
|
||||
|
||||
// Lower Land
|
||||
inline uint32_t do_26(Map::map_pos centre, Map::map_pos pointA, Map::map_pos pointB, uint16_t di, uint8_t flags)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = centre.x;
|
||||
regs.cx = centre.y;
|
||||
regs.edx = pointB.x << 16 | pointA.x;
|
||||
regs.ebp = pointB.y << 16 | pointA.y;
|
||||
regs.bl = flags;
|
||||
regs.di = di;
|
||||
return doCommand(26, regs);
|
||||
}
|
||||
|
||||
// Lower/Raise Land Mountain
|
||||
inline uint32_t do_27(Map::map_pos centre, Map::map_pos pointA, Map::map_pos pointB, uint16_t di, uint8_t flags)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = centre.x;
|
||||
regs.cx = centre.y;
|
||||
regs.edx = pointB.x << 16 | pointA.x;
|
||||
regs.ebp = pointB.y << 16 | pointA.y;
|
||||
regs.bl = flags;
|
||||
regs.di = di;
|
||||
return doCommand(27, regs);
|
||||
}
|
||||
|
||||
// Raise Water
|
||||
inline uint32_t do_28(Map::map_pos pointA, Map::map_pos pointB, uint8_t flags)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = pointA.x;
|
||||
regs.cx = pointA.y;
|
||||
regs.di = pointB.x;
|
||||
regs.bp = pointB.y;
|
||||
regs.bl = flags;
|
||||
return doCommand(28, regs);
|
||||
}
|
||||
|
||||
// Lower Water
|
||||
inline uint32_t do_29(Map::map_pos pointA, Map::map_pos pointB, uint8_t flags)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = pointA.x;
|
||||
regs.cx = pointA.y;
|
||||
regs.di = pointB.x;
|
||||
regs.bp = pointB.y;
|
||||
regs.bl = flags;
|
||||
return doCommand(29, regs);
|
||||
}
|
||||
|
||||
// Change company name
|
||||
inline bool do_30(uint16_t cx, uint16_t ax, uint32_t edx, uint32_t ebp, uint32_t edi)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.cx = cx; // company id
|
||||
regs.ax = ax; // [ 0, 1, 2]
|
||||
regs.edx = edx; // part of name buffer
|
||||
regs.ebp = ebp; // part of name buffer
|
||||
regs.edi = edi; // part of name buffer
|
||||
return doCommand(30, regs) != FAILURE;
|
||||
}
|
||||
|
||||
// Change company owner name
|
||||
inline bool do_31(uint16_t cx, uint16_t ax, uint32_t edx, uint32_t ebp, uint32_t edi)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.cx = cx; // company id
|
||||
regs.ax = ax; // [ 0, 1, 2]
|
||||
regs.edx = edx; // part of name buffer
|
||||
regs.ebp = ebp; // part of name buffer
|
||||
regs.edi = edi; // part of name buffer
|
||||
return doCommand(31, regs) != FAILURE;
|
||||
}
|
||||
|
||||
// Rename town
|
||||
inline void do_46(uint16_t cx, uint16_t ax, uint32_t edx, uint32_t ebp, uint32_t edi)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.cx = cx; // town number or 0
|
||||
regs.ax = ax; // [ 0, 1, 2]
|
||||
regs.edx = edx; // part of name buffer
|
||||
regs.ebp = ebp; // part of name buffer
|
||||
regs.edi = edi; // part of name buffer
|
||||
doCommand(46, regs);
|
||||
}
|
||||
|
||||
// Remove industry
|
||||
inline bool do_48(uint8_t industryId)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.dx = industryId;
|
||||
return doCommand(48, regs) != FAILURE;
|
||||
}
|
||||
|
||||
// Remove town
|
||||
inline bool do_50(uint8_t townId)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.edi = townId;
|
||||
return doCommand(50, regs) != FAILURE;
|
||||
}
|
||||
|
||||
// Remove company headquarters (or build - needs to be checked)
|
||||
// Note: The game seems to call do_55, then do_54 in success case and returns 0x0 code
|
||||
// if we try to build over the existing only do_54 is called and it returns 0x80000000 (in regs.ebx)
|
||||
inline uint32_t do_54(uint8_t bl, uint16_t ax, uint16_t cx, uint16_t di, uint16_t dx)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = bl; // flags
|
||||
regs.cx = cx; // x
|
||||
regs.ax = ax; // y
|
||||
regs.di = di; // z
|
||||
regs.dx = dx; // company index (value 1 in testing case)
|
||||
return doCommand(54, regs);
|
||||
}
|
||||
|
||||
// Build company headquarters (or remove - needs to be checked)
|
||||
// Note: The game seems to call do_55, then do_54 in success case
|
||||
// if we try to build over the existing one do_55 is not called
|
||||
inline void do_55(uint8_t bl, uint16_t ax, uint16_t cx, uint16_t di)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = bl; // flags
|
||||
regs.cx = cx; // x?
|
||||
regs.ax = ax; // y?
|
||||
regs.di = di; // z?
|
||||
doCommand(55, regs);
|
||||
}
|
||||
|
||||
// Change company face
|
||||
inline bool do_65(const ObjectManager::header& object, uint8_t company)
|
||||
{
|
||||
auto objPtr = reinterpret_cast<const int32_t*>(&object);
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.eax = *objPtr++;
|
||||
regs.ecx = *objPtr++;
|
||||
regs.edx = *objPtr++;
|
||||
regs.edi = *objPtr;
|
||||
regs.bh = company;
|
||||
return doCommand(65, regs) != FAILURE;
|
||||
}
|
||||
|
||||
// Clear Land
|
||||
inline void do_66(Map::map_pos centre, Map::map_pos pointA, Map::map_pos pointB, uint8_t flags)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = centre.x;
|
||||
regs.cx = centre.y;
|
||||
regs.edx = pointB.x << 16 | pointA.x;
|
||||
regs.ebp = pointB.y << 16 | pointA.y;
|
||||
regs.bl = flags;
|
||||
doCommand(66, regs);
|
||||
}
|
||||
|
||||
// Load multiplayer map
|
||||
inline void do_67(const char* filename)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.ebp = reinterpret_cast<int32_t>(filename);
|
||||
doCommand(67, regs);
|
||||
}
|
||||
|
||||
// Send chat message
|
||||
inline void do_71(int32_t ax, char* string)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.ax = ax;
|
||||
memcpy(®s.ecx, &string[0], 4);
|
||||
memcpy(®s.edx, &string[4], 4);
|
||||
memcpy(®s.ebp, &string[8], 4);
|
||||
memcpy(®s.edi, &string[12], 4);
|
||||
doCommand(71, regs);
|
||||
}
|
||||
|
||||
// Update owner status
|
||||
inline void do_73(thing_id_t id)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.ax = -2;
|
||||
regs.cx = id;
|
||||
doCommand(73, regs);
|
||||
}
|
||||
|
||||
// Update owner status
|
||||
inline void do_73(Map::map_pos position)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.ax = position.x;
|
||||
regs.cx = position.y;
|
||||
doCommand(73, regs);
|
||||
}
|
||||
|
||||
// Rename industry
|
||||
inline void do_79(uint16_t cx, uint16_t ax, uint32_t edx, uint32_t ebp, uint32_t edi)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
regs.cx = cx; // industry number or 0
|
||||
regs.ax = ax; // [ 0, 1, 2]
|
||||
regs.edx = edx; // part of name buffer
|
||||
regs.ebp = ebp; // part of name buffer
|
||||
regs.edi = edi; // part of name buffer
|
||||
doCommand(79, regs);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
#include "../Audio/Audio.h"
|
||||
#include "../CompanyManager.h"
|
||||
#include "../GameException.hpp"
|
||||
#include "../Localisation/StringIds.h"
|
||||
#include "../OpenLoco.h"
|
||||
#include "../Ui/WindowManager.h"
|
||||
#include "../Ui/WindowType.h"
|
||||
#include "GameCommands.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
// 0x0043483D
|
||||
static uint32_t changeCompanyColour(uint8_t flags, uint8_t type, bool toggleMode, CompanyId_t companyId, bool isSecondary, uint8_t value)
|
||||
{
|
||||
GameCommands::setExpenditureType(ExpenditureType::Miscellaneous);
|
||||
GameCommands::setPosition({ static_cast<int16_t>(0x8000), 0, 0 });
|
||||
|
||||
auto* company = CompanyManager::get(companyId);
|
||||
|
||||
if (flags & Flags::apply)
|
||||
{
|
||||
// Toggling vehicle palette
|
||||
if (toggleMode)
|
||||
{
|
||||
if (value)
|
||||
company->customVehicleColoursSet |= (1 << type);
|
||||
else
|
||||
company->customVehicleColoursSet &= ~(1 << type);
|
||||
}
|
||||
// Setting a colour
|
||||
else
|
||||
{
|
||||
ColourScheme* colours;
|
||||
if (type == 0)
|
||||
colours = &company->mainColours;
|
||||
else
|
||||
colours = &company->vehicleColours[type - 1];
|
||||
|
||||
if (!isSecondary)
|
||||
colours->primary = value;
|
||||
else
|
||||
colours->secondary = value;
|
||||
}
|
||||
|
||||
company->updateVehicleColours();
|
||||
CompanyManager::updateColours();
|
||||
company->updateHeadquartersColour();
|
||||
Ui::WindowManager::invalidate(Ui::WindowType::company);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sub_431E6A(companyId, nullptr))
|
||||
return GameCommands::FAILURE;
|
||||
|
||||
if (toggleMode || type > 0 || isSecondary)
|
||||
return 0;
|
||||
|
||||
// Check whether the requested colour is available
|
||||
uint32_t unavailableColours = CompanyManager::competingColourMask(companyId);
|
||||
if (unavailableColours & (1 << value))
|
||||
{
|
||||
setErrorText(StringIds::empty);
|
||||
return GameCommands::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void changeCompanyColour(registers& regs)
|
||||
{
|
||||
regs.ebx = changeCompanyColour(regs.bl, regs.cl, regs.dh, regs.dl, regs.ah, regs.al);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
#include "Cheat.h"
|
||||
#include "../CompanyManager.h"
|
||||
#include "../Economy/Currency.h"
|
||||
#include "../Entities/EntityManager.h"
|
||||
#include "../Interop/Interop.hpp"
|
||||
#include "../Map/TileManager.h"
|
||||
#include "../StationManager.h"
|
||||
#include "../TownManager.h"
|
||||
#include "../Types.hpp"
|
||||
#include "../Vehicles/Vehicle.h"
|
||||
#include "GameCommands.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
namespace Cheats
|
||||
{
|
||||
static uint32_t acquireAssets(CompanyId_t targetCompanyId)
|
||||
{
|
||||
auto ourCompanyId = CompanyManager::updatingCompanyId();
|
||||
|
||||
// First phase: change ownership of all tile elements that currently belong to the target company.
|
||||
for (auto& element : TileManager::getElements())
|
||||
{
|
||||
auto roadElement = element.asRoad();
|
||||
if (roadElement != nullptr)
|
||||
{
|
||||
roadElement->setOwner(ourCompanyId);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto trackElement = element.asTrack();
|
||||
if (trackElement != nullptr)
|
||||
{
|
||||
trackElement->setOwner(ourCompanyId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Second phase: change ownership of all stations that currently belong to the target company.
|
||||
for (auto& station : StationManager::stations())
|
||||
{
|
||||
if (station.owner != targetCompanyId)
|
||||
continue;
|
||||
|
||||
station.owner = ourCompanyId;
|
||||
}
|
||||
|
||||
// Third phase: change ownership of all vehicles that currently belong to the target company.
|
||||
for (auto vehicle : EntityManager::VehicleList())
|
||||
{
|
||||
if (vehicle->owner != targetCompanyId)
|
||||
continue;
|
||||
|
||||
Vehicles::Vehicle train(vehicle);
|
||||
train.head->owner = ourCompanyId;
|
||||
train.veh1->owner = ourCompanyId;
|
||||
train.veh2->owner = ourCompanyId;
|
||||
train.tail->owner = ourCompanyId;
|
||||
|
||||
for (auto& car : train.cars)
|
||||
{
|
||||
for (auto& component : car)
|
||||
{
|
||||
component.front->owner = ourCompanyId;
|
||||
component.body->owner = ourCompanyId;
|
||||
component.back->owner = ourCompanyId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t addCash(currency32_t amount)
|
||||
{
|
||||
auto company = CompanyManager::getPlayerCompany();
|
||||
company->cash = company->cash + amount;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t clearLoan()
|
||||
{
|
||||
auto company = CompanyManager::getPlayerCompany();
|
||||
company->current_loan = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t companyRatings(bool absolute, int32_t value)
|
||||
{
|
||||
auto companyId = CompanyManager::getControllingId();
|
||||
|
||||
for (auto& town : TownManager::towns())
|
||||
{
|
||||
// Does this town have a rating for our company?
|
||||
if (!(town.companies_with_rating &= (1 << companyId)))
|
||||
continue;
|
||||
|
||||
int16_t newRanking{};
|
||||
if (absolute)
|
||||
{
|
||||
newRanking = value * max_company_rating;
|
||||
}
|
||||
else
|
||||
{
|
||||
newRanking = town.company_ratings[companyId] + max_company_rating;
|
||||
newRanking *= 1.0f + (1.0f / value);
|
||||
newRanking -= max_company_rating;
|
||||
}
|
||||
|
||||
// Set the new rating.
|
||||
town.company_ratings[companyId] = std::clamp<int16_t>(newRanking, min_company_rating, max_company_rating);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t switchCompany(CompanyId_t targetCompanyId)
|
||||
{
|
||||
auto ourId = CompanyManager::getControllingId();
|
||||
auto otherId = CompanyManager::getSecondaryPlayerId();
|
||||
|
||||
// Already controlling the target company?
|
||||
if (targetCompanyId == ourId)
|
||||
return 0;
|
||||
|
||||
// Is the other player controlling the target company? Swap companies.
|
||||
if (targetCompanyId == otherId)
|
||||
{
|
||||
CompanyManager::setSecondaryPlayerId(ourId);
|
||||
CompanyManager::setControllingId(otherId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Change control over to the other company.
|
||||
CompanyManager::setControllingId(targetCompanyId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t toggleBankruptcy(CompanyId_t targetCompanyId)
|
||||
{
|
||||
auto company = CompanyManager::get(targetCompanyId);
|
||||
company->challenge_flags ^= CompanyFlags::bankrupt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t toggleJail(CompanyId_t targetCompanyId)
|
||||
{
|
||||
auto company = CompanyManager::get(targetCompanyId);
|
||||
company->jail_status = 30;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t vehicleReliability(int32_t newReliablity)
|
||||
{
|
||||
auto ourCompanyId = CompanyManager::updatingCompanyId();
|
||||
|
||||
for (auto vehicle : EntityManager::VehicleList())
|
||||
{
|
||||
if (vehicle->owner != ourCompanyId)
|
||||
continue;
|
||||
|
||||
Vehicles::Vehicle train(vehicle);
|
||||
train.veh2->reliability = newReliablity;
|
||||
|
||||
// Set reliability for the first car's front bogie component.
|
||||
auto& car = *(train.cars.begin());
|
||||
auto& component = *(car.begin());
|
||||
component.front->reliability = newReliablity * 256;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t cheat(CheatCommand command, int32_t param1, int32_t param2, int32_t param3)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case CheatCommand::acquireAssets:
|
||||
return Cheats::acquireAssets(param1);
|
||||
|
||||
case CheatCommand::addCash:
|
||||
return Cheats::addCash(param1);
|
||||
|
||||
case CheatCommand::clearLoan:
|
||||
return Cheats::clearLoan();
|
||||
|
||||
case CheatCommand::companyRatings:
|
||||
return Cheats::companyRatings(param1, param2);
|
||||
|
||||
case CheatCommand::switchCompany:
|
||||
return Cheats::switchCompany(param1);
|
||||
|
||||
case CheatCommand::toggleBankruptcy:
|
||||
return Cheats::toggleBankruptcy(param1);
|
||||
|
||||
case CheatCommand::toggleJail:
|
||||
return Cheats::toggleJail(param1);
|
||||
|
||||
case CheatCommand::vehicleReliability:
|
||||
return Cheats::vehicleReliability(param1);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cheat(registers& regs)
|
||||
{
|
||||
regs.ebx = cheat(CheatCommand(regs.eax), regs.ebx, regs.ecx, regs.edx);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#include <cstdint>
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
enum class CheatCommand : uint8_t
|
||||
{
|
||||
acquireAssets,
|
||||
addCash,
|
||||
clearLoan,
|
||||
companyRatings,
|
||||
switchCompany,
|
||||
toggleBankruptcy,
|
||||
toggleJail,
|
||||
vehicleReliability,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,466 @@
|
|||
#include "GameCommands.h"
|
||||
#include "../Audio/Audio.h"
|
||||
#include "../Company.h"
|
||||
#include "../CompanyManager.h"
|
||||
#include "../Localisation/FormatArguments.hpp"
|
||||
#include "../Localisation/StringIds.h"
|
||||
#include "../Map/Tile.h"
|
||||
#include "../Objects/ObjectManager.h"
|
||||
#include "../Objects/RoadObject.h"
|
||||
#include "../Objects/TrackObject.h"
|
||||
#include "../StationManager.h"
|
||||
#include "../Ui/WindowManager.h"
|
||||
#include "../Vehicles/Vehicle.h"
|
||||
#include <cassert>
|
||||
|
||||
using namespace OpenLoco::Ui;
|
||||
using namespace OpenLoco::Map;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
static loco_global<CompanyId_t, 0x009C68EB> _updating_company_id;
|
||||
static loco_global<uint8_t, 0x00508F08> game_command_nest_level;
|
||||
static loco_global<CompanyId_t[2], 0x00525E3C> _player_company;
|
||||
static loco_global<uint8_t, 0x00508F17> paused_state;
|
||||
|
||||
static uint16_t _gameCommandFlags;
|
||||
|
||||
static loco_global<TileElement*, 0x009C68D0> _9C68D0;
|
||||
|
||||
static loco_global<Pos3, 0x009C68E0> _gGameCommandPosition;
|
||||
static loco_global<string_id, 0x009C68E6> _gGameCommandErrorText;
|
||||
static loco_global<string_id, 0x009C68E8> _gGameCommandErrorTitle;
|
||||
static loco_global<uint8_t, 0x009C68EA> _gGameCommandExpenditureType; // premultiplied by 4
|
||||
static loco_global<uint8_t, 0x009C68EE> _errorCompanyId;
|
||||
static loco_global<string_id[8], 0x112C826> _commonFormatArgs;
|
||||
|
||||
using GameCommandFunc = void (*)(registers& regs);
|
||||
|
||||
struct GameCommandInfo
|
||||
{
|
||||
GameCommand id;
|
||||
GameCommandFunc implementation;
|
||||
uintptr_t originalAddress; // original array: 0x004F9548
|
||||
bool unpausesGame; // original array: 0x004F9688
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
static constexpr GameCommandInfo _gameCommandDefinitions[82] = {
|
||||
{ GameCommand::vehicleRearrange, nullptr, 0x004AF1DF, true },
|
||||
{ GameCommand::vehiclePlace, nullptr, 0x004B01B6, true },
|
||||
{ GameCommand::vehiclePickup, vehiclePickup, 0x004B0826, true },
|
||||
{ GameCommand::vehicleReverse, nullptr, 0x004ADAA8, true },
|
||||
{ GameCommand::vehiclePassSignal, nullptr, 0x004B0B50, true },
|
||||
{ GameCommand::vehicleCreate, Vehicles::create, 0x004AE5E4, true },
|
||||
{ GameCommand::vehicleSell, nullptr, 0x004AED34, true },
|
||||
{ GameCommand::createTrack, nullptr, 0x0049BB98, true },
|
||||
{ GameCommand::removeTrack, nullptr, 0x0049C7F2, true },
|
||||
{ GameCommand::changeLoan, nullptr, 0x0046DE88, false },
|
||||
{ GameCommand::vehicleRename, Vehicles::rename, 0x004B6572, false },
|
||||
{ GameCommand::changeStationName, renameStation, 0x00490756, false },
|
||||
{ GameCommand::vehicleLocalExpress, nullptr, 0x004B694B, true },
|
||||
{ GameCommand::createSignal, nullptr, 0x00488BDB, true },
|
||||
{ GameCommand::removeSignal, nullptr, 0x004891E4, true },
|
||||
{ GameCommand::createTrainStation, nullptr, 0x0048BB20, true },
|
||||
{ GameCommand::removeTrackStation, nullptr, 0x0048C402, true },
|
||||
{ GameCommand::createTrackMod, nullptr, 0x004A6479, true },
|
||||
{ GameCommand::removeTrackMod, nullptr, 0x004A668A, true },
|
||||
{ GameCommand::changeCompanyColourScheme, changeCompanyColour, 0x0043483D, false },
|
||||
{ GameCommand::pauseGame, togglePause, 0x00431E32, false },
|
||||
{ GameCommand::loadSaveQuitGame, loadSaveQuit, 0x0043BFCB, false },
|
||||
{ GameCommand::removeTree, removeTree, 0x004BB392, true },
|
||||
{ GameCommand::createTree, nullptr, 0x004BB138, true },
|
||||
{ GameCommand::changeLandMaterial, nullptr, 0x00468EDD, true },
|
||||
{ GameCommand::raiseLand, nullptr, 0x00463702, true },
|
||||
{ GameCommand::lowerLand, nullptr, 0x004638C6, true },
|
||||
{ GameCommand::lowerRaiseLandMountain, nullptr, 0x00462DCE, true },
|
||||
{ GameCommand::raiseWater, nullptr, 0x004C4F19, true },
|
||||
{ GameCommand::lowerWater, nullptr, 0x004C5126, true },
|
||||
{ GameCommand::changeCompanyName, nullptr, 0x00434914, false },
|
||||
{ GameCommand::changeCompanyOwnerName, nullptr, 0x00434A58, false },
|
||||
{ GameCommand::createWall, nullptr, 0x004C436C, true },
|
||||
{ GameCommand::removeWall, nullptr, 0x004C466C, true },
|
||||
{ GameCommand::gc_unk_34, nullptr, 0x004C4717, false },
|
||||
{ GameCommand::vehicleOrderInsert, nullptr, 0x0047036E, false },
|
||||
{ GameCommand::vehicleOrderDelete, nullptr, 0x0047057A, false },
|
||||
{ GameCommand::vehicleOrderSkip, Vehicles::orderSkip, 0x0047071A, false },
|
||||
{ GameCommand::createRoad, nullptr, 0x00475FBC, true },
|
||||
{ GameCommand::removeRoad, nullptr, 0x004775A5, true },
|
||||
{ GameCommand::createRoadMod, nullptr, 0x0047A21E, true },
|
||||
{ GameCommand::removeRoadMod, nullptr, 0x0047A42F, true },
|
||||
{ GameCommand::createRoadStation, nullptr, 0x0048C708, true },
|
||||
{ GameCommand::removeRoadStation, nullptr, 0x0048D2AC, true },
|
||||
{ GameCommand::createBuilding, nullptr, 0x0042D133, true },
|
||||
{ GameCommand::removeBuilding, nullptr, 0x0042D74E, true },
|
||||
{ GameCommand::renameTown, renameTown, 0x0049B11E, false },
|
||||
{ GameCommand::createIndustry, nullptr, 0x0045436B, true },
|
||||
{ GameCommand::removeIndustry, nullptr, 0x00455943, true },
|
||||
{ GameCommand::createTown, nullptr, 0x00496C22, true },
|
||||
{ GameCommand::removeTown, nullptr, 0x0049711F, true },
|
||||
{ GameCommand::gc_unk_51, nullptr, 0x004A6FDC, true },
|
||||
{ GameCommand::gc_unk_52, nullptr, 0x004A734F, true },
|
||||
{ GameCommand::gc_unk_53, nullptr, 0x0047AF0B, true },
|
||||
{ GameCommand::buildCompanyHeadquarters, nullptr, 0x0042ECFC, true },
|
||||
{ GameCommand::removeCompanyHeadquarters, nullptr, 0x0042EEAF, true },
|
||||
{ GameCommand::createAirport, nullptr, 0x00492C41, true },
|
||||
{ GameCommand::removeAirport, nullptr, 0x00493559, true },
|
||||
{ GameCommand::vehiclePlaceAir, nullptr, 0x004267BE, true },
|
||||
{ GameCommand::vehiclePickupAir, nullptr, 0x00426B29, true },
|
||||
{ GameCommand::createPort, nullptr, 0x00493AA7, true },
|
||||
{ GameCommand::removePort, nullptr, 0x00494570, true },
|
||||
{ GameCommand::vehiclePlaceWater, nullptr, 0x0042773C, true },
|
||||
{ GameCommand::vehiclePickupWater, nullptr, 0x004279CC, true },
|
||||
{ GameCommand::vehicleRefit, nullptr, 0x0042F6DB, false },
|
||||
{ GameCommand::changeCompanyFace, nullptr, 0x00435506, false },
|
||||
{ GameCommand::clearLand, nullptr, 0x00469CCB, true },
|
||||
{ GameCommand::loadMultiplayerMap, nullptr, 0x00444DA0, false },
|
||||
{ GameCommand::gc_unk_68, nullptr, 0x0046F8A5, false },
|
||||
{ GameCommand::gc_unk_69, nullptr, 0x004454BE, false },
|
||||
{ GameCommand::gc_unk_70, nullptr, 0x004456C8, false },
|
||||
{ GameCommand::sendChatMessage, nullptr, 0x0046F976, false },
|
||||
{ GameCommand::multiplayerSave, nullptr, 0x004A0ACD, false },
|
||||
{ GameCommand::updateOwnerStatus, nullptr, 0x004383CA, false },
|
||||
{ GameCommand::vehicleSpeedControl, nullptr, 0x004BAB63, true },
|
||||
{ GameCommand::vehicleOrderUp, nullptr, 0x00470CD2, false },
|
||||
{ GameCommand::vehicleOrderDown, nullptr, 0x00470E06, false },
|
||||
{ GameCommand::vehicleApplyShuntCheat, nullptr, 0x004BAC53, false },
|
||||
{ GameCommand::applyFreeCashCheat, nullptr, 0x00438A08, false },
|
||||
{ GameCommand::renameIndustry, renameIndustry, 0x00455029, false },
|
||||
{ GameCommand::vehicleClone, Vehicles::cloneVehicle, 0, true },
|
||||
{ GameCommand::cheat, cheat, 0, true },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
void registerHooks()
|
||||
{
|
||||
registerHook(
|
||||
0x00431315,
|
||||
[](registers& regs) FORCE_ALIGN_ARG_POINTER -> uint8_t {
|
||||
registers backup = regs;
|
||||
auto ebx = doCommand(GameCommand(regs.esi), backup);
|
||||
|
||||
regs = backup;
|
||||
regs.ebx = ebx;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
static uint32_t loc_4314EA();
|
||||
static uint32_t loc_4313C6(int esi, const registers& regs);
|
||||
|
||||
static bool commandRequiresUnpausingGame(GameCommand command, uint16_t flags)
|
||||
{
|
||||
if ((flags & (Flags::flag_4 | Flags::flag_6)) != 0)
|
||||
return false;
|
||||
|
||||
auto& gameCommand = _gameCommandDefinitions[static_cast<uint32_t>(command)];
|
||||
if (!gameCommand.unpausesGame || isPauseOverrideEnabled())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0x00431315
|
||||
uint32_t doCommand(GameCommand command, const registers& regs)
|
||||
{
|
||||
uint16_t flags = regs.bx;
|
||||
uint32_t esi = static_cast<uint32_t>(command);
|
||||
|
||||
_gameCommandFlags = regs.bx;
|
||||
if (game_command_nest_level != 0)
|
||||
return loc_4313C6(esi, regs);
|
||||
|
||||
if ((flags & Flags::apply) == 0)
|
||||
{
|
||||
return loc_4313C6(esi, regs);
|
||||
}
|
||||
|
||||
if (commandRequiresUnpausingGame(command, flags) && _updating_company_id == _player_company[0])
|
||||
{
|
||||
if (getPauseFlags() & 1)
|
||||
{
|
||||
paused_state = paused_state ^ 1;
|
||||
WindowManager::invalidate(WindowType::timeToolbar);
|
||||
Audio::unpauseSound();
|
||||
Ui::Windows::PlayerInfoPanel::invalidateFrame();
|
||||
}
|
||||
|
||||
if (getGameSpeed() != 0)
|
||||
{
|
||||
setGameSpeed(0);
|
||||
WindowManager::invalidate(WindowType::timeToolbar);
|
||||
}
|
||||
|
||||
if (isPaused())
|
||||
{
|
||||
_gGameCommandErrorText = StringIds::empty;
|
||||
return 0x80000000;
|
||||
}
|
||||
}
|
||||
|
||||
if (_updating_company_id == _player_company[0] && isNetworked())
|
||||
{
|
||||
assert(false);
|
||||
registers fnRegs = regs;
|
||||
call(0x0046E34A, fnRegs); // some network stuff. Untested
|
||||
}
|
||||
|
||||
return loc_4313C6(esi, regs);
|
||||
}
|
||||
|
||||
static void callGameCommandFunction(uint32_t command, registers& regs)
|
||||
{
|
||||
auto& gameCommand = _gameCommandDefinitions[command];
|
||||
if (gameCommand.implementation != nullptr)
|
||||
{
|
||||
gameCommand.implementation(regs);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto addr = gameCommand.originalAddress;
|
||||
call(addr, regs);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t loc_4313C6(int esi, const registers& regs)
|
||||
{
|
||||
uint16_t flags = regs.bx;
|
||||
_gGameCommandErrorText = StringIds::null;
|
||||
game_command_nest_level++;
|
||||
|
||||
uint16_t flagsBackup = _gameCommandFlags;
|
||||
registers fnRegs1 = regs;
|
||||
fnRegs1.bl &= ~Flags::apply;
|
||||
callGameCommandFunction(esi, fnRegs1);
|
||||
int32_t ebx = fnRegs1.ebx;
|
||||
_gameCommandFlags = flagsBackup;
|
||||
|
||||
if (ebx != static_cast<int32_t>(0x80000000))
|
||||
{
|
||||
if (isEditorMode())
|
||||
ebx = 0;
|
||||
|
||||
if (game_command_nest_level == 1)
|
||||
{
|
||||
if ((_gameCommandFlags & Flags::flag_2) == 0
|
||||
&& (_gameCommandFlags & Flags::flag_6) == 0
|
||||
&& ebx != 0)
|
||||
{
|
||||
registers regs2;
|
||||
regs2.ebp = ebx;
|
||||
call(0x0046DD06, regs2);
|
||||
ebx = regs2.ebp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ebx == static_cast<int32_t>(0x80000000))
|
||||
{
|
||||
if (flags & Flags::apply)
|
||||
{
|
||||
return loc_4314EA();
|
||||
}
|
||||
else
|
||||
{
|
||||
game_command_nest_level--;
|
||||
return ebx;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & 1) == 0)
|
||||
{
|
||||
game_command_nest_level--;
|
||||
return ebx;
|
||||
}
|
||||
|
||||
uint16_t flagsBackup2 = _gameCommandFlags;
|
||||
registers fnRegs2 = regs;
|
||||
callGameCommandFunction(esi, fnRegs2);
|
||||
int32_t ebx2 = fnRegs2.ebx;
|
||||
_gameCommandFlags = flagsBackup2;
|
||||
|
||||
if (ebx2 == static_cast<int32_t>(0x80000000))
|
||||
{
|
||||
return loc_4314EA();
|
||||
}
|
||||
|
||||
if (isEditorMode())
|
||||
{
|
||||
ebx = 0;
|
||||
}
|
||||
|
||||
if (ebx2 < ebx)
|
||||
{
|
||||
ebx = ebx2;
|
||||
}
|
||||
|
||||
game_command_nest_level--;
|
||||
if (game_command_nest_level != 0)
|
||||
return ebx;
|
||||
|
||||
if ((flagsBackup2 & Flags::flag_5) != 0)
|
||||
return ebx;
|
||||
|
||||
// Apply to company money
|
||||
CompanyManager::applyPaymentToCompany(CompanyManager::updatingCompanyId(), ebx, getExpenditureType());
|
||||
|
||||
if (ebx != 0 && _updating_company_id == _player_company[0])
|
||||
{
|
||||
// Add flying cost text
|
||||
CompanyManager::spendMoneyEffect(getPosition() + Map::Pos3{ 0, 0, 24 }, _updating_company_id, ebx);
|
||||
}
|
||||
|
||||
return ebx;
|
||||
}
|
||||
|
||||
static uint32_t loc_4314EA()
|
||||
{
|
||||
game_command_nest_level--;
|
||||
if (game_command_nest_level != 0)
|
||||
return 0x80000000;
|
||||
|
||||
if (_updating_company_id != _player_company[0])
|
||||
return 0x80000000;
|
||||
|
||||
if (_gameCommandFlags & Flags::flag_3)
|
||||
return 0x80000000;
|
||||
|
||||
if (_gGameCommandErrorText != 0xFFFE)
|
||||
{
|
||||
Windows::showError(_gGameCommandErrorTitle, _gGameCommandErrorText);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
// advanced errors
|
||||
if (_9C68D0 != (void*)-1)
|
||||
{
|
||||
auto tile = (TileElement*)_9C68D0;
|
||||
|
||||
switch (tile->type())
|
||||
{
|
||||
case ElementType::track: // 4
|
||||
{
|
||||
auto trackElement = tile->asTrack();
|
||||
if (trackElement == nullptr)
|
||||
break; // throw exception?
|
||||
|
||||
TrackObject* pObject = ObjectManager::get<TrackObject>(trackElement->trackObjectId());
|
||||
if (pObject == nullptr)
|
||||
break;
|
||||
|
||||
auto formatter = FormatArguments::common();
|
||||
formatter.push(pObject->name);
|
||||
formatter.push(CompanyManager::get(_errorCompanyId)->name);
|
||||
Windows::Error::openWithCompetitor(_gGameCommandErrorTitle, StringIds::error_reason_stringid_belongs_to, _errorCompanyId);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
case ElementType::road: //0x1C
|
||||
{
|
||||
auto roadElement = tile->asRoad();
|
||||
if (roadElement == nullptr)
|
||||
break; // throw exception?
|
||||
|
||||
RoadObject* pObject = ObjectManager::get<RoadObject>(roadElement->roadObjectId());
|
||||
if (pObject == nullptr)
|
||||
break;
|
||||
|
||||
auto formatter = FormatArguments::common();
|
||||
formatter.push(pObject->name);
|
||||
formatter.push(CompanyManager::get(_errorCompanyId)->name);
|
||||
Windows::Error::openWithCompetitor(_gGameCommandErrorTitle, StringIds::error_reason_stringid_belongs_to, _errorCompanyId);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
case ElementType::station: // 8
|
||||
{
|
||||
auto stationElement = tile->asStation();
|
||||
if (stationElement == nullptr)
|
||||
break; // throw exception?
|
||||
|
||||
Station* pStation = StationManager::get(stationElement->stationId());
|
||||
if (pStation == nullptr)
|
||||
break;
|
||||
|
||||
auto formatter = FormatArguments::common();
|
||||
formatter.push(pStation->name);
|
||||
formatter.push(pStation->town);
|
||||
formatter.push(CompanyManager::get(_errorCompanyId)->name);
|
||||
Windows::Error::openWithCompetitor(_gGameCommandErrorTitle, StringIds::error_reason_stringid_belongs_to, _errorCompanyId);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
case ElementType::signal: // 0x0C
|
||||
{
|
||||
auto formatter = FormatArguments::common();
|
||||
formatter.push(CompanyManager::get(_errorCompanyId)->name);
|
||||
Windows::Error::openWithCompetitor(_gGameCommandErrorTitle, StringIds::error_reason_signal_belongs_to, _errorCompanyId);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
auto formatter = FormatArguments::common();
|
||||
formatter.push(CompanyManager::get(_errorCompanyId)->name);
|
||||
Windows::Error::openWithCompetitor(_gGameCommandErrorTitle, StringIds::error_reason_stringid_belongs_to, _errorCompanyId);
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
// 0x00431E6A
|
||||
// al : company
|
||||
// esi : tile
|
||||
bool sub_431E6A(const CompanyId_t company, Map::TileElement* const tile /*= nullptr*/)
|
||||
{
|
||||
if (company == CompanyId::neutral)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (_updating_company_id == company || _updating_company_id == CompanyId::neutral)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
_gGameCommandErrorText = -2;
|
||||
_errorCompanyId = company;
|
||||
_9C68D0 = tile == nullptr ? reinterpret_cast<Map::TileElement*>(-1) : tile;
|
||||
return false;
|
||||
}
|
||||
|
||||
const Map::Pos3& getPosition()
|
||||
{
|
||||
return _gGameCommandPosition;
|
||||
}
|
||||
|
||||
void setPosition(const Map::Pos3& pos)
|
||||
{
|
||||
_gGameCommandPosition = pos;
|
||||
}
|
||||
|
||||
void setErrorText(const string_id message)
|
||||
{
|
||||
_gGameCommandErrorText = message;
|
||||
}
|
||||
|
||||
string_id getErrorText()
|
||||
{
|
||||
return _gGameCommandErrorText;
|
||||
}
|
||||
|
||||
void setErrorTitle(const string_id title)
|
||||
{
|
||||
_gGameCommandErrorTitle = title;
|
||||
}
|
||||
|
||||
ExpenditureType getExpenditureType()
|
||||
{
|
||||
return ExpenditureType(_gGameCommandExpenditureType / 4);
|
||||
}
|
||||
|
||||
void setExpenditureType(const ExpenditureType type)
|
||||
{
|
||||
_gGameCommandExpenditureType = static_cast<uint8_t>(type) * 4;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,77 @@
|
|||
#include "../Audio/Audio.h"
|
||||
#include "../Game.h"
|
||||
#include "../Tutorial.h"
|
||||
#include "../Ui/WindowManager.h"
|
||||
#include "../Ui/WindowType.h"
|
||||
#include "GameCommands.h"
|
||||
|
||||
#pragma warning(disable : 4702)
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
static loco_global<LoadOrQuitMode, 0x0050A002> _savePromptType;
|
||||
|
||||
// 0x0043BFCB
|
||||
static uint32_t loadSaveQuit(const uint8_t flags, uint8_t dl, uint8_t di)
|
||||
{
|
||||
if ((flags & Flags::apply) == 0)
|
||||
return 0;
|
||||
|
||||
if (dl == 1)
|
||||
{
|
||||
Ui::WindowManager::close(Ui::WindowType::saveGamePrompt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dl == 0)
|
||||
{
|
||||
_savePromptType = static_cast<LoadOrQuitMode>(di);
|
||||
Ui::Windows::TextInput::cancel();
|
||||
Ui::Windows::PromptSaveWindow::open(di);
|
||||
|
||||
if (!isTitleMode())
|
||||
{
|
||||
// 0x0043C369
|
||||
// NB: tutorial recording has been omitted.
|
||||
if (Tutorial::state() == Tutorial::State::playing)
|
||||
{
|
||||
Tutorial::stop();
|
||||
}
|
||||
else if (!isNetworked() || _savePromptType != LoadOrQuitMode::quitGamePrompt)
|
||||
{
|
||||
if (getScreenAge() >= 0xF00)
|
||||
{
|
||||
auto window = Ui::WindowManager::bringToFront(Ui::WindowType::saveGamePrompt);
|
||||
Audio::playSound(Audio::SoundId::openWindow, window->x + (window->width / 2));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x0043BFE3
|
||||
switch (_savePromptType)
|
||||
{
|
||||
case LoadOrQuitMode::loadGamePrompt:
|
||||
Game::loadGame();
|
||||
break;
|
||||
|
||||
case LoadOrQuitMode::returnToTitlePrompt:
|
||||
Game::returnToTitle();
|
||||
break;
|
||||
|
||||
case LoadOrQuitMode::quitGamePrompt:
|
||||
Game::quitGame();
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void loadSaveQuit(registers& regs)
|
||||
{
|
||||
regs.ebx = loadSaveQuit(regs.bl, regs.dl, regs.di);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
#include "../Economy/Economy.h"
|
||||
#include "../Economy/Expenditures.h"
|
||||
#include "../Map/TileManager.h"
|
||||
#include "../Objects/ObjectManager.h"
|
||||
#include "../Objects/TreeObject.h"
|
||||
#include "../OpenLoco.h"
|
||||
#include "../S5/S5.h"
|
||||
#include "GameCommands.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
// 0x004BB392
|
||||
static uint32_t removeTree(const Map::Pos3& pos, const uint8_t type, const uint8_t elementType, const uint8_t flags)
|
||||
{
|
||||
GameCommands::setExpenditureType(ExpenditureType::Construction);
|
||||
|
||||
auto tileHeight = Map::TileManager::getHeight(pos);
|
||||
GameCommands::setPosition(Map::Pos3(pos.x + Map::tile_size / 2, pos.y + Map::tile_size / 2, tileHeight.landHeight));
|
||||
|
||||
auto tile = Map::TileManager::get(pos);
|
||||
for (auto& element : tile)
|
||||
{
|
||||
// TODO: refactor! Figure out what info it actually needs.
|
||||
if (element.rawData()[0] != elementType)
|
||||
continue;
|
||||
|
||||
if (element.baseZ() * 4 != pos.z)
|
||||
continue;
|
||||
|
||||
auto treeElement = element.asTree();
|
||||
if (treeElement == nullptr)
|
||||
continue;
|
||||
|
||||
if (treeElement->treeObjectId() != type)
|
||||
continue;
|
||||
|
||||
auto treeObj = ObjectManager::get<TreeObject>(treeElement->treeObjectId());
|
||||
currency32_t removalCost = Economy::getInflationAdjustedCost(treeObj->clear_cost_factor, treeObj->cost_index, 12);
|
||||
|
||||
if (flags & Flags::apply)
|
||||
Map::TileManager::removeElement(element);
|
||||
|
||||
auto& options = S5::getOptions();
|
||||
options.madeAnyChanges = 1;
|
||||
|
||||
return removalCost;
|
||||
}
|
||||
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
void removeTree(registers& regs)
|
||||
{
|
||||
TreeRemovalArgs args(regs);
|
||||
regs.ebx = removeTree(args.pos, args.type, args.elementType, regs.bl);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
#include "../Economy/Expenditures.h"
|
||||
#include "../Industry.h"
|
||||
#include "../IndustryManager.h"
|
||||
#include "../Interop/Interop.hpp"
|
||||
#include "../Localisation/FormatArguments.hpp"
|
||||
#include "../Localisation/StringIds.h"
|
||||
#include "../Localisation/StringManager.h"
|
||||
#include "../Types.hpp"
|
||||
#include "GameCommands.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
/**
|
||||
* 0x00455029
|
||||
* Renames a particular industry.
|
||||
*
|
||||
* This command is called 3 times before the buffer is applied. Each time, 12 chars of the 36 char buffer are provided.
|
||||
* The resulting industry name has a maximum length of 31 chars; the last bytes are not used.
|
||||
*
|
||||
* @param flags @<bl> - game command flags
|
||||
* @param industryId @<cx> - industry id
|
||||
* @param index @<ax> - update index (in order of: 1, 2, 0)
|
||||
* @param buffer0 @<edx> - First part (4 chars) of the 12 update buffer
|
||||
* @param buffer1 @<dx> - Second part (4 chars) of the 12 update buffer
|
||||
* @param buffer2 @<bp> - Third part (4 chars) of the 12 update buffer
|
||||
* @return @<ebx> - returns 0 if rename is successful; otherwise GameCommands::FAILURE
|
||||
*/
|
||||
static uint32_t renameIndustry(const uint8_t flags, IndustryId_t industryId, int16_t index, uint32_t buffer0, uint32_t buffer1, uint32_t buffer2)
|
||||
{
|
||||
GameCommands::setExpenditureType(ExpenditureType::Miscellaneous);
|
||||
|
||||
// Keep track of the industry id over several calls.
|
||||
static IndustryId_t _industryId{};
|
||||
if (index == 1)
|
||||
_industryId = industryId;
|
||||
|
||||
static uint32_t renameBuffer[9];
|
||||
|
||||
// Fill buffer over calls into the renameBuffer
|
||||
if ((flags & GameCommands::Flags::apply) != 0)
|
||||
{
|
||||
static const std::array<int, 3> transformTable = { 2, 0, 1 };
|
||||
int arrayIndex = transformTable.at(index);
|
||||
renameBuffer[arrayIndex * 3] = buffer0;
|
||||
renameBuffer[arrayIndex * 3 + 1] = buffer1;
|
||||
renameBuffer[arrayIndex * 3 + 2] = buffer2;
|
||||
}
|
||||
|
||||
// Applying the buffer?
|
||||
if (index != 0)
|
||||
return 0;
|
||||
|
||||
char renameStringBuffer[37] = "";
|
||||
memcpy(renameStringBuffer, renameBuffer, sizeof(renameBuffer));
|
||||
renameStringBuffer[36] = '\0';
|
||||
|
||||
// Ensure the new name isn't empty.
|
||||
if (strlen(renameStringBuffer) == 0)
|
||||
return 0;
|
||||
|
||||
// Figure out the current name for this industry.
|
||||
char currentIndustryName[256] = "";
|
||||
auto industry = IndustryManager::get(_industryId);
|
||||
auto args = FormatArguments::common(industry->town);
|
||||
StringManager::formatString(currentIndustryName, industry->name, &args);
|
||||
|
||||
// Verify the new name actually differs from the old one.
|
||||
if (strcmp(currentIndustryName, renameStringBuffer) == 0)
|
||||
return 0;
|
||||
|
||||
// Allocate a string id for the new name.
|
||||
string_id allocatedStringId = StringManager::userStringAllocate(renameStringBuffer, 0);
|
||||
if (allocatedStringId == StringIds::empty)
|
||||
return GameCommands::FAILURE;
|
||||
|
||||
// Bailing out early?
|
||||
if ((flags & GameCommands::Flags::apply) == 0)
|
||||
{
|
||||
StringManager::emptyUserString(allocatedStringId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply the new name to the industry.
|
||||
string_id oldStringId = industry->name;
|
||||
industry->name = allocatedStringId;
|
||||
StringManager::emptyUserString(oldStringId);
|
||||
Gfx::invalidateScreen();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void renameIndustry(registers& regs)
|
||||
{
|
||||
regs.ebx = renameIndustry(regs.bl, regs.cx, regs.ax, regs.edx, regs.ebp, regs.edi);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
#include "../Economy/Expenditures.h"
|
||||
#include "../Industry.h"
|
||||
#include "../Interop/Interop.hpp"
|
||||
#include "../Localisation/FormatArguments.hpp"
|
||||
#include "../Localisation/StringIds.h"
|
||||
#include "../Localisation/StringManager.h"
|
||||
#include "../StationManager.h"
|
||||
#include "../TownManager.h"
|
||||
#include "../Types.hpp"
|
||||
#include "GameCommands.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
/**
|
||||
* 0x00490756
|
||||
* Renames a particular station.
|
||||
*
|
||||
* This command is called 3 times before the buffer is applied. Each time, 12 chars of the 36 char buffer are provided.
|
||||
* The resulting station name has a maximum length of 31 chars; the last bytes are not used.
|
||||
*
|
||||
* @param flags @<bl> - game command flags
|
||||
* @param stationId @<cx> - station id
|
||||
* @param index @<ax> - update index (in order of: 1, 2, 0)
|
||||
* @param buffer0 @<edx> - First part (4 chars) of the 12 update buffer
|
||||
* @param buffer1 @<dx> - Second part (4 chars) of the 12 update buffer
|
||||
* @param buffer2 @<bp> - Third part (4 chars) of the 12 update buffer
|
||||
* @return @<ebx> - returns 0 if rename is successful; otherwise GameCommands::FAILURE
|
||||
*/
|
||||
static uint32_t renameStation(const uint8_t flags, StationId_t stationId, int16_t index, uint32_t buffer0, uint32_t buffer1, uint32_t buffer2)
|
||||
{
|
||||
GameCommands::setExpenditureType(ExpenditureType::Miscellaneous);
|
||||
|
||||
// Keep track of the station id over several calls.
|
||||
static StationId_t _stationId{};
|
||||
if (index == 1)
|
||||
_stationId = stationId;
|
||||
|
||||
static uint32_t renameBuffer[9];
|
||||
|
||||
// Fill buffer over calls into the renameBuffer
|
||||
if ((flags & GameCommands::Flags::apply) != 0)
|
||||
{
|
||||
static const std::array<int, 3> transformTable = { 2, 0, 1 };
|
||||
int arrayIndex = transformTable.at(index);
|
||||
renameBuffer[arrayIndex * 3] = buffer0;
|
||||
renameBuffer[arrayIndex * 3 + 1] = buffer1;
|
||||
renameBuffer[arrayIndex * 3 + 2] = buffer2;
|
||||
}
|
||||
|
||||
// Applying the buffer?
|
||||
if (index != 0)
|
||||
return 0;
|
||||
|
||||
char renameStringBuffer[37] = "";
|
||||
memcpy(renameStringBuffer, renameBuffer, sizeof(renameBuffer));
|
||||
renameStringBuffer[36] = '\0';
|
||||
|
||||
// Figure out the current name for this station.
|
||||
char currentStationName[256] = "";
|
||||
auto station = StationManager::get(_stationId);
|
||||
auto args = FormatArguments::common(station->town);
|
||||
StringManager::formatString(currentStationName, station->name, &args);
|
||||
|
||||
// Verify the new name actually differs from the old one.
|
||||
if (strcmp(currentStationName, renameStringBuffer) == 0)
|
||||
return 0;
|
||||
|
||||
string_id oldStringId = station->name;
|
||||
|
||||
// If an empty string is given, generate one instead.
|
||||
if (strlen(renameStringBuffer) == 0)
|
||||
{
|
||||
// Are we bailing out early?
|
||||
if ((flags & GameCommands::Flags::apply) == 0)
|
||||
return 0;
|
||||
|
||||
station->name = StationManager::generateNewStationName(_stationId, station->town, Map::Pos3(station->x, station->y, station->z), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate a string id for the new name.
|
||||
string_id allocatedStringId = StringManager::userStringAllocate(renameStringBuffer, 0);
|
||||
if (allocatedStringId == StringIds::empty)
|
||||
return GameCommands::FAILURE;
|
||||
|
||||
// Are we bailing out early?
|
||||
if ((flags & GameCommands::Flags::apply) == 0)
|
||||
{
|
||||
StringManager::emptyUserString(allocatedStringId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply the new name to the station.
|
||||
station->name = allocatedStringId;
|
||||
}
|
||||
|
||||
StringManager::emptyUserString(oldStringId);
|
||||
station->updateLabel();
|
||||
Gfx::invalidateScreen();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void renameStation(registers& regs)
|
||||
{
|
||||
regs.ebx = renameStation(regs.bl, regs.cx, regs.ax, regs.edx, regs.ebp, regs.edi);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
#include "../Economy/Expenditures.h"
|
||||
#include "../Industry.h"
|
||||
#include "../Interop/Interop.hpp"
|
||||
#include "../Localisation/FormatArguments.hpp"
|
||||
#include "../Localisation/StringIds.h"
|
||||
#include "../Localisation/StringManager.h"
|
||||
#include "../StationManager.h"
|
||||
#include "../TownManager.h"
|
||||
#include "../Types.hpp"
|
||||
#include "GameCommands.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
/**
|
||||
* 0x00455029
|
||||
* Renames a particular town.
|
||||
*
|
||||
* This command is called 3 times before the buffer is applied. Each time, 12 chars of the 36 char buffer are provided.
|
||||
* The resulting town name has a maximum length of 31 chars; the last bytes are not used.
|
||||
*
|
||||
* @param flags @<bl> - game command flags
|
||||
* @param townId @<cx> - town id
|
||||
* @param index @<ax> - update index (in order of: 1, 2, 0)
|
||||
* @param buffer0 @<edx> - First part (4 chars) of the 12 update buffer
|
||||
* @param buffer1 @<dx> - Second part (4 chars) of the 12 update buffer
|
||||
* @param buffer2 @<bp> - Third part (4 chars) of the 12 update buffer
|
||||
* @return @<ebx> - returns 0 if rename is successful; otherwise GameCommands::FAILURE
|
||||
*/
|
||||
static uint32_t renameTown(const uint8_t flags, TownId_t townId, int16_t index, uint32_t buffer0, uint32_t buffer1, uint32_t buffer2)
|
||||
{
|
||||
GameCommands::setExpenditureType(ExpenditureType::Miscellaneous);
|
||||
|
||||
// Keep track of the town id over several calls.
|
||||
static TownId_t _townId{};
|
||||
if (index == 1)
|
||||
_townId = townId;
|
||||
|
||||
static uint32_t renameBuffer[9];
|
||||
|
||||
// Fill buffer over calls into the renameBuffer
|
||||
if ((flags & GameCommands::Flags::apply) != 0)
|
||||
{
|
||||
static const std::array<int, 3> transformTable = { 2, 0, 1 };
|
||||
int arrayIndex = transformTable.at(index);
|
||||
renameBuffer[arrayIndex * 3] = buffer0;
|
||||
renameBuffer[arrayIndex * 3 + 1] = buffer1;
|
||||
renameBuffer[arrayIndex * 3 + 2] = buffer2;
|
||||
}
|
||||
|
||||
// Applying the buffer?
|
||||
if (index != 0)
|
||||
return 0;
|
||||
|
||||
char renameStringBuffer[37] = "";
|
||||
memcpy(renameStringBuffer, renameBuffer, sizeof(renameBuffer));
|
||||
renameStringBuffer[36] = '\0';
|
||||
|
||||
// Ensure the new name isn't empty.
|
||||
if (strlen(renameStringBuffer) == 0)
|
||||
return 0;
|
||||
|
||||
// Figure out the current name for this town.
|
||||
char currentTownName[256] = "";
|
||||
auto town = TownManager::get(_townId);
|
||||
StringManager::formatString(currentTownName, town->name);
|
||||
|
||||
// Verify the new name actually differs from the old one.
|
||||
if (strcmp(currentTownName, renameStringBuffer) == 0)
|
||||
return 0;
|
||||
|
||||
// Allocate a string id for the new name.
|
||||
string_id allocatedStringId = StringManager::userStringAllocate(renameStringBuffer, 0);
|
||||
if (allocatedStringId == StringIds::empty)
|
||||
return GameCommands::FAILURE;
|
||||
|
||||
// Bailing out early?
|
||||
if ((flags & GameCommands::Flags::apply) == 0)
|
||||
{
|
||||
StringManager::emptyUserString(allocatedStringId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply the new name to the town.
|
||||
string_id oldStringId = town->name;
|
||||
town->name = allocatedStringId;
|
||||
StringManager::emptyUserString(oldStringId);
|
||||
|
||||
// Recalculate labels for the town and (surrounding) stations.
|
||||
town->updateLabel();
|
||||
StationManager::updateLabels();
|
||||
Gfx::invalidateScreen();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void renameTown(registers& regs)
|
||||
{
|
||||
regs.ebx = renameTown(regs.bl, regs.cx, regs.ax, regs.edx, regs.ebp, regs.edi);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#include "../Audio/Audio.h"
|
||||
#include "../GameException.hpp"
|
||||
#include "../OpenLoco.h"
|
||||
#include "../Ui/WindowManager.h"
|
||||
#include "../Ui/WindowType.h"
|
||||
#include "GameCommands.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
// 0x00431E32
|
||||
uint32_t togglePause(uint8_t flags)
|
||||
{
|
||||
if ((flags & Flags::apply) == 0)
|
||||
return 0;
|
||||
|
||||
Ui::WindowManager::invalidate(Ui::WindowType::timeToolbar);
|
||||
|
||||
if (isPaused())
|
||||
{
|
||||
unsetPauseFlag(1 << 0);
|
||||
Audio::unpauseSound();
|
||||
}
|
||||
else
|
||||
{
|
||||
setPauseFlag(1 << 0);
|
||||
Audio::pauseSound();
|
||||
Ui::Windows::TimePanel::invalidateFrame();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void togglePause(registers& regs)
|
||||
{
|
||||
regs.ebx = togglePause(regs.bl);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
#include "../Audio/Audio.h"
|
||||
#include "../Economy/Expenditures.h"
|
||||
#include "../Interop/Interop.hpp"
|
||||
#include "../Types.hpp"
|
||||
#include "../Utility/Prng.hpp"
|
||||
#include "../Vehicles/Vehicle.h"
|
||||
#include "GameCommands.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::GameCommands
|
||||
{
|
||||
static loco_global<Utility::prng, 0x00525E20> _prng;
|
||||
|
||||
// 0x0048B15B
|
||||
static void playPickupSound(Vehicles::Vehicle2* veh2)
|
||||
{
|
||||
const auto frequency = _prng->randNext(20003, 24098);
|
||||
Audio::playSound(Audio::SoundId::vehiclePickup, veh2->position, -1000, frequency);
|
||||
}
|
||||
|
||||
// 0x004B0826
|
||||
static uint32_t vehiclePickup(const uint8_t flags, EntityId_t headId)
|
||||
{
|
||||
GameCommands::setExpenditureType(ExpenditureType::TrainRunningCosts);
|
||||
|
||||
auto train = Vehicles::Vehicle(headId);
|
||||
auto* head = train.head;
|
||||
auto* veh2 = train.veh2;
|
||||
|
||||
GameCommands::setPosition(veh2->position);
|
||||
|
||||
if (!GameCommands::sub_431E6A(head->owner))
|
||||
return FAILURE;
|
||||
|
||||
if (!head->canBeModified())
|
||||
return FAILURE;
|
||||
|
||||
if (!(flags & GameCommands::Flags::apply))
|
||||
return 0;
|
||||
|
||||
if (!(flags & GameCommands::Flags::flag_6))
|
||||
playPickupSound(veh2);
|
||||
|
||||
head->liftUpVehicle();
|
||||
|
||||
// Clear ghost flag on primary vehicle pieces and all car components.
|
||||
train.head->var_38 &= ~(Vehicles::Flags38::isGhost);
|
||||
train.veh1->var_38 &= ~(Vehicles::Flags38::isGhost);
|
||||
train.veh2->var_38 &= ~(Vehicles::Flags38::isGhost);
|
||||
train.tail->var_38 &= ~(Vehicles::Flags38::isGhost);
|
||||
|
||||
for (auto& car : train.cars)
|
||||
{
|
||||
for (auto& component : car)
|
||||
{
|
||||
component.front->var_38 &= ~(Vehicles::Flags38::isGhost);
|
||||
component.back->var_38 &= ~(Vehicles::Flags38::isGhost);
|
||||
component.body->var_38 &= ~(Vehicles::Flags38::isGhost);
|
||||
}
|
||||
}
|
||||
|
||||
head->var_0C |= Vehicles::Flags0C::commandStop;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vehiclePickup(registers& regs)
|
||||
{
|
||||
regs.ebx = vehiclePickup(regs.bl, regs.di);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
namespace OpenLoco
|
||||
{
|
||||
enum class GameException
|
||||
{
|
||||
Interrupt = 0
|
||||
};
|
||||
}
|
|
@ -37,7 +37,7 @@ namespace OpenLoco::Colour
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t getShade(colour_t colour, uint8_t shade)
|
||||
uint8_t getShade(Colour_t colour, uint8_t shade)
|
||||
{
|
||||
colour &= ~Colour::inset_flag;
|
||||
assert(colour <= 31);
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
namespace OpenLoco
|
||||
{
|
||||
using colour_t = uint8_t;
|
||||
using palette_index_t = uint8_t;
|
||||
using Colour_t = uint8_t;
|
||||
using PaletteIndex_t = uint8_t;
|
||||
|
||||
namespace Colour
|
||||
{
|
||||
|
@ -13,108 +13,113 @@ namespace OpenLoco
|
|||
constexpr uint8_t inset_flag = 1 << 6;
|
||||
constexpr uint8_t translucent_flag = 1 << 7;
|
||||
|
||||
constexpr colour_t black = 0;
|
||||
constexpr colour_t grey = 1;
|
||||
constexpr colour_t white = 2;
|
||||
constexpr colour_t dark_purple = 3;
|
||||
constexpr colour_t light_purple = 4;
|
||||
constexpr colour_t bright_purple = 5;
|
||||
constexpr colour_t dark_blue = 6;
|
||||
constexpr colour_t light_blue = 7;
|
||||
constexpr colour_t icy_blue = 8;
|
||||
constexpr colour_t teal = 9;
|
||||
constexpr colour_t aquamarine = 10;
|
||||
constexpr colour_t saturated_green = 11;
|
||||
constexpr colour_t dark_green = 12;
|
||||
constexpr colour_t moss_green = 13;
|
||||
constexpr colour_t bright_green = 14;
|
||||
constexpr colour_t olive_green = 15;
|
||||
constexpr colour_t dark_olive_green = 16;
|
||||
constexpr colour_t bright_yellow = 17;
|
||||
constexpr colour_t yellow = 18;
|
||||
constexpr colour_t dark_yellow = 19;
|
||||
constexpr colour_t light_orange = 20;
|
||||
constexpr colour_t dark_orange = 21;
|
||||
constexpr colour_t light_brown = 22;
|
||||
constexpr colour_t saturated_brown = 23;
|
||||
constexpr colour_t dark_brown = 24;
|
||||
constexpr colour_t salmon_pink = 25;
|
||||
constexpr colour_t bordeaux_red = 26;
|
||||
constexpr colour_t saturated_red = 27;
|
||||
constexpr colour_t bright_red = 28;
|
||||
constexpr colour_t dark_pink = 29;
|
||||
constexpr colour_t bright_pink = 30;
|
||||
constexpr colour_t light_pink = 31;
|
||||
constexpr Colour_t black = 0;
|
||||
constexpr Colour_t grey = 1;
|
||||
constexpr Colour_t white = 2;
|
||||
constexpr Colour_t dark_purple = 3;
|
||||
constexpr Colour_t light_purple = 4;
|
||||
constexpr Colour_t bright_purple = 5;
|
||||
constexpr Colour_t dark_blue = 6;
|
||||
constexpr Colour_t light_blue = 7;
|
||||
constexpr Colour_t icy_blue = 8;
|
||||
constexpr Colour_t teal = 9;
|
||||
constexpr Colour_t aquamarine = 10;
|
||||
constexpr Colour_t saturated_green = 11;
|
||||
constexpr Colour_t dark_green = 12;
|
||||
constexpr Colour_t moss_green = 13;
|
||||
constexpr Colour_t bright_green = 14;
|
||||
constexpr Colour_t olive_green = 15;
|
||||
constexpr Colour_t dark_olive_green = 16;
|
||||
constexpr Colour_t bright_yellow = 17;
|
||||
constexpr Colour_t yellow = 18;
|
||||
constexpr Colour_t dark_yellow = 19;
|
||||
constexpr Colour_t light_orange = 20;
|
||||
constexpr Colour_t dark_orange = 21;
|
||||
constexpr Colour_t light_brown = 22;
|
||||
constexpr Colour_t saturated_brown = 23;
|
||||
constexpr Colour_t dark_brown = 24;
|
||||
constexpr Colour_t salmon_pink = 25;
|
||||
constexpr Colour_t bordeaux_red = 26;
|
||||
constexpr Colour_t saturated_red = 27;
|
||||
constexpr Colour_t bright_red = 28;
|
||||
constexpr Colour_t dark_pink = 29;
|
||||
constexpr Colour_t bright_pink = 30;
|
||||
constexpr Colour_t light_pink = 31;
|
||||
|
||||
constexpr colour_t outline(colour_t c)
|
||||
constexpr Colour_t outline(Colour_t c)
|
||||
{
|
||||
return c | outline_flag;
|
||||
}
|
||||
|
||||
constexpr colour_t inset(colour_t c)
|
||||
constexpr Colour_t inset(Colour_t c)
|
||||
{
|
||||
return c | inset_flag;
|
||||
}
|
||||
|
||||
constexpr colour_t translucent(colour_t c)
|
||||
constexpr Colour_t translucent(Colour_t c)
|
||||
{
|
||||
return c | translucent_flag;
|
||||
}
|
||||
|
||||
constexpr colour_t opaque(colour_t c)
|
||||
constexpr Colour_t opaque(Colour_t c)
|
||||
{
|
||||
return c & ~translucent_flag;
|
||||
}
|
||||
|
||||
void initColourMap();
|
||||
palette_index_t getShade(colour_t colour, uint8_t shade);
|
||||
PaletteIndex_t getShade(Colour_t colour, uint8_t shade);
|
||||
}
|
||||
|
||||
namespace PaletteIndex
|
||||
{
|
||||
constexpr palette_index_t transparent = 0;
|
||||
constexpr palette_index_t index_0A = 0x0A;
|
||||
constexpr palette_index_t index_0C = 0x0C;
|
||||
constexpr palette_index_t index_0E = 0x0E;
|
||||
constexpr palette_index_t index_11 = 0x11;
|
||||
constexpr palette_index_t index_12 = 0x12;
|
||||
constexpr palette_index_t index_15 = 0x15;
|
||||
constexpr palette_index_t index_1F = 0x1F;
|
||||
constexpr palette_index_t index_24 = 0x24;
|
||||
constexpr palette_index_t index_29 = 0x29;
|
||||
constexpr palette_index_t index_2E = 0x2E;
|
||||
constexpr palette_index_t index_30 = 0x30;
|
||||
constexpr palette_index_t index_35 = 0x35;
|
||||
constexpr palette_index_t index_38 = 0x38;
|
||||
constexpr palette_index_t index_3F = 0x3F;
|
||||
constexpr palette_index_t index_41 = 0x41;
|
||||
constexpr palette_index_t index_43 = 0x43;
|
||||
constexpr palette_index_t index_4B = 0x4B;
|
||||
constexpr palette_index_t index_50 = 0x50;
|
||||
constexpr palette_index_t index_58 = 0x58;
|
||||
constexpr palette_index_t index_64 = 0x64;
|
||||
constexpr palette_index_t index_66 = 0x66;
|
||||
constexpr palette_index_t index_67 = 0x67;
|
||||
constexpr palette_index_t index_68 = 0x68;
|
||||
constexpr palette_index_t index_71 = 0x71;
|
||||
constexpr palette_index_t index_7D = 0x7D;
|
||||
constexpr palette_index_t index_85 = 0x85;
|
||||
constexpr palette_index_t index_89 = 0x89;
|
||||
constexpr palette_index_t index_9D = 0x9D;
|
||||
constexpr palette_index_t index_A1 = 0xA1;
|
||||
constexpr palette_index_t index_A2 = 0xA2;
|
||||
constexpr palette_index_t index_A3 = 0xA3;
|
||||
constexpr palette_index_t index_AC = 0xAC;
|
||||
constexpr palette_index_t index_AD = 0xAD;
|
||||
constexpr palette_index_t index_B8 = 0xB8;
|
||||
constexpr palette_index_t index_BA = 0xBA;
|
||||
constexpr palette_index_t index_BB = 0xBB;
|
||||
constexpr palette_index_t index_BC = 0xBC;
|
||||
constexpr palette_index_t index_C3 = 0xC3;
|
||||
constexpr palette_index_t index_C6 = 0xC6;
|
||||
constexpr palette_index_t index_D0 = 0xD0;
|
||||
constexpr palette_index_t index_D3 = 0xD3;
|
||||
constexpr palette_index_t index_DB = 0xDB;
|
||||
constexpr palette_index_t index_DE = 0xDE;
|
||||
constexpr PaletteIndex_t transparent = 0;
|
||||
constexpr PaletteIndex_t index_0A = 0x0A;
|
||||
constexpr PaletteIndex_t index_0C = 0x0C;
|
||||
constexpr PaletteIndex_t index_0E = 0x0E;
|
||||
constexpr PaletteIndex_t index_11 = 0x11;
|
||||
constexpr PaletteIndex_t index_12 = 0x12;
|
||||
constexpr PaletteIndex_t index_15 = 0x15;
|
||||
constexpr PaletteIndex_t index_1F = 0x1F;
|
||||
constexpr PaletteIndex_t index_24 = 0x24;
|
||||
constexpr PaletteIndex_t index_29 = 0x29;
|
||||
constexpr PaletteIndex_t index_2C = 0x2C;
|
||||
constexpr PaletteIndex_t index_2E = 0x2E;
|
||||
constexpr PaletteIndex_t index_30 = 0x30;
|
||||
constexpr PaletteIndex_t index_31 = 0x31;
|
||||
constexpr PaletteIndex_t index_32 = 0x32;
|
||||
constexpr PaletteIndex_t index_35 = 0x35;
|
||||
constexpr PaletteIndex_t index_38 = 0x38;
|
||||
constexpr PaletteIndex_t index_3B = 0x3B;
|
||||
constexpr PaletteIndex_t index_3F = 0x3F;
|
||||
constexpr PaletteIndex_t index_41 = 0x41;
|
||||
constexpr PaletteIndex_t index_43 = 0x43;
|
||||
constexpr PaletteIndex_t index_4B = 0x4B;
|
||||
constexpr PaletteIndex_t index_50 = 0x50;
|
||||
constexpr PaletteIndex_t index_58 = 0x58;
|
||||
constexpr PaletteIndex_t index_64 = 0x64;
|
||||
constexpr PaletteIndex_t index_66 = 0x66;
|
||||
constexpr PaletteIndex_t index_67 = 0x67;
|
||||
constexpr PaletteIndex_t index_68 = 0x68;
|
||||
constexpr PaletteIndex_t index_71 = 0x71;
|
||||
constexpr PaletteIndex_t index_74 = 0x74;
|
||||
constexpr PaletteIndex_t index_7D = 0x7D;
|
||||
constexpr PaletteIndex_t index_85 = 0x85;
|
||||
constexpr PaletteIndex_t index_89 = 0x89;
|
||||
constexpr PaletteIndex_t index_9D = 0x9D;
|
||||
constexpr PaletteIndex_t index_A1 = 0xA1;
|
||||
constexpr PaletteIndex_t index_A2 = 0xA2;
|
||||
constexpr PaletteIndex_t index_A3 = 0xA3;
|
||||
constexpr PaletteIndex_t index_AC = 0xAC;
|
||||
constexpr PaletteIndex_t index_AD = 0xAD;
|
||||
constexpr PaletteIndex_t index_B8 = 0xB8;
|
||||
constexpr PaletteIndex_t index_BA = 0xBA;
|
||||
constexpr PaletteIndex_t index_BB = 0xBB;
|
||||
constexpr PaletteIndex_t index_BC = 0xBC;
|
||||
constexpr PaletteIndex_t index_C3 = 0xC3;
|
||||
constexpr PaletteIndex_t index_C6 = 0xC6;
|
||||
constexpr PaletteIndex_t index_D0 = 0xD0;
|
||||
constexpr PaletteIndex_t index_D3 = 0xD3;
|
||||
constexpr PaletteIndex_t index_DB = 0xDB;
|
||||
constexpr PaletteIndex_t index_DE = 0xDE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "Gfx.h"
|
||||
#include "../Config.h"
|
||||
#include "../Console.h"
|
||||
#include "../Drawing/SoftwareDrawingEngine.h"
|
||||
#include "../Environment.h"
|
||||
|
@ -15,6 +16,7 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
using namespace OpenLoco::Utility;
|
||||
|
@ -40,34 +42,141 @@ namespace OpenLoco::Gfx
|
|||
constexpr uint32_t g1_count_objects = 0x40000;
|
||||
constexpr uint32_t g1_count_temporary = 0x1000;
|
||||
|
||||
static loco_global<drawpixelinfo_t, 0x0050B884> _screen_dpi;
|
||||
static loco_global<drawpixelinfo_t, 0x005233B8> _windowDPI;
|
||||
static loco_global<Context, 0x0050B884> _screenContext;
|
||||
|
||||
static loco_global<g1_element[g1_expected_count::disc + g1_count_temporary + g1_count_objects], 0x9E2424> _g1Elements;
|
||||
static loco_global<G1Element[g1_expected_count::disc + g1_count_temporary + g1_count_objects], 0x9E2424> _g1Elements;
|
||||
|
||||
static std::unique_ptr<std::byte[]> _g1Buffer;
|
||||
static loco_global<uint16_t[147], 0x050B8C8> _paletteToG1Offset;
|
||||
|
||||
static loco_global<uint16_t, 0x112C824> _currentFontFlags;
|
||||
static loco_global<int16_t, 0x112C876> _currentFontSpriteBase;
|
||||
static loco_global<uint8_t[224 * 4], 0x112C884> _characterWidths;
|
||||
static loco_global<uint8_t[4], 0x1136594> _windowColours;
|
||||
|
||||
static palette_index_t _textColours[8] = { 0 };
|
||||
static PaletteIndex_t _textColours[8] = { 0 };
|
||||
|
||||
drawpixelinfo_t& screenDpi()
|
||||
Ui::Rect Context::getDrawableRect() const
|
||||
{
|
||||
return _screen_dpi;
|
||||
auto zoom = zoom_level;
|
||||
auto left = x >> zoom;
|
||||
auto top = y >> zoom;
|
||||
auto right = (width >> zoom) + left;
|
||||
auto bottom = (height >> zoom) + top;
|
||||
return Ui::Rect::fromLTRB(left, top, right, bottom);
|
||||
}
|
||||
|
||||
static std::vector<g1_element> convertElements(const std::vector<g1_element32_t>& elements32)
|
||||
Ui::Rect Context::getUiRect() const
|
||||
{
|
||||
auto elements = std::vector<g1_element>();
|
||||
return Ui::Rect::fromLTRB(x, y, x + width, y + height);
|
||||
}
|
||||
|
||||
Context& screenContext()
|
||||
{
|
||||
return _screenContext;
|
||||
}
|
||||
|
||||
// 0x004FFAE8
|
||||
uint32_t applyGhostToImage(uint32_t imageId)
|
||||
{
|
||||
if (Config::get().construction_marker)
|
||||
{
|
||||
return Gfx::recolourTranslucent(imageId, PaletteIndex::index_31);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Gfx::recolour(imageId, PaletteIndex::index_2C);
|
||||
}
|
||||
}
|
||||
|
||||
const PaletteMap& PaletteMap::getDefault()
|
||||
{
|
||||
static bool initialised = false;
|
||||
static uint8_t data[256];
|
||||
static PaletteMap defaultMap(data);
|
||||
if (!initialised)
|
||||
{
|
||||
std::iota(std::begin(data), std::end(data), 0);
|
||||
}
|
||||
return defaultMap;
|
||||
}
|
||||
|
||||
uint8_t& PaletteMap::operator[](size_t index)
|
||||
{
|
||||
assert(index < _dataLength);
|
||||
|
||||
// Provide safety in release builds
|
||||
if (index >= _dataLength)
|
||||
{
|
||||
static uint8_t dummy;
|
||||
return dummy;
|
||||
}
|
||||
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
uint8_t PaletteMap::operator[](size_t index) const
|
||||
{
|
||||
assert(index < _dataLength);
|
||||
|
||||
// Provide safety in release builds
|
||||
if (index >= _dataLength)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
uint8_t PaletteMap::blend(uint8_t src, uint8_t dst) const
|
||||
{
|
||||
// src = 0 would be transparent so there is no blend palette for that, hence (src - 1)
|
||||
assert(src != 0 && (src - 1) < _numMaps);
|
||||
assert(dst < _mapLength);
|
||||
auto idx = ((src - 1) * 256) + dst;
|
||||
return (*this)[idx];
|
||||
}
|
||||
|
||||
void PaletteMap::copy(size_t dstIndex, const PaletteMap& src, size_t srcIndex, size_t length)
|
||||
{
|
||||
auto maxLength = std::min(_mapLength - srcIndex, _mapLength - dstIndex);
|
||||
assert(length <= maxLength);
|
||||
auto copyLength = std::min(length, maxLength);
|
||||
std::memcpy(&_data[dstIndex], &src._data[srcIndex], copyLength);
|
||||
}
|
||||
|
||||
std::optional<uint32_t> getPaletteG1Index(Colour_t paletteId)
|
||||
{
|
||||
if (paletteId < std::size(_paletteToG1Offset))
|
||||
{
|
||||
return _paletteToG1Offset[paletteId];
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<PaletteMap> getPaletteMapForColour(Colour_t paletteId)
|
||||
{
|
||||
auto g1Index = getPaletteG1Index(paletteId);
|
||||
if (g1Index)
|
||||
{
|
||||
auto g1 = getG1Element(*g1Index);
|
||||
if (g1 != nullptr)
|
||||
{
|
||||
return PaletteMap(g1->offset, g1->height, g1->width);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static std::vector<G1Element> convertElements(const std::vector<G1Element32>& elements32)
|
||||
{
|
||||
auto elements = std::vector<G1Element>();
|
||||
elements.reserve(elements32.size());
|
||||
std::transform(
|
||||
elements32.begin(),
|
||||
elements32.end(),
|
||||
std::back_inserter(elements),
|
||||
[](g1_element32_t src) { return g1_element(src); });
|
||||
[](G1Element32 src) { return G1Element(src); });
|
||||
return elements;
|
||||
}
|
||||
|
||||
|
@ -81,7 +190,7 @@ namespace OpenLoco::Gfx
|
|||
throw std::runtime_error("Opening g1 file failed.");
|
||||
}
|
||||
|
||||
g1_header_t header;
|
||||
G1Header header;
|
||||
if (!readData(stream, header))
|
||||
{
|
||||
throw std::runtime_error("Reading g1 file header failed.");
|
||||
|
@ -98,7 +207,7 @@ namespace OpenLoco::Gfx
|
|||
}
|
||||
|
||||
// Read element headers
|
||||
auto elements32 = std::vector<g1_element32_t>(header.num_entries);
|
||||
auto elements32 = std::vector<G1Element32>(header.num_entries);
|
||||
if (!readData(stream, elements32.data(), header.num_entries))
|
||||
{
|
||||
throw std::runtime_error("Reading g1 element headers failed.");
|
||||
|
@ -139,36 +248,120 @@ namespace OpenLoco::Gfx
|
|||
}
|
||||
|
||||
// 0x00447485
|
||||
// edi: dpi
|
||||
// edi: context
|
||||
// ebp: fill
|
||||
void clear(drawpixelinfo_t& dpi, uint32_t fill)
|
||||
void clear(Context& context, uint32_t fill)
|
||||
{
|
||||
int32_t w = dpi.width / (1 << dpi.zoom_level);
|
||||
int32_t h = dpi.height / (1 << dpi.zoom_level);
|
||||
uint8_t* ptr = dpi.bits;
|
||||
int32_t w = context.width / (1 << context.zoom_level);
|
||||
int32_t h = context.height / (1 << context.zoom_level);
|
||||
uint8_t* ptr = context.bits;
|
||||
|
||||
for (int32_t y = 0; y < h; y++)
|
||||
{
|
||||
std::fill_n(ptr, w, fill);
|
||||
ptr += w + dpi.pitch;
|
||||
ptr += w + context.pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void clearSingle(drawpixelinfo_t& dpi, uint8_t paletteId)
|
||||
void clearSingle(Context& context, uint8_t paletteId)
|
||||
{
|
||||
auto fill = (paletteId << 24) | (paletteId << 16) | (paletteId << 8) | paletteId;
|
||||
clear(dpi, fill);
|
||||
clear(context, fill);
|
||||
}
|
||||
|
||||
// 0x004957C4
|
||||
int16_t clipString(int16_t width, char* string)
|
||||
{
|
||||
if (width < 6)
|
||||
{
|
||||
*string = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
registers regs;
|
||||
regs.di = width;
|
||||
regs.esi = (int32_t)string;
|
||||
call(0x004957C4, regs);
|
||||
return regs.cx;
|
||||
// If width of the full string is less than allowed width then we don't need to clip
|
||||
auto clippedWidth = getStringWidth(string);
|
||||
if (clippedWidth <= width)
|
||||
{
|
||||
return clippedWidth;
|
||||
}
|
||||
|
||||
// Append each character 1 by 1 with an ellipsis on the end until width is exceeded
|
||||
std::string bestString;
|
||||
std::string curString;
|
||||
|
||||
for (const auto* chr = string; *chr != '\0'; ++chr)
|
||||
{
|
||||
curString.push_back(*chr);
|
||||
switch (*chr)
|
||||
{
|
||||
case ControlCodes::move_x:
|
||||
curString.push_back(*++chr);
|
||||
break;
|
||||
|
||||
case ControlCodes::adjust_palette:
|
||||
case 3:
|
||||
case 4:
|
||||
curString.push_back(*++chr);
|
||||
break;
|
||||
|
||||
case ControlCodes::newline:
|
||||
case ControlCodes::newline_smaller:
|
||||
case ControlCodes::font_small:
|
||||
case ControlCodes::font_large:
|
||||
case ControlCodes::font_bold:
|
||||
case ControlCodes::font_regular:
|
||||
case ControlCodes::outline:
|
||||
case ControlCodes::outline_off:
|
||||
case ControlCodes::window_colour_1:
|
||||
case ControlCodes::window_colour_2:
|
||||
case ControlCodes::window_colour_3:
|
||||
case ControlCodes::window_colour_4:
|
||||
break;
|
||||
|
||||
case ControlCodes::inline_sprite_str:
|
||||
curString.push_back(*++chr);
|
||||
curString.push_back(*++chr);
|
||||
curString.push_back(*++chr);
|
||||
curString.push_back(*++chr);
|
||||
break;
|
||||
|
||||
case ControlCodes::newline_x_y:
|
||||
curString.push_back(*++chr);
|
||||
curString.push_back(*++chr);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (*chr <= 0x16)
|
||||
{
|
||||
curString.push_back(*++chr);
|
||||
curString.push_back(*++chr);
|
||||
}
|
||||
else if (*chr < 32)
|
||||
{
|
||||
curString.push_back(*++chr);
|
||||
curString.push_back(*++chr);
|
||||
curString.push_back(*++chr);
|
||||
curString.push_back(*++chr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
auto ellipseString = curString;
|
||||
ellipseString.append("...");
|
||||
|
||||
auto ellipsedWidth = getStringWidth(ellipseString.c_str());
|
||||
if (ellipsedWidth < width)
|
||||
{
|
||||
// Keep best string with ellipse
|
||||
bestString = ellipseString;
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(string, bestString.c_str());
|
||||
return getStringWidth(string);
|
||||
}
|
||||
}
|
||||
return getStringWidth(string);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -232,7 +425,7 @@ namespace OpenLoco::Gfx
|
|||
case ControlCodes::window_colour_1:
|
||||
case ControlCodes::window_colour_2:
|
||||
case ControlCodes::window_colour_3:
|
||||
case 0x10:
|
||||
case ControlCodes::window_colour_4:
|
||||
break;
|
||||
|
||||
case ControlCodes::inline_sprite_str:
|
||||
|
@ -260,7 +453,104 @@ namespace OpenLoco::Gfx
|
|||
return width;
|
||||
}
|
||||
|
||||
static void setTextColours(palette_index_t pal1, palette_index_t pal2, palette_index_t pal3)
|
||||
/**
|
||||
* 0x004955BC
|
||||
*
|
||||
* @param buffer @<esi>
|
||||
* @return width @<cx>
|
||||
*/
|
||||
uint16_t getMaxStringWidth(const char* buffer)
|
||||
{
|
||||
uint16_t width = 0;
|
||||
uint16_t maxWidth = 0;
|
||||
const uint8_t* str = reinterpret_cast<const uint8_t*>(buffer);
|
||||
int16_t fontSpriteBase = _currentFontSpriteBase;
|
||||
|
||||
while (*str != (uint8_t)0)
|
||||
{
|
||||
const uint8_t chr = *str;
|
||||
str++;
|
||||
|
||||
if (chr >= 32)
|
||||
{
|
||||
width += _characterWidths[chr - 32 + fontSpriteBase];
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (chr)
|
||||
{
|
||||
case ControlCodes::move_x:
|
||||
maxWidth = std::max(width, maxWidth);
|
||||
width = *str;
|
||||
str++;
|
||||
break;
|
||||
|
||||
case ControlCodes::adjust_palette:
|
||||
case 3:
|
||||
case 4:
|
||||
str++;
|
||||
break;
|
||||
|
||||
case ControlCodes::newline:
|
||||
case ControlCodes::newline_smaller:
|
||||
continue;
|
||||
|
||||
case ControlCodes::font_small:
|
||||
fontSpriteBase = Font::small;
|
||||
break;
|
||||
|
||||
case ControlCodes::font_large:
|
||||
fontSpriteBase = Font::large;
|
||||
break;
|
||||
|
||||
case ControlCodes::font_bold:
|
||||
fontSpriteBase = Font::medium_bold;
|
||||
break;
|
||||
|
||||
case ControlCodes::font_regular:
|
||||
fontSpriteBase = Font::medium_normal;
|
||||
break;
|
||||
|
||||
case ControlCodes::outline:
|
||||
case ControlCodes::outline_off:
|
||||
case ControlCodes::window_colour_1:
|
||||
case ControlCodes::window_colour_2:
|
||||
case ControlCodes::window_colour_3:
|
||||
case ControlCodes::window_colour_4:
|
||||
break;
|
||||
|
||||
case ControlCodes::newline_x_y:
|
||||
maxWidth = std::max(width, maxWidth);
|
||||
width = *str;
|
||||
str += 2;
|
||||
break;
|
||||
|
||||
case ControlCodes::inline_sprite_str:
|
||||
{
|
||||
const uint32_t image = reinterpret_cast<const uint32_t*>(str)[0];
|
||||
const uint32_t imageId = image & 0x7FFFF;
|
||||
str += 4;
|
||||
width += _g1Elements[imageId].width;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if (chr <= 0x16)
|
||||
{
|
||||
str += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
str += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
maxWidth = std::max(width, maxWidth);
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
static void setTextColours(PaletteIndex_t pal1, PaletteIndex_t pal2, PaletteIndex_t pal3)
|
||||
{
|
||||
if ((_currentFontFlags & text_draw_flags::inset) != 0)
|
||||
return;
|
||||
|
@ -282,9 +572,9 @@ namespace OpenLoco::Gfx
|
|||
}
|
||||
|
||||
// 0x00451189
|
||||
static Gfx::point_t loopNewline(drawpixelinfo_t* context, Gfx::point_t origin, uint8_t* str)
|
||||
static Ui::Point loopNewline(Context* context, Ui::Point origin, uint8_t* str)
|
||||
{
|
||||
Gfx::point_t pos = origin;
|
||||
Ui::Point pos = origin;
|
||||
while (true)
|
||||
{
|
||||
// When offscreen in y dimension don't draw text
|
||||
|
@ -401,6 +691,12 @@ namespace OpenLoco::Gfx
|
|||
setTextColours(Colour::getShade(hue, 9), PaletteIndex::index_0A, PaletteIndex::index_0A);
|
||||
break;
|
||||
}
|
||||
case ControlCodes::window_colour_4:
|
||||
{
|
||||
int hue = _windowColours[3];
|
||||
setTextColours(Colour::getShade(hue, 9), PaletteIndex::index_0A, PaletteIndex::index_0A);
|
||||
break;
|
||||
}
|
||||
|
||||
case ControlCodes::inline_sprite_str:
|
||||
{
|
||||
|
@ -509,38 +805,38 @@ namespace OpenLoco::Gfx
|
|||
* @param context @<edi>
|
||||
* @param text @<esi>
|
||||
*/
|
||||
Gfx::point_t drawString(drawpixelinfo_t* context, int16_t x, int16_t y, uint8_t colour, void* str)
|
||||
Ui::Point drawString(Context& context, int16_t x, int16_t y, uint8_t colour, void* str)
|
||||
{
|
||||
// 0x00E04348, 0x00E0434A
|
||||
Gfx::point_t origin = { x, y };
|
||||
Ui::Point origin = { x, y };
|
||||
|
||||
if (colour == FormatFlags::fe)
|
||||
{
|
||||
return loopNewline(context, origin, (uint8_t*)str);
|
||||
return loopNewline(&context, origin, (uint8_t*)str);
|
||||
}
|
||||
|
||||
if (colour == FormatFlags::fd)
|
||||
{
|
||||
_currentFontFlags = 0;
|
||||
setTextColour(0);
|
||||
return loopNewline(context, origin, (uint8_t*)str);
|
||||
return loopNewline(&context, origin, (uint8_t*)str);
|
||||
}
|
||||
|
||||
if (x >= context->x + context->width)
|
||||
if (x >= context.x + context.width)
|
||||
return origin;
|
||||
|
||||
if (x < context->x - 1280)
|
||||
if (x < context.x - 1280)
|
||||
return origin;
|
||||
|
||||
if (y >= context->y + context->height)
|
||||
if (y >= context.y + context.height)
|
||||
return origin;
|
||||
|
||||
if (y < context->y - 90)
|
||||
if (y < context.y - 90)
|
||||
return origin;
|
||||
|
||||
if (colour == FormatFlags::ff)
|
||||
{
|
||||
return loopNewline(context, origin, (uint8_t*)str);
|
||||
return loopNewline(&context, origin, (uint8_t*)str);
|
||||
}
|
||||
|
||||
_currentFontFlags = 0;
|
||||
|
@ -599,7 +895,7 @@ namespace OpenLoco::Gfx
|
|||
setTextColours(Colour::getShade(colour, 9), PaletteIndex::index_0A, PaletteIndex::index_0A);
|
||||
}
|
||||
|
||||
return loopNewline(context, origin, (uint8_t*)str);
|
||||
return loopNewline(&context, origin, (uint8_t*)str);
|
||||
}
|
||||
|
||||
// 0x00495224
|
||||
|
@ -609,9 +905,9 @@ namespace OpenLoco::Gfx
|
|||
// cx: x
|
||||
// dx: y
|
||||
// esi: args
|
||||
// edi: dpi
|
||||
// edi: context
|
||||
int16_t drawString_495224(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
int16_t width,
|
||||
|
@ -625,8 +921,8 @@ namespace OpenLoco::Gfx
|
|||
regs.bp = width;
|
||||
regs.cx = x;
|
||||
regs.dx = y;
|
||||
regs.esi = (int32_t)args;
|
||||
regs.edi = (int32_t)&dpi;
|
||||
regs.esi = X86Pointer(args);
|
||||
regs.edi = X86Pointer(&context);
|
||||
call(0x00495224, regs);
|
||||
|
||||
return regs.dx;
|
||||
|
@ -638,9 +934,9 @@ namespace OpenLoco::Gfx
|
|||
// cx: x
|
||||
// dx: y
|
||||
// esi: args
|
||||
// edi: dpi
|
||||
// edi: context
|
||||
void drawString_494B3F(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t colour,
|
||||
|
@ -652,22 +948,22 @@ namespace OpenLoco::Gfx
|
|||
regs.bx = stringId;
|
||||
regs.cx = x;
|
||||
regs.dx = y;
|
||||
regs.esi = (int32_t)args;
|
||||
regs.edi = (int32_t)&dpi;
|
||||
regs.esi = X86Pointer(args);
|
||||
regs.edi = X86Pointer(&context);
|
||||
call(0x00494B3F, regs);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dpi @<edi>
|
||||
* @param context @<edi>
|
||||
* @param origin {x @<cx>, y @<dx>}
|
||||
* @param colour @<al>
|
||||
* @param stringId @<bx>
|
||||
* @param args @<edi>
|
||||
*/
|
||||
void drawString_494B3F(
|
||||
drawpixelinfo_t& dpi,
|
||||
point_t* origin,
|
||||
Context& context,
|
||||
Point* origin,
|
||||
uint8_t colour,
|
||||
string_id stringId,
|
||||
const void* args)
|
||||
|
@ -677,8 +973,8 @@ namespace OpenLoco::Gfx
|
|||
regs.bx = stringId;
|
||||
regs.cx = origin->x;
|
||||
regs.dx = origin->y;
|
||||
regs.esi = (int32_t)args;
|
||||
regs.edi = (int32_t)&dpi;
|
||||
regs.esi = X86Pointer(args);
|
||||
regs.edi = X86Pointer(&context);
|
||||
call(0x00494B3F, regs);
|
||||
|
||||
origin->x = regs.cx;
|
||||
|
@ -691,10 +987,10 @@ namespace OpenLoco::Gfx
|
|||
// cx: x
|
||||
// dx: y
|
||||
// esi: args
|
||||
// edi: dpi
|
||||
// edi: context
|
||||
// bp: width
|
||||
void drawString_494BBF(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
int16_t width,
|
||||
|
@ -707,8 +1003,8 @@ namespace OpenLoco::Gfx
|
|||
regs.bx = stringId;
|
||||
regs.cx = x;
|
||||
regs.dx = y;
|
||||
regs.esi = (int32_t)args;
|
||||
regs.edi = (int32_t)&dpi;
|
||||
regs.esi = X86Pointer(args);
|
||||
regs.edi = X86Pointer(&context);
|
||||
regs.bp = width;
|
||||
call(0x00494BBF, regs);
|
||||
}
|
||||
|
@ -719,9 +1015,9 @@ namespace OpenLoco::Gfx
|
|||
// cx: x
|
||||
// dx: y
|
||||
// esi: args
|
||||
// edi: dpi
|
||||
// edi: context
|
||||
void drawString_494C78(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t colour,
|
||||
|
@ -733,8 +1029,8 @@ namespace OpenLoco::Gfx
|
|||
regs.bx = stringId;
|
||||
regs.cx = x;
|
||||
regs.dx = y;
|
||||
regs.esi = (int32_t)args;
|
||||
regs.edi = (int32_t)&dpi;
|
||||
regs.esi = X86Pointer(args);
|
||||
regs.edi = X86Pointer(&context);
|
||||
call(0x00494C78, regs);
|
||||
}
|
||||
|
||||
|
@ -744,9 +1040,9 @@ namespace OpenLoco::Gfx
|
|||
// cx: x
|
||||
// dx: y
|
||||
// esi: args
|
||||
// edi: dpi
|
||||
// edi: context
|
||||
void drawStringUnderline(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t colour,
|
||||
|
@ -758,8 +1054,8 @@ namespace OpenLoco::Gfx
|
|||
regs.bx = stringId;
|
||||
regs.cx = x;
|
||||
regs.dx = y;
|
||||
regs.esi = (int32_t)args;
|
||||
regs.edi = (int32_t)&dpi;
|
||||
regs.esi = X86Pointer(args);
|
||||
regs.edi = X86Pointer(&context);
|
||||
call(0x00494CB2, regs);
|
||||
}
|
||||
|
||||
|
@ -769,9 +1065,9 @@ namespace OpenLoco::Gfx
|
|||
// cx: x
|
||||
// dx: y
|
||||
// esi: args
|
||||
// edi: dpi
|
||||
// edi: context
|
||||
void drawStringLeftUnderline(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t colour,
|
||||
|
@ -783,8 +1079,8 @@ namespace OpenLoco::Gfx
|
|||
regs.bx = stringId;
|
||||
regs.cx = x;
|
||||
regs.dx = y;
|
||||
regs.esi = (int32_t)args;
|
||||
regs.edi = (int32_t)&dpi;
|
||||
regs.esi = X86Pointer(args);
|
||||
regs.edi = X86Pointer(&context);
|
||||
call(0x00494D78, regs);
|
||||
}
|
||||
|
||||
|
@ -794,9 +1090,9 @@ namespace OpenLoco::Gfx
|
|||
// cx: x
|
||||
// dx: y
|
||||
// esi: args
|
||||
// edi: dpi
|
||||
// edi: context
|
||||
void drawStringCentred(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t colour,
|
||||
|
@ -808,8 +1104,8 @@ namespace OpenLoco::Gfx
|
|||
regs.bx = stringId;
|
||||
regs.cx = x;
|
||||
regs.dx = y;
|
||||
regs.esi = (int32_t)args;
|
||||
regs.edi = (int32_t)&dpi;
|
||||
regs.esi = X86Pointer(args);
|
||||
regs.edi = X86Pointer(&context);
|
||||
call(0x00494DE8, regs);
|
||||
}
|
||||
|
||||
|
@ -820,9 +1116,9 @@ namespace OpenLoco::Gfx
|
|||
// cx: x
|
||||
// dx: y
|
||||
// esi: args
|
||||
// edi: dpi
|
||||
// edi: context
|
||||
void drawStringCentredClipped(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
int16_t width,
|
||||
|
@ -831,8 +1127,8 @@ namespace OpenLoco::Gfx
|
|||
const void* args)
|
||||
{
|
||||
registers regs;
|
||||
regs.edi = (int32_t)&dpi;
|
||||
regs.esi = (int32_t)args;
|
||||
regs.edi = X86Pointer(&context);
|
||||
regs.esi = X86Pointer(args);
|
||||
regs.ebx = stringId;
|
||||
regs.cx = x;
|
||||
regs.dx = y;
|
||||
|
@ -850,27 +1146,29 @@ namespace OpenLoco::Gfx
|
|||
* @param colour @<al>
|
||||
* @param stringId @<bx>
|
||||
* @param args @<esi>
|
||||
* returns width @<ax>
|
||||
*/
|
||||
void drawStringCentredWrapped(
|
||||
drawpixelinfo_t* context,
|
||||
point_t* origin,
|
||||
uint16_t drawStringCentredWrapped(
|
||||
Context& context,
|
||||
Point& origin,
|
||||
uint16_t width,
|
||||
uint8_t colour,
|
||||
string_id stringId,
|
||||
const void* args)
|
||||
{
|
||||
registers regs;
|
||||
regs.edi = (uintptr_t)context;
|
||||
regs.esi = (uintptr_t)args;
|
||||
regs.cx = origin->x;
|
||||
regs.dx = origin->y;
|
||||
regs.edi = X86Pointer(&context);
|
||||
regs.esi = X86Pointer(args);
|
||||
regs.cx = origin.x;
|
||||
regs.dx = origin.y;
|
||||
regs.bp = width;
|
||||
regs.al = colour;
|
||||
regs.bx = stringId;
|
||||
call(0x00494ECF, regs);
|
||||
|
||||
origin->x = regs.cx;
|
||||
origin->y = regs.dx;
|
||||
origin.x = regs.cx;
|
||||
origin.y = regs.dx;
|
||||
return regs.ax;
|
||||
}
|
||||
|
||||
// 0x00494E33
|
||||
|
@ -880,9 +1178,9 @@ namespace OpenLoco::Gfx
|
|||
// cx: x
|
||||
// dx: y
|
||||
// esi: args
|
||||
// edi: dpi
|
||||
// edi: context
|
||||
void drawStringCentredRaw(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
int16_t width,
|
||||
|
@ -890,8 +1188,8 @@ namespace OpenLoco::Gfx
|
|||
const void* args)
|
||||
{
|
||||
registers regs;
|
||||
regs.edi = (int32_t)&dpi;
|
||||
regs.esi = (int32_t)args;
|
||||
regs.edi = X86Pointer(&context);
|
||||
regs.esi = X86Pointer(args);
|
||||
regs.cx = x;
|
||||
regs.dx = y;
|
||||
regs.al = colour;
|
||||
|
@ -905,7 +1203,7 @@ namespace OpenLoco::Gfx
|
|||
uint16_t getStringWidthNewLined(const char* buffer)
|
||||
{
|
||||
registers regs;
|
||||
regs.esi = (uintptr_t)buffer;
|
||||
regs.esi = X86Pointer(buffer);
|
||||
call(0x00495715, regs);
|
||||
return regs.cx;
|
||||
}
|
||||
|
@ -914,7 +1212,7 @@ namespace OpenLoco::Gfx
|
|||
{
|
||||
// gfx_wrap_string
|
||||
registers regs;
|
||||
regs.esi = (uintptr_t)buffer;
|
||||
regs.esi = X86Pointer(buffer);
|
||||
regs.di = stringWidth;
|
||||
call(0x00495301, regs);
|
||||
|
||||
|
@ -922,7 +1220,7 @@ namespace OpenLoco::Gfx
|
|||
}
|
||||
|
||||
// 0x004474BA
|
||||
static void drawRectImpl(Gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour)
|
||||
static void drawRectImpl(Gfx::Context& context, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = left;
|
||||
|
@ -930,22 +1228,22 @@ namespace OpenLoco::Gfx
|
|||
regs.cx = top;
|
||||
regs.dx = bottom;
|
||||
regs.ebp = colour;
|
||||
regs.edi = (uint32_t)dpi;
|
||||
regs.edi = X86Pointer(&context);
|
||||
call(0x004474BA, regs);
|
||||
}
|
||||
|
||||
void fillRect(Gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour)
|
||||
void fillRect(Gfx::Context& context, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour)
|
||||
{
|
||||
drawRectImpl(dpi, left, top, right, bottom, colour);
|
||||
drawRectImpl(context, left, top, right, bottom, colour);
|
||||
}
|
||||
|
||||
void drawRect(Gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t colour)
|
||||
void drawRect(Gfx::Context& context, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t colour)
|
||||
{
|
||||
// This makes the function signature more like a drawing application
|
||||
drawRectImpl(dpi, x, y, x + dx - 1, y + dy - 1, colour);
|
||||
drawRectImpl(context, x, y, x + dx - 1, y + dy - 1, colour);
|
||||
}
|
||||
|
||||
void fillRectInset(Gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour, uint8_t flags)
|
||||
void fillRectInset(Gfx::Context& context, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour, uint8_t flags)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = left;
|
||||
|
@ -953,19 +1251,19 @@ namespace OpenLoco::Gfx
|
|||
regs.cx = top;
|
||||
regs.dx = bottom;
|
||||
regs.ebp = colour;
|
||||
regs.edi = (uint32_t)dpi;
|
||||
regs.edi = X86Pointer(&context);
|
||||
regs.si = flags;
|
||||
call(0x004C58C7, regs);
|
||||
}
|
||||
|
||||
void drawRectInset(Gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t colour, uint8_t flags)
|
||||
void drawRectInset(Gfx::Context& context, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t colour, uint8_t flags)
|
||||
{
|
||||
// This makes the function signature more like a drawing application
|
||||
fillRectInset(dpi, x, y, x + dx - 1, y + dy - 1, colour, flags);
|
||||
fillRectInset(context, x, y, x + dx - 1, y + dy - 1, colour, flags);
|
||||
}
|
||||
|
||||
// 0x00452DA4
|
||||
void drawLine(Gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour)
|
||||
void drawLine(Gfx::Context& context, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = left;
|
||||
|
@ -973,7 +1271,7 @@ namespace OpenLoco::Gfx
|
|||
regs.cx = right;
|
||||
regs.dx = bottom;
|
||||
regs.ebp = colour;
|
||||
regs.edi = (uint32_t)dpi;
|
||||
regs.edi = X86Pointer(&context);
|
||||
call(0x00452DA4, regs);
|
||||
}
|
||||
|
||||
|
@ -1030,9 +1328,9 @@ namespace OpenLoco::Gfx
|
|||
engine->drawDirtyBlocks();
|
||||
}
|
||||
|
||||
if (Input::hasFlag(Input::input_flags::flag5))
|
||||
if (Input::hasFlag(Input::Flags::flag5))
|
||||
{
|
||||
call(0x004072EC); // NOP on _NO_LOCO_WIN32_
|
||||
Ui::processMessagesMini();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1067,44 +1365,54 @@ namespace OpenLoco::Gfx
|
|||
redrawScreenRect(Rect::fromLTRB(left, top, right, bottom));
|
||||
}
|
||||
|
||||
void drawImage(Gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint32_t image)
|
||||
void drawImage(Gfx::Context* context, int16_t x, int16_t y, uint32_t image)
|
||||
{
|
||||
registers regs;
|
||||
regs.cx = x;
|
||||
regs.dx = y;
|
||||
regs.ebx = image;
|
||||
regs.edi = (uint32_t)dpi;
|
||||
regs.edi = X86Pointer(context);
|
||||
call(0x00448C79, regs);
|
||||
}
|
||||
|
||||
uint32_t recolour(uint32_t image)
|
||||
{
|
||||
return (1 << 29) | image;
|
||||
return ImageIdFlags::remap | image;
|
||||
}
|
||||
|
||||
uint32_t recolour(uint32_t image, uint8_t colour)
|
||||
{
|
||||
return (1 << 29) | (colour << 19) | image;
|
||||
return ImageIdFlags::remap | (colour << 19) | image;
|
||||
}
|
||||
|
||||
uint32_t recolour2(uint32_t image, uint8_t colour1, uint8_t colour2)
|
||||
{
|
||||
return ImageIdFlags::remap | ImageIdFlags::remap2 | (colour1 << 19) | (colour2 << 24) | image;
|
||||
}
|
||||
|
||||
uint32_t recolour2(uint32_t image, ColourScheme colourScheme)
|
||||
{
|
||||
return recolour2(image, colourScheme.primary, colourScheme.secondary);
|
||||
}
|
||||
|
||||
uint32_t recolourTranslucent(uint32_t image, uint8_t colour)
|
||||
{
|
||||
return (1 << 30) | (colour << 19) | image;
|
||||
return ImageIdFlags::translucent | (colour << 19) | image;
|
||||
}
|
||||
|
||||
loco_global<uint8_t*, 0x0050B860> _50B860;
|
||||
loco_global<uint32_t, 0x00E04324> _E04324;
|
||||
|
||||
void drawImageSolid(Gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint32_t image, uint8_t palette_index)
|
||||
void drawImageSolid(Gfx::Context* context, int16_t x, int16_t y, uint32_t image, uint8_t palette_index)
|
||||
{
|
||||
uint8_t palette[256];
|
||||
memset(palette, palette_index, 256);
|
||||
palette[0] = 0;
|
||||
|
||||
drawImagePaletteSet(dpi, x, y, image, palette);
|
||||
drawImagePaletteSet(context, x, y, image, palette);
|
||||
}
|
||||
|
||||
void drawImagePaletteSet(Gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint32_t image, uint8_t* palette)
|
||||
void drawImagePaletteSet(Gfx::Context* context, int16_t x, int16_t y, uint32_t image, uint8_t* palette)
|
||||
{
|
||||
_50B860 = palette;
|
||||
_E04324 = 0x20000000;
|
||||
|
@ -1112,30 +1420,30 @@ namespace OpenLoco::Gfx
|
|||
regs.cx = x;
|
||||
regs.dx = y;
|
||||
regs.ebx = image;
|
||||
regs.edi = (uint32_t)dpi;
|
||||
regs.edi = X86Pointer(context);
|
||||
call(0x00448D90, regs);
|
||||
}
|
||||
|
||||
bool clipDrawpixelinfo(Gfx::drawpixelinfo_t** dst, Gfx::drawpixelinfo_t* src, int16_t x, int16_t y, int16_t width, int16_t height)
|
||||
// 0x004CEC50
|
||||
std::optional<Gfx::Context> clipContext(const Gfx::Context& src, const Ui::Rect& newRect)
|
||||
{
|
||||
registers regs;
|
||||
regs.ax = x;
|
||||
regs.bx = width;
|
||||
regs.edi = (int32_t)src;
|
||||
regs.dx = height;
|
||||
regs.cx = y;
|
||||
call(0x4cec50, regs);
|
||||
*dst = (Gfx::drawpixelinfo_t*)regs.edi;
|
||||
const Ui::Rect oldRect = src.getUiRect();
|
||||
Ui::Rect intersect = oldRect.intersection(newRect);
|
||||
const auto stride = oldRect.size.width + src.pitch;
|
||||
const int16_t newPitch = stride - intersect.size.width;
|
||||
auto* newBits = src.bits + (stride * (intersect.origin.y - oldRect.origin.y) + (intersect.origin.x - oldRect.origin.x));
|
||||
intersect.origin.x = std::max(0, oldRect.origin.x - newRect.origin.x);
|
||||
intersect.origin.y = std::max(0, oldRect.origin.y - newRect.origin.y);
|
||||
Gfx::Context newContext{ newBits, static_cast<int16_t>(intersect.origin.x), static_cast<int16_t>(intersect.origin.y), static_cast<int16_t>(intersect.size.width), static_cast<int16_t>(intersect.size.height), newPitch, 0 };
|
||||
|
||||
return *dst != nullptr;
|
||||
if (newContext.width <= 0 || newContext.height <= 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return { newContext };
|
||||
}
|
||||
|
||||
bool clipDrawpixelinfo(Gfx::drawpixelinfo_t** dst, Gfx::drawpixelinfo_t* src, point_t pos, Gfx::ui_size_t size)
|
||||
{
|
||||
return clipDrawpixelinfo(dst, src, pos.x, pos.y, size.width, size.height);
|
||||
}
|
||||
|
||||
g1_element* getG1Element(uint32_t id)
|
||||
G1Element* getG1Element(uint32_t id)
|
||||
{
|
||||
if (id < _g1Elements.size())
|
||||
{
|
||||
|
@ -1143,4 +1451,9 @@ namespace OpenLoco::Gfx
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setCurrentFontSpriteBase(int16_t value)
|
||||
{
|
||||
_currentFontSpriteBase = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Core/Optional.hpp"
|
||||
#include "../OpenLoco.h"
|
||||
#include "../Types.hpp"
|
||||
#include "../Ui/Rect.h"
|
||||
#include "Types.h"
|
||||
#include "../Ui/Types.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace OpenLoco
|
||||
{
|
||||
using Colour_t = uint8_t;
|
||||
}
|
||||
|
||||
namespace OpenLoco::Gfx
|
||||
{
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct drawpixelinfo_t
|
||||
struct Context
|
||||
{
|
||||
uint8_t* bits; // 0x00
|
||||
int16_t x; // 0x04
|
||||
|
@ -19,17 +25,20 @@ namespace OpenLoco::Gfx
|
|||
int16_t height; // 0x0A
|
||||
int16_t pitch; // 0x0C note: this is actually (pitch - width)
|
||||
uint16_t zoom_level; // 0x0E
|
||||
|
||||
Ui::Rect getUiRect() const;
|
||||
Ui::Rect getDrawableRect() const;
|
||||
};
|
||||
|
||||
drawpixelinfo_t& screenDpi();
|
||||
Context& screenContext();
|
||||
|
||||
struct g1_header_t
|
||||
struct G1Header
|
||||
{
|
||||
uint32_t num_entries;
|
||||
uint32_t total_size;
|
||||
};
|
||||
|
||||
struct g1_element32_t
|
||||
struct G1Element32
|
||||
{
|
||||
uint32_t offset; // 0x00
|
||||
int16_t width; // 0x04
|
||||
|
@ -41,7 +50,7 @@ namespace OpenLoco::Gfx
|
|||
};
|
||||
|
||||
// A version that can be 64-bit when ready...
|
||||
struct g1_element
|
||||
struct G1Element
|
||||
{
|
||||
uint8_t* offset = nullptr;
|
||||
int16_t width = 0;
|
||||
|
@ -51,8 +60,8 @@ namespace OpenLoco::Gfx
|
|||
uint16_t flags = 0;
|
||||
int16_t unused = 0;
|
||||
|
||||
g1_element() = default;
|
||||
g1_element(const g1_element32_t& src)
|
||||
G1Element() = default;
|
||||
G1Element(const G1Element32& src)
|
||||
: offset((uint8_t*)src.offset)
|
||||
, width(src.width)
|
||||
, height(src.height)
|
||||
|
@ -65,20 +74,73 @@ namespace OpenLoco::Gfx
|
|||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
namespace ImageIdFlags
|
||||
{
|
||||
constexpr uint32_t remap = 1 << 29;
|
||||
constexpr uint32_t translucent = 1 << 30;
|
||||
constexpr uint32_t remap2 = 1 << 31;
|
||||
}
|
||||
|
||||
drawpixelinfo_t& screenDpi();
|
||||
/**
|
||||
* Represents an 8-bit indexed map that maps from one palette index to another.
|
||||
*/
|
||||
struct PaletteMap
|
||||
{
|
||||
private:
|
||||
uint8_t* _data{};
|
||||
uint32_t _dataLength{};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-private-field"
|
||||
uint16_t _numMaps;
|
||||
#pragma clang diagnostic pop
|
||||
uint16_t _mapLength;
|
||||
|
||||
public:
|
||||
static const PaletteMap& getDefault();
|
||||
|
||||
PaletteMap() = default;
|
||||
|
||||
PaletteMap(uint8_t* data, uint16_t numMaps, uint16_t mapLength)
|
||||
: _data(data)
|
||||
, _dataLength(numMaps * mapLength)
|
||||
, _numMaps(numMaps)
|
||||
, _mapLength(mapLength)
|
||||
{
|
||||
}
|
||||
|
||||
template<std::size_t TSize>
|
||||
PaletteMap(uint8_t (&map)[TSize])
|
||||
: _data(map)
|
||||
, _dataLength(static_cast<uint32_t>(std::size(map)))
|
||||
, _numMaps(1)
|
||||
, _mapLength(static_cast<uint16_t>(std::size(map)))
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t& operator[](size_t index);
|
||||
uint8_t operator[](size_t index) const;
|
||||
uint8_t* data() const { return _data; }
|
||||
uint8_t blend(uint8_t src, uint8_t dst) const;
|
||||
void copy(size_t dstIndex, const PaletteMap& src, size_t srcIndex, size_t length);
|
||||
};
|
||||
|
||||
std::optional<uint32_t> getPaletteG1Index(Colour_t paletteId);
|
||||
std::optional<PaletteMap> getPaletteMapForColour(Colour_t paletteId);
|
||||
|
||||
Context& screenContext();
|
||||
|
||||
void loadG1();
|
||||
void clear(drawpixelinfo_t& dpi, uint32_t fill);
|
||||
void clearSingle(drawpixelinfo_t& dpi, uint8_t paletteId);
|
||||
void clear(Context& context, uint32_t fill);
|
||||
void clearSingle(Context& context, uint8_t paletteId);
|
||||
|
||||
int16_t clipString(int16_t width, char* string);
|
||||
uint16_t getStringWidth(const char* buffer);
|
||||
uint16_t getMaxStringWidth(const char* buffer);
|
||||
|
||||
Gfx::point_t drawString(drawpixelinfo_t* context, int16_t x, int16_t y, uint8_t colour, void* str);
|
||||
Ui::Point drawString(Context& context, int16_t x, int16_t y, uint8_t colour, void* str);
|
||||
|
||||
int16_t drawString_495224(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
int16_t width,
|
||||
|
@ -86,20 +148,20 @@ namespace OpenLoco::Gfx
|
|||
string_id stringId,
|
||||
const void* args = nullptr);
|
||||
void drawString_494B3F(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t colour,
|
||||
string_id stringId,
|
||||
const void* args = nullptr);
|
||||
void drawString_494B3F(
|
||||
drawpixelinfo_t& dpi,
|
||||
point_t* origin,
|
||||
Context& context,
|
||||
Ui::Point* origin,
|
||||
uint8_t colour,
|
||||
string_id stringId,
|
||||
const void* args = nullptr);
|
||||
void drawString_494BBF(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
int16_t width,
|
||||
|
@ -107,50 +169,50 @@ namespace OpenLoco::Gfx
|
|||
string_id stringId,
|
||||
const void* args = nullptr);
|
||||
void drawString_494C78(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t colour,
|
||||
string_id stringId,
|
||||
const void* args = nullptr);
|
||||
void drawStringUnderline(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t colour,
|
||||
string_id stringId,
|
||||
const void* args);
|
||||
void drawStringLeftUnderline(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t colour,
|
||||
string_id stringId,
|
||||
const void* args = nullptr);
|
||||
void drawStringCentred(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
uint8_t colour,
|
||||
string_id stringId,
|
||||
const void* args = nullptr);
|
||||
void drawStringCentredClipped(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
int16_t width,
|
||||
uint8_t colour,
|
||||
string_id stringId,
|
||||
const void* args = nullptr);
|
||||
void drawStringCentredWrapped(
|
||||
drawpixelinfo_t* context,
|
||||
point_t* origin,
|
||||
uint16_t drawStringCentredWrapped(
|
||||
Context& context,
|
||||
Ui::Point& origin,
|
||||
uint16_t width,
|
||||
uint8_t colour,
|
||||
string_id stringId,
|
||||
const void* args = nullptr);
|
||||
void drawStringCentredRaw(
|
||||
drawpixelinfo_t& dpi,
|
||||
Context& context,
|
||||
int16_t x,
|
||||
int16_t y,
|
||||
int16_t width,
|
||||
|
@ -159,17 +221,20 @@ namespace OpenLoco::Gfx
|
|||
uint16_t getStringWidthNewLined(const char* buffer);
|
||||
std::pair<uint16_t, uint16_t> wrapString(const char* buffer, uint16_t stringWidth);
|
||||
|
||||
void fillRect(Gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour);
|
||||
void drawRect(Gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t colour);
|
||||
void fillRectInset(Gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour, uint8_t flags);
|
||||
void drawRectInset(Gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t colour, uint8_t flags);
|
||||
void drawLine(Gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour);
|
||||
void drawImage(Gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint32_t image);
|
||||
void drawImageSolid(Gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint32_t image, uint8_t palette_index);
|
||||
void drawImagePaletteSet(Gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint32_t image, uint8_t* palette);
|
||||
void fillRect(Gfx::Context& context, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour);
|
||||
void drawRect(Gfx::Context& context, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t colour);
|
||||
void fillRectInset(Gfx::Context& context, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour, uint8_t flags);
|
||||
void drawRectInset(Gfx::Context& context, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t colour, uint8_t flags);
|
||||
void drawLine(Gfx::Context& context, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour);
|
||||
void drawImage(Gfx::Context* context, int16_t x, int16_t y, uint32_t image);
|
||||
void drawImageSolid(Gfx::Context* context, int16_t x, int16_t y, uint32_t image, uint8_t palette_index);
|
||||
void drawImagePaletteSet(Gfx::Context* context, int16_t x, int16_t y, uint32_t image, uint8_t* palette);
|
||||
uint32_t recolour(uint32_t image);
|
||||
uint32_t recolour(uint32_t image, uint8_t colour);
|
||||
uint32_t recolour2(uint32_t image, uint8_t colour1, uint8_t colour2);
|
||||
uint32_t recolour2(uint32_t image, ColourScheme colourScheme);
|
||||
uint32_t recolourTranslucent(uint32_t image, uint8_t colour);
|
||||
uint32_t applyGhostToImage(uint32_t imageId);
|
||||
|
||||
void invalidateScreen();
|
||||
void setDirtyBlocks(int32_t left, int32_t top, int32_t right, int32_t bottom);
|
||||
|
@ -179,7 +244,9 @@ namespace OpenLoco::Gfx
|
|||
void redrawScreenRect(Ui::Rect rect);
|
||||
void redrawScreenRect(int16_t left, int16_t top, int16_t right, int16_t bottom);
|
||||
|
||||
bool clipDrawpixelinfo(Gfx::drawpixelinfo_t** dst, Gfx::drawpixelinfo_t* src, int16_t x, int16_t y, int16_t width, int16_t height);
|
||||
bool clipDrawpixelinfo(Gfx::drawpixelinfo_t** dst, Gfx::drawpixelinfo_t* src, Gfx::point_t pos, Gfx::ui_size_t size);
|
||||
g1_element* getG1Element(uint32_t id);
|
||||
std::optional<Gfx::Context> clipContext(const Gfx::Context& src, const Ui::Rect& newRect);
|
||||
|
||||
G1Element* getG1Element(uint32_t id);
|
||||
|
||||
void setCurrentFontSpriteBase(int16_t value);
|
||||
}
|
||||
|
|
|
@ -8,23 +8,59 @@ namespace OpenLoco::ImageIds
|
|||
{
|
||||
constexpr uint32_t null = 0xFFFFFFFF;
|
||||
|
||||
constexpr uint32_t construction_arrow_north = 428;
|
||||
constexpr uint32_t construction_arrow_east = 429;
|
||||
constexpr uint32_t construction_arrow_south = 430;
|
||||
constexpr uint32_t construction_arrow_west = 431;
|
||||
constexpr uint32_t construction_arrow_north2 = 432;
|
||||
constexpr uint32_t construction_arrow_east2 = 433;
|
||||
constexpr uint32_t construction_arrow_south2 = 434;
|
||||
constexpr uint32_t construction_arrow_west2 = 435;
|
||||
constexpr uint32_t construction_arrow_north3 = 436;
|
||||
constexpr uint32_t construction_arrow_east3 = 437;
|
||||
constexpr uint32_t construction_arrow_south3 = 438;
|
||||
constexpr uint32_t construction_arrow_west3 = 439;
|
||||
constexpr uint32_t construction_arrow_north_east = 440;
|
||||
constexpr uint32_t construction_arrow_south_east = 441;
|
||||
constexpr uint32_t construction_arrow_south_west = 442;
|
||||
constexpr uint32_t construction_arrow_north_west = 443;
|
||||
|
||||
constexpr uint32_t blank_tile = 448;
|
||||
constexpr uint32_t currency_symbol = 1919;
|
||||
|
||||
constexpr uint32_t text_palette = 2169;
|
||||
|
||||
constexpr uint32_t window_resize_handle = 2305;
|
||||
constexpr uint32_t colour_swatch_recolourable = 2306;
|
||||
constexpr uint32_t colour_swatch_recolourable_raised = 2307;
|
||||
constexpr uint32_t colour_swatch_recolourable_pressed = 2308;
|
||||
|
||||
constexpr uint32_t company_list_dropdown_icon = 2309;
|
||||
constexpr uint32_t icon_parent_folder = 2310;
|
||||
constexpr uint32_t icon_folder = 2311;
|
||||
|
||||
constexpr uint32_t close_button = 2321;
|
||||
constexpr uint32_t curved_border_left = 2315;
|
||||
constexpr uint32_t curved_border_right = 2316;
|
||||
|
||||
constexpr uint32_t close_button = 2321;
|
||||
constexpr uint32_t frame_background_image = 2322;
|
||||
constexpr uint32_t frame_background_image_alt = 2323;
|
||||
constexpr uint32_t inline_green_up_arrow = 2324;
|
||||
constexpr uint32_t inline_red_down_arrow = 2325;
|
||||
|
||||
constexpr uint32_t progressbar_style0_frame0 = 2326;
|
||||
constexpr uint32_t progressbar_style0_frame1 = 2327;
|
||||
constexpr uint32_t progressbar_style0_frame2 = 2328;
|
||||
constexpr uint32_t progressbar_style0_frame3 = 2329;
|
||||
constexpr uint32_t progressbar_track = 2330;
|
||||
constexpr uint32_t progressbar_style1_frame0 = 2331;
|
||||
constexpr uint32_t progressbar_style1_frame1 = 2332;
|
||||
constexpr uint32_t progressbar_style1_frame2 = 2333;
|
||||
constexpr uint32_t progressbar_style1_frame3 = 2334;
|
||||
constexpr uint32_t construction_straight = 2335;
|
||||
|
||||
constexpr uint32_t step_back = 2336;
|
||||
constexpr uint32_t step_forward = 2337;
|
||||
constexpr uint32_t red_arrow_up = 2338;
|
||||
constexpr uint32_t red_arrow_down = 2339;
|
||||
constexpr uint32_t construction_left_hand_curve_very_small = 2340;
|
||||
constexpr uint32_t construction_right_hand_curve_very_small = 2341;
|
||||
constexpr uint32_t construction_left_hand_curve_small = 2342;
|
||||
|
@ -52,6 +88,16 @@ namespace OpenLoco::ImageIds
|
|||
constexpr uint32_t centre_viewport = 2364;
|
||||
constexpr uint32_t rotate_object = 2365;
|
||||
|
||||
constexpr uint32_t red_flag = 2369;
|
||||
constexpr uint32_t green_flag = 2370;
|
||||
constexpr uint32_t yellow_flag = 2371;
|
||||
constexpr uint32_t airport_pickup = 2372;
|
||||
constexpr uint32_t airport_place = 2373;
|
||||
constexpr uint32_t pass_signal = 2374;
|
||||
constexpr uint32_t route_delete = 2375;
|
||||
constexpr uint32_t route_skip = 2376;
|
||||
constexpr uint32_t route_wait = 2377;
|
||||
constexpr uint32_t route_force_unload = 2378;
|
||||
constexpr uint32_t show_station_catchment = 2379;
|
||||
constexpr uint32_t plant_cluster_selected_tree = 2380;
|
||||
constexpr uint32_t plant_cluster_random_tree = 2381;
|
||||
|
@ -59,6 +105,8 @@ namespace OpenLoco::ImageIds
|
|||
constexpr uint32_t music_controls_stop = 2383;
|
||||
constexpr uint32_t music_controls_play = 2384;
|
||||
constexpr uint32_t music_controls_next = 2385;
|
||||
constexpr uint32_t refit_cargo_button = 2386;
|
||||
constexpr uint32_t tab_vehicle_background = 2387;
|
||||
|
||||
constexpr uint32_t tab_display = 2391;
|
||||
constexpr uint32_t tab_control = 2392;
|
||||
|
@ -132,6 +180,231 @@ namespace OpenLoco::ImageIds
|
|||
constexpr uint32_t random_map_watermark = 2468;
|
||||
constexpr uint32_t height_map_compass = 2469;
|
||||
|
||||
constexpr uint32_t number_circle_00 = 3238;
|
||||
constexpr uint32_t number_circle_01 = 3239;
|
||||
constexpr uint32_t number_circle_02 = 3240;
|
||||
constexpr uint32_t number_circle_03 = 3241;
|
||||
constexpr uint32_t number_circle_04 = 3242;
|
||||
constexpr uint32_t number_circle_05 = 3243;
|
||||
constexpr uint32_t number_circle_06 = 3244;
|
||||
constexpr uint32_t number_circle_07 = 3245;
|
||||
constexpr uint32_t number_circle_08 = 3246;
|
||||
constexpr uint32_t number_circle_09 = 3247;
|
||||
constexpr uint32_t number_circle_10 = 3248;
|
||||
constexpr uint32_t number_circle_11 = 3249;
|
||||
constexpr uint32_t number_circle_12 = 3250;
|
||||
constexpr uint32_t number_circle_13 = 3251;
|
||||
constexpr uint32_t number_circle_14 = 3252;
|
||||
constexpr uint32_t number_circle_15 = 3253;
|
||||
constexpr uint32_t number_circle_16 = 3254;
|
||||
constexpr uint32_t number_circle_17 = 3255;
|
||||
constexpr uint32_t number_circle_18 = 3256;
|
||||
constexpr uint32_t number_circle_19 = 3257;
|
||||
constexpr uint32_t number_circle_20 = 3258;
|
||||
constexpr uint32_t number_circle_21 = 3259;
|
||||
constexpr uint32_t number_circle_22 = 3260;
|
||||
constexpr uint32_t number_circle_23 = 3261;
|
||||
constexpr uint32_t number_circle_24 = 3262;
|
||||
constexpr uint32_t number_circle_25 = 3263;
|
||||
constexpr uint32_t number_circle_26 = 3264;
|
||||
constexpr uint32_t number_circle_27 = 3265;
|
||||
constexpr uint32_t number_circle_28 = 3266;
|
||||
constexpr uint32_t number_circle_29 = 3267;
|
||||
constexpr uint32_t number_circle_30 = 3268;
|
||||
constexpr uint32_t number_circle_31 = 3269;
|
||||
constexpr uint32_t number_circle_32 = 3270;
|
||||
constexpr uint32_t number_circle_33 = 3271;
|
||||
constexpr uint32_t number_circle_34 = 3272;
|
||||
constexpr uint32_t number_circle_35 = 3273;
|
||||
constexpr uint32_t number_circle_36 = 3274;
|
||||
constexpr uint32_t number_circle_37 = 3275;
|
||||
constexpr uint32_t number_circle_38 = 3276;
|
||||
constexpr uint32_t number_circle_39 = 3277;
|
||||
constexpr uint32_t number_circle_40 = 3278;
|
||||
constexpr uint32_t number_circle_41 = 3279;
|
||||
constexpr uint32_t number_circle_42 = 3280;
|
||||
constexpr uint32_t number_circle_43 = 3281;
|
||||
constexpr uint32_t number_circle_44 = 3282;
|
||||
constexpr uint32_t number_circle_45 = 3283;
|
||||
constexpr uint32_t number_circle_46 = 3284;
|
||||
constexpr uint32_t number_circle_47 = 3285;
|
||||
constexpr uint32_t number_circle_48 = 3286;
|
||||
constexpr uint32_t number_circle_49 = 3287;
|
||||
constexpr uint32_t number_circle_50 = 3288;
|
||||
constexpr uint32_t number_circle_51 = 3289;
|
||||
constexpr uint32_t number_circle_52 = 3290;
|
||||
constexpr uint32_t number_circle_53 = 3291;
|
||||
constexpr uint32_t number_circle_54 = 3292;
|
||||
constexpr uint32_t number_circle_55 = 3293;
|
||||
constexpr uint32_t number_circle_56 = 3294;
|
||||
constexpr uint32_t number_circle_57 = 3295;
|
||||
constexpr uint32_t number_circle_58 = 3296;
|
||||
constexpr uint32_t number_circle_59 = 3297;
|
||||
constexpr uint32_t number_circle_60 = 3298;
|
||||
constexpr uint32_t number_circle_61 = 3299;
|
||||
constexpr uint32_t number_circle_62 = 3300;
|
||||
constexpr uint32_t number_circle_63 = 3301;
|
||||
constexpr uint32_t vehicle_crash_0_00 = 3302;
|
||||
constexpr uint32_t vehicle_crash_0_01 = 3303;
|
||||
constexpr uint32_t vehicle_crash_0_02 = 3304;
|
||||
constexpr uint32_t vehicle_crash_0_03 = 3305;
|
||||
constexpr uint32_t vehicle_crash_0_04 = 3306;
|
||||
constexpr uint32_t vehicle_crash_0_05 = 3307;
|
||||
constexpr uint32_t vehicle_crash_0_06 = 3308;
|
||||
constexpr uint32_t vehicle_crash_0_07 = 3309;
|
||||
constexpr uint32_t vehicle_crash_0_08 = 3310;
|
||||
constexpr uint32_t vehicle_crash_0_09 = 3311;
|
||||
constexpr uint32_t vehicle_crash_0_10 = 3312;
|
||||
constexpr uint32_t vehicle_crash_0_11 = 3313;
|
||||
constexpr uint32_t vehicle_crash_1_00 = 3314;
|
||||
constexpr uint32_t vehicle_crash_1_01 = 3315;
|
||||
constexpr uint32_t vehicle_crash_1_02 = 3316;
|
||||
constexpr uint32_t vehicle_crash_1_03 = 3317;
|
||||
constexpr uint32_t vehicle_crash_1_04 = 3318;
|
||||
constexpr uint32_t vehicle_crash_1_05 = 3319;
|
||||
constexpr uint32_t vehicle_crash_1_06 = 3320;
|
||||
constexpr uint32_t vehicle_crash_1_07 = 3321;
|
||||
constexpr uint32_t vehicle_crash_1_08 = 3322;
|
||||
constexpr uint32_t vehicle_crash_1_09 = 3323;
|
||||
constexpr uint32_t vehicle_crash_1_10 = 3324;
|
||||
constexpr uint32_t vehicle_crash_1_11 = 3325;
|
||||
constexpr uint32_t vehicle_crash_2_00 = 3326;
|
||||
constexpr uint32_t vehicle_crash_2_01 = 3327;
|
||||
constexpr uint32_t vehicle_crash_2_02 = 3328;
|
||||
constexpr uint32_t vehicle_crash_2_03 = 3329;
|
||||
constexpr uint32_t vehicle_crash_2_04 = 3330;
|
||||
constexpr uint32_t vehicle_crash_2_05 = 3331;
|
||||
constexpr uint32_t vehicle_crash_2_06 = 3332;
|
||||
constexpr uint32_t vehicle_crash_2_07 = 3333;
|
||||
constexpr uint32_t vehicle_crash_2_08 = 3334;
|
||||
constexpr uint32_t vehicle_crash_2_09 = 3335;
|
||||
constexpr uint32_t vehicle_crash_2_10 = 3336;
|
||||
constexpr uint32_t vehicle_crash_2_11 = 3337;
|
||||
constexpr uint32_t vehicle_crash_3_00 = 3338;
|
||||
constexpr uint32_t vehicle_crash_3_01 = 3339;
|
||||
constexpr uint32_t vehicle_crash_3_02 = 3340;
|
||||
constexpr uint32_t vehicle_crash_3_03 = 3341;
|
||||
constexpr uint32_t vehicle_crash_3_04 = 3342;
|
||||
constexpr uint32_t vehicle_crash_3_05 = 3343;
|
||||
constexpr uint32_t vehicle_crash_3_06 = 3344;
|
||||
constexpr uint32_t vehicle_crash_3_07 = 3345;
|
||||
constexpr uint32_t vehicle_crash_3_08 = 3346;
|
||||
constexpr uint32_t vehicle_crash_3_09 = 3347;
|
||||
constexpr uint32_t vehicle_crash_3_10 = 3348;
|
||||
constexpr uint32_t vehicle_crash_3_11 = 3349;
|
||||
constexpr uint32_t vehicle_crash_4_00 = 3350;
|
||||
constexpr uint32_t vehicle_crash_4_01 = 3351;
|
||||
constexpr uint32_t vehicle_crash_4_02 = 3352;
|
||||
constexpr uint32_t vehicle_crash_4_03 = 3353;
|
||||
constexpr uint32_t vehicle_crash_4_04 = 3354;
|
||||
constexpr uint32_t vehicle_crash_4_05 = 3355;
|
||||
constexpr uint32_t vehicle_crash_4_06 = 3356;
|
||||
constexpr uint32_t vehicle_crash_4_07 = 3357;
|
||||
constexpr uint32_t vehicle_crash_4_08 = 3358;
|
||||
constexpr uint32_t vehicle_crash_4_09 = 3359;
|
||||
constexpr uint32_t vehicle_crash_4_10 = 3360;
|
||||
constexpr uint32_t vehicle_crash_4_11 = 3361;
|
||||
constexpr uint32_t explosion_smoke_00 = 3362;
|
||||
constexpr uint32_t explosion_smoke_01 = 3363;
|
||||
constexpr uint32_t explosion_smoke_02 = 3364;
|
||||
constexpr uint32_t explosion_smoke_03 = 3365;
|
||||
constexpr uint32_t explosion_smoke_04 = 3366;
|
||||
constexpr uint32_t explosion_smoke_05 = 3367;
|
||||
constexpr uint32_t explosion_smoke_06 = 3368;
|
||||
constexpr uint32_t explosion_smoke_07 = 3369;
|
||||
constexpr uint32_t explosion_smoke_08 = 3370;
|
||||
constexpr uint32_t explosion_smoke_09 = 3371;
|
||||
constexpr uint32_t explosion_cloud_00 = 3372;
|
||||
constexpr uint32_t explosion_cloud_01 = 3373;
|
||||
constexpr uint32_t explosion_cloud_02 = 3374;
|
||||
constexpr uint32_t explosion_cloud_03 = 3375;
|
||||
constexpr uint32_t explosion_cloud_04 = 3376;
|
||||
constexpr uint32_t explosion_cloud_05 = 3377;
|
||||
constexpr uint32_t explosion_cloud_06 = 3378;
|
||||
constexpr uint32_t explosion_cloud_07 = 3379;
|
||||
constexpr uint32_t explosion_cloud_08 = 3380;
|
||||
constexpr uint32_t explosion_cloud_09 = 3381;
|
||||
constexpr uint32_t explosion_cloud_10 = 3382;
|
||||
constexpr uint32_t explosion_cloud_11 = 3383;
|
||||
constexpr uint32_t explosion_cloud_12 = 3384;
|
||||
constexpr uint32_t explosion_cloud_13 = 3385;
|
||||
constexpr uint32_t explosion_cloud_14 = 3386;
|
||||
constexpr uint32_t explosion_cloud_15 = 3387;
|
||||
constexpr uint32_t explosion_cloud_16 = 3388;
|
||||
constexpr uint32_t explosion_cloud_17 = 3389;
|
||||
constexpr uint32_t fireball_00 = 3390;
|
||||
constexpr uint32_t fireball_01 = 3391;
|
||||
constexpr uint32_t fireball_02 = 3392;
|
||||
constexpr uint32_t fireball_03 = 3393;
|
||||
constexpr uint32_t fireball_04 = 3394;
|
||||
constexpr uint32_t fireball_05 = 3395;
|
||||
constexpr uint32_t fireball_06 = 3396;
|
||||
constexpr uint32_t fireball_07 = 3397;
|
||||
constexpr uint32_t fireball_08 = 3398;
|
||||
constexpr uint32_t fireball_09 = 3399;
|
||||
constexpr uint32_t fireball_10 = 3400;
|
||||
constexpr uint32_t fireball_11 = 3401;
|
||||
constexpr uint32_t fireball_12 = 3402;
|
||||
constexpr uint32_t fireball_13 = 3403;
|
||||
constexpr uint32_t fireball_14 = 3404;
|
||||
constexpr uint32_t fireball_15 = 3405;
|
||||
constexpr uint32_t fireball_16 = 3406;
|
||||
constexpr uint32_t fireball_17 = 3407;
|
||||
constexpr uint32_t fireball_18 = 3408;
|
||||
constexpr uint32_t fireball_19 = 3409;
|
||||
constexpr uint32_t fireball_20 = 3410;
|
||||
constexpr uint32_t fireball_21 = 3411;
|
||||
constexpr uint32_t fireball_22 = 3412;
|
||||
constexpr uint32_t fireball_23 = 3413;
|
||||
constexpr uint32_t fireball_24 = 3414;
|
||||
constexpr uint32_t fireball_25 = 3415;
|
||||
constexpr uint32_t fireball_26 = 3416;
|
||||
constexpr uint32_t fireball_27 = 3417;
|
||||
constexpr uint32_t fireball_28 = 3418;
|
||||
constexpr uint32_t fireball_29 = 3419;
|
||||
constexpr uint32_t fireball_30 = 3420;
|
||||
constexpr uint32_t splash_00 = 3421;
|
||||
constexpr uint32_t splash_01 = 3422;
|
||||
constexpr uint32_t splash_02 = 3423;
|
||||
constexpr uint32_t splash_03 = 3424;
|
||||
constexpr uint32_t splash_04 = 3425;
|
||||
constexpr uint32_t splash_05 = 3426;
|
||||
constexpr uint32_t splash_06 = 3427;
|
||||
constexpr uint32_t splash_07 = 3428;
|
||||
constexpr uint32_t splash_08 = 3429;
|
||||
constexpr uint32_t splash_09 = 3430;
|
||||
constexpr uint32_t splash_10 = 3431;
|
||||
constexpr uint32_t splash_11 = 3432;
|
||||
constexpr uint32_t splash_12 = 3433;
|
||||
constexpr uint32_t splash_13 = 3434;
|
||||
constexpr uint32_t splash_14 = 3435;
|
||||
constexpr uint32_t splash_15 = 3436;
|
||||
constexpr uint32_t splash_16 = 3437;
|
||||
constexpr uint32_t splash_17 = 3438;
|
||||
constexpr uint32_t splash_18 = 3439;
|
||||
constexpr uint32_t splash_19 = 3440;
|
||||
constexpr uint32_t splash_20 = 3441;
|
||||
constexpr uint32_t splash_21 = 3442;
|
||||
constexpr uint32_t splash_22 = 3443;
|
||||
constexpr uint32_t splash_23 = 3444;
|
||||
constexpr uint32_t splash_24 = 3445;
|
||||
constexpr uint32_t splash_25 = 3446;
|
||||
constexpr uint32_t splash_26 = 3447;
|
||||
constexpr uint32_t splash_27 = 3448;
|
||||
|
||||
constexpr uint32_t smoke_00 = 3465;
|
||||
constexpr uint32_t smoke_01 = 3466;
|
||||
constexpr uint32_t smoke_02 = 3467;
|
||||
constexpr uint32_t smoke_03 = 3468;
|
||||
constexpr uint32_t smoke_04 = 3469;
|
||||
constexpr uint32_t smoke_05 = 3470;
|
||||
constexpr uint32_t smoke_06 = 3471;
|
||||
constexpr uint32_t smoke_07 = 3472;
|
||||
constexpr uint32_t smoke_08 = 3473;
|
||||
constexpr uint32_t smoke_09 = 3474;
|
||||
constexpr uint32_t smoke_10 = 3475;
|
||||
constexpr uint32_t smoke_11 = 3476;
|
||||
|
||||
constexpr uint32_t tab_object_settings = 3505;
|
||||
constexpr uint32_t tab_object_audio = 3506;
|
||||
constexpr uint32_t tab_object_currency = 3507;
|
||||
|
@ -172,13 +445,13 @@ namespace OpenLoco::ImageIds
|
|||
constexpr uint32_t news_background_new_right = 3542;
|
||||
constexpr uint32_t volume_slider_track = 3543;
|
||||
constexpr uint32_t volume_slider_thumb = 3544;
|
||||
|
||||
constexpr uint32_t speed_control_track = 3545;
|
||||
constexpr uint32_t speed_control_thumb = 3546;
|
||||
constexpr uint32_t title_menu_sparkle = 3547;
|
||||
constexpr uint32_t title_menu_save = 3548;
|
||||
constexpr uint32_t title_menu_lesson_l = 3549;
|
||||
constexpr uint32_t title_menu_lesson_a = 3550;
|
||||
constexpr uint32_t title_menu_lesson_p = 3551;
|
||||
|
||||
constexpr uint32_t title_menu_globe_spin_0 = 3552;
|
||||
constexpr uint32_t title_menu_globe_spin_1 = 3553;
|
||||
constexpr uint32_t title_menu_globe_spin_2 = 3554;
|
||||
|
@ -211,7 +484,6 @@ namespace OpenLoco::ImageIds
|
|||
constexpr uint32_t title_menu_globe_spin_29 = 3581;
|
||||
constexpr uint32_t title_menu_globe_spin_30 = 3582;
|
||||
constexpr uint32_t title_menu_globe_spin_31 = 3583;
|
||||
|
||||
constexpr uint32_t title_menu_globe_construct_0 = 3584;
|
||||
constexpr uint32_t title_menu_globe_construct_1 = 3585;
|
||||
constexpr uint32_t title_menu_globe_construct_2 = 3586;
|
||||
|
@ -244,7 +516,6 @@ namespace OpenLoco::ImageIds
|
|||
constexpr uint32_t title_menu_globe_construct_29 = 3613;
|
||||
constexpr uint32_t title_menu_globe_construct_30 = 3614;
|
||||
constexpr uint32_t title_menu_globe_construct_31 = 3615;
|
||||
|
||||
constexpr uint32_t chris_sawyer_logo_small = 3616;
|
||||
constexpr uint32_t chris_sawyer_logo_intro_left = 3617;
|
||||
constexpr uint32_t chris_sawyer_logo_intro_right = 3618;
|
||||
|
@ -252,10 +523,10 @@ namespace OpenLoco::ImageIds
|
|||
constexpr uint32_t atari_logo_intro_left = 3620;
|
||||
constexpr uint32_t atari_logo_intro_right = 3620;
|
||||
constexpr uint32_t atari_logo_small = UNUSED_IMG(3623);
|
||||
|
||||
constexpr uint32_t locomotion_logo = 3624;
|
||||
constexpr uint32_t wide_tab = 3625;
|
||||
|
||||
constexpr uint32_t scenario_completed_tick = 3629;
|
||||
constexpr uint32_t owner_jailed = 3630;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace OpenLoco::Gfx
|
||||
{
|
||||
struct point_t
|
||||
{
|
||||
int16_t x = 0;
|
||||
int16_t y = 0;
|
||||
|
||||
constexpr point_t(){};
|
||||
|
||||
constexpr point_t(int16_t x, int16_t y)
|
||||
: x(x)
|
||||
, y(y)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const point_t& rhs)
|
||||
{
|
||||
return x == rhs.x && y == rhs.y;
|
||||
}
|
||||
|
||||
bool operator==(const int16_t rhs)
|
||||
{
|
||||
return x == rhs && y == rhs;
|
||||
}
|
||||
|
||||
point_t& operator+=(const point_t& rhs)
|
||||
{
|
||||
x += rhs.x;
|
||||
y += rhs.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
point_t& operator-=(const point_t& rhs)
|
||||
{
|
||||
x -= rhs.x;
|
||||
y -= rhs.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend point_t operator+(point_t lhs, const point_t& rhs)
|
||||
{
|
||||
lhs += rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend point_t operator-(point_t lhs, const point_t& rhs)
|
||||
{
|
||||
lhs -= rhs;
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
struct ui_size_t
|
||||
{
|
||||
uint16_t width = 0;
|
||||
uint16_t height = 0;
|
||||
|
||||
constexpr ui_size_t(uint16_t width, uint16_t height)
|
||||
: width(width)
|
||||
, height(height)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
|
@ -2,13 +2,12 @@
|
|||
#include "Graphics/Colour.h"
|
||||
#include "Interop/Interop.hpp"
|
||||
#include "Map/Tile.h"
|
||||
#include "Objects/InterfaceSkinObject.h"
|
||||
#include "Objects/ObjectManager.h"
|
||||
#include "OpenLoco.h"
|
||||
#include "Tutorial.h"
|
||||
#include "Ui.h"
|
||||
#include "Ui/WindowManager.h"
|
||||
#include "ViewportManager.h"
|
||||
#include "Widget.h"
|
||||
#include "Window.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
@ -29,37 +28,22 @@ namespace OpenLoco::Gui
|
|||
|
||||
if (OpenLoco::isTitleMode())
|
||||
{
|
||||
Ui::Windows::openTitleMenu();
|
||||
Ui::Windows::openTitleExit();
|
||||
Ui::Windows::openTitleLogo();
|
||||
Ui::Windows::openTitleVersion();
|
||||
Ui::TitleOptions::open();
|
||||
Ui::Windows::TitleMenu::open();
|
||||
Ui::Windows::TitleExit::open();
|
||||
Ui::Windows::TitleLogo::open();
|
||||
Ui::Windows::TitleVersion::open();
|
||||
Ui::Windows::TitleOptions::open();
|
||||
}
|
||||
else
|
||||
{
|
||||
Windows::ToolbarTop::Game::open();
|
||||
|
||||
Windows::PlayerInfoPanel::open();
|
||||
TimePanel::open();
|
||||
Windows::TimePanel::open();
|
||||
|
||||
if (OpenLoco::Tutorial::state() != Tutorial::tutorial_state::none)
|
||||
if (OpenLoco::Tutorial::state() != Tutorial::State::none)
|
||||
{
|
||||
|
||||
auto window = WindowManager::createWindow(
|
||||
WindowType::tutorial,
|
||||
Gfx::point_t(140, Ui::height() - 27),
|
||||
Gfx::ui_size_t(Ui::width() - 280, 27),
|
||||
Ui::WindowFlags::stick_to_front | Ui::WindowFlags::transparent | Ui::WindowFlags::no_background,
|
||||
(Ui::window_event_list*)0x4fa10c);
|
||||
window->widgets = (Ui::widget_t*)0x509de0;
|
||||
window->initScrollWidgets();
|
||||
|
||||
auto skin = OpenLoco::ObjectManager::get<interface_skin_object>();
|
||||
if (skin != nullptr)
|
||||
{
|
||||
window->colours[0] = Colour::translucent(skin->colour_06);
|
||||
window->colours[1] = Colour::translucent(skin->colour_07);
|
||||
}
|
||||
Windows::Tutorial::open();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,12 +56,6 @@ namespace OpenLoco::Gui
|
|||
const int32_t uiWidth = Ui::width();
|
||||
const int32_t uiHeight = Ui::height();
|
||||
|
||||
if (OpenLoco::isEditorMode())
|
||||
{
|
||||
call(0x43CD35);
|
||||
return;
|
||||
}
|
||||
|
||||
auto window = WindowManager::getMainWindow();
|
||||
if (window)
|
||||
{
|
||||
|
@ -116,6 +94,13 @@ namespace OpenLoco::Gui
|
|||
window->x = std::max(uiWidth, 640) - window->width;
|
||||
}
|
||||
|
||||
window = WindowManager::find(WindowType::editorToolbar);
|
||||
if (window)
|
||||
{
|
||||
window->y = uiHeight - window->height;
|
||||
window->width = std::max(uiWidth, 640);
|
||||
}
|
||||
|
||||
window = WindowManager::find(WindowType::titleMenu);
|
||||
if (window)
|
||||
{
|
||||
|
@ -145,7 +130,7 @@ namespace OpenLoco::Gui
|
|||
window = WindowManager::find(WindowType::tutorial);
|
||||
if (window)
|
||||
{
|
||||
if (Tutorial::state() == Tutorial::tutorial_state::none)
|
||||
if (Tutorial::state() == Tutorial::State::none)
|
||||
{
|
||||
WindowManager::close(window);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "Industry.h"
|
||||
#include "Interop/Interop.hpp"
|
||||
#include "Localisation/StringIds.h"
|
||||
#include "Map/AnimationManager.h"
|
||||
#include "Map/TileManager.h"
|
||||
#include "Objects/CargoObject.h"
|
||||
#include "Objects/IndustryObject.h"
|
||||
|
@ -13,26 +14,43 @@ using namespace OpenLoco::Map;
|
|||
|
||||
namespace OpenLoco
|
||||
{
|
||||
industry_id_t industry::id() const
|
||||
struct Unk4F9274
|
||||
{
|
||||
auto first = (industry*)0x005C455C;
|
||||
return (industry_id_t)(this - first);
|
||||
Pos2 pos;
|
||||
uint8_t unk;
|
||||
};
|
||||
static const Unk4F9274 word_4F9274[] = {
|
||||
{ { 0, 0 }, 0 },
|
||||
{ { Location::null, 0 }, 0 }
|
||||
};
|
||||
static const Unk4F9274 word_4F927C[] = {
|
||||
{ { 0, 0 }, 0 },
|
||||
{ { 0, 32 }, 1 },
|
||||
{ { 32, 32 }, 2 },
|
||||
{ { 32, 0 }, 3 },
|
||||
{ { Location::null, 0 }, 0 }
|
||||
};
|
||||
|
||||
IndustryId_t Industry::id() const
|
||||
{
|
||||
auto first = (Industry*)0x005C455C;
|
||||
return (IndustryId_t)(this - first);
|
||||
}
|
||||
|
||||
industry_object* industry::object() const
|
||||
IndustryObject* Industry::object() const
|
||||
{
|
||||
return ObjectManager::get<industry_object>(object_id);
|
||||
return ObjectManager::get<IndustryObject>(object_id);
|
||||
}
|
||||
|
||||
bool industry::empty() const
|
||||
bool Industry::empty() const
|
||||
{
|
||||
return name == StringIds::null;
|
||||
}
|
||||
|
||||
bool industry::canReceiveCargo() const
|
||||
bool Industry::canReceiveCargo() const
|
||||
{
|
||||
auto receiveCargoState = false;
|
||||
for (const auto& receivedCargo : ObjectManager::get<industry_object>(object_id)->required_cargo_type)
|
||||
for (const auto& receivedCargo : ObjectManager::get<IndustryObject>(object_id)->required_cargo_type)
|
||||
{
|
||||
if (receivedCargo != 0xff)
|
||||
receiveCargoState = true;
|
||||
|
@ -40,10 +58,10 @@ namespace OpenLoco
|
|||
return receiveCargoState;
|
||||
}
|
||||
|
||||
bool industry::canProduceCargo() const
|
||||
bool Industry::canProduceCargo() const
|
||||
{
|
||||
auto produceCargoState = false;
|
||||
for (const auto& producedCargo : ObjectManager::get<industry_object>(object_id)->produced_cargo_type)
|
||||
for (const auto& producedCargo : ObjectManager::get<IndustryObject>(object_id)->produced_cargo_type)
|
||||
{
|
||||
if (producedCargo != 0xff)
|
||||
produceCargoState = true;
|
||||
|
@ -51,13 +69,13 @@ namespace OpenLoco
|
|||
return produceCargoState;
|
||||
}
|
||||
|
||||
static bool findTree(surface_element* surface)
|
||||
static bool findTree(SurfaceElement* surface)
|
||||
{
|
||||
auto element = surface;
|
||||
while (!element->isLast())
|
||||
{
|
||||
element++;
|
||||
if (element->type() == element_type::tree)
|
||||
if (element->type() == ElementType::tree)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -66,14 +84,14 @@ namespace OpenLoco
|
|||
}
|
||||
|
||||
// 0x0045935F
|
||||
void industry::getStatusString(const char* buffer)
|
||||
void Industry::getStatusString(const char* buffer)
|
||||
{
|
||||
char* ptr = (char*)buffer;
|
||||
*ptr = '\0';
|
||||
auto industryObj = object();
|
||||
|
||||
// Closing Down
|
||||
if (flags & IndustryFlags::closing_down)
|
||||
if (flags & IndustryFlags::closingDown)
|
||||
{
|
||||
ptr = StringManager::formatString(ptr, StringIds::industry_closing_down);
|
||||
return;
|
||||
|
@ -114,7 +132,7 @@ namespace OpenLoco
|
|||
}
|
||||
|
||||
// 0x00453275
|
||||
void industry::update()
|
||||
void Industry::update()
|
||||
{
|
||||
if (!(flags & IndustryFlags::flag_01) && under_construction == 0xFF)
|
||||
{
|
||||
|
@ -124,7 +142,7 @@ namespace OpenLoco
|
|||
sub_45329B(tile_loop.current());
|
||||
|
||||
// loc_453318
|
||||
if (tile_loop.next() == map_pos())
|
||||
if (tile_loop.next() == Pos2())
|
||||
{
|
||||
sub_453354();
|
||||
break;
|
||||
|
@ -134,7 +152,7 @@ namespace OpenLoco
|
|||
}
|
||||
|
||||
// 0x0045329B
|
||||
void industry::sub_45329B(const map_pos& pos)
|
||||
void Industry::sub_45329B(const Pos2& pos)
|
||||
{
|
||||
const auto& surface = TileManager::get(pos).surface();
|
||||
if (surface != nullptr)
|
||||
|
@ -159,7 +177,7 @@ namespace OpenLoco
|
|||
}
|
||||
}
|
||||
|
||||
void industry::sub_453354()
|
||||
void Industry::sub_453354()
|
||||
{
|
||||
// 0x00453366
|
||||
int16_t tmp_a = var_DB / 16;
|
||||
|
@ -187,7 +205,7 @@ namespace OpenLoco
|
|||
{
|
||||
if (prng.randBool())
|
||||
{
|
||||
Map::map_pos randTile{ static_cast<coord_t>(x + (prng.randNext(-15, 16) * 32)), static_cast<coord_t>(y + (prng.randNext(-15, 16) * 32)) };
|
||||
Map::Pos2 randTile{ static_cast<coord_t>(x + (prng.randNext(-15, 16) * 32)), static_cast<coord_t>(y + (prng.randNext(-15, 16) * 32)) };
|
||||
uint8_t bl = obj->var_ED;
|
||||
uint8_t bh = obj->var_EE;
|
||||
if (obj->var_EF != 0xFF && prng.randBool())
|
||||
|
@ -202,7 +220,7 @@ namespace OpenLoco
|
|||
}
|
||||
}
|
||||
|
||||
void industry::sub_454A43(map_pos pos, uint8_t bl, uint8_t bh, uint8_t dl)
|
||||
void Industry::sub_454A43(const Pos2& pos, uint8_t bl, uint8_t bh, uint8_t dl)
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = bl;
|
||||
|
@ -213,4 +231,44 @@ namespace OpenLoco
|
|||
regs.dh = id();
|
||||
call(0x00454A43, regs);
|
||||
}
|
||||
|
||||
// 0x00459D43
|
||||
void Industry::createMapAnimations()
|
||||
{
|
||||
for (size_t i = 0; i < numTiles; i++)
|
||||
{
|
||||
auto& tilePos = tiles[i];
|
||||
auto baseZ = (tilePos.z & ~Location::null) / 4;
|
||||
auto tile = TileManager::get(tilePos);
|
||||
|
||||
for (auto& el : tile)
|
||||
{
|
||||
auto industryEl = el.asIndustry();
|
||||
if (industryEl == nullptr)
|
||||
continue;
|
||||
|
||||
if (industryEl->baseZ() != baseZ)
|
||||
continue;
|
||||
|
||||
auto tileIndustry = industryEl->industry();
|
||||
if (tileIndustry != nullptr)
|
||||
{
|
||||
auto industryObject = tileIndustry->object();
|
||||
if (industryObject != nullptr)
|
||||
{
|
||||
auto animOffsets = word_4F9274;
|
||||
if (industryObject->var_C6 & (1 << industryEl->var_6_1F()))
|
||||
{
|
||||
animOffsets = word_4F927C;
|
||||
}
|
||||
while (animOffsets[0].pos.x != Location::null)
|
||||
{
|
||||
AnimationManager::createAnimation(3, animOffsets->pos + tilePos, baseZ);
|
||||
animOffsets++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "Localisation/StringManager.h"
|
||||
#include "Map/Tile.h"
|
||||
#include "Map/TileLoop.hpp"
|
||||
#include "Objects/IndustryObject.h"
|
||||
#include "Town.h"
|
||||
#include "Types.hpp"
|
||||
#include "Utility/Prng.hpp"
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
namespace OpenLoco
|
||||
{
|
||||
using namespace Map;
|
||||
using industry_id_t = uint8_t;
|
||||
struct IndustryObject;
|
||||
|
||||
namespace IndustryId
|
||||
{
|
||||
constexpr industry_id_t null = std::numeric_limits<industry_id_t>::max();
|
||||
constexpr IndustryId_t null = std::numeric_limits<IndustryId_t>::max();
|
||||
}
|
||||
|
||||
namespace IndustryFlags
|
||||
{
|
||||
constexpr uint16_t flag_01 = 1 << 0;
|
||||
constexpr uint16_t sorted = 1 << 1;
|
||||
constexpr uint16_t closing_down = 1 << 2;
|
||||
constexpr uint16_t closingDown = 1 << 2;
|
||||
constexpr uint16_t flag_04 = 1 << 3;
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct industry
|
||||
struct Industry
|
||||
{
|
||||
string_id name;
|
||||
coord_t x; // 0x02
|
||||
|
@ -37,18 +33,21 @@ namespace OpenLoco
|
|||
Utility::prng prng; // 0x08
|
||||
uint8_t object_id; // 0x10
|
||||
uint8_t under_construction; // 0x11 (0xFF = Finished)
|
||||
uint8_t pad_12[0xD5 - 0x12];
|
||||
town_id_t town; // 0xD5
|
||||
Map::tile_loop tile_loop; // 0xD7
|
||||
uint16_t pad_12;
|
||||
uint8_t numTiles; // 0x14
|
||||
Map::Pos3 tiles[32]; // 0x15
|
||||
TownId_t town; // 0xD5
|
||||
Map::TileLoop tile_loop; // 0xD7
|
||||
int16_t var_DB;
|
||||
int16_t var_DD;
|
||||
uint8_t var_DF;
|
||||
uint8_t owner; // 0xE0
|
||||
uint8_t pad_E1[0x189 - 0xE1];
|
||||
uint16_t produced_cargo_quantity[2]; // 0x189
|
||||
uint8_t pad_18D[0x193 - 0x18D];
|
||||
uint16_t var_18D[3];
|
||||
uint16_t required_cargo_quantity[3]; // 0x193
|
||||
uint8_t pad_199[0x1A3 - 0x199];
|
||||
uint16_t var_199[3];
|
||||
uint8_t pad_19F[0x1A3 - 0x19F];
|
||||
uint16_t produced_cargo_max[2]; // 0x1A3 (produced_cargo_quantity / 8)
|
||||
uint8_t produced_cargo_transported[2]; // 0x1A7 (%)
|
||||
uint8_t history_size[2]; // 0x1A9 (<= 20 * 12)
|
||||
|
@ -57,17 +56,20 @@ namespace OpenLoco
|
|||
int32_t history_min_production[2]; // 0x38B
|
||||
uint8_t pad_393[0x453 - 0x393];
|
||||
|
||||
industry_id_t id() const;
|
||||
industry_object* object() const;
|
||||
IndustryId_t id() const;
|
||||
IndustryObject* object() const;
|
||||
bool empty() const;
|
||||
bool canReceiveCargo() const;
|
||||
bool canProduceCargo() const;
|
||||
void getStatusString(const char* buffer);
|
||||
|
||||
void update();
|
||||
void sub_45329B(const map_pos& pos);
|
||||
void sub_45329B(const Map::Pos2& pos);
|
||||
void sub_453354();
|
||||
void sub_454A43(map_pos pos, uint8_t bl, uint8_t bh, uint8_t dl);
|
||||
void sub_454A43(const Map::Pos2& pos, uint8_t bl, uint8_t bh, uint8_t dl);
|
||||
void createMapAnimations();
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(Industry) == 0x453);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,33 @@
|
|||
#include "IndustryManager.h"
|
||||
#include "CompanyManager.h"
|
||||
#include "Interop/Interop.hpp"
|
||||
#include "Math/Vector.hpp"
|
||||
#include "Objects/IndustryObject.h"
|
||||
#include "OpenLoco.h"
|
||||
#include "Ui/WindowManager.h"
|
||||
|
||||
using namespace OpenLoco::Interop;
|
||||
|
||||
namespace OpenLoco::IndustryManager
|
||||
{
|
||||
static loco_global<industry[max_industries], 0x005C455C> _industries;
|
||||
static loco_global<Industry[max_industries], 0x005C455C> _industries;
|
||||
|
||||
std::array<industry, max_industries>& industries()
|
||||
// 0x00453214
|
||||
void reset()
|
||||
{
|
||||
auto arr = (std::array<industry, max_industries>*)_industries.get();
|
||||
return *arr;
|
||||
for (auto& industry : _industries)
|
||||
{
|
||||
industry.name = StringIds::null;
|
||||
}
|
||||
Ui::Windows::IndustryList::reset();
|
||||
}
|
||||
|
||||
industry* get(industry_id_t id)
|
||||
LocoFixedVector<Industry> industries()
|
||||
{
|
||||
return LocoFixedVector<Industry>(_industries);
|
||||
}
|
||||
|
||||
Industry* get(IndustryId_t id)
|
||||
{
|
||||
if (id >= _industries.size())
|
||||
{
|
||||
|
@ -32,10 +44,7 @@ namespace OpenLoco::IndustryManager
|
|||
CompanyManager::updatingCompanyId(CompanyId::neutral);
|
||||
for (auto& industry : industries())
|
||||
{
|
||||
if (!industry.empty())
|
||||
{
|
||||
industry.update();
|
||||
}
|
||||
industry.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,4 +54,33 @@ namespace OpenLoco::IndustryManager
|
|||
{
|
||||
call(0x0045383B);
|
||||
}
|
||||
|
||||
// 0x00459D2D
|
||||
void createAllMapAnimations()
|
||||
{
|
||||
if (!(addr<0x00525E28, uint32_t>() & (1 << 0)))
|
||||
return;
|
||||
|
||||
for (auto& industry : industries())
|
||||
{
|
||||
industry.createMapAnimations();
|
||||
}
|
||||
}
|
||||
|
||||
// 0x048FE92
|
||||
bool industryNearPosition(const Map::Pos2& position, uint32_t flags)
|
||||
{
|
||||
for (auto& industry : industries())
|
||||
{
|
||||
auto industryObj = industry.object();
|
||||
if ((industryObj->flags & flags) == 0)
|
||||
continue;
|
||||
|
||||
auto manhattanDistance = Math::Vector::manhattanDistance(Map::Pos2{ industry.x, industry.y }, position);
|
||||
if (manhattanDistance / Map::tile_size < 11)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "Core/LocoFixedVector.hpp"
|
||||
#include "Industry.h"
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
@ -8,8 +9,11 @@ namespace OpenLoco::IndustryManager
|
|||
{
|
||||
constexpr size_t max_industries = 128;
|
||||
|
||||
std::array<industry, max_industries>& industries();
|
||||
industry* get(industry_id_t id);
|
||||
void reset();
|
||||
LocoFixedVector<Industry> industries();
|
||||
Industry* get(IndustryId_t id);
|
||||
void update();
|
||||
void updateMonthly();
|
||||
void createAllMapAnimations();
|
||||
bool industryNearPosition(const Map::Pos2& position, uint32_t flags);
|
||||
}
|
||||
|
|
|
@ -13,40 +13,39 @@ using namespace OpenLoco::Interop;
|
|||
namespace OpenLoco::Input
|
||||
{
|
||||
loco_global<uint32_t, 0x00523368> _flags;
|
||||
static loco_global<uint8_t, 0x0052336D> _state;
|
||||
static int32_t _cursor_drag_start_x;
|
||||
static int32_t _cursor_drag_start_y;
|
||||
static loco_global<State, 0x0052336D> _state;
|
||||
static Ui::Point32 _cursor_drag_start;
|
||||
loco_global<uint32_t, 0x00525374> _cursor_drag_state;
|
||||
|
||||
void init()
|
||||
{
|
||||
_flags = 0;
|
||||
_state = 0;
|
||||
_state = State::reset;
|
||||
}
|
||||
|
||||
bool hasFlag(input_flags value)
|
||||
bool hasFlag(uint32_t value)
|
||||
{
|
||||
return (_flags & (uint32_t)value) != 0;
|
||||
return (_flags & value) != 0;
|
||||
}
|
||||
|
||||
void setFlag(input_flags value)
|
||||
void setFlag(uint32_t value)
|
||||
{
|
||||
_flags |= (uint32_t)value;
|
||||
_flags |= value;
|
||||
}
|
||||
|
||||
void resetFlag(input_flags value)
|
||||
void resetFlag(uint32_t value)
|
||||
{
|
||||
_flags &= ~(uint32_t)value;
|
||||
_flags &= ~value;
|
||||
}
|
||||
|
||||
input_state state()
|
||||
State state()
|
||||
{
|
||||
return (input_state)*_state;
|
||||
return *_state;
|
||||
}
|
||||
|
||||
void state(input_state state)
|
||||
void state(State state)
|
||||
{
|
||||
_state = (uint8_t)state;
|
||||
_state = state;
|
||||
}
|
||||
|
||||
// 0x00406FEC
|
||||
|
@ -60,7 +59,8 @@ namespace OpenLoco::Input
|
|||
if (_cursor_drag_state == 0)
|
||||
{
|
||||
_cursor_drag_state = 1;
|
||||
Ui::getCursorPos(_cursor_drag_start_x, _cursor_drag_start_y);
|
||||
auto cursor = Ui::getCursorPos();
|
||||
_cursor_drag_start = cursor;
|
||||
Ui::hideCursor();
|
||||
}
|
||||
}
|
||||
|
@ -70,8 +70,19 @@ namespace OpenLoco::Input
|
|||
if (_cursor_drag_state != 0)
|
||||
{
|
||||
_cursor_drag_state = 0;
|
||||
Ui::setCursorPos(_cursor_drag_start_x, _cursor_drag_start_y);
|
||||
Ui::setCursorPos(_cursor_drag_start.x, _cursor_drag_start.y);
|
||||
Ui::showCursor();
|
||||
}
|
||||
}
|
||||
|
||||
Ui::Point getNextDragOffset()
|
||||
{
|
||||
auto current = Ui::getCursorPos();
|
||||
|
||||
auto delta = current - _cursor_drag_start;
|
||||
|
||||
Ui::setCursorPos(_cursor_drag_start.x, _cursor_drag_start.y);
|
||||
|
||||
return { static_cast<int16_t>(delta.x), static_cast<int16_t>(delta.y) };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,44 +5,50 @@
|
|||
|
||||
namespace OpenLoco::Input
|
||||
{
|
||||
enum class mouse_button : uint16_t
|
||||
enum class MouseButton : uint16_t
|
||||
{
|
||||
released = 0,
|
||||
left_pressed = 1,
|
||||
left_released = 2,
|
||||
right_pressed = 3,
|
||||
right_released = 4,
|
||||
leftPressed = 1,
|
||||
leftReleased = 2,
|
||||
rightPressed = 3,
|
||||
rightReleased = 4,
|
||||
};
|
||||
|
||||
enum class input_state
|
||||
enum class State : uint8_t
|
||||
{
|
||||
reset, // 0
|
||||
normal, // 1
|
||||
widget_pressed, // 2
|
||||
positioning_window, // 3
|
||||
viewport_right, // 4
|
||||
dropdown_active, // 5
|
||||
viewport_left, // 6
|
||||
scroll_left, // 7
|
||||
resizing, // 8
|
||||
scroll_right, // 9
|
||||
reset, // 0
|
||||
normal, // 1
|
||||
widgetPressed, // 2
|
||||
positioningWindow, // 3
|
||||
viewportRight, // 4
|
||||
dropdownActive, // 5
|
||||
viewportLeft, // 6
|
||||
scrollLeft, // 7
|
||||
resizing, // 8
|
||||
scrollRight, // 9
|
||||
};
|
||||
|
||||
enum class input_flags
|
||||
namespace Flags
|
||||
{
|
||||
widget_pressed = 1 << 0,
|
||||
flag1 = 1 << 1,
|
||||
flag2 = 1 << 2,
|
||||
tool_active = 1 << 3,
|
||||
flag4 = 1 << 4,
|
||||
flag5 = 1 << 5,
|
||||
flag6 = 1 << 6,
|
||||
viewport_scrolling = 1 << 7,
|
||||
};
|
||||
constexpr uint32_t widgetPressed = 1 << 0;
|
||||
constexpr uint32_t flag1 = 1 << 1;
|
||||
constexpr uint32_t flag2 = 1 << 2;
|
||||
constexpr uint32_t toolActive = 1 << 3;
|
||||
constexpr uint32_t flag4 = 1 << 4;
|
||||
constexpr uint32_t flag5 = 1 << 5;
|
||||
constexpr uint32_t flag6 = 1 << 6;
|
||||
constexpr uint32_t viewportScrolling = 1 << 7;
|
||||
}
|
||||
|
||||
namespace MapSelectionFlags
|
||||
{
|
||||
constexpr uint8_t catchment_area = 1 << 5;
|
||||
constexpr uint8_t enable = 1 << 0;
|
||||
constexpr uint8_t enableConstruct = (1 << 1);
|
||||
constexpr uint8_t unk_02 = 1 << 2;
|
||||
constexpr uint8_t unk_03 = 1 << 3;
|
||||
constexpr uint8_t unk_04 = 1 << 4; // Vehicle orders?
|
||||
constexpr uint8_t catchmentArea = 1 << 5;
|
||||
constexpr uint8_t unk_6 = 1 << 6;
|
||||
};
|
||||
|
||||
namespace KeyModifier
|
||||
|
@ -55,31 +61,40 @@ namespace OpenLoco::Input
|
|||
|
||||
void init();
|
||||
void initMouse();
|
||||
bool hasFlag(input_flags value);
|
||||
void setFlag(input_flags value);
|
||||
void resetFlag(input_flags value);
|
||||
input_state state();
|
||||
void state(input_state);
|
||||
bool hasFlag(uint32_t value);
|
||||
void setFlag(uint32_t value);
|
||||
void resetFlag(uint32_t value);
|
||||
State state();
|
||||
void state(State);
|
||||
|
||||
Gfx::point_t getMouseLocation();
|
||||
Ui::Point getMouseLocation();
|
||||
Ui::Point getMouseLocation2();
|
||||
bool isHovering(Ui::WindowType);
|
||||
bool isHovering(Ui::WindowType, Ui::window_number);
|
||||
bool isHovering(Ui::WindowType type, Ui::window_number number, Ui::widget_index widgetIndex);
|
||||
Ui::widget_index getHoveredWidgetIndex();
|
||||
bool isHovering(Ui::WindowType, Ui::WindowNumber_t);
|
||||
bool isHovering(Ui::WindowType type, Ui::WindowNumber_t number, Ui::WidgetIndex_t widgetIndex);
|
||||
Ui::WidgetIndex_t getHoveredWidgetIndex();
|
||||
|
||||
bool isDropdownActive(Ui::WindowType type, Ui::widget_index index);
|
||||
bool isDropdownActive(Ui::WindowType type, Ui::WidgetIndex_t index);
|
||||
|
||||
bool isPressed(Ui::WindowType type, Ui::window_number number);
|
||||
bool isPressed(Ui::WindowType type, Ui::window_number number, Ui::widget_index index);
|
||||
Ui::widget_index getPressedWidgetIndex();
|
||||
bool isPressed(Ui::WindowType type, Ui::WindowNumber_t number);
|
||||
bool isPressed(Ui::WindowType type, Ui::WindowNumber_t number, Ui::WidgetIndex_t index);
|
||||
Ui::WidgetIndex_t getPressedWidgetIndex();
|
||||
void setPressedWidgetIndex(Ui::WidgetIndex_t index);
|
||||
|
||||
void updateCursorPosition();
|
||||
|
||||
bool isToolActive(Ui::WindowType, Ui::window_number);
|
||||
bool toolSet(Ui::window* w, int16_t widgetIndex, uint8_t tool);
|
||||
void toolCancel();
|
||||
void toolCancel(Ui::WindowType, Ui::window_number);
|
||||
Ui::Window* toolGetActiveWindow();
|
||||
bool isToolActive(Ui::WindowType);
|
||||
|
||||
bool isToolActive(Ui::WindowType, Ui::WindowNumber_t);
|
||||
bool isToolActive(Ui::WindowType, Ui::WindowNumber_t, int16_t);
|
||||
bool toolSet(Ui::Window* w, int16_t widgetIndex, Ui::CursorId cursorId);
|
||||
void toolCancel();
|
||||
void toolCancel(Ui::WindowType, Ui::WindowNumber_t);
|
||||
int16_t getToolWidgetIndex();
|
||||
|
||||
void enqueueText(const char* text);
|
||||
void enqueueKey(uint32_t key);
|
||||
bool hasKeyModifier(uint8_t modifier);
|
||||
uint16_t getMapSelectionFlags();
|
||||
bool hasMapSelectionFlag(uint8_t flags);
|
||||
|
@ -87,17 +102,24 @@ namespace OpenLoco::Input
|
|||
void resetMapSelectionFlag(uint8_t flags);
|
||||
|
||||
void handleKeyboard();
|
||||
void handleMouse(int16_t x, int16_t y, mouse_button button);
|
||||
mouse_button getLastKnownButtonState();
|
||||
void handleMouse(int16_t x, int16_t y, MouseButton button);
|
||||
MouseButton getLastKnownButtonState();
|
||||
void enqueueMouseButton(int32_t button);
|
||||
void moveMouse(int32_t x, int32_t y, int32_t relX, int32_t relY);
|
||||
void sub_407218();
|
||||
void sub_407231();
|
||||
Ui::Point getNextDragOffset();
|
||||
void processMouseOver(int16_t x, int16_t y);
|
||||
void processKeyboardInput();
|
||||
|
||||
Gfx::point_t getTooltipMouseLocation();
|
||||
void setTooltipMouseLocation(const Gfx::point_t& loc);
|
||||
void windowPositionBegin(int16_t x, int16_t y, Ui::Window* window, Ui::WidgetIndex_t widget_index);
|
||||
|
||||
Ui::Point getScrollLastLocation();
|
||||
Ui::Point getDragLastLocation();
|
||||
Ui::Point getTooltipMouseLocation();
|
||||
void setTooltipMouseLocation(const Ui::Point& loc);
|
||||
uint16_t getTooltipTimeout();
|
||||
void setTooltipTimeout(uint16_t tooltipTimeout);
|
||||
|
||||
void setClickRepeatTicks(uint16_t ticks);
|
||||
}
|
||||
|
|
|
@ -2,16 +2,17 @@
|
|||
#include "../CompanyManager.h"
|
||||
#include "../Config.h"
|
||||
#include "../Console.h"
|
||||
#include "../GameCommands.h"
|
||||
#include "../Entities/EntityManager.h"
|
||||
#include "../GameCommands/GameCommands.h"
|
||||
#include "../Input.h"
|
||||
#include "../Interop/Interop.hpp"
|
||||
#include "../Intro.h"
|
||||
#include "../Localisation/StringIds.h"
|
||||
#include "../OpenLoco.h"
|
||||
#include "../Things/ThingManager.h"
|
||||
#include "../Tutorial.h"
|
||||
#include "../Ui.h"
|
||||
#include "../Ui/Screenshot.h"
|
||||
#include "../Vehicles/Vehicle.h"
|
||||
#include "../Win32.h"
|
||||
#include "ShortcutManager.h"
|
||||
#include <cstdint>
|
||||
|
@ -52,6 +53,10 @@ namespace OpenLoco::Input
|
|||
static loco_global<Ui::WindowType, 0x005233B6> _modalWindowType;
|
||||
static loco_global<char[16], 0x0112C826> _commonFormatArgs;
|
||||
static std::string _cheatBuffer; // 0x0011364A5
|
||||
static loco_global<key[64], 0x0113E300> _keyQueue;
|
||||
static loco_global<uint32_t, 0x00525388> _keyQueueLastWrite;
|
||||
static loco_global<uint32_t, 0x00525384> _keyQueueReadIndex;
|
||||
static loco_global<uint32_t, 0x00525380> _keyQueueWriteIndex;
|
||||
static loco_global<uint8_t[256], 0x01140740> _keyboardState;
|
||||
static loco_global<uint8_t, 0x011364A4> _11364A4;
|
||||
|
||||
|
@ -69,14 +74,14 @@ namespace OpenLoco::Input
|
|||
|
||||
static void loc_4BECDE()
|
||||
{
|
||||
setScreenFlag(ScreenFlags::unknown_6);
|
||||
setScreenFlag(ScreenFlags::driverCheatEnabled);
|
||||
|
||||
Audio::playSound(Audio::sound_id::click_press, Ui::width() / 2);
|
||||
Audio::playSound(Audio::SoundId::clickPress, Ui::width() / 2);
|
||||
}
|
||||
|
||||
static void loc_4BED04()
|
||||
{
|
||||
if ((getScreenFlags() & ScreenFlags::unknown_6) == 0)
|
||||
if ((getScreenFlags() & ScreenFlags::driverCheatEnabled) == 0)
|
||||
{
|
||||
return;
|
||||
// Only works when DRIVER mode is active
|
||||
|
@ -89,18 +94,15 @@ namespace OpenLoco::Input
|
|||
if (w->type != WindowType::vehicle)
|
||||
continue;
|
||||
|
||||
auto t = ThingManager::get<OpenLoco::vehicle>(w->number);
|
||||
auto t = EntityManager::get<Vehicles::VehicleBase>(w->number);
|
||||
if (t->owner != CompanyManager::getControllingId())
|
||||
continue;
|
||||
|
||||
if (t->mode != TransportMode::rail)
|
||||
if (t->getTransportMode() != TransportMode::rail)
|
||||
continue;
|
||||
|
||||
registers regs;
|
||||
regs.cx = w->number;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
GameCommands::doCommand(77, regs);
|
||||
Audio::playSound(Audio::sound_id::click_press, Ui::width() / 2);
|
||||
GameCommands::do_77(w->number);
|
||||
Audio::playSound(Audio::SoundId::clickPress, Ui::width() / 2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -108,38 +110,36 @@ namespace OpenLoco::Input
|
|||
|
||||
static void loc_4BED79()
|
||||
{
|
||||
registers regs;
|
||||
regs.bl = GameCommandFlag::apply;
|
||||
GameCommands::doCommand(78, regs);
|
||||
GameCommands::do_78();
|
||||
|
||||
Audio::playSound(Audio::sound_id::click_press, Ui::width() / 2);
|
||||
Audio::playSound(Audio::SoundId::clickPress, Ui::width() / 2);
|
||||
}
|
||||
|
||||
static void loc_4BEFEF()
|
||||
{
|
||||
switch (Tutorial::state())
|
||||
{
|
||||
case Tutorial::tutorial_state::none:
|
||||
case Tutorial::State::none:
|
||||
break;
|
||||
|
||||
case Tutorial::tutorial_state::playing:
|
||||
case Tutorial::State::playing:
|
||||
{
|
||||
const uint16_t next = Tutorial::nextInput();
|
||||
_keyModifier = next;
|
||||
if ((_keyModifier & KeyModifier::unknown) == 0)
|
||||
return;
|
||||
|
||||
ToolTip::closeAndReset();
|
||||
Windows::ToolTip::closeAndReset();
|
||||
|
||||
auto tutStringId = Tutorial::nextString();
|
||||
auto main = WindowManager::getMainWindow();
|
||||
auto cursor = getMouseLocation();
|
||||
|
||||
ToolTip::update(main, 0, tutStringId, cursor.x, cursor.y);
|
||||
Windows::ToolTip::update(main, 0, tutStringId, cursor.x, cursor.y);
|
||||
break;
|
||||
}
|
||||
|
||||
case Tutorial::tutorial_state::recording:
|
||||
case Tutorial::State::recording:
|
||||
{
|
||||
call(0x004BF005);
|
||||
break;
|
||||
|
@ -147,11 +147,43 @@ namespace OpenLoco::Input
|
|||
}
|
||||
}
|
||||
|
||||
// 0x00406FBA
|
||||
void enqueueKey(uint32_t keycode)
|
||||
{
|
||||
uint32_t writeIndex = _keyQueueWriteIndex;
|
||||
auto nextWriteIndex = (writeIndex + 1) % std::size(_keyQueue);
|
||||
if (nextWriteIndex == _keyQueueReadIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_keyQueueLastWrite = writeIndex;
|
||||
_keyQueue[writeIndex] = { keycode, 0 };
|
||||
_keyQueueWriteIndex = nextWriteIndex;
|
||||
}
|
||||
|
||||
void enqueueText(const char* text)
|
||||
{
|
||||
if (text != nullptr && text[0] != '\0')
|
||||
{
|
||||
auto index = _keyQueueLastWrite;
|
||||
_keyQueue[index].charCode = text[0];
|
||||
}
|
||||
}
|
||||
|
||||
// 0x00407028
|
||||
static key* getNextKey()
|
||||
{
|
||||
registers regs;
|
||||
call(0x00407028, regs);
|
||||
return (key*)regs.eax;
|
||||
uint32_t readIndex = _keyQueueReadIndex;
|
||||
if (readIndex == _keyQueueWriteIndex)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
auto* out = &_keyQueue[readIndex];
|
||||
readIndex++;
|
||||
// Wrap around at _keyQueue size
|
||||
readIndex %= std::size(_keyQueue);
|
||||
_keyQueueReadIndex = readIndex;
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool tryShortcut(Shortcut sc, uint32_t keyCode, uint8_t modifiers)
|
||||
|
@ -249,32 +281,32 @@ namespace OpenLoco::Input
|
|||
{
|
||||
while (true)
|
||||
{
|
||||
auto eax = getNextKey();
|
||||
if (eax == 0)
|
||||
auto* nextKey = getNextKey();
|
||||
if (nextKey == nullptr)
|
||||
{
|
||||
loc_4BEFEF();
|
||||
break;
|
||||
}
|
||||
|
||||
if (eax->keyCode >= 255)
|
||||
if (nextKey->keyCode >= 255)
|
||||
continue;
|
||||
|
||||
if (eax->keyCode == 0x10) // VK_SHIFT
|
||||
if (nextKey->keyCode == 0x10) // VK_SHIFT
|
||||
continue;
|
||||
|
||||
if (eax->keyCode == 0x11) // VK_CONTROL
|
||||
if (nextKey->keyCode == 0x11) // VK_CONTROL
|
||||
continue;
|
||||
|
||||
if ((_keyModifier & KeyModifier::cheat) != 0)
|
||||
{
|
||||
if (eax->charCode >= 'a' && eax->charCode <= 'z')
|
||||
if (nextKey->charCode >= 'a' && nextKey->charCode <= 'z')
|
||||
{
|
||||
eax->charCode = toupper(eax->charCode);
|
||||
nextKey->charCode = toupper(nextKey->charCode);
|
||||
}
|
||||
|
||||
if (eax->charCode >= 'A' && eax->charCode <= 'Z')
|
||||
if (nextKey->charCode >= 'A' && nextKey->charCode <= 'Z')
|
||||
{
|
||||
_cheatBuffer += eax->charCode;
|
||||
_cheatBuffer += nextKey->charCode;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
@ -283,10 +315,10 @@ namespace OpenLoco::Input
|
|||
auto ti = WindowManager::find(WindowType::textInput);
|
||||
if (ti != nullptr)
|
||||
{
|
||||
if (tryShortcut(Shortcut::screenshot, eax->keyCode, _keyModifier))
|
||||
if (tryShortcut(Shortcut::screenshot, nextKey->keyCode, _keyModifier))
|
||||
continue;
|
||||
|
||||
Ui::TextInput::sub_4CE910(eax->charCode, eax->keyCode);
|
||||
Ui::Windows::TextInput::handleInput(nextKey->charCode, nextKey->keyCode);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -295,13 +327,10 @@ namespace OpenLoco::Input
|
|||
ti = WindowManager::find(WindowType::fileBrowserPrompt);
|
||||
if (ti != nullptr)
|
||||
{
|
||||
if (tryShortcut(Shortcut::screenshot, eax->keyCode, _keyModifier))
|
||||
if (tryShortcut(Shortcut::screenshot, nextKey->keyCode, _keyModifier))
|
||||
continue;
|
||||
|
||||
registers regs;
|
||||
regs.eax = eax->charCode;
|
||||
regs.ebx = eax->keyCode;
|
||||
call(0x0044685C, regs);
|
||||
Ui::Windows::PromptBrowse::handleInput(nextKey->charCode, nextKey->keyCode);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -311,21 +340,19 @@ namespace OpenLoco::Input
|
|||
ti = WindowManager::find(WindowType::confirmationPrompt);
|
||||
if (ti != nullptr)
|
||||
{
|
||||
registers regs;
|
||||
regs.eax = eax->charCode;
|
||||
regs.ebx = eax->keyCode;
|
||||
call(0x0044685C, regs);
|
||||
Ui::Windows::PromptOkCancel::handleInput(nextKey->charCode, nextKey->keyCode);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ti = WindowManager::find(WindowType::editKeyboardShortcut);
|
||||
if (ti != nullptr)
|
||||
{
|
||||
editShortcut(eax);
|
||||
editShortcut(nextKey);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Tutorial::state() == Tutorial::tutorial_state::playing)
|
||||
if (Tutorial::state() == Tutorial::State::playing)
|
||||
{
|
||||
Tutorial::stop();
|
||||
continue;
|
||||
|
@ -335,46 +362,46 @@ namespace OpenLoco::Input
|
|||
{
|
||||
for (int i = 0; i < 35; i++)
|
||||
{
|
||||
if (tryShortcut((Shortcut)i, eax->keyCode, _keyModifier))
|
||||
if (tryShortcut((Shortcut)i, nextKey->keyCode, _keyModifier))
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Intro::state() == (Intro::intro_state)9)
|
||||
if (Intro::state() == (Intro::State)9)
|
||||
{
|
||||
Intro::state(Intro::intro_state::end);
|
||||
Intro::state(Intro::State::end);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Intro::state() != Intro::intro_state::none)
|
||||
if (Intro::state() != Intro::State::none)
|
||||
{
|
||||
Intro::state((Intro::intro_state)8);
|
||||
Intro::state((Intro::State)8);
|
||||
}
|
||||
|
||||
if (tryShortcut(Shortcut::sendMessage, eax->keyCode, _keyModifier))
|
||||
if (tryShortcut(Shortcut::sendMessage, nextKey->keyCode, _keyModifier))
|
||||
continue;
|
||||
|
||||
if (tryShortcut(Shortcut::screenshot, eax->keyCode, _keyModifier))
|
||||
if (tryShortcut(Shortcut::screenshot, nextKey->keyCode, _keyModifier))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
static void edgeScroll()
|
||||
{
|
||||
if (Tutorial::state() != Tutorial::tutorial_state::none)
|
||||
if (Tutorial::state() != Tutorial::State::none)
|
||||
return;
|
||||
|
||||
if (Config::get().edge_scrolling == 0)
|
||||
return;
|
||||
|
||||
if (Input::state() != input_state::normal && Input::state() != input_state::dropdown_active)
|
||||
if (Input::state() != State::normal && Input::state() != State::dropdownActive)
|
||||
return;
|
||||
|
||||
if (hasKeyModifier(KeyModifier::shift) || hasKeyModifier(KeyModifier::control))
|
||||
return;
|
||||
|
||||
Gfx::point_t delta = { 0, 0 };
|
||||
Ui::Point delta = { 0, 0 };
|
||||
auto cursor = getMouseLocation();
|
||||
|
||||
if (cursor.x == 0)
|
||||
|
@ -407,12 +434,12 @@ namespace OpenLoco::Input
|
|||
delta.y *= 1 << viewport->zoom;
|
||||
main->viewport_configurations[0].saved_view_x += delta.x;
|
||||
main->viewport_configurations[0].saved_view_y += delta.y;
|
||||
Input::setFlag(input_flags::viewport_scrolling);
|
||||
Input::setFlag(Flags::viewportScrolling);
|
||||
}
|
||||
|
||||
static void keyScroll()
|
||||
{
|
||||
if (Tutorial::state() != Tutorial::tutorial_state::none)
|
||||
if (Tutorial::state() != Tutorial::State::none)
|
||||
return;
|
||||
|
||||
if (*_modalWindowType != WindowType::undefined)
|
||||
|
@ -421,7 +448,7 @@ namespace OpenLoco::Input
|
|||
if (WindowManager::find(WindowType::textInput) != nullptr)
|
||||
return;
|
||||
|
||||
Gfx::point_t delta = { 0, 0 };
|
||||
Ui::Point delta = { 0, 0 };
|
||||
|
||||
if (_keyboardState[DIK_LEFT] & 0x80)
|
||||
delta.x -= 8;
|
||||
|
@ -453,7 +480,7 @@ namespace OpenLoco::Input
|
|||
delta.y *= 1 << viewport->zoom;
|
||||
main->viewport_configurations[0].saved_view_x += delta.x;
|
||||
main->viewport_configurations[0].saved_view_y += delta.y;
|
||||
Input::setFlag(input_flags::viewport_scrolling);
|
||||
Input::setFlag(Flags::viewportScrolling);
|
||||
}
|
||||
|
||||
// 0x004BE92A
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue