Merge branch 'master' into jgrpp
# Conflicts: # src/genworld_gui.cpp # src/gfx.cpp # src/lang/korean.txt # src/linkgraph/linkgraph_gui.cpp # src/linkgraph/linkgraph_gui.h # src/music.cpp # src/table/settings.ini # src/town_cmd.cpp # src/train_cmd.cpp
This commit is contained in:
144
README.md
144
README.md
@@ -314,9 +314,10 @@ The easiest way to contact the OpenTTD team is by submitting bug reports or
|
||||
posting comments in our forums. You can also chat with us on IRC (#openttd
|
||||
on irc.oftc.net).
|
||||
|
||||
The OpenTTD homepage is [http://www.openttd.org/](http://www.openttd.org/).
|
||||
The OpenTTD homepage is https://www.openttd.org.
|
||||
|
||||
You can also find the OpenTTD forums at [http://forum.openttd.org/](http://forum.openttd.org/).
|
||||
You can also find the OpenTTD forums at
|
||||
[https://www.tt-forums.net](https://www.tt-forums.net/viewforum.php?f=55).
|
||||
|
||||
### 2.1) Reporting bugs
|
||||
|
||||
@@ -325,11 +326,11 @@ through the file called 'known-bugs.txt' which is distributed with OpenTTD
|
||||
like this readme.
|
||||
|
||||
For tracking our bugs we are using GitHub's issue tracker. You can find
|
||||
the tracker at [https://github.com/OpenTTD/OpenTTD/issues](https://github.com/OpenTTD/OpenTTD/issues). Before actually reporting take a look
|
||||
through the already reported bugs there to see if the bug is already known.
|
||||
The 'known-bugs.txt' file might be a bit outdated at the moment you are
|
||||
reading it as only bugs known before the release are documented there. Also
|
||||
look through the recently closed bugs.
|
||||
the tracker at https://github.com/OpenTTD/OpenTTD/issues. Before actually
|
||||
reporting take a look through the already reported bugs there to see if
|
||||
the bug is already known. The 'known-bugs.txt' file might be a bit outdated
|
||||
at the moment you are reading it as only bugs known before the release
|
||||
are documented there. Also look through the recently closed bugs.
|
||||
|
||||
When you are sure it is not already reported you should:
|
||||
|
||||
@@ -352,8 +353,8 @@ following information in your bug report:
|
||||
- Bug details, including instructions how to reproduce it
|
||||
- Platform (Windows, Linux, FreeBSD, …) and compiler (including version) if
|
||||
you compiled OpenTTD yourself.
|
||||
- The processor architecture of your OS (32 bits Windows, 64 bits Windows,
|
||||
Linux on an ARM, Mac OS X on a PowerPC, …)
|
||||
- The processor architecture of your OS (32-bit Windows, 64-bit Windows,
|
||||
Linux on an ARM, Mac OS X on a PowerPC, etc.)
|
||||
- Attach a saved game **and** a screenshot if possible
|
||||
- If this bug only occurred recently please note the last version without
|
||||
the bug and the first version including the bug. That way we can fix it
|
||||
@@ -426,7 +427,7 @@ platforms are:
|
||||
- DOS (Allegro)
|
||||
- FreeBSD (SDL)
|
||||
- Linux (SDL or Allegro)
|
||||
- MacOS X (universal) (Cocoa video and sound drivers)
|
||||
- macOS (universal) (Cocoa video and sound drivers)
|
||||
- MorphOS (SDL)
|
||||
- OpenBSD (SDL)
|
||||
- OS/2 (SDL)
|
||||
@@ -482,9 +483,9 @@ when using other versions of the game.
|
||||
The free data files, split into OpenGFX for graphics, OpenSFX for sounds and
|
||||
OpenMSX for music can be found at:
|
||||
|
||||
- [http://www.openttd.org/download-opengfx](http://www.openttd.org/download-opengfx) for OpenGFX
|
||||
- [http://www.openttd.org/download-opensfx](http://www.openttd.org/download-opensfx) for OpenSFX
|
||||
- [http://www.openttd.org/download-openmsx](http://www.openttd.org/download-openmsx) for OpenMSX
|
||||
- https://www.openttd.org/download-opengfx for OpenGFX
|
||||
- https://www.openttd.org/download-opensfx for OpenSFX
|
||||
- https://www.openttd.org/download-openmsx for OpenMSX
|
||||
|
||||
Please follow the readme of these packages about the installation procedure.
|
||||
The Windows installer can optionally download and install these packages.
|
||||
@@ -496,20 +497,20 @@ have to copy the data files from the CD-ROM into the baseset/ directory. It
|
||||
does not matter whether you copy them from the DOS or Windows version of
|
||||
Transport Tycoon Deluxe. The Windows install can optionally copy these files.
|
||||
You need to copy the following files:
|
||||
- sample.cat
|
||||
- trg1r.grf or TRG1.GRF
|
||||
- trgcr.grf or TRGC.GRF
|
||||
- trghr.grf or TRGH.GRF
|
||||
- trgir.grf or TRGI.GRF
|
||||
- trgtr.grf or TRGT.GRF
|
||||
- sample.cat
|
||||
- trg1r.grf or TRG1.GRF
|
||||
- trgcr.grf or TRGC.GRF
|
||||
- trghr.grf or TRGH.GRF
|
||||
- trgir.grf or TRGI.GRF
|
||||
- trgtr.grf or TRGT.GRF
|
||||
|
||||
#### 4.1.3) Original Transport Tycoon Deluxe music
|
||||
|
||||
If you want the Transport Tycoon Deluxe music, copy the appropriate files from
|
||||
the original game into the baseset folder.
|
||||
- TTD for Windows: All files in the gm/ folder (gm_tt00.gm up to gm_tt21.gm)
|
||||
- TTD for DOS: The GM.CAT file
|
||||
- Transport Tycoon Original: The GM.CAT file, but rename it to GM-TTO.CAT
|
||||
- TTD for Windows: All files in the gm/ folder (gm_tt00.gm up to gm_tt21.gm)
|
||||
- TTD for DOS: The GM.CAT file
|
||||
- Transport Tycoon Original: The GM.CAT file, but rename it to GM-TTO.CAT
|
||||
|
||||
#### 4.1.4) AIs
|
||||
|
||||
@@ -518,10 +519,9 @@ not possible or you want to use an AI that has not been uploaded to the content
|
||||
download system download the tar file and place it in the ai/ directory. If the
|
||||
AI needs libraries you will have to download those too and put them in the
|
||||
ai/library/ directory. All AIs and AI Libraries that have been uploaded to
|
||||
the content download system can be found at http://noai.openttd.org/downloads/
|
||||
the content download system can be found at https://noai.openttd.org/downloads.
|
||||
The AIs and libraries can be found their in the form of .tar.gz packages.
|
||||
OpenTTD can read inside tar files but it does not extract .tar.gz files by
|
||||
itself.
|
||||
OpenTTD can read inside tar files but it does not extract .tar.gz files by itself.
|
||||
|
||||
To figure out which libraries you need for an AI you have to start the AI and
|
||||
wait for an error message to pop up. The error message will tell you
|
||||
@@ -553,26 +553,27 @@ your operating system:
|
||||
For non-Windows operating systems OpenTTD will not scan for files in this
|
||||
directory if it is your personal directory, i.e. '~/', or when it is the
|
||||
root directory, i.e. '/'.
|
||||
|
||||
2. Your personal directory
|
||||
- Windows:
|
||||
- `C:\My Documents\OpenTTD` (95, 98, ME)
|
||||
- `C:\Documents and Settings\<username>\My Documents\OpenTTD` (2000, XP)
|
||||
- `C:\Users\<username>\Documents\OpenTTD` (Vista, 7)
|
||||
- Mac OSX: `~/Documents/OpenTTD`
|
||||
- `C:\Users\<username>\Documents\OpenTTD` (Vista, 7, 8.1, 10)
|
||||
- macOS: `~/Documents/OpenTTD`
|
||||
- Linux: `$XDG_DATA_HOME/openttd` which is usually `~/.local/share/openttd`
|
||||
when built with XDG base directory support, otherwise `~/.openttd`
|
||||
3. The shared directory
|
||||
- Windows:
|
||||
- `C:\Documents and Settings\All Users\Shared Documents\OpenTTD` (2000, XP)
|
||||
- `C:\Users\Public\Documents\OpenTTD` (Vista, 7)
|
||||
- Mac OSX: `/Library/Application Support/OpenTTD`
|
||||
- `C:\Users\Public\Documents\OpenTTD` (Vista, 7, 8.1, 10)
|
||||
- macOS: `/Library/Application Support/OpenTTD`
|
||||
- Linux: not available
|
||||
4. The binary directory (where the OpenTTD executable is)
|
||||
- Windows: `C:\Program Files\OpenTTD`
|
||||
- Linux: `/usr/games`
|
||||
5. The installation directory (Linux only)
|
||||
- Linux: `/usr/share/games/openttd`
|
||||
6. The application bundle (Mac OSX only)
|
||||
6. The application bundle (macOS only)
|
||||
|
||||
It includes the OpenTTD files (grf+lng) and it will work as long as they
|
||||
are not touched
|
||||
@@ -584,14 +585,14 @@ chosen main OpenTTD directory:
|
||||
| --- | --- | --- |
|
||||
| Config File | (no subdirectory) | |
|
||||
| Screenshots | screenshot | |
|
||||
| Base Graphics | baseset| (or a subdirectory thereof) |
|
||||
| Sound Sets | baseset| (or a subdirectory thereof) |
|
||||
| NewGRFs | newgrf| (or a subdirectory thereof) |
|
||||
| 32bpp Sets | newgrf| (or a subdirectory thereof) |
|
||||
| Music Sets | baseset| (or a subdirectory thereof) |
|
||||
| AIs | ai| (or a subdirectory thereof) |
|
||||
| AI Libraries | ai/library| (or a subdirectory thereof) |
|
||||
| Game Scripts (GS) | game| (or a subdirectory thereof) |
|
||||
| Base Graphics | baseset | (or a subdirectory thereof) |
|
||||
| Sound Sets | baseset | (or a subdirectory thereof) |
|
||||
| NewGRFs | newgrf | (or a subdirectory thereof) |
|
||||
| 32bpp Sets | newgrf | (or a subdirectory thereof) |
|
||||
| Music Sets | baseset | (or a subdirectory thereof) |
|
||||
| AIs | ai | (or a subdirectory thereof) |
|
||||
| AI Libraries | ai/library | (or a subdirectory thereof) |
|
||||
| Game Scripts (GS) | game | (or a subdirectory thereof) |
|
||||
| GS Libraries | game/library | (or a subdirectory thereof) |
|
||||
| Savegames | save | |
|
||||
| Automatic Savegames | save/autosave | |
|
||||
@@ -662,17 +663,16 @@ OpenTTD has a lot of features going beyond the original Transport Tycoon Deluxe
|
||||
emulation. Unfortunately, there is currently no comprehensive list of features,
|
||||
but there is a basic features list on the web, and some optional features can be
|
||||
controlled through the Advanced Settings dialog. We also implement some
|
||||
features known from [TTDPatch](http://www.ttdpatch.net/).
|
||||
features known from [TTDPatch](https://www.ttdpatch.net).
|
||||
|
||||
Several important non-standard controls:
|
||||
|
||||
- Ctrl modifies many commands and makes them more powerful. For example Ctrl
|
||||
clicking on signals with the build signal tool changes their behaviour, holding
|
||||
Ctrl while the track build tool is activated changes it to the track removal
|
||||
tool, and so on. See [http://wiki.openttd.org/Hidden_features](http://wiki.openttd.org/Hidden_features)
|
||||
clicking on signals with the build signal tool changes their behaviour,
|
||||
holding Ctrl while the track build tool is activated changes it to the track
|
||||
removal tool, and so on. See https://wiki.openttd.org/Hidden_features
|
||||
for a non-comprehensive list or look at the tooltips.
|
||||
- Ingame console. More information at
|
||||
[http://wiki.openttd.org/index.php/Console](http://wiki.openttd.org/index.php/Console)
|
||||
- Ingame console. More information at https://wiki.openttd.org/Console
|
||||
- Hovering over a GUI element shows tooltips. This can be changed to right click
|
||||
via the advanced settings.
|
||||
|
||||
@@ -724,7 +724,7 @@ you need to add WITH_SDL to the project settings.
|
||||
|
||||
PNG (WITH_PNG) and ZLIB (WITH_ZLIB) support is enabled by default. For these
|
||||
to work you need their development files. For best results, download the
|
||||
openttd-useful.zip file from [http://www.openttd.org/download-openttd-useful](http://www.openttd.org/download-openttd-useful)
|
||||
openttd-useful.zip file from https://www.openttd.org/download-openttd-useful.
|
||||
Put the header files into your compiler's include/ directory and the
|
||||
library (.lib) files into the lib/ directory.
|
||||
For more help with VS see docs/Readme_Windows_MSVC.txt.
|
||||
@@ -741,7 +741,7 @@ Use '`gmake`', but do a '`./configure`' before the first build.
|
||||
OpenTTD can be built with GNU '`make`'. On non-GNU systems it is called '`gmake`'.
|
||||
However, for the first build one has to do a '`./configure`' first.
|
||||
|
||||
### MacOS X:
|
||||
### macOS:
|
||||
|
||||
Use '`make`' or Xcode (which will then call make for you)
|
||||
This will give you a binary for your CPU type (PPC/Intel)
|
||||
@@ -794,7 +794,7 @@ The following libraries are used by OpenTTD for:
|
||||
OpenTTD does not require any of the libraries to be present, but without
|
||||
liblzma you cannot open most recent savegames and without zlib you cannot
|
||||
open most older savegames or use the content downloading system.
|
||||
Without libSDL/liballegro on non-Windows and non-MacOS X machines you have
|
||||
Without libSDL/liballegro on non-Windows and non-macOS machines you have
|
||||
no graphical user interface; you would be building a dedicated server.
|
||||
|
||||
### 7.2) Supported compilers
|
||||
@@ -817,7 +817,7 @@ The following compilers are known not to compile OpenTTD:
|
||||
- GNU Compiler Collection (GCC) 3.2 and earlier.
|
||||
These old versions fail due to OpenTTD's template usage.
|
||||
- GNU Compiler Collection (GCC) 4.5. It optimizes enums too aggressively.
|
||||
See http://bugs.openttd.org/task/5513 and references therein.
|
||||
See https://github.com/OpenTTD/OpenTTD/issues/5513 and references therein.
|
||||
- Intel C++ Compiler (ICC) 11.1 and earlier.
|
||||
- Version 10.0 and earlier fail a configure check and fail with recent
|
||||
system headers.
|
||||
@@ -833,7 +833,7 @@ Patches to support more compilers are welcome.
|
||||
|
||||
To recompile the extra graphics needed to play with the original Transport
|
||||
Tycoon Deluxe graphics you need GRFCodec (which includes NFORenum) as well.
|
||||
GRFCodec can be found at: [http://www.openttd.org/download-grfcodec](http://www.openttd.org/download-grfcodec)
|
||||
GRFCodec can be found at https://www.openttd.org/download-grfcodec.
|
||||
The compilation of these extra graphics does generally not happen, unless
|
||||
you remove the graphics file using '`make maintainer-clean`'.
|
||||
|
||||
@@ -846,17 +846,16 @@ modification of the base set files by the build process.
|
||||
|
||||
## 8.0) Translating
|
||||
|
||||
See [http://www.openttd.org/development](http://www.openttd.org/development) for up-to-date information.
|
||||
See https://www.openttd.org/development for up-to-date information.
|
||||
|
||||
The use of the online Translator service, located at
|
||||
[http://translator.openttd.org/](http://translator.openttd.org/), is highly
|
||||
encouraged. For getting an account simply follow the guidelines in the FAQ of
|
||||
the translator website.
|
||||
https://translator.openttd.org, is highly encouraged. For getting an account
|
||||
simply follow the guidelines in the FAQ of the translator website.
|
||||
|
||||
If for some reason the website is down for a longer period of time, the
|
||||
information below might be of help.
|
||||
|
||||
Please contact the translations manager ([http://www.openttd.org/contact](http://www.openttd.org/contact))
|
||||
Please contact the translations manager (https://www.openttd.org/contact)
|
||||
before beginning the translation process! This avoids double work, as
|
||||
someone else may have already started translating to the same language.
|
||||
|
||||
@@ -875,8 +874,8 @@ Note: Do not alter the following parts of the file:
|
||||
|
||||
- String identifiers (the first word on each line)
|
||||
- Parts of the strings which are in curly braces (such as {STRING})
|
||||
- Lines beginning with ## (such as ##id), other than the first two lines of
|
||||
the file
|
||||
- Lines beginning with ## (such as ##id), other than the first two lines
|
||||
of the file
|
||||
|
||||
### 8.2) Previewing
|
||||
|
||||
@@ -908,7 +907,7 @@ If the game is acting strange and you feel adventurous you can try the
|
||||
debugging output. The 'name' variable can help you to display only some type of
|
||||
debugging messages. This is mostly undocumented so best is to look in the
|
||||
source code file debug.c for the various debugging types. For more information
|
||||
look at [http://wiki.openttd.org/index.php/Command_line](http://wiki.openttd.org/index.php/Command_line).
|
||||
look at https://wiki.openttd.org/Command_line.
|
||||
|
||||
The most frequent problem is missing data files. Please install OpenGFX and
|
||||
possibly OpenSFX and OpenMSX. See section 4.1.1 for more information.
|
||||
@@ -932,16 +931,16 @@ and add a suitable font for the small, medium and / or large font, e.g.:
|
||||
|
||||
You should use a font name like 'Tahoma' or a path to the desired font.
|
||||
|
||||
Any NewGRF file used in a game is stored inside the savegame and will refuse
|
||||
to load if you do not have that NewGRF file available. A list of missing files
|
||||
can be viewed in the NewGRF window accessible from the file load dialogue window.
|
||||
Any NewGRF file used in a game is stored inside the savegame and will refuse to
|
||||
load if you do not have that NewGRF file available. A list of missing files can
|
||||
be viewed in the NewGRF window accessible from the file load dialogue window.
|
||||
|
||||
You can try to obtain the missing files from that NewGRF dialogue or – if they
|
||||
are not available online – you can search manually through our [forum's graphics
|
||||
development section](http://www.tt-forums.net/viewforum.php?f=66) or GrfCrawler
|
||||
(http://grfcrawler.tt-forums.net/). Put the NewGRF files in OpenTTD's newgrf folder
|
||||
(see section 4.2 'OpenTTD directories') and rescan the list of available NewGRFs.
|
||||
Once you have all missing files, you are set to go.
|
||||
are not available online – you can search manually through our
|
||||
[forum's graphics development section](https://www.tt-forums.net/viewforum.php?f=66)
|
||||
or [GRFCrawler](https://grfcrawler.tt-forums.net). Put the NewGRF files in
|
||||
OpenTTD's newgrf folder (see section 4.2 'OpenTTD directories') and rescan the
|
||||
list of available NewGRFs. Once you have all missing files, you are set to go.
|
||||
|
||||
## 10.0) Licensing
|
||||
|
||||
@@ -970,11 +969,13 @@ os/dos/exe2coff/copying.dj for the exact licensing terms.
|
||||
The CWSDPMI implementation in os/dos/cwsdpmi is distributed under a
|
||||
custom binary-only license that prohibits modification. The exact
|
||||
licensing terms can be found in os/dos/cwsdpmi/cwsdpmi.txt. The sources
|
||||
for these files can be downloaded at its author site, at:
|
||||
[http://homer.rice.edu/~sandmann/cwsdpmi/csdpmi5s.zip](http://homer.rice.edu/~sandmann/cwsdpmi/csdpmi5s.zip)
|
||||
for these files can be downloaded at its author site, at
|
||||
http://homer.rice.edu/~sandmann/cwsdpmi/csdpmi5s.zip.
|
||||
|
||||
CONTRIBUTING.md is adapted from [Bootstrap](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md)
|
||||
under the [Creative Commons Attribution 3.0 Unported License](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE)
|
||||
CONTRIBUTING.md is adapted from
|
||||
[Bootstrap](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md)
|
||||
under the [Creative Commons Attribution 3.0 Unported
|
||||
License](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE)
|
||||
terms for Bootstrap documentation.
|
||||
|
||||
## X.X) Credits
|
||||
@@ -998,7 +999,7 @@ terms for Bootstrap documentation.
|
||||
### Inactive Developers:
|
||||
|
||||
- Jean-François Claeys (Belugas) - GUI, newindustries and more (0.4.5 - 1.0)
|
||||
- Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles (0.3 - 0.7)
|
||||
- Bjarni Corfitzen (Bjarni) - macOS port, coder and vehicles (0.3 - 0.7)
|
||||
- Victor Fischer (Celestar) - Programming everywhere you need him to (0.3 - 0.6)
|
||||
- Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;) (0.4.5 - 0.6)
|
||||
- Jonathan Coome (Maedhros) - High priest of the NewGRF Temple (0.5 - 0.6)
|
||||
@@ -1012,7 +1013,7 @@ terms for Bootstrap documentation.
|
||||
|
||||
- Tamás Faragó (Darkvater) - Ex-Lead coder (0.3 - 0.5)
|
||||
- Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3 - 0.3)
|
||||
- Emil Djupfeld (egladil) - MacOSX port (0.4 - 0.6)
|
||||
- Emil Djupfeld (egladil) - macOS port (0.4 - 0.6)
|
||||
- Simon Sasburg (HackyKid) - Bug fixer (0.4 - 0.4.5)
|
||||
- Ludvig Strigeus (ludde) - Original author of OpenTTD, main coder (0.1 - 0.3)
|
||||
- Cian Duffy (MYOB) - BeOS port / manual writing (0.1 - 0.3)
|
||||
@@ -1037,4 +1038,3 @@ terms for Bootstrap documentation.
|
||||
- All Translators - For their support to make OpenTTD a truly international game
|
||||
- Bug Reporters - Thanks for all bug reports
|
||||
- Chris Sawyer - For an amazing game!
|
||||
|
||||
|
@@ -142,5 +142,17 @@ GM_TT19.GM = Funk Central
|
||||
GM_TT20.GM = Jammit
|
||||
GM_TT21.GM = Movin' On
|
||||
|
||||
; MIDI timecodes where the playback should attemp to start and stop short.
|
||||
; This is to allow fixing undesired silences in original MIDI files.
|
||||
; However not all music drivers may support this.
|
||||
[timingtrim]
|
||||
; Theme has two beats silence at the beginning which prevents clean looping.
|
||||
GM_TT00.GM = 768:53760
|
||||
; Can't Get There From Here from the Windows version has a long silence at the end,
|
||||
; followed by a solo repeat. This isn't in the original DOS version music and is likely
|
||||
; unintentional from the people who converted the music from the DOS version.
|
||||
; Actual song ends after measure 152.
|
||||
GM_TT10.GM = 0:235008
|
||||
|
||||
[origin]
|
||||
default = You can find it on your Transport Tycoon Deluxe CD-ROM.
|
||||
|
@@ -1,3 +1,44 @@
|
||||
1.8.0 (2018-04-01)
|
||||
------------------------------------------------------------------------
|
||||
(None)
|
||||
|
||||
|
||||
1.8.0-RC1 (2018-03-21)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: [GFX] Climate-specific Action5 extra airport sprites [FS#6664] (r27976)
|
||||
- Feature: Draw vertical separators at tile distance in the train depot GUI (r27938, r27899)
|
||||
- Feature: [Build] MSVC 2017 project file generator. Most noticeable, std:c++latest is enabled (r27920, r27919, r27918, r27917)
|
||||
- Feature: [Build] Project file generator for kdevelop 4/5 [FS#6577] (r27897)
|
||||
- Feature: Add option to close windows with right click [FS#4950] (r27826, r27825)
|
||||
- Feature: Vehicle Group Info: Add profits and occupancy display to group vehicle list (r27822)
|
||||
- Feature: Display aircraft type in vehicle preview/purchase/detail windows (r27802, r27799, r27797)
|
||||
- Change: [NewGRF] Various performance improvements to resolving VA2 (r27989, r27985, r27984, r27983, r27982)
|
||||
- Change: [NewGRF] Increase maximum allowed vehicle sprite size to reduce clipping of ships (r27987)
|
||||
- Change: Check companies for bankruptcy before subtracting reoccuring monthly costs [FS#6679] (r27981)
|
||||
- Change: [GFX] Replace the office building sprite on various toyland airports with a better fitting sprite [FS#6664] (r27977)
|
||||
- Change: [GFX] The switch-toolbar icon contained pixels from the fire cycle. Replace the whole icon with a new version [FS#6654] (r27961)
|
||||
- Change: Reword texts in industry view, when stockpiling is used (r27952)
|
||||
- Change: Remove the gap between windows when positioning them after opening [FS#6568] (r27934, r27900)
|
||||
- Change: [Build] Enable usage of static_assert for MSVC (r27916)
|
||||
- Change: [Build] Preserve PKG_CONFIG_PATH and PKG_CONFIG_LIBDIR environment variables in config.cache file [FS#6614] (r27902)
|
||||
- Change: Do not cancel headquarter construction and engine-preview-query when shift-clicking (r27889)
|
||||
- Change: Parse extmidi command string for parameters to pass on (r27834)
|
||||
- Change: Draw images in centre of buttons (r27829, r27821)
|
||||
- Fix: Store the map variety setting in the savegame like the other mapgen settings, so restarting maps considers it [FS#6673] (r27978)
|
||||
- Fix: Hair selection was missing one option [FS#6642] (r27975)
|
||||
- Fix: Avoid tile operations outside map border when building lock [FS#6662] (r27973)
|
||||
- Fix: Catenary sprites got mixed up for depots [FS#6670] (r27972)
|
||||
- Fix: Make automatic window-positioning RTL-aware (r27934, r27900)
|
||||
- Fix: Automatic window-positioning now uses GUI-scale/style dependent sizes/distances instead of fixed pixel values (r27934, r27900)
|
||||
- Fix: [NewGRF] While executing random triggers, var 5F should include the new triggers (r27928)
|
||||
- Fix: [NewGRF] Reset used random triggers only after all A123 chains have been resolved, so that all RA2 in all chains can test the shared triggers (r27928)
|
||||
- Fix: [NewGRF] Industry random triggers are stored per tile, even when randomising the shared random bits of the parent industry (r27928)
|
||||
- Fix: [NPF] Reserved track bits were not accounted for when trying to find any safe position (r27912)
|
||||
- Fix: Do not modify argv[0] [FS#6575] (r27886)
|
||||
- Fix: Do not search directories when opening ini files as we already have their full path [FS#6421] (r27816)
|
||||
- Fix: Road tunnel/bridge heads have no trackbits wrt. catenary drawing (r27812)
|
||||
|
||||
|
||||
1.7.2 (2017-12-24)
|
||||
------------------------------------------------------------------------
|
||||
(None)
|
||||
|
10
config.lib
10
config.lib
@@ -2987,6 +2987,11 @@ detect_library() {
|
||||
eval "res=\$$2"
|
||||
if [ -z "$res" ]; then
|
||||
log 2 " trying /mingw/include/$4$5... no"
|
||||
eval "$2=`ls -1 /mingw$cpu_type/include/$4*.h 2>/dev/null | egrep \"\/$5\$\"`"
|
||||
fi
|
||||
eval "res=\$$2"
|
||||
if [ -z "$res" ]; then
|
||||
log 2 " trying /mingw$cpu_type/include/$4$5... no"
|
||||
eval "$2=`ls -1 /opt/local/include/$4*.h 2>/dev/null | egrep \"\/$5\$\"`"
|
||||
fi
|
||||
eval "res=\$$2"
|
||||
@@ -3035,6 +3040,11 @@ detect_library() {
|
||||
eval "res=\$$2"
|
||||
if [ -z "$res" ]; then
|
||||
log 2 " trying /mingw/lib/$3... no"
|
||||
eval "$2=`ls /mingw$cpu_type/lib/*.a 2>/dev/null | egrep \"\/$3\$\"`"
|
||||
fi
|
||||
eval "res=\$$2"
|
||||
if [ -z "$res" ]; then
|
||||
log 2 " trying /mingw$cpu_type/lib/$3... no"
|
||||
log 1 "configure: error: $2 couldn't be found"
|
||||
log 1 "configure: error: you requested a static link, but I can't find $3"
|
||||
|
||||
|
@@ -90,5 +90,17 @@ GM_TT19.GM = Funk Central
|
||||
GM_TT20.GM = Jammit
|
||||
GM_TT21.GM = Movin' On
|
||||
|
||||
; MIDI timecodes where the playback should attemp to start and stop short.
|
||||
; This is to allow fixing undesired silences in original MIDI files.
|
||||
; However not all music drivers may support this.
|
||||
[timingtrim]
|
||||
; Theme has two beats silence at the beginning which prevents clean looping.
|
||||
GM_TT00.GM = 768:53760
|
||||
; Can't Get There From Here from the Windows version has a long silence at the end,
|
||||
; followed by a solo repeat. This isn't in the original DOS version music and is likely
|
||||
; unintentional from the people who converted the music from the DOS version.
|
||||
; Actual song ends after measure 152.
|
||||
GM_TT10.GM = 0:235008
|
||||
|
||||
[origin]
|
||||
default = You can find it on your Transport Tycoon Deluxe CD-ROM.
|
||||
|
2
src/3rdparty/squirrel/squirrel/sqdebug.cpp
vendored
2
src/3rdparty/squirrel/squirrel/sqdebug.cpp
vendored
@@ -115,7 +115,7 @@ void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger ty
|
||||
SQInteger found = 0;
|
||||
for(SQInteger i=0; i<16; i++)
|
||||
{
|
||||
SQInteger mask = 0x00000001 << i;
|
||||
SQInteger mask = 0x00000001LL << i;
|
||||
if(typemask & (mask)) {
|
||||
if(found>0) StringCat(exptypes,SQString::Create(_ss(this), "|", -1), exptypes);
|
||||
found ++;
|
||||
|
@@ -26,6 +26,7 @@ struct ContentInfo;
|
||||
struct MD5File {
|
||||
/** The result of a checksum check */
|
||||
enum ChecksumResult {
|
||||
CR_UNKNOWN, ///< The file has not been checked yet
|
||||
CR_MATCH, ///< The file did exist and the md5 checksum did match
|
||||
CR_MISMATCH, ///< The file did exist, just the md5 checksum did not match
|
||||
CR_NO_FILE, ///< The file did not exist
|
||||
@@ -34,6 +35,7 @@ struct MD5File {
|
||||
const char *filename; ///< filename
|
||||
uint8 hash[16]; ///< md5 sum of the file
|
||||
const char *missing_warning; ///< warning when this file is missing
|
||||
ChecksumResult check_result; ///< cached result of md5 check
|
||||
|
||||
ChecksumResult CheckMD5(Subdirectory subdir, size_t max_size) const;
|
||||
};
|
||||
@@ -301,6 +303,9 @@ struct MusicSongInfo {
|
||||
const char *filename; ///< file on disk containing song (when used in MusicSet class, this pointer is owned by MD5File object for the file)
|
||||
MusicTrackType filetype; ///< decoder required for song file
|
||||
int cat_index; ///< entry index in CAT file, for filetype==MTT_MPSMIDI
|
||||
bool loop; ///< song should play in a tight loop if possible, never ending
|
||||
int override_start; ///< MIDI ticks to skip over in beginning
|
||||
int override_end; ///< MIDI tick to end the song at (0 if no override)
|
||||
};
|
||||
|
||||
/** All data of a music set. */
|
||||
|
@@ -129,7 +129,11 @@ bool BaseSet<T, Tnum_files, Tsearch_in_tars>::FillSetDetails(IniFile *ini, const
|
||||
file->missing_warning = stredup(item->value);
|
||||
}
|
||||
|
||||
switch (T::CheckMD5(file, BASESET_DIR)) {
|
||||
file->check_result = T::CheckMD5(file, BASESET_DIR);
|
||||
switch (file->check_result) {
|
||||
case MD5File::CR_UNKNOWN:
|
||||
break;
|
||||
|
||||
case MD5File::CR_MATCH:
|
||||
this->valid_files++;
|
||||
this->found_files++;
|
||||
|
@@ -207,6 +207,7 @@ void UpdateFontHeightCache()
|
||||
class FreeTypeFontCache : public FontCache {
|
||||
private:
|
||||
FT_Face face; ///< The font face associated with this font.
|
||||
int req_size; ///< Requested font size.
|
||||
|
||||
typedef SmallMap<uint32, SmallPair<size_t, const void*> > FontTable; ///< Table with font table cache
|
||||
FontTable font_tables; ///< Cached font tables.
|
||||
@@ -235,6 +236,7 @@ private:
|
||||
|
||||
GlyphEntry *GetGlyphPtr(GlyphID key);
|
||||
void SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool duplicate = false);
|
||||
void SetFontSize(FontSize fs, FT_Face face, int pixels);
|
||||
|
||||
public:
|
||||
FreeTypeFontCache(FontSize fs, FT_Face face, int pixels);
|
||||
@@ -271,20 +273,26 @@ static const byte SHADOW_COLOUR = 2;
|
||||
* @param face The font that has to be loaded.
|
||||
* @param pixels The number of pixels this font should be high.
|
||||
*/
|
||||
FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : FontCache(fs), face(face), glyph_to_sprite(NULL)
|
||||
FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : FontCache(fs), face(face), req_size(pixels), glyph_to_sprite(NULL)
|
||||
{
|
||||
assert(face != NULL);
|
||||
|
||||
this->SetFontSize(fs, face, pixels);
|
||||
}
|
||||
|
||||
void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels)
|
||||
{
|
||||
if (pixels == 0) {
|
||||
/* Try to determine a good height based on the minimal height recommended by the font. */
|
||||
pixels = _default_font_height[this->fs];
|
||||
int scaled_height = ScaleGUITrad(_default_font_height[this->fs]);
|
||||
pixels = scaled_height;
|
||||
|
||||
TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head);
|
||||
if (head != NULL) {
|
||||
/* Font height is minimum height plus the difference between the default
|
||||
* height for this font size and the small size. */
|
||||
int diff = _default_font_height[this->fs] - _default_font_height[FS_SMALL];
|
||||
pixels = Clamp(min(head->Lowest_Rec_PPEM, 20) + diff, _default_font_height[this->fs], MAX_FONT_SIZE);
|
||||
int diff = scaled_height - ScaleGUITrad(_default_font_height[FS_SMALL]);
|
||||
pixels = Clamp(min(head->Lowest_Rec_PPEM, 20) + diff, scaled_height, MAX_FONT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +409,7 @@ found_face:
|
||||
FreeTypeFontCache::~FreeTypeFontCache()
|
||||
{
|
||||
FT_Done_Face(this->face);
|
||||
this->face = NULL;
|
||||
this->ClearFontCache();
|
||||
|
||||
for (FontTable::iterator iter = this->font_tables.Begin(); iter != this->font_tables.End(); iter++) {
|
||||
@@ -430,6 +439,9 @@ void FreeTypeFontCache::ClearFontCache()
|
||||
this->glyph_to_sprite = NULL;
|
||||
|
||||
Layouter::ResetFontCache(this->fs);
|
||||
|
||||
/* GUI scaling might have changed, determine font size anew if it was automatically selected. */
|
||||
if (this->face != NULL && this->req_size == 0) this->SetFontSize(this->fs, this->face, this->req_size);
|
||||
}
|
||||
|
||||
FreeTypeFontCache::GlyphEntry *FreeTypeFontCache::GetGlyphPtr(GlyphID key)
|
||||
|
@@ -314,7 +314,7 @@ static DropDownList *BuildMapsizeDropDown(int other_dimension)
|
||||
|
||||
for (uint i = MIN_MAP_SIZE_BITS; i <= MAX_MAP_SIZE_BITS; i++) {
|
||||
DropDownListParamStringItem *item = new DropDownListParamStringItem((i + other_dimension > MAX_MAP_TILES_BITS) ? STR_RED_INT : STR_JUST_INT, i, false);
|
||||
item->SetParam(0, 1 << i);
|
||||
item->SetParam(0, 1LL << i);
|
||||
*list->Append() = item;
|
||||
}
|
||||
|
||||
@@ -372,8 +372,8 @@ struct GenerateLandscapeWindow : public Window {
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_GL_START_DATE_TEXT: SetDParam(0, ConvertYMDToDate(_settings_newgame.game_creation.starting_year, 0, 1)); break;
|
||||
case WID_GL_MAPSIZE_X_PULLDOWN: SetDParam(0, 1 << _settings_newgame.game_creation.map_x); break;
|
||||
case WID_GL_MAPSIZE_Y_PULLDOWN: SetDParam(0, 1 << _settings_newgame.game_creation.map_y); break;
|
||||
case WID_GL_MAPSIZE_X_PULLDOWN: SetDParam(0, 1LL << _settings_newgame.game_creation.map_x); break;
|
||||
case WID_GL_MAPSIZE_Y_PULLDOWN: SetDParam(0, 1LL << _settings_newgame.game_creation.map_y); break;
|
||||
case WID_GL_MAX_HEIGHTLEVEL_TEXT: SetDParam(0, _settings_newgame.construction.max_heightlevel); break;
|
||||
case WID_GL_SNOW_LEVEL_TEXT: SetDParam(0, _settings_newgame.game_creation.snow_line_height); break;
|
||||
|
||||
@@ -948,11 +948,11 @@ struct CreateScenarioWindow : public Window
|
||||
break;
|
||||
|
||||
case WID_CS_MAPSIZE_X_PULLDOWN:
|
||||
SetDParam(0, 1 << _settings_newgame.game_creation.map_x);
|
||||
SetDParam(0, 1LL << _settings_newgame.game_creation.map_x);
|
||||
break;
|
||||
|
||||
case WID_CS_MAPSIZE_Y_PULLDOWN:
|
||||
SetDParam(0, 1 << _settings_newgame.game_creation.map_y);
|
||||
SetDParam(0, 1LL << _settings_newgame.game_creation.map_y);
|
||||
break;
|
||||
|
||||
case WID_CS_FLAT_LAND_HEIGHT_TEXT:
|
||||
|
@@ -1111,6 +1111,7 @@ void DoPaletteAnimations()
|
||||
/**
|
||||
* Determine a contrasty text colour for a coloured background.
|
||||
* @param background Background colour.
|
||||
* @param threshold Background colour brightness threshold below which the background is considered dark and TC_WHITE is returned, range: 0 - 255, default 128.
|
||||
* @return TC_BLACK or TC_WHITE depending on what gives a better contrast.
|
||||
*/
|
||||
TextColour GetContrastColour(uint8 background, uint8 threshold)
|
||||
@@ -1120,7 +1121,7 @@ TextColour GetContrastColour(uint8 background, uint8 threshold)
|
||||
* The following formula computes 1000 * brightness^2, with brightness being in range 0 to 255. */
|
||||
uint sq1000_brightness = c.r * c.r * 299 + c.g * c.g * 587 + c.b * c.b * 114;
|
||||
/* Compare with threshold brightness which defaults to 128 (50%) */
|
||||
return sq1000_brightness < threshold * 128 * 1000 ? TC_WHITE : TC_BLACK;
|
||||
return sq1000_brightness < ((uint) threshold) * ((uint) threshold) * 1000 ? TC_WHITE : TC_BLACK;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -597,6 +597,7 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str,
|
||||
Font *f = Layouter::GetFont(state.fontsize, state.cur_colour);
|
||||
|
||||
line.buffer = buff_begin;
|
||||
fontMapping.Clear();
|
||||
|
||||
/*
|
||||
* Go through the whole string while adding Font instances to the font map
|
||||
|
@@ -3074,6 +3074,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Unesi im
|
||||
STR_TOWN_DIRECTORY_CAPTION :{WHITE}Gradovi
|
||||
STR_TOWN_DIRECTORY_NONE :{ORANGE}- Ništa -
|
||||
STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA})
|
||||
STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Grad){BLACK} ({COMMA})
|
||||
STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Imena gradova - klikni na ime kako bi centrirao pogled na grad. Ctrl+klik otvara novi prozor sa lokacijom grada
|
||||
STR_TOWN_POPULATION :{BLACK}Svjetsko stanovništvo: {COMMA}
|
||||
|
||||
@@ -3081,6 +3082,7 @@ STR_TOWN_POPULATION :{BLACK}Svjetsko
|
||||
STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN}
|
||||
STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Metropola)
|
||||
STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Stanovništvo: {ORANGE}{COMMA}{BLACK} Kuće: {ORANGE}{COMMA}
|
||||
STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} prošli mjesec: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA}
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Potrebno tereta za rast grada:
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} potrebno
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} potrebno zimi
|
||||
@@ -4329,6 +4331,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ovo
|
||||
STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... cesta je orijentirana u krivom smjeru
|
||||
STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... prolazne postaje ne mogu imati zavoje
|
||||
STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... prolazne postaje ne mogu imati raskrižja
|
||||
STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... cesta je jednosmjerna ili je blokirana
|
||||
|
||||
# Station destruction related errors
|
||||
STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Nije moguće ukloniti dio postaje...
|
||||
@@ -4580,6 +4583,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Originalni zvuk
|
||||
STR_BASESOUNDS_WIN_DESCRIPTION :Originalni zvukovi za Transport Tycoon Deluxe Windows izdanje.
|
||||
STR_BASESOUNDS_NONE_DESCRIPTION :Zvučni paket bez ikakvih zvukova.
|
||||
STR_BASEMUSIC_WIN_DESCRIPTION :Originalna glazba za Transport Tycoon Deluxe Windows izdanje.
|
||||
STR_BASEMUSIC_DOS_DESCRIPTION :Originalna glazba za Transport Tycoon Deluxe DOS izdanje.
|
||||
STR_BASEMUSIC_TTO_DESCRIPTION :Originalna glazba za Transport Tycoon (original/editor svijeta) DOS izdanje.
|
||||
STR_BASEMUSIC_NONE_DESCRIPTION :Glazbeni paket bez ikakve glazbe.
|
||||
|
||||
##id 0x2000
|
||||
|
@@ -889,10 +889,10 @@ STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLAC
|
||||
|
||||
# Extra view window
|
||||
STR_EXTRA_VIEW_PORT_TITLE :{WHITE}Viewport {COMMA}
|
||||
STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Copy to viewport
|
||||
STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Change viewport
|
||||
STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT :{BLACK}Copy the location of the main view to this viewport
|
||||
STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Paste from viewport
|
||||
STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Paste the location of this viewport to the main view
|
||||
STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Change main view
|
||||
STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Copy the location of this viewport to the main view
|
||||
|
||||
# Game options window
|
||||
STR_GAME_OPTIONS_CAPTION :{WHITE}Game Options
|
||||
@@ -3465,6 +3465,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Enter a
|
||||
STR_TOWN_DIRECTORY_CAPTION :{WHITE}Towns
|
||||
STR_TOWN_DIRECTORY_NONE :{ORANGE}- None -
|
||||
STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA})
|
||||
STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (City){BLACK} ({COMMA})
|
||||
STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Town names - click on name to centre main view on town. Ctrl+Click opens a new viewport on town location
|
||||
STR_TOWN_POPULATION :{BLACK}World population: {COMMA}
|
||||
|
||||
|
@@ -2979,6 +2979,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Entrer u
|
||||
STR_TOWN_DIRECTORY_CAPTION :{WHITE}Villes
|
||||
STR_TOWN_DIRECTORY_NONE :{ORANGE}− Aucune −
|
||||
STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA})
|
||||
STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Métropole){BLACK} ({COMMA})
|
||||
STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Noms des villes - Cliquer sur un nom pour centrer la vue principale sur la ville. Ctrl-clic pour ouvrir une nouvelle vue sur la ville.
|
||||
STR_TOWN_POPULATION :{BLACK}Population mondiale{NBSP}: {COMMA}
|
||||
|
||||
@@ -4235,6 +4236,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... cett
|
||||
STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... mauvaise orientation de la route
|
||||
STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... les arrêts ne peuvent pas avoir de virages
|
||||
STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... les arrêts ne peuvent pas avoir de jonctions
|
||||
STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... la route est à sens unique ou bloquée
|
||||
|
||||
# Station destruction related errors
|
||||
STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Impossible de supprimer une partie de la gare...
|
||||
@@ -4486,6 +4488,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Sons originaux
|
||||
STR_BASESOUNDS_WIN_DESCRIPTION :Sons originaux de Transport Tycoon Deluxe (version Windows).
|
||||
STR_BASESOUNDS_NONE_DESCRIPTION :Un pack de sons sans sons.
|
||||
STR_BASEMUSIC_WIN_DESCRIPTION :Musiques originales de Transport Tycoon Deluxe (version Windows).
|
||||
STR_BASEMUSIC_DOS_DESCRIPTION :Musiques originales de Transport Tycoon Deluxe (version DOS).
|
||||
STR_BASEMUSIC_TTO_DESCRIPTION :Musiques originales de Transport Tycoon (version Originale/World Editor).
|
||||
STR_BASEMUSIC_NONE_DESCRIPTION :Un pack de musiques sans musiques.
|
||||
|
||||
##id 0x2000
|
||||
|
@@ -3090,6 +3090,7 @@ STR_TOWN_POPULATION :{BLACK}Παγκ
|
||||
STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN}
|
||||
STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Πόλη)
|
||||
STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Πληθυσμός: {ORANGE}{COMMA}{BLACK} Σπίτια: {ORANGE}{COMMA}
|
||||
STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} προηγούμενος μήνας: {ORANGE}{COMMA}{BLACK} μέγιστο: {ORANGE}{COMMA}
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Εμπορεύματα που χρειάζονται για την επέκταση της πόλης:
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} απαιτείται
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} απαιτείται τον χειμώνα
|
||||
@@ -4344,6 +4345,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... αυ
|
||||
STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... ο δρόμος βλέπει σε λάθος κατεύθυνση
|
||||
STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... οι μη τερματικοί σταθμοί δε μπορούν να έχουν στροφές
|
||||
STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... οι μη τερματικοί σταθμοί δε μπορούν να έχουν διασταυρώσεις
|
||||
STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... ο δρόμος είναι μονόδρομος η μπλοκαρισμένος
|
||||
|
||||
# Station destruction related errors
|
||||
STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Δεν μπορεί να αφαιρεθεί μέρος του σταθμού...
|
||||
@@ -4595,6 +4597,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Αρχικοί
|
||||
STR_BASESOUNDS_WIN_DESCRIPTION :Αρχικοί ήχοι από το Transport Tycoon Deluxe έκδοση Windows.
|
||||
STR_BASESOUNDS_NONE_DESCRIPTION :Ένα πάκετο ήχων χώρις ήχους.
|
||||
STR_BASEMUSIC_WIN_DESCRIPTION :Αρχική μουσική από το Transport Tycoon Deluxe έκδοση Windows.
|
||||
STR_BASEMUSIC_DOS_DESCRIPTION :Αρχική μουσική από το Transport Tycoon Deluxe έκδοση DOS.
|
||||
STR_BASEMUSIC_TTO_DESCRIPTION :Αρχική μουσική από το Transport Tycoon (Αρχικός Επεξεργαστής Κόσμου) έκδοση DOS.
|
||||
STR_BASEMUSIC_NONE_DESCRIPTION :Ένα πάκετο μουσικής χωρίς πραγματική μουσική.
|
||||
|
||||
##id 0x2000
|
||||
|
@@ -3008,6 +3008,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Inserire
|
||||
STR_TOWN_DIRECTORY_CAPTION :{WHITE}Città
|
||||
STR_TOWN_DIRECTORY_NONE :{ORANGE}- Nessuna -
|
||||
STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA})
|
||||
STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Metropoli){BLACK} ({COMMA})
|
||||
STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Nomi delle città - fare clic su un nome per centrare la visuale principale sulla città. CTRL+clic la mostra in una mini visuale.
|
||||
STR_TOWN_POPULATION :{BLACK}Popolazione mondiale: {COMMA}
|
||||
|
||||
|
@@ -1400,7 +1400,7 @@ STR_CONFIG_SETTING_LIVERIES_ALL :모든 회사
|
||||
STR_CONFIG_SETTING_PREFER_TEAMCHAT :엔터(ENTER) 키로 같은 팀끼리 채팅: {STRING}
|
||||
STR_CONFIG_SETTING_PREFER_TEAMCHAT_HELPTEXT :이 설정을 켜면, 멀티 플레이시 같은 회사 간의 채팅을 <CTRL+ENTER> 키 대신 <ENTER> 키로 할 수 있게 됩니다.
|
||||
STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING :마우스 휠 동작: {STRING}
|
||||
STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING_HELPTEXT :상하좌우로 회전 가능한 마우스 휠(2차원 마우스휠)로 지도를 스크롤할 수 있게 허용합니다.
|
||||
STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING_HELPTEXT :상하좌우로 회전 가능한 마우스 휠(2차원 마우스 휠)로 지도를 스크롤할 수 있게 허용합니다.
|
||||
STR_CONFIG_SETTING_SCROLLWHEEL_ZOOM :화면 확대/축소
|
||||
STR_CONFIG_SETTING_SCROLLWHEEL_SCROLL :지도 스크롤
|
||||
STR_CONFIG_SETTING_SCROLLWHEEL_OFF :끄기
|
||||
|
@@ -3360,6 +3360,7 @@ STR_TOWN_POPULATION :{BLACK}Populacj
|
||||
STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN}
|
||||
STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Miasto)
|
||||
STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Populacja: {ORANGE}{COMMA}{BLACK} Domów: {ORANGE}{COMMA}
|
||||
STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} w ostatnim miesiącu: {ORANGE}{COMMA}{BLACK} najwięcej: {ORANGE}{COMMA}
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Towar potrzebny do rozwoju miasta:
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{RED}Wymagana {ORANGE}{STRING}
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} wymagane zimą
|
||||
@@ -4614,6 +4615,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ta d
|
||||
STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... droga jest zorientowana w złym kierunku
|
||||
STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... przystanki przelotowe nie mogą mieć zakrętów
|
||||
STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... przystanki przelotowe nie mogą mieć skrzyżowań
|
||||
STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... droga jest jednokierunkowa lub zablokowana
|
||||
|
||||
# Station destruction related errors
|
||||
STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Nie można usunąć części stacji...
|
||||
|
@@ -3171,6 +3171,7 @@ STR_TOWN_POPULATION :{BLACK}Насе
|
||||
STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN}
|
||||
STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Мегаполис)
|
||||
STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Население: {ORANGE}{COMMA}{BLACK} Зданий: {ORANGE}{COMMA}
|
||||
STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} в прошлом месяце: {ORANGE}{COMMA}{BLACK} Макс.: {ORANGE}{COMMA}
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Груз, необходимый для роста города:
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} требу{G 0 е е е ю}тся
|
||||
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} требу{G 0 е е е ю}тся зимой
|
||||
@@ -4442,6 +4443,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... эт
|
||||
STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... неверное направление дороги
|
||||
STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... на проходных остановках нельзя делать повороты
|
||||
STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... на проходных остановках нельзя делать перекрёстки
|
||||
STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... дорога односторонняя или заблокирована
|
||||
|
||||
# Station destruction related errors
|
||||
STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Невозможно удалить часть станции...
|
||||
@@ -4510,7 +4512,7 @@ STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION :{WHITE}Недо
|
||||
STR_ERROR_MUST_REMOVE_SIGNALS_FIRST :{WHITE}Сначала удалите сигналы
|
||||
STR_ERROR_NO_SUITABLE_RAILROAD_TRACK :{WHITE}Нет подходящих рельсов
|
||||
STR_ERROR_MUST_REMOVE_RAILROAD_TRACK :{WHITE}Сначала удалите рельсы
|
||||
STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}Дорога односторонняя или блокирована
|
||||
STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}Дорога односторонняя или заблокирована
|
||||
STR_ERROR_CROSSING_DISALLOWED :{WHITE}Через этот вид рельсов запрещено строить переезды
|
||||
STR_ERROR_CAN_T_BUILD_SIGNALS_HERE :{WHITE}Здесь невозможно поставить сигнал...
|
||||
STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK :{WHITE}Здесь невозможно проложить рельсы...
|
||||
@@ -4686,13 +4688,15 @@ STR_ERROR_CAN_T_DELETE_SIGN :{WHITE}Не у
|
||||
STR_DESKTOP_SHORTCUT_COMMENT :Экономический симулятор на основе игры «Transport Tycoon Deluxe»
|
||||
|
||||
# Translatable descriptions in media/baseset/*.ob* files
|
||||
STR_BASEGRAPHICS_DOS_DESCRIPTION :Оригинальная графика из Transport Tycoon Deluxe для DOS.
|
||||
STR_BASEGRAPHICS_DOS_DE_DESCRIPTION :Оригинальная графика из немецкой версии Transport Tycoon Deluxe для DOS.
|
||||
STR_BASEGRAPHICS_WIN_DESCRIPTION :Оригинальная графика из Transport Tycoon Deluxe для Windows.
|
||||
STR_BASESOUNDS_DOS_DESCRIPTION :Оригинальный набор звукового оформления из игры Transport Tycoon Deluxe для DOS.
|
||||
STR_BASESOUNDS_WIN_DESCRIPTION :Оригинальный набор звукового оформления из игры Transport Tycoon Deluxe для Windows.
|
||||
STR_BASEGRAPHICS_DOS_DESCRIPTION :Графика из Transport Tycoon Deluxe для DOS.
|
||||
STR_BASEGRAPHICS_DOS_DE_DESCRIPTION :Графика из немецкой версии Transport Tycoon Deluxe для DOS.
|
||||
STR_BASEGRAPHICS_WIN_DESCRIPTION :Графика из Transport Tycoon Deluxe для Windows.
|
||||
STR_BASESOUNDS_DOS_DESCRIPTION :Набор звукового оформления из игры Transport Tycoon Deluxe для DOS.
|
||||
STR_BASESOUNDS_WIN_DESCRIPTION :Набор звукового оформления из игры Transport Tycoon Deluxe для Windows.
|
||||
STR_BASESOUNDS_NONE_DESCRIPTION :"Пустой" набор звукового оформления, не содержащий никаких звуков.
|
||||
STR_BASEMUSIC_WIN_DESCRIPTION :Оригинальный набор музыкального оформления из игры Transport Tycoon Deluxe для Windows.
|
||||
STR_BASEMUSIC_WIN_DESCRIPTION :Набор музыкального оформления из игры Transport Tycoon Deluxe для Windows.
|
||||
STR_BASEMUSIC_DOS_DESCRIPTION :Набор музыкального оформления из игры Transport Tycoon Deluxe для DOS.
|
||||
STR_BASEMUSIC_TTO_DESCRIPTION :Набор музыкального оформления из игры Transport Tycoon Deluxe для DOS.
|
||||
STR_BASEMUSIC_NONE_DESCRIPTION :"Пустой" набор музыкального оформления, не содержащий никакой музыки.
|
||||
|
||||
##id 0x2000
|
||||
|
@@ -674,7 +674,9 @@ STR_PLAYLIST_TRACK_NAME :{TINY_FONT}{LTB
|
||||
STR_PLAYLIST_TRACK_INDEX :{TINY_FONT}{BLACK}全部音轨列表
|
||||
STR_PLAYLIST_PROGRAM :{TINY_FONT}{BLACK}当前选用'{STRING}'列表
|
||||
STR_PLAYLIST_CLEAR :{TINY_FONT}{BLACK}清除
|
||||
STR_PLAYLIST_CHANGE_SET :更改设置
|
||||
STR_PLAYLIST_TOOLTIP_CLEAR_CURRENT_PROGRAM_CUSTOM1 :{BLACK}清除当前列表中曲目{}(仅限自定义1或自定义2)
|
||||
STR_PLAYLIST_TOOLTIP_CHANGE_SET :{BLACK}选择另一种已安装的音乐
|
||||
STR_PLAYLIST_TOOLTIP_CLICK_TO_ADD_TRACK :{BLACK}点击音乐曲目以加入当前播放列表{}(仅限自定义1或自定义2)
|
||||
STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK :{BLACK}点击音乐曲目以从当前播放列表中删除{}(仅限自定义1或自定义2)
|
||||
|
||||
@@ -1334,6 +1336,7 @@ STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_HELPTEXT :设置缩略地
|
||||
STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_GREEN :绿色
|
||||
STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_DARK_GREEN :深绿色
|
||||
STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_VIOLET :紫色
|
||||
STR_CONFIG_SETTING_SCROLLMODE_RMB :鼠标右键移动地图
|
||||
STR_CONFIG_SETTING_SMOOTH_SCROLLING :平滑视角滚动: {STRING}
|
||||
STR_CONFIG_SETTING_SMOOTH_SCROLLING_HELPTEXT :设置在缩略图上点击或者发出转到特定目标的命令时主视角的转换方式,如果“打开”本选项,视角平缓滚动,“关闭”时直接跳转到目标位置
|
||||
STR_CONFIG_SETTING_MEASURE_TOOLTIP :建设时显示测量数据:{STRING}
|
||||
@@ -3647,6 +3650,7 @@ STR_VEHICLE_INFO_AGE :{COMMA} 年 ({C
|
||||
STR_VEHICLE_INFO_AGE_RED :{RED}{COMMA} 年 ({COMMA})
|
||||
|
||||
STR_VEHICLE_INFO_MAX_SPEED :{BLACK}最大速度:{LTBLUE}{VELOCITY}
|
||||
STR_VEHICLE_INFO_MAX_SPEED_TYPE :{BLACK}最高速度: {LTBLUE}{VELOCITY} {BLACK}飞机种类: {LTBLUE}{STRING}
|
||||
STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED :{BLACK}重量:{LTBLUE}{WEIGHT_SHORT} {BLACK}功率:{LTBLUE}{POWER}{BLACK} 最大速度:{LTBLUE}{VELOCITY}
|
||||
STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :{BLACK}重量:{LTBLUE}{WEIGHT_SHORT} {BLACK}功率:{LTBLUE}{POWER}{BLACK} 最大速度:{LTBLUE}{VELOCITY} {BLACK}最大牵引力:{LTBLUE}{FORCE}
|
||||
|
||||
@@ -4464,6 +4468,7 @@ STR_BASESOUNDS_DOS_DESCRIPTION :运输大亨DOS
|
||||
STR_BASESOUNDS_WIN_DESCRIPTION :Transport Tycoon Deluxe Windows (运输大亨Windows豪华版)的原版音效包.
|
||||
STR_BASESOUNDS_NONE_DESCRIPTION :一个空的音效包.
|
||||
STR_BASEMUSIC_WIN_DESCRIPTION :Transport Tycoon Deluxe(运输大亨Windows豪华版)的原版音乐包
|
||||
STR_BASEMUSIC_DOS_DESCRIPTION :运输大亨DOS豪华版原版音乐。
|
||||
STR_BASEMUSIC_NONE_DESCRIPTION :一个没有实际内容的音乐包.
|
||||
|
||||
##id 0x2000
|
||||
|
@@ -475,7 +475,7 @@ void LinkGraphOverlay::SetCompanyMask(uint32 company_mask)
|
||||
/** Make a number of rows with buttons for each company for the linkgraph legend window. */
|
||||
NWidgetBase *MakeCompanyButtonRowsLinkGraphGUI(int *biggest_index)
|
||||
{
|
||||
return MakeCompanyButtonRows(biggest_index, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST, 3, 0);
|
||||
return MakeCompanyButtonRows(biggest_index, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST, 3, STR_NULL);
|
||||
}
|
||||
|
||||
NWidgetBase *MakeSaturationLegendLinkGraphGUI(int *biggest_index)
|
||||
@@ -652,30 +652,46 @@ void LinkGraphLegendWindow::DrawWidget(const Rect &r, int widget) const
|
||||
if (this->IsWidgetDisabled(widget)) return;
|
||||
CargoSpec *cargo = CargoSpec::Get(widget - WID_LGL_CARGO_FIRST);
|
||||
GfxFillRect(r.left + 2, r.top + 2, r.right - 2, r.bottom - 2, cargo->legend_colour);
|
||||
DrawString(r.left, r.right, (r.top + r.bottom + 1 - FONT_HEIGHT_SMALL) / 2, cargo->abbrev, GetContrastColour(cargo->legend_colour, 42), SA_HOR_CENTER);
|
||||
DrawString(r.left, r.right, (r.top + r.bottom + 1 - FONT_HEIGHT_SMALL) / 2, cargo->abbrev, GetContrastColour(cargo->legend_colour, 73), SA_HOR_CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
bool LinkGraphLegendWindow::OnHoverCommon(Point pt, int widget, TooltipCloseCondition close_cond)
|
||||
{
|
||||
if (IsInsideMM(widget, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST + 1)) {
|
||||
if (this->IsWidgetDisabled(widget)) {
|
||||
GuiShowTooltips(this, STR_LINKGRAPH_LEGEND_SELECT_COMPANIES, 0, NULL, close_cond);
|
||||
} else {
|
||||
uint64 params[2];
|
||||
CompanyID cid = (CompanyID)(widget - WID_LGL_COMPANY_FIRST);
|
||||
params[0] = STR_LINKGRAPH_LEGEND_SELECT_COMPANIES;
|
||||
params[1] = cid;
|
||||
GuiShowTooltips(this, STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP, 2, params, close_cond);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) {
|
||||
if (this->IsWidgetDisabled(widget)) return false;
|
||||
CargoSpec *cargo = CargoSpec::Get(widget - WID_LGL_CARGO_FIRST);
|
||||
uint64 params[1];
|
||||
params[0] = cargo->name;
|
||||
GuiShowTooltips(this, STR_BLACK_STRING, 1, params, close_cond);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LinkGraphLegendWindow::OnHover(Point pt, int widget)
|
||||
{
|
||||
if (IsInsideMM(widget, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST + 1)) {
|
||||
uint64 params[5];
|
||||
if (this->IsWidgetDisabled(widget)) {
|
||||
GuiShowTooltips(this, STR_LINKGRAPH_LEGEND_SELECT_COMPANIES, 0, params, TCC_HOVER);
|
||||
} else {
|
||||
CompanyID cid = (CompanyID)(widget - WID_LGL_COMPANY_FIRST);
|
||||
params[0] = STR_LINKGRAPH_LEGEND_SELECT_COMPANIES;
|
||||
params[1] = cid;
|
||||
GuiShowTooltips(this, STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP, 2, params, TCC_HOVER);
|
||||
}
|
||||
}
|
||||
if (IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) {
|
||||
if (this->IsWidgetDisabled(widget)) return;
|
||||
CargoSpec *cargo = CargoSpec::Get(widget - WID_LGL_CARGO_FIRST);
|
||||
uint64 params[5];
|
||||
params[0] = cargo->name;
|
||||
GuiShowTooltips(this, STR_BLACK_STRING, 1, params, TCC_HOVER);
|
||||
this->OnHoverCommon(pt, widget, TCC_HOVER);
|
||||
}
|
||||
|
||||
bool LinkGraphLegendWindow::OnRightClick(Point pt, int widget)
|
||||
{
|
||||
if (_settings_client.gui.hover_delay_ms == 0) {
|
||||
return this->OnHoverCommon(pt, widget, TCC_RIGHT_CLICK);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "../company_func.h"
|
||||
#include "../station_base.h"
|
||||
#include "../widget_type.h"
|
||||
#include "../window_gui.h"
|
||||
#include "linkgraph_base.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@@ -118,6 +119,7 @@ public:
|
||||
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize);
|
||||
virtual void DrawWidget(const Rect &r, int widget) const;
|
||||
virtual void OnHover(Point pt, int widget) override;
|
||||
virtual bool OnRightClick(Point pt, int widget) override;
|
||||
virtual void OnClick(Point pt, int widget, int click_count);
|
||||
virtual void OnInvalidateData(int data = 0, bool gui_scope = true);
|
||||
|
||||
@@ -126,6 +128,7 @@ private:
|
||||
|
||||
void UpdateOverlayCompanies();
|
||||
void UpdateOverlayCargoes();
|
||||
bool OnHoverCommon(Point pt, int widget, TooltipCloseCondition close_cond);
|
||||
};
|
||||
|
||||
#endif /* LINKGRAPH_GUI_H */
|
||||
|
@@ -125,9 +125,11 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
|
||||
this->num_available = 0;
|
||||
IniGroup *names = ini->GetGroup("names");
|
||||
IniGroup *catindex = ini->GetGroup("catindex");
|
||||
for (uint i = 0, j = 1; i < lengthof(this->songinfo); i++) {
|
||||
IniGroup *timingtrim = ini->GetGroup("timingtrim");
|
||||
uint tracknr = 1;
|
||||
for (uint i = 0; i < lengthof(this->songinfo); i++) {
|
||||
const char *filename = this->files[i].filename;
|
||||
if (names == NULL || StrEmpty(filename)) {
|
||||
if (names == NULL || StrEmpty(filename) || this->files[i].check_result == MD5File::CR_NO_FILE) {
|
||||
this->songinfo[i].songname[0] = '\0';
|
||||
continue;
|
||||
}
|
||||
@@ -142,7 +144,8 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
|
||||
char *songname = GetMusicCatEntryName(filename, this->songinfo[i].cat_index);
|
||||
if (songname == NULL) {
|
||||
DEBUG(grf, 1, "Base music set song missing from CAT file: %s/%d", filename, this->songinfo[i].cat_index);
|
||||
return false;
|
||||
this->songinfo[i].songname[0] = '\0';
|
||||
continue;
|
||||
}
|
||||
strecpy(this->songinfo[i].songname, songname, lastof(this->songinfo[i].songname));
|
||||
free(songname);
|
||||
@@ -150,15 +153,16 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
|
||||
this->songinfo[i].filetype = MTT_STANDARDMIDI;
|
||||
}
|
||||
|
||||
const char *trimmed_filename = filename;
|
||||
/* As we possibly add a path to the filename and we compare
|
||||
* on the filename with the path as in the .obm, we need to
|
||||
* keep stripping path elements until we find a match. */
|
||||
for (const char *p = filename; p != NULL; p = strchr(p, PATHSEPCHAR)) {
|
||||
for (; trimmed_filename != NULL; trimmed_filename = strchr(trimmed_filename, PATHSEPCHAR)) {
|
||||
/* Remove possible double path separator characters from
|
||||
* the beginning, so we don't start reading e.g. root. */
|
||||
while (*p == PATHSEPCHAR) p++;
|
||||
while (*trimmed_filename == PATHSEPCHAR) trimmed_filename++;
|
||||
|
||||
item = names->GetItem(p, false);
|
||||
item = names->GetItem(trimmed_filename, false);
|
||||
if (item != NULL && !StrEmpty(item->value)) break;
|
||||
}
|
||||
|
||||
@@ -172,7 +176,21 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
|
||||
}
|
||||
this->num_available++;
|
||||
|
||||
this->songinfo[i].tracknr = j++;
|
||||
/* Number the theme song (if any) track 0, rest are normal */
|
||||
if (i == 0) {
|
||||
this->songinfo[i].tracknr = 0;
|
||||
} else {
|
||||
this->songinfo[i].tracknr = tracknr++;
|
||||
}
|
||||
|
||||
item = timingtrim->GetItem(trimmed_filename, false);
|
||||
if (item != NULL && !StrEmpty(item->value)) {
|
||||
const char *endpos = strchr(item->value, ':');
|
||||
if (endpos != NULL) {
|
||||
this->songinfo[i].override_start = atoi(item->value);
|
||||
this->songinfo[i].override_end = atoi(endpos + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
@@ -693,6 +693,9 @@ static void MidiThreadProc(void *)
|
||||
current_segment.start_block = bl;
|
||||
break;
|
||||
} else {
|
||||
/* Skip the transmission delay compensation performed in the Win32 MIDI driver.
|
||||
* The DMusic driver will most likely be used with the MS softsynth, which is not subject to transmission delays.
|
||||
*/
|
||||
DEBUG(driver, 2, "DMusic: timer: start from block %d (ticktime %d, realtime %.3f, bytes %d)", (int)bl, (int)block.ticktime, ((int)block.realtime) / 1000.0, (int)preload_bytes);
|
||||
playback_start_time -= block.realtime * MIDITIME_TO_REFTIME;
|
||||
break;
|
||||
@@ -723,14 +726,6 @@ static void MidiThreadProc(void *)
|
||||
while (current_block < current_file.blocks.size()) {
|
||||
MidiFile::DataBlock &block = current_file.blocks[current_block];
|
||||
|
||||
/* check that block is not in the future */
|
||||
REFERENCE_TIME playback_time = current_time - playback_start_time;
|
||||
if (block.realtime * MIDITIME_TO_REFTIME > playback_time + 3 *_playback.preload_time * MS_TO_REFTIME) {
|
||||
/* Stop the thread loop until we are at the preload time of the next block. */
|
||||
next_timeout = Clamp(((int64)block.realtime * MIDITIME_TO_REFTIME - playback_time) / MS_TO_REFTIME - _playback.preload_time, 0, 1000);
|
||||
DEBUG(driver, 9, "DMusic thread: Next event in %u ms (music %u, ref %lld)", next_timeout, block.realtime * MIDITIME_TO_REFTIME, playback_time);
|
||||
break;
|
||||
}
|
||||
/* check that block isn't at end-of-song override */
|
||||
if (current_segment.end > 0 && block.ticktime >= current_segment.end) {
|
||||
if (current_segment.loop) {
|
||||
@@ -743,6 +738,14 @@ static void MidiThreadProc(void *)
|
||||
next_timeout = 0;
|
||||
break;
|
||||
}
|
||||
/* check that block is not in the future */
|
||||
REFERENCE_TIME playback_time = current_time - playback_start_time;
|
||||
if (block.realtime * MIDITIME_TO_REFTIME > playback_time + 3 *_playback.preload_time * MS_TO_REFTIME) {
|
||||
/* Stop the thread loop until we are at the preload time of the next block. */
|
||||
next_timeout = Clamp(((int64)block.realtime * MIDITIME_TO_REFTIME - playback_time) / MS_TO_REFTIME - _playback.preload_time, 0, 1000);
|
||||
DEBUG(driver, 9, "DMusic thread: Next event in %u ms (music %u, ref %lld)", next_timeout, block.realtime * MIDITIME_TO_REFTIME, playback_time);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Timestamp of the current block. */
|
||||
block_time = playback_start_time + block.realtime * MIDITIME_TO_REFTIME;
|
||||
@@ -829,8 +832,8 @@ static void MidiThreadProc(void *)
|
||||
/* end? */
|
||||
if (current_block == current_file.blocks.size()) {
|
||||
if (current_segment.loop) {
|
||||
current_block = 0;
|
||||
clock->GetTime(&playback_start_time);
|
||||
current_block = current_segment.start_block;
|
||||
playback_start_time = block_time - current_file.blocks[current_block].realtime * MIDITIME_TO_REFTIME;
|
||||
} else {
|
||||
_playback.do_stop = true;
|
||||
}
|
||||
@@ -1232,9 +1235,9 @@ void MusicDriver_DMusic::PlaySong(const MusicSongInfo &song)
|
||||
|
||||
if (!_playback.next_file.LoadSong(song)) return;
|
||||
|
||||
_playback.next_segment.start = 0;
|
||||
_playback.next_segment.end = 0;
|
||||
_playback.next_segment.loop = false;
|
||||
_playback.next_segment.start = song.override_start;
|
||||
_playback.next_segment.end = song.override_end;
|
||||
_playback.next_segment.loop = song.loop;
|
||||
|
||||
_playback.do_start = true;
|
||||
SetEvent(_thread_event);
|
||||
|
@@ -184,7 +184,7 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW
|
||||
/* find first block after start time and pretend playback started earlier
|
||||
* this is to allow all blocks prior to the actual start to still affect playback,
|
||||
* as they may contain important controller and program changes */
|
||||
size_t preload_bytes = 0;
|
||||
uint preload_bytes = 0;
|
||||
for (size_t bl = 0; bl < _midi.current_file.blocks.size(); bl++) {
|
||||
MidiFile::DataBlock &block = _midi.current_file.blocks[bl];
|
||||
preload_bytes += block.data.Length();
|
||||
@@ -194,8 +194,13 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW
|
||||
_midi.current_segment.start_block = bl;
|
||||
break;
|
||||
} else {
|
||||
/* Calculate offset start time for playback.
|
||||
* The preload_bytes are used to compensate for delay in transmission over traditional serial MIDI interfaces,
|
||||
* which have a bitrate of 31,250 bits/sec, and transmit 1+8+1 start/data/stop bits per byte.
|
||||
* The delay compensation is needed to avoid time-compression of following messages.
|
||||
*/
|
||||
DEBUG(driver, 2, "Win32-MIDI: timer: start from block %d (ticktime %d, realtime %.3f, bytes %d)", (int)bl, (int)block.ticktime, ((int)block.realtime) / 1000.0, (int)preload_bytes);
|
||||
_midi.playback_start_time -= block.realtime / 1000;
|
||||
_midi.playback_start_time -= block.realtime / 1000 - preload_bytes * 1000 / 3125;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -209,10 +214,6 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW
|
||||
while (_midi.current_block < _midi.current_file.blocks.size()) {
|
||||
MidiFile::DataBlock &block = _midi.current_file.blocks[_midi.current_block];
|
||||
|
||||
/* check that block is not in the future */
|
||||
if (block.realtime / 1000 > playback_time) {
|
||||
break;
|
||||
}
|
||||
/* check that block isn't at end-of-song override */
|
||||
if (_midi.current_segment.end > 0 && block.ticktime >= _midi.current_segment.end) {
|
||||
if (_midi.current_segment.loop) {
|
||||
@@ -223,6 +224,10 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* check that block is not in the future */
|
||||
if (block.realtime / 1000 > playback_time) {
|
||||
break;
|
||||
}
|
||||
|
||||
byte *data = block.data.Begin();
|
||||
size_t remaining = block.data.Length();
|
||||
@@ -297,8 +302,8 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW
|
||||
/* end? */
|
||||
if (_midi.current_block == _midi.current_file.blocks.size()) {
|
||||
if (_midi.current_segment.loop) {
|
||||
_midi.current_block = 0;
|
||||
_midi.playback_start_time = timeGetTime();
|
||||
_midi.current_block = _midi.current_segment.start_block;
|
||||
_midi.playback_start_time = timeGetTime() - _midi.current_file.blocks[_midi.current_block].realtime / 1000;
|
||||
} else {
|
||||
_midi.do_stop = true;
|
||||
}
|
||||
@@ -315,9 +320,9 @@ void MusicDriver_Win32::PlaySong(const MusicSongInfo &song)
|
||||
return;
|
||||
}
|
||||
|
||||
_midi.next_segment.start = 0;
|
||||
_midi.next_segment.end = 0;
|
||||
_midi.next_segment.loop = false;
|
||||
_midi.next_segment.start = song.override_start;
|
||||
_midi.next_segment.end = song.override_end;
|
||||
_midi.next_segment.loop = song.loop;
|
||||
|
||||
DEBUG(driver, 2, "Win32-MIDI: PlaySong: setting flag");
|
||||
_midi.do_stop = _midi.playing;
|
||||
|
@@ -10,6 +10,7 @@
|
||||
/** @file music_gui.cpp GUI for the music playback. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <vector>
|
||||
#include "openttd.h"
|
||||
#include "base_media_base.h"
|
||||
#include "music/music_driver.hpp"
|
||||
@@ -35,246 +36,385 @@
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
/**
|
||||
* Get the name of the song.
|
||||
* @param index of the song.
|
||||
* @return the name of the song.
|
||||
*/
|
||||
static const char *GetSongName(int index)
|
||||
{
|
||||
return BaseMusic::GetUsedSet()->songinfo[index].songname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the track number of the song.
|
||||
* @param index of the song.
|
||||
* @return the track number of the song.
|
||||
*/
|
||||
static int GetTrackNumber(int index)
|
||||
{
|
||||
return BaseMusic::GetUsedSet()->songinfo[index].tracknr;
|
||||
}
|
||||
struct MusicSystem {
|
||||
struct PlaylistEntry : MusicSongInfo {
|
||||
const MusicSet *set; ///< music set the song comes from
|
||||
uint set_index; ///< index of song in set
|
||||
|
||||
/** The currently played song */
|
||||
static byte _music_wnd_cursong = 1;
|
||||
/** Whether a song is currently played */
|
||||
static bool _song_is_active = false;
|
||||
PlaylistEntry(const MusicSet *set, uint set_index) : MusicSongInfo(set->songinfo[set_index]), set(set), set_index(set_index) { }
|
||||
bool IsValid() const { return !StrEmpty(this->songname); }
|
||||
};
|
||||
typedef std::vector<PlaylistEntry> Playlist;
|
||||
|
||||
/** Indices of the songs in the current playlist */
|
||||
static byte _cur_playlist[NUM_SONGS_PLAYLIST + 1];
|
||||
enum PlaylistChoices {
|
||||
PLCH_ALLMUSIC,
|
||||
PLCH_OLDSTYLE,
|
||||
PLCH_NEWSTYLE,
|
||||
PLCH_EZYSTREET,
|
||||
PLCH_CUSTOM1,
|
||||
PLCH_CUSTOM2,
|
||||
PLCH_THEMEONLY,
|
||||
PLCH_MAX,
|
||||
};
|
||||
|
||||
/** Indices of all songs */
|
||||
static byte _playlist_all[NUM_SONGS_AVAILABLE + 1];
|
||||
/** Indices of all old style songs */
|
||||
static byte _playlist_old_style[NUM_SONGS_CLASS + 1];
|
||||
/** Indices of all new style songs */
|
||||
static byte _playlist_new_style[NUM_SONGS_CLASS + 1];
|
||||
/** Indices of all ezy street songs */
|
||||
static byte _playlist_ezy_street[NUM_SONGS_CLASS + 1];
|
||||
Playlist active_playlist; ///< current play order of songs, including any shuffle
|
||||
Playlist displayed_playlist; ///< current playlist as displayed in GUI, never in shuffled order
|
||||
Playlist music_set; ///< all songs in current music set, in set order
|
||||
|
||||
assert_compile(lengthof(_settings_client.music.custom_1) == NUM_SONGS_PLAYLIST + 1);
|
||||
assert_compile(lengthof(_settings_client.music.custom_2) == NUM_SONGS_PLAYLIST + 1);
|
||||
PlaylistChoices selected_playlist;
|
||||
|
||||
/** The different playlists that can be played. */
|
||||
static byte * const _playlists[] = {
|
||||
_playlist_all,
|
||||
_playlist_old_style,
|
||||
_playlist_new_style,
|
||||
_playlist_ezy_street,
|
||||
_settings_client.music.custom_1,
|
||||
_settings_client.music.custom_2,
|
||||
void BuildPlaylists();
|
||||
|
||||
void ChangePlaylist(PlaylistChoices pl);
|
||||
void ChangeMusicSet(const char *set_name);
|
||||
void Shuffle();
|
||||
void Unshuffle();
|
||||
|
||||
void Play();
|
||||
void Stop();
|
||||
void Next();
|
||||
void Prev();
|
||||
void CheckStatus();
|
||||
|
||||
bool IsPlaying() const;
|
||||
bool IsShuffle() const;
|
||||
PlaylistEntry GetCurrentSong() const;
|
||||
|
||||
bool IsCustomPlaylist() const;
|
||||
void PlaylistAdd(size_t song_index);
|
||||
void PlaylistRemove(size_t song_index);
|
||||
void PlaylistClear();
|
||||
|
||||
private:
|
||||
void ChangePlaylistPosition(int ofs);
|
||||
int playlist_position;
|
||||
|
||||
void SaveCustomPlaylist(PlaylistChoices pl);
|
||||
|
||||
Playlist standard_playlists[PLCH_MAX];
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate a playlist.
|
||||
* @param playlist The playlist to validate.
|
||||
* @param last The last location in the list.
|
||||
*/
|
||||
void ValidatePlaylist(byte *playlist, byte *last)
|
||||
{
|
||||
while (*playlist != 0 && playlist <= last) {
|
||||
/* Song indices are saved off-by-one so 0 is "nothing". */
|
||||
if (*playlist <= NUM_SONGS_AVAILABLE && !StrEmpty(GetSongName(*playlist - 1))) {
|
||||
playlist++;
|
||||
continue;
|
||||
}
|
||||
for (byte *p = playlist; *p != 0 && p <= last; p++) {
|
||||
p[0] = p[1];
|
||||
}
|
||||
}
|
||||
MusicSystem _music;
|
||||
|
||||
/* Make sure the list is null terminated. */
|
||||
*last = 0;
|
||||
}
|
||||
|
||||
/** Prepare the playlists */
|
||||
void InitializeMusic()
|
||||
/** Rebuild all playlists for the current music set */
|
||||
void MusicSystem::BuildPlaylists()
|
||||
{
|
||||
uint j = 0;
|
||||
const MusicSet *set = BaseMusic::GetUsedSet();
|
||||
|
||||
/* Clear current playlists */
|
||||
for (size_t i = 0; i < lengthof(this->standard_playlists); ++i) this->standard_playlists[i].clear();
|
||||
this->music_set.clear();
|
||||
|
||||
/* Build standard playlists, and a list of available music */
|
||||
for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
|
||||
if (StrEmpty(GetSongName(i))) continue;
|
||||
_playlist_all[j++] = i + 1;
|
||||
}
|
||||
/* Terminate the list */
|
||||
_playlist_all[j] = 0;
|
||||
PlaylistEntry entry(set, i);
|
||||
if (!entry.IsValid()) continue;
|
||||
|
||||
/* Now make the 'styled' playlists */
|
||||
for (uint k = 0; k < NUM_SONG_CLASSES; k++) {
|
||||
j = 0;
|
||||
for (uint i = 0; i < NUM_SONGS_CLASS; i++) {
|
||||
int id = k * NUM_SONGS_CLASS + i + 1;
|
||||
if (StrEmpty(GetSongName(id))) continue;
|
||||
_playlists[k + 1][j++] = id + 1;
|
||||
this->music_set.push_back(entry);
|
||||
|
||||
/* Add theme song to theme-only playlist */
|
||||
if (i == 0) this->standard_playlists[PLCH_THEMEONLY].push_back(entry);
|
||||
|
||||
/* Don't add the theme song to standard playlists */
|
||||
if (i > 0) {
|
||||
this->standard_playlists[PLCH_ALLMUSIC].push_back(entry);
|
||||
uint theme = (i - 1) / NUM_SONGS_CLASS;
|
||||
this->standard_playlists[PLCH_OLDSTYLE + theme].push_back(entry);
|
||||
}
|
||||
/* Terminate the list */
|
||||
_playlists[k + 1][j] = 0;
|
||||
}
|
||||
|
||||
ValidatePlaylist(_settings_client.music.custom_1, lastof(_settings_client.music.custom_1));
|
||||
ValidatePlaylist(_settings_client.music.custom_2, lastof(_settings_client.music.custom_2));
|
||||
|
||||
if (BaseMusic::GetUsedSet()->num_available < _music_wnd_cursong) {
|
||||
/* If there are less songs than the currently played song,
|
||||
* just pause and reset to no song. */
|
||||
_music_wnd_cursong = 0;
|
||||
_song_is_active = false;
|
||||
/* Load custom playlists
|
||||
* Song index offsets are 1-based, zero indicates invalid/end-of-list value */
|
||||
for (uint i = 0; i < NUM_SONGS_PLAYLIST; i++) {
|
||||
if (_settings_client.music.custom_1[i] > 0) {
|
||||
PlaylistEntry entry(set, _settings_client.music.custom_1[i] - 1);
|
||||
if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM1].push_back(entry);
|
||||
}
|
||||
if (_settings_client.music.custom_2[i] > 0) {
|
||||
PlaylistEntry entry(set, _settings_client.music.custom_2[i] - 1);
|
||||
if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM2].push_back(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SkipToPrevSong()
|
||||
/**
|
||||
* Switch to another playlist, or reload the current one.
|
||||
* @param pl Playlist to select
|
||||
*/
|
||||
void MusicSystem::ChangePlaylist(PlaylistChoices pl)
|
||||
{
|
||||
byte *b = _cur_playlist;
|
||||
byte *p = b;
|
||||
byte t;
|
||||
assert(pl < PLCH_MAX && pl >= PLCH_ALLMUSIC);
|
||||
|
||||
if (b[0] == 0) return; // empty playlist
|
||||
this->displayed_playlist = this->standard_playlists[pl];
|
||||
this->active_playlist = this->displayed_playlist;
|
||||
this->selected_playlist = pl;
|
||||
this->playlist_position = 0;
|
||||
|
||||
do p++; while (p[0] != 0); // find the end
|
||||
if (this->selected_playlist != PLCH_THEMEONLY) _settings_client.music.playlist = this->selected_playlist;
|
||||
|
||||
t = *--p; // and copy the bytes
|
||||
while (p != b) {
|
||||
p--;
|
||||
p[1] = p[0];
|
||||
if (_settings_client.music.shuffle) {
|
||||
this->Shuffle();
|
||||
/* Shuffle() will also Play() if necessary, only start once */
|
||||
} else if (_settings_client.music.playing) {
|
||||
this->Play();
|
||||
}
|
||||
*b = t;
|
||||
|
||||
_song_is_active = false;
|
||||
InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
|
||||
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
|
||||
}
|
||||
|
||||
static void SkipToNextSong()
|
||||
/**
|
||||
* Change to named music set, and reset playback.
|
||||
* @param set_name Name of music set to select
|
||||
*/
|
||||
void MusicSystem::ChangeMusicSet(const char *set_name)
|
||||
{
|
||||
byte *b = _cur_playlist;
|
||||
byte t;
|
||||
BaseMusic::SetSet(set_name);
|
||||
|
||||
t = b[0];
|
||||
if (t != 0) {
|
||||
while (b[1] != 0) {
|
||||
b[0] = b[1];
|
||||
b++;
|
||||
}
|
||||
b[0] = t;
|
||||
}
|
||||
this->BuildPlaylists();
|
||||
this->ChangePlaylist(this->selected_playlist);
|
||||
|
||||
_song_is_active = false;
|
||||
InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true);
|
||||
}
|
||||
|
||||
static void MusicVolumeChanged(byte new_vol)
|
||||
/** Enable shuffle mode and restart playback */
|
||||
void MusicSystem::Shuffle()
|
||||
{
|
||||
MusicDriver::GetInstance()->SetVolume(new_vol);
|
||||
}
|
||||
_settings_client.music.shuffle = true;
|
||||
|
||||
static void DoPlaySong()
|
||||
{
|
||||
char filename[MAX_PATH];
|
||||
MusicSongInfo songinfo = BaseMusic::GetUsedSet()->songinfo[_music_wnd_cursong - 1]; // copy
|
||||
if (FioFindFullPath(filename, lastof(filename), BASESET_DIR, songinfo.filename) == NULL) {
|
||||
FioFindFullPath(filename, lastof(filename), OLD_GM_DIR, songinfo.filename);
|
||||
this->active_playlist = this->displayed_playlist;
|
||||
for (size_t i = 0; i < this->active_playlist.size(); i++) {
|
||||
size_t shuffle_index = InteractiveRandom() % (this->active_playlist.size() - i);
|
||||
std::swap(this->active_playlist[i], this->active_playlist[i + shuffle_index]);
|
||||
}
|
||||
songinfo.filename = filename; // non-owned pointer
|
||||
MusicDriver::GetInstance()->PlaySong(songinfo);
|
||||
SetWindowDirty(WC_MUSIC_WINDOW, 0);
|
||||
|
||||
if (_settings_client.music.playing) this->Play();
|
||||
|
||||
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
|
||||
}
|
||||
|
||||
static void DoStopMusic()
|
||||
/** Disable shuffle and restart playback */
|
||||
void MusicSystem::Unshuffle()
|
||||
{
|
||||
_settings_client.music.shuffle = false;
|
||||
this->active_playlist = this->displayed_playlist;
|
||||
|
||||
if (_settings_client.music.playing) this->Play();
|
||||
|
||||
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
|
||||
}
|
||||
|
||||
/** Start/restart playback at current song */
|
||||
void MusicSystem::Play()
|
||||
{
|
||||
/* Always set the playing flag, even if there is no music */
|
||||
_settings_client.music.playing = true;
|
||||
MusicDriver::GetInstance()->StopSong();
|
||||
/* Make sure playlist_position is a valid index, if playlist has changed etc. */
|
||||
this->ChangePlaylistPosition(0);
|
||||
|
||||
/* If there is no music, don't try to play it */
|
||||
if (this->active_playlist.empty()) return;
|
||||
|
||||
MusicSongInfo song = this->active_playlist[this->playlist_position];
|
||||
if (_game_mode == GM_MENU && this->selected_playlist == PLCH_THEMEONLY) song.loop = true;
|
||||
MusicDriver::GetInstance()->PlaySong(song);
|
||||
|
||||
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
|
||||
}
|
||||
|
||||
/** Stop playback and set flag that we don't intend to play music */
|
||||
void MusicSystem::Stop()
|
||||
{
|
||||
MusicDriver::GetInstance()->StopSong();
|
||||
SetWindowDirty(WC_MUSIC_WINDOW, 0);
|
||||
}
|
||||
|
||||
/** Reload the active playlist data from playlist selection and shuffle setting */
|
||||
static void ResetPlaylist()
|
||||
{
|
||||
uint i = 0;
|
||||
uint j = 0;
|
||||
|
||||
memset(_cur_playlist, 0, sizeof(_cur_playlist));
|
||||
do {
|
||||
/* File is the index into the file table of the music set. The play list uses 0 as 'no entry',
|
||||
* so we need to subtract 1. In case of 'no entry' (file = -1), just skip adding it outright. */
|
||||
int file = _playlists[_settings_client.music.playlist][i] - 1;
|
||||
if (file >= 0) {
|
||||
const char *filename = BaseMusic::GetUsedSet()->files[file].filename;
|
||||
/* We are now checking for the existence of that file prior
|
||||
* to add it to the list of available songs */
|
||||
if (!StrEmpty(filename) && FioCheckFileExists(filename, BASESET_DIR)) {
|
||||
_cur_playlist[j] = _playlists[_settings_client.music.playlist][i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
} while (_playlists[_settings_client.music.playlist][++i] != 0 && j < lengthof(_cur_playlist) - 1);
|
||||
|
||||
/* Do not shuffle when on the intro-start window, as the song to play has to be the original TTD Theme*/
|
||||
if (_settings_client.music.shuffle && _game_mode != GM_MENU) {
|
||||
i = 500;
|
||||
do {
|
||||
uint32 r = InteractiveRandom();
|
||||
byte *a = &_cur_playlist[GB(r, 0, 5)];
|
||||
byte *b = &_cur_playlist[GB(r, 8, 5)];
|
||||
|
||||
if (*a != 0 && *b != 0) {
|
||||
byte t = *a;
|
||||
*a = *b;
|
||||
*b = t;
|
||||
}
|
||||
} while (--i);
|
||||
}
|
||||
}
|
||||
|
||||
static void StopMusic()
|
||||
{
|
||||
_music_wnd_cursong = 0;
|
||||
DoStopMusic();
|
||||
_song_is_active = false;
|
||||
SetWindowWidgetDirty(WC_MUSIC_WINDOW, 0, 9);
|
||||
}
|
||||
|
||||
/** Begin playing the next song on the playlist */
|
||||
static void PlayPlaylistSong()
|
||||
{
|
||||
if (_cur_playlist[0] == 0) {
|
||||
ResetPlaylist();
|
||||
/* if there is not songs in the playlist, it may indicate
|
||||
* no file on the gm folder, or even no gm folder.
|
||||
* Stop the playback, then */
|
||||
if (_cur_playlist[0] == 0) {
|
||||
_song_is_active = false;
|
||||
_music_wnd_cursong = 0;
|
||||
_settings_client.music.playing = false;
|
||||
|
||||
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
|
||||
}
|
||||
|
||||
/** Skip to next track */
|
||||
void MusicSystem::Next()
|
||||
{
|
||||
this->ChangePlaylistPosition(+1);
|
||||
if (_settings_client.music.playing) this->Play();
|
||||
|
||||
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
|
||||
}
|
||||
|
||||
/** Skip to previous track */
|
||||
void MusicSystem::Prev()
|
||||
{
|
||||
this->ChangePlaylistPosition(-1);
|
||||
if (_settings_client.music.playing) this->Play();
|
||||
|
||||
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
|
||||
}
|
||||
|
||||
/** Check that music is playing if it should, and that appropriate playlist is active for game/main menu */
|
||||
void MusicSystem::CheckStatus()
|
||||
{
|
||||
if ((_game_mode == GM_MENU) != (this->selected_playlist == PLCH_THEMEONLY)) {
|
||||
/* Make sure the theme-only playlist is active when on the title screen, and not during gameplay */
|
||||
this->ChangePlaylist((_game_mode == GM_MENU) ? PLCH_THEMEONLY : (PlaylistChoices)_settings_client.music.playlist);
|
||||
}
|
||||
if (this->active_playlist.empty()) return;
|
||||
/* If we were supposed to be playing, but music has stopped, move to next song */
|
||||
if (this->IsPlaying() && !MusicDriver::GetInstance()->IsSongPlaying()) this->Next();
|
||||
}
|
||||
|
||||
/** Is the player getting music right now? */
|
||||
bool MusicSystem::IsPlaying() const
|
||||
{
|
||||
return _settings_client.music.playing && !this->active_playlist.empty();
|
||||
}
|
||||
|
||||
/** Is shuffle mode enabled? */
|
||||
bool MusicSystem::IsShuffle() const
|
||||
{
|
||||
return _settings_client.music.shuffle;
|
||||
}
|
||||
|
||||
/** Return the current song, or a dummy if none */
|
||||
MusicSystem::PlaylistEntry MusicSystem::GetCurrentSong() const
|
||||
{
|
||||
if (!this->IsPlaying()) return PlaylistEntry(BaseMusic::GetUsedSet(), 0);
|
||||
return this->active_playlist[this->playlist_position];
|
||||
}
|
||||
|
||||
/** Is one of the custom playlists selected? */
|
||||
bool MusicSystem::IsCustomPlaylist() const
|
||||
{
|
||||
return (this->selected_playlist == PLCH_CUSTOM1) || (this->selected_playlist == PLCH_CUSTOM2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a song to a custom playlist.
|
||||
* Always adds to the currently active playlist.
|
||||
* @param song_index Index of song in the current music set to add
|
||||
*/
|
||||
void MusicSystem::PlaylistAdd(size_t song_index)
|
||||
{
|
||||
if (!this->IsCustomPlaylist()) return;
|
||||
|
||||
/* Pick out song from the music set */
|
||||
if (song_index >= this->music_set.size()) return;
|
||||
PlaylistEntry entry = this->music_set[song_index];
|
||||
|
||||
/* Check for maximum length */
|
||||
if (this->standard_playlists[this->selected_playlist].size() >= NUM_SONGS_PLAYLIST) return;
|
||||
|
||||
/* Add it to the appropriate playlist, and the display */
|
||||
this->standard_playlists[this->selected_playlist].push_back(entry);
|
||||
this->displayed_playlist.push_back(entry);
|
||||
|
||||
/* Add it to the active playlist, if playback is shuffled select a random position to add at */
|
||||
if (this->active_playlist.empty()) {
|
||||
this->active_playlist.push_back(entry);
|
||||
if (this->IsPlaying()) this->Play();
|
||||
} else if (this->IsShuffle()) {
|
||||
/* Generate a random position between 0 and n (inclusive, new length) to insert at */
|
||||
size_t maxpos = this->displayed_playlist.size();
|
||||
size_t newpos = InteractiveRandom() % maxpos;
|
||||
this->active_playlist.insert(this->active_playlist.begin() + newpos, entry);
|
||||
/* Make sure to shift up the current playback position if the song was inserted before it */
|
||||
if ((int)newpos <= this->playlist_position) this->playlist_position++;
|
||||
} else {
|
||||
this->active_playlist.push_back(entry);
|
||||
}
|
||||
|
||||
this->SaveCustomPlaylist(this->selected_playlist);
|
||||
|
||||
InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a song from a custom playlist.
|
||||
* @param song_index Index in the custom playlist to remove.
|
||||
*/
|
||||
void MusicSystem::PlaylistRemove(size_t song_index)
|
||||
{
|
||||
if (!this->IsCustomPlaylist()) return;
|
||||
|
||||
Playlist &pl = this->standard_playlists[this->selected_playlist];
|
||||
if (song_index >= pl.size()) return;
|
||||
|
||||
/* Remove from "simple" playlists */
|
||||
PlaylistEntry song = pl[song_index];
|
||||
pl.erase(pl.begin() + song_index);
|
||||
this->displayed_playlist.erase(this->displayed_playlist.begin() + song_index);
|
||||
|
||||
/* Find in actual active playlist (may be shuffled) and remove,
|
||||
* if it's the current song restart playback */
|
||||
for (size_t i = 0; i < this->active_playlist.size(); i++) {
|
||||
Playlist::iterator s2 = this->active_playlist.begin() + i;
|
||||
if (s2->filename == song.filename && s2->cat_index == song.cat_index) {
|
||||
this->active_playlist.erase(s2);
|
||||
if ((int)i == this->playlist_position && this->IsPlaying()) this->Play();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->SaveCustomPlaylist(this->selected_playlist);
|
||||
|
||||
InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all songs from the current custom playlist.
|
||||
* Effectively stops playback too.
|
||||
*/
|
||||
void MusicSystem::PlaylistClear()
|
||||
{
|
||||
if (!this->IsCustomPlaylist()) return;
|
||||
|
||||
this->standard_playlists[this->selected_playlist].clear();
|
||||
this->ChangePlaylist(this->selected_playlist);
|
||||
|
||||
this->SaveCustomPlaylist(this->selected_playlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change playlist position pointer by the given offset, making sure to keep it within valid range.
|
||||
* If the playlist is empty, position is always set to 0.
|
||||
* @param ofs Amount to move playlist position by.
|
||||
*/
|
||||
void MusicSystem::ChangePlaylistPosition(int ofs)
|
||||
{
|
||||
if (this->active_playlist.empty()) {
|
||||
this->playlist_position = 0;
|
||||
} else {
|
||||
this->playlist_position += ofs;
|
||||
while (this->playlist_position >= (int)this->active_playlist.size()) this->playlist_position -= (int)this->active_playlist.size();
|
||||
while (this->playlist_position < 0) this->playlist_position += (int)this->active_playlist.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a custom playlist to settings after modification.
|
||||
* @param pl Playlist to store back
|
||||
*/
|
||||
void MusicSystem::SaveCustomPlaylist(PlaylistChoices pl)
|
||||
{
|
||||
byte *settings_pl;
|
||||
if (pl == PLCH_CUSTOM1) {
|
||||
settings_pl = _settings_client.music.custom_1;
|
||||
} else if (pl == PLCH_CUSTOM2) {
|
||||
settings_pl = _settings_client.music.custom_2;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t num = 0;
|
||||
MemSetT(settings_pl, 0, NUM_SONGS_PLAYLIST);
|
||||
|
||||
for (Playlist::const_iterator song = this->standard_playlists[pl].begin(); song != this->standard_playlists[pl].end(); ++song) {
|
||||
/* Music set indices in the settings playlist are 1-based, 0 means unused slot */
|
||||
settings_pl[num++] = (byte)song->set_index + 1;
|
||||
}
|
||||
_music_wnd_cursong = _cur_playlist[0];
|
||||
DoPlaySong();
|
||||
_song_is_active = true;
|
||||
|
||||
SetWindowWidgetDirty(WC_MUSIC_WINDOW, 0, 9);
|
||||
}
|
||||
|
||||
void ResetMusic()
|
||||
{
|
||||
_music_wnd_cursong = 1;
|
||||
DoPlaySong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check music playback status and start/stop/song-finished.
|
||||
@@ -282,30 +422,7 @@ void ResetMusic()
|
||||
*/
|
||||
void MusicLoop()
|
||||
{
|
||||
if (!_settings_client.music.playing && _song_is_active) {
|
||||
StopMusic();
|
||||
} else if (_settings_client.music.playing && !_song_is_active) {
|
||||
PlayPlaylistSong();
|
||||
}
|
||||
|
||||
if (!_song_is_active) return;
|
||||
|
||||
if (!MusicDriver::GetInstance()->IsSongPlaying()) {
|
||||
if (_game_mode != GM_MENU) {
|
||||
StopMusic();
|
||||
SkipToNextSong();
|
||||
PlayPlaylistSong();
|
||||
} else {
|
||||
ResetMusic();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SelectPlaylist(byte list)
|
||||
{
|
||||
_settings_client.music.playlist = list;
|
||||
InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
|
||||
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
|
||||
_music.CheckStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,29 +432,20 @@ static void SelectPlaylist(byte list)
|
||||
void ChangeMusicSet(int index)
|
||||
{
|
||||
if (BaseMusic::GetIndexOfUsedSet() == index) return;
|
||||
|
||||
/* Resume playback after switching?
|
||||
* Always if music is already playing, and also if the user is switching
|
||||
* away from an empty music set.
|
||||
* If the user switches away from an empty set, assume it's because they
|
||||
* want to hear music now. */
|
||||
bool shouldplay = _song_is_active || (BaseMusic::GetUsedSet()->num_available == 0);
|
||||
StopMusic();
|
||||
|
||||
const char *name = BaseMusic::GetSet(index)->name;
|
||||
BaseMusic::SetSet(name);
|
||||
free(BaseMusic::ini_set);
|
||||
BaseMusic::ini_set = stredup(name);
|
||||
|
||||
InitializeMusic();
|
||||
ResetPlaylist();
|
||||
_settings_client.music.playing = shouldplay;
|
||||
|
||||
InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
|
||||
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
|
||||
InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true);
|
||||
_music.ChangeMusicSet(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the music system for use.
|
||||
* Called from \c InitializeGame
|
||||
*/
|
||||
void InitializeMusic()
|
||||
{
|
||||
_music.BuildPlaylists();
|
||||
}
|
||||
|
||||
|
||||
struct MusicTrackSelectionWindow : public Window {
|
||||
MusicTrackSelectionWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
|
||||
{
|
||||
@@ -394,13 +502,10 @@ struct MusicTrackSelectionWindow : public Window {
|
||||
case WID_MTS_LIST_LEFT: case WID_MTS_LIST_RIGHT: {
|
||||
Dimension d = {0, 0};
|
||||
|
||||
for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
|
||||
const char *song_name = GetSongName(i);
|
||||
if (StrEmpty(song_name)) continue;
|
||||
|
||||
SetDParam(0, GetTrackNumber(i));
|
||||
for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) {
|
||||
SetDParam(0, song->tracknr);
|
||||
SetDParam(1, 2);
|
||||
SetDParamStr(2, GetSongName(i));
|
||||
SetDParamStr(2, song->songname);
|
||||
Dimension d2 = GetStringBoundingBox(STR_PLAYLIST_TRACK_NAME);
|
||||
d.width = max(d.width, d2.width);
|
||||
d.height += d2.height;
|
||||
@@ -420,13 +525,10 @@ struct MusicTrackSelectionWindow : public Window {
|
||||
GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK);
|
||||
|
||||
int y = r.top + WD_FRAMERECT_TOP;
|
||||
for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
|
||||
const char *song_name = GetSongName(i);
|
||||
if (StrEmpty(song_name)) continue;
|
||||
|
||||
SetDParam(0, GetTrackNumber(i));
|
||||
for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) {
|
||||
SetDParam(0, song->tracknr);
|
||||
SetDParam(1, 2);
|
||||
SetDParamStr(2, song_name);
|
||||
SetDParamStr(2, song->songname);
|
||||
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
|
||||
y += FONT_HEIGHT_SMALL;
|
||||
}
|
||||
@@ -437,11 +539,10 @@ struct MusicTrackSelectionWindow : public Window {
|
||||
GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK);
|
||||
|
||||
int y = r.top + WD_FRAMERECT_TOP;
|
||||
for (const byte *p = _playlists[_settings_client.music.playlist]; *p != 0; p++) {
|
||||
uint i = *p - 1;
|
||||
SetDParam(0, GetTrackNumber(i));
|
||||
for (MusicSystem::Playlist::const_iterator song = _music.active_playlist.begin(); song != _music.active_playlist.end(); ++song) {
|
||||
SetDParam(0, song->tracknr);
|
||||
SetDParam(1, 2);
|
||||
SetDParamStr(2, GetSongName(i));
|
||||
SetDParamStr(2, song->songname);
|
||||
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
|
||||
y += FONT_HEIGHT_SMALL;
|
||||
}
|
||||
@@ -455,42 +556,13 @@ struct MusicTrackSelectionWindow : public Window {
|
||||
switch (widget) {
|
||||
case WID_MTS_LIST_LEFT: { // add to playlist
|
||||
int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL);
|
||||
|
||||
if (_settings_client.music.playlist < 4) return;
|
||||
if (!IsInsideMM(y, 0, BaseMusic::GetUsedSet()->num_available)) return;
|
||||
|
||||
byte *p = _playlists[_settings_client.music.playlist];
|
||||
for (uint i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) {
|
||||
if (p[i] == 0) {
|
||||
/* Find the actual song number */
|
||||
for (uint j = 0; j < NUM_SONGS_AVAILABLE; j++) {
|
||||
if (GetTrackNumber(j) == y + 1) {
|
||||
p[i] = j + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
p[i + 1] = 0;
|
||||
this->SetDirty();
|
||||
ResetPlaylist();
|
||||
break;
|
||||
}
|
||||
}
|
||||
_music.PlaylistAdd(y);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_MTS_LIST_RIGHT: { // remove from playlist
|
||||
int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL);
|
||||
|
||||
if (_settings_client.music.playlist < 4) return;
|
||||
if (!IsInsideMM(y, 0, NUM_SONGS_PLAYLIST)) return;
|
||||
|
||||
byte *p = _playlists[_settings_client.music.playlist];
|
||||
for (uint i = y; i != NUM_SONGS_PLAYLIST - 1; i++) {
|
||||
p[i] = p[i + 1];
|
||||
}
|
||||
|
||||
this->SetDirty();
|
||||
ResetPlaylist();
|
||||
_music.PlaylistRemove(y);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -502,17 +574,12 @@ struct MusicTrackSelectionWindow : public Window {
|
||||
}
|
||||
|
||||
case WID_MTS_CLEAR: // clear
|
||||
for (uint i = 0; _playlists[_settings_client.music.playlist][i] != 0; i++) _playlists[_settings_client.music.playlist][i] = 0;
|
||||
this->SetDirty();
|
||||
StopMusic();
|
||||
ResetPlaylist();
|
||||
_music.PlaylistClear();
|
||||
break;
|
||||
|
||||
case WID_MTS_ALL: case WID_MTS_OLD: case WID_MTS_NEW:
|
||||
case WID_MTS_EZY: case WID_MTS_CUSTOM1: case WID_MTS_CUSTOM2: // set playlist
|
||||
SelectPlaylist(widget - WID_MTS_ALL);
|
||||
StopMusic();
|
||||
ResetPlaylist();
|
||||
_music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_MTS_ALL));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -627,8 +694,8 @@ struct MusicWindow : public Window {
|
||||
|
||||
case WID_M_TRACK_NAME: {
|
||||
Dimension d = GetStringBoundingBox(STR_MUSIC_TITLE_NONE);
|
||||
for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
|
||||
SetDParamStr(0, GetSongName(i));
|
||||
for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) {
|
||||
SetDParamStr(0, song->songname);
|
||||
d = maxdim(d, GetStringBoundingBox(STR_MUSIC_TITLE_NAME));
|
||||
}
|
||||
d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
|
||||
@@ -654,8 +721,8 @@ struct MusicWindow : public Window {
|
||||
break;
|
||||
}
|
||||
StringID str = STR_MUSIC_TRACK_NONE;
|
||||
if (_song_is_active != 0 && _music_wnd_cursong != 0) {
|
||||
SetDParam(0, GetTrackNumber(_music_wnd_cursong - 1));
|
||||
if (_music.IsPlaying()) {
|
||||
SetDParam(0, _music.GetCurrentSong().tracknr);
|
||||
SetDParam(1, 2);
|
||||
str = STR_MUSIC_TRACK_DIGIT;
|
||||
}
|
||||
@@ -668,9 +735,9 @@ struct MusicWindow : public Window {
|
||||
StringID str = STR_MUSIC_TITLE_NONE;
|
||||
if (BaseMusic::GetUsedSet()->num_available == 0) {
|
||||
str = STR_MUSIC_TITLE_NOMUSIC;
|
||||
} else if (_song_is_active != 0 && _music_wnd_cursong != 0) {
|
||||
} else if (_music.IsPlaying()) {
|
||||
str = STR_MUSIC_TITLE_NAME;
|
||||
SetDParamStr(0, GetSongName(_music_wnd_cursong - 1));
|
||||
SetDParamStr(0, _music.GetCurrentSong().songname);
|
||||
}
|
||||
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_FROMSTRING, SA_HOR_CENTER);
|
||||
break;
|
||||
@@ -710,23 +777,19 @@ struct MusicWindow : public Window {
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_M_PREV: // skip to prev
|
||||
if (!_song_is_active) return;
|
||||
SkipToPrevSong();
|
||||
this->SetDirty();
|
||||
_music.Prev();
|
||||
break;
|
||||
|
||||
case WID_M_NEXT: // skip to next
|
||||
if (!_song_is_active) return;
|
||||
SkipToNextSong();
|
||||
this->SetDirty();
|
||||
_music.Next();
|
||||
break;
|
||||
|
||||
case WID_M_STOP: // stop playing
|
||||
_settings_client.music.playing = false;
|
||||
_music.Stop();
|
||||
break;
|
||||
|
||||
case WID_M_PLAY: // start playing
|
||||
_settings_client.music.playing = true;
|
||||
_music.Play();
|
||||
break;
|
||||
|
||||
case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: { // volume sliders
|
||||
@@ -741,7 +804,7 @@ struct MusicWindow : public Window {
|
||||
if (new_vol < 3) new_vol = 0;
|
||||
if (new_vol != *vol) {
|
||||
*vol = new_vol;
|
||||
if (widget == WID_M_MUSIC_VOL) MusicVolumeChanged(new_vol);
|
||||
if (widget == WID_M_MUSIC_VOL) MusicDriver::GetInstance()->SetVolume(new_vol);
|
||||
this->SetDirty();
|
||||
}
|
||||
|
||||
@@ -750,12 +813,13 @@ struct MusicWindow : public Window {
|
||||
}
|
||||
|
||||
case WID_M_SHUFFLE: // toggle shuffle
|
||||
_settings_client.music.shuffle ^= 1;
|
||||
this->SetWidgetLoweredState(WID_M_SHUFFLE, _settings_client.music.shuffle);
|
||||
if (_music.IsShuffle()) {
|
||||
_music.Unshuffle();
|
||||
} else {
|
||||
_music.Shuffle();
|
||||
}
|
||||
this->SetWidgetLoweredState(WID_M_SHUFFLE, _music.IsShuffle());
|
||||
this->SetWidgetDirty(WID_M_SHUFFLE);
|
||||
StopMusic();
|
||||
ResetPlaylist();
|
||||
this->SetDirty();
|
||||
break;
|
||||
|
||||
case WID_M_PROGRAMME: // show track selection
|
||||
@@ -764,10 +828,7 @@ struct MusicWindow : public Window {
|
||||
|
||||
case WID_M_ALL: case WID_M_OLD: case WID_M_NEW:
|
||||
case WID_M_EZY: case WID_M_CUSTOM1: case WID_M_CUSTOM2: // playlist
|
||||
SelectPlaylist(widget - WID_M_ALL);
|
||||
StopMusic();
|
||||
ResetPlaylist();
|
||||
this->SetDirty();
|
||||
_music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_M_ALL));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -4749,7 +4749,7 @@ static void NewSpriteGroup(ByteReader *buf)
|
||||
}
|
||||
}
|
||||
|
||||
group->num_ranges = optimised.size();
|
||||
group->num_ranges = (uint)optimised.size(); // cast is safe, there should never be 2**31 elements here
|
||||
if (group->num_ranges > 0) {
|
||||
group->ranges = MallocT<DeterministicSpriteGroupRange>(group->num_ranges);
|
||||
MemCpyT(group->ranges, &optimised.front(), group->num_ranges);
|
||||
|
@@ -393,8 +393,7 @@ static void LoadIntroGame(bool load_newgrfs = true)
|
||||
|
||||
CheckForMissingGlyphs();
|
||||
|
||||
/* Play main theme */
|
||||
if (MusicDriver::GetInstance()->IsSongPlaying()) ResetMusic();
|
||||
MusicLoop(); // ensure music is correct
|
||||
}
|
||||
|
||||
void MakeNewgameSettingsLive()
|
||||
|
@@ -688,6 +688,10 @@ static void ShipController(Ship *v)
|
||||
if (v->current_order.IsType(OT_LEAVESTATION)) {
|
||||
v->current_order.Free();
|
||||
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
|
||||
/* Test if continuing forward would lead to a dead-end, moving into the dock. */
|
||||
DiagDirection exitdir = VehicleExitDir(v->direction, v->state);
|
||||
TileIndex tile = TileAddByDiagDir(v->tile, exitdir);
|
||||
if (TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0, exitdir)) == TRACK_BIT_NONE) goto reverse_direction;
|
||||
} else if (v->dest_tile != 0) {
|
||||
/* We have a target, let's see if we reached it... */
|
||||
if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
|
||||
|
195
src/town_cmd.cpp
195
src/town_cmd.cpp
@@ -47,6 +47,7 @@
|
||||
#include "game/game.hpp"
|
||||
#include "zoom_func.h"
|
||||
#include "zoning.h"
|
||||
#include "scope.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
#include "table/town_land.h"
|
||||
@@ -1736,6 +1737,9 @@ void UpdateTownMaxPass(Town *t)
|
||||
t->supplied[CT_MAIL].old_max = t->cache.population >> 4;
|
||||
}
|
||||
|
||||
static void UpdateTownGrowthRate(Town *t);
|
||||
static void UpdateTownGrowth(Town *t);
|
||||
|
||||
/**
|
||||
* Does the actual town creation.
|
||||
*
|
||||
@@ -1815,6 +1819,7 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize
|
||||
|
||||
t->cache.num_houses -= x;
|
||||
UpdateTownRadius(t);
|
||||
UpdateTownGrowthRate(t);
|
||||
UpdateTownMaxPass(t);
|
||||
UpdateAirportsNoise();
|
||||
}
|
||||
@@ -2523,6 +2528,7 @@ static void DoBuildHouse(Town *t, TileIndex tile, HouseID house, byte random_bit
|
||||
|
||||
MakeTownHouse(tile, t, construction_counter, construction_stage, house, random_bits);
|
||||
UpdateTownRadius(t);
|
||||
UpdateTownGrowthRate(t);
|
||||
UpdateTownCargoes(t, tile);
|
||||
}
|
||||
|
||||
@@ -2781,8 +2787,6 @@ const CargoSpec *FindFirstCargoWithTownEffect(TownEffect effect)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void UpdateTownGrowRate(Town *t);
|
||||
|
||||
/**
|
||||
* Change the cargo goal of a town.
|
||||
* @param tile Unused.
|
||||
@@ -2811,7 +2815,7 @@ CommandCost CmdTownCargoGoal(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
t->goal[te] = p2;
|
||||
UpdateTownGrowRate(t);
|
||||
UpdateTownGrowth(t);
|
||||
InvalidateWindowData(WC_TOWN_VIEW, index);
|
||||
}
|
||||
|
||||
@@ -2861,7 +2865,7 @@ CommandCost CmdTownGrowthRate(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
if (p2 == 0) {
|
||||
/* Just clear the flag, UpdateTownGrowRate will determine a proper growth rate */
|
||||
/* Just clear the flag, UpdateTownGrowth will determine a proper growth rate */
|
||||
ClrBit(t->flags, TOWN_CUSTOM_GROWTH);
|
||||
} else {
|
||||
uint old_rate = t->growth_rate;
|
||||
@@ -2875,7 +2879,7 @@ CommandCost CmdTownGrowthRate(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
|
||||
t->growth_rate = p2;
|
||||
SetBit(t->flags, TOWN_CUSTOM_GROWTH);
|
||||
}
|
||||
UpdateTownGrowRate(t);
|
||||
UpdateTownGrowth(t);
|
||||
InvalidateWindowData(WC_TOWN_VIEW, p1);
|
||||
}
|
||||
|
||||
@@ -3162,7 +3166,7 @@ static CommandCost TownActionFundBuildings(Town *t, DoCommandFlag flags)
|
||||
t->fund_buildings_months = 3;
|
||||
|
||||
/* Enable growth (also checking GameScript's opinion) */
|
||||
UpdateTownGrowRate(t);
|
||||
UpdateTownGrowth(t);
|
||||
|
||||
/* Build a new house, but add a small delay to make sure
|
||||
* that spamming funding doesn't let town grow any faster
|
||||
@@ -3368,10 +3372,121 @@ static void UpdateTownRating(Town *t)
|
||||
SetWindowDirty(WC_TOWN_AUTHORITY, t->index);
|
||||
}
|
||||
|
||||
static void UpdateTownGrowRate(Town *t)
|
||||
|
||||
/**
|
||||
* Updates town grow counter after growth rate change.
|
||||
* Preserves relative house builting progress whenever it can.
|
||||
* @param town The town to calculate grow counter for
|
||||
* @param prev_growth_rate Town growth rate before it changed (one that was used with grow counter to be updated)
|
||||
*/
|
||||
static void UpdateTownGrowCounter(Town *t, uint16 prev_growth_rate)
|
||||
{
|
||||
if (t->growth_rate == TOWN_GROWTH_RATE_NONE) return;
|
||||
if (prev_growth_rate == TOWN_GROWTH_RATE_NONE) {
|
||||
t->grow_counter = min(t->growth_rate, t->grow_counter);
|
||||
return;
|
||||
}
|
||||
t->grow_counter = RoundDivSU((uint32)t->grow_counter * (t->growth_rate + 1), prev_growth_rate + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates amount of active stations in the range of town (HZB_TOWN_EDGE).
|
||||
* @param town The town to calculate stations for
|
||||
* @returns Amount of active stations
|
||||
*/
|
||||
static int CountActiveStations(Town *t)
|
||||
{
|
||||
int n = 0;
|
||||
const Station *st;
|
||||
FOR_ALL_STATIONS(st) {
|
||||
if (DistanceSquare(st->xy, t->xy) <= t->cache.squared_town_zone_radius[0]) {
|
||||
if (st->time_since_load <= 20 || st->time_since_unload <= 20) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates town growth rate in normal conditions (custom growth rate not set).
|
||||
* If town growth speed is set to None(0) returns the same rate as if it was Normal(2).
|
||||
* @param town The town to calculate growth rate for
|
||||
* @returns Calculated growth rate
|
||||
*/
|
||||
static uint GetNormalGrowthRate(Town *t)
|
||||
{
|
||||
static const uint16 _grow_count_values[2][6] = {
|
||||
{ 120, 120, 120, 100, 80, 60 }, // Fund new buildings has been activated
|
||||
{ 320, 420, 300, 220, 160, 100 } // Normal values
|
||||
};
|
||||
|
||||
int n = CountActiveStations(t);
|
||||
uint16 m = _grow_count_values[t->fund_buildings_months != 0 ? 0 : 1][min(n, 5)];
|
||||
|
||||
int growth_multiplier;
|
||||
if (_settings_game.economy.town_growth_rate == 0) {
|
||||
growth_multiplier = 1;
|
||||
} else if (_settings_game.economy.town_growth_rate > 0) {
|
||||
growth_multiplier = _settings_game.economy.town_growth_rate - 1;
|
||||
} else {
|
||||
growth_multiplier = _settings_game.economy.town_growth_rate;
|
||||
}
|
||||
|
||||
if (growth_multiplier < 0) {
|
||||
m <<= (-growth_multiplier);
|
||||
} else {
|
||||
m >>= growth_multiplier;
|
||||
}
|
||||
if (t->larger_town) m /= 2;
|
||||
|
||||
if (_settings_game.economy.town_growth_cargo_transported > 0) {
|
||||
uint32 inverse_m = UINT32_MAX / m;
|
||||
auto calculate_cargo_ratio_fix15 = [](const TransportedCargoStat<uint32> &stat) -> uint32 {
|
||||
return stat.old_max ? ((uint64) (stat.old_act << 15)) / stat.old_max : 1 << 15;
|
||||
};
|
||||
uint32 cargo_ratio_fix16 = calculate_cargo_ratio_fix15(t->supplied[CT_PASSENGERS]) + calculate_cargo_ratio_fix15(t->supplied[CT_MAIL]);
|
||||
uint32 cargo_dependant_part = (((uint64) cargo_ratio_fix16) * ((uint64) inverse_m) * _settings_game.economy.town_growth_cargo_transported) >> 16;
|
||||
uint32 non_cargo_dependant_part = ((uint64) inverse_m) * (100 - _settings_game.economy.town_growth_cargo_transported);
|
||||
uint32 total = (cargo_dependant_part + non_cargo_dependant_part);
|
||||
if (total == 0) {
|
||||
ClrBit(t->flags, TOWN_IS_GROWING);
|
||||
return UINT16_MAX;
|
||||
}
|
||||
m = ((uint64) UINT32_MAX * 100) / total;
|
||||
}
|
||||
|
||||
return TownTicksToGameTicks(m / (t->cache.num_houses / 50 + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates town growth rate.
|
||||
* @param town The town to update growth rate for
|
||||
*/
|
||||
static void UpdateTownGrowthRate(Town *t)
|
||||
{
|
||||
if (HasBit(t->flags, TOWN_CUSTOM_GROWTH)) return;
|
||||
uint old_rate = t->growth_rate;
|
||||
t->growth_rate = GetNormalGrowthRate(t);
|
||||
UpdateTownGrowCounter(t, old_rate);
|
||||
SetWindowDirty(WC_TOWN_VIEW, t->index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates town growth state (whether it is growing or not).
|
||||
* @param town The town to update growth for
|
||||
*/
|
||||
static void UpdateTownGrowth(Town *t)
|
||||
{
|
||||
auto guard = scope_guard([t]() {
|
||||
SetWindowDirty(WC_TOWN_VIEW, t->index);
|
||||
});
|
||||
|
||||
SetBit(t->flags, TOWN_IS_GROWING);
|
||||
UpdateTownGrowthRate(t);
|
||||
if (!HasBit(t->flags, TOWN_IS_GROWING)) return;
|
||||
|
||||
ClrBit(t->flags, TOWN_IS_GROWING);
|
||||
|
||||
if (_settings_game.economy.town_growth_rate == 0 && t->fund_buildings_months == 0) return;
|
||||
|
||||
@@ -3398,71 +3513,9 @@ static void UpdateTownGrowRate(Town *t)
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Towns are processed every TOWN_GROWTH_TICKS ticks, and this is the
|
||||
* number of times towns are processed before a new building is built.
|
||||
*/
|
||||
static const uint16 _grow_count_values[2][6] = {
|
||||
{ 120, 120, 120, 100, 80, 60 }, // Fund new buildings has been activated
|
||||
{ 320, 420, 300, 220, 160, 100 } // Normal values
|
||||
};
|
||||
|
||||
int n = 0;
|
||||
|
||||
const Station *st;
|
||||
FOR_ALL_STATIONS(st) {
|
||||
if (DistanceSquare(st->xy, t->xy) <= t->cache.squared_town_zone_radius[0]) {
|
||||
if (st->time_since_load <= 20 || st->time_since_unload <= 20) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16 m;
|
||||
|
||||
if (t->fund_buildings_months != 0) {
|
||||
m = _grow_count_values[0][min(n, 5)];
|
||||
} else {
|
||||
m = _grow_count_values[1][min(n, 5)];
|
||||
if (n == 0 && !Chance16(1, 12)) return;
|
||||
}
|
||||
|
||||
/* Use the normal growth rate values if new buildings have been funded in
|
||||
* this town and the growth rate is set to none. */
|
||||
int growth_multiplier;
|
||||
if (_settings_game.economy.town_growth_rate == 0) {
|
||||
growth_multiplier = 1;
|
||||
} else if (_settings_game.economy.town_growth_rate > 0) {
|
||||
growth_multiplier = _settings_game.economy.town_growth_rate - 1;
|
||||
} else {
|
||||
growth_multiplier = _settings_game.economy.town_growth_rate;
|
||||
}
|
||||
|
||||
if (growth_multiplier < 0) {
|
||||
m <<= (-growth_multiplier);
|
||||
} else {
|
||||
m >>= growth_multiplier;
|
||||
}
|
||||
if (t->larger_town) m /= 2;
|
||||
|
||||
if (_settings_game.economy.town_growth_cargo_transported > 0) {
|
||||
uint32 inverse_m = UINT32_MAX / m;
|
||||
auto calculate_cargo_ratio_fix15 = [](const TransportedCargoStat<uint32> &stat) -> uint32 {
|
||||
return stat.old_max ? ((uint64) (stat.old_act << 15)) / stat.old_max : 1 << 15;
|
||||
};
|
||||
uint32 cargo_ratio_fix16 = calculate_cargo_ratio_fix15(t->supplied[CT_PASSENGERS]) + calculate_cargo_ratio_fix15(t->supplied[CT_MAIL]);
|
||||
uint32 cargo_dependant_part = (((uint64) cargo_ratio_fix16) * ((uint64) inverse_m) * _settings_game.economy.town_growth_cargo_transported) >> 16;
|
||||
uint32 non_cargo_dependant_part = ((uint64) inverse_m) * (100 - _settings_game.economy.town_growth_cargo_transported);
|
||||
uint32 total = (cargo_dependant_part + non_cargo_dependant_part);
|
||||
if (total == 0) return;
|
||||
m = ((uint64) UINT32_MAX * 100) / total;
|
||||
}
|
||||
|
||||
t->growth_rate = TownTicksToGameTicks(m / (t->cache.num_houses / 50 + 1));
|
||||
t->grow_counter = min(t->growth_rate, t->grow_counter);
|
||||
if (t->fund_buildings_months == 0 && CountActiveStations(t) == 0 && !Chance16(1, 12)) return;
|
||||
|
||||
SetBit(t->flags, TOWN_IS_GROWING);
|
||||
SetWindowDirty(WC_TOWN_VIEW, t->index);
|
||||
}
|
||||
|
||||
static void UpdateTownAmounts(Town *t)
|
||||
@@ -3698,8 +3751,8 @@ void TownsMonthlyLoop()
|
||||
}
|
||||
|
||||
UpdateTownAmounts(t);
|
||||
UpdateTownGrowth(t);
|
||||
UpdateTownRating(t);
|
||||
UpdateTownGrowRate(t);
|
||||
UpdateTownUnwanted(t);
|
||||
UpdateTownCargoes(t);
|
||||
}
|
||||
|
@@ -753,6 +753,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string to draw the town name.
|
||||
* @param t Town to draw.
|
||||
* @return The string to use.
|
||||
*/
|
||||
static StringID GetTownString(const Town *t)
|
||||
{
|
||||
return t->larger_town ? STR_TOWN_DIRECTORY_CITY : STR_TOWN_DIRECTORY_TOWN;
|
||||
}
|
||||
|
||||
virtual void DrawWidget(const Rect &r, int widget) const
|
||||
{
|
||||
switch (widget) {
|
||||
@@ -791,7 +801,7 @@ public:
|
||||
|
||||
SetDParam(0, t->index);
|
||||
SetDParam(1, t->cache.population);
|
||||
DrawString(text_left, text_right, y + (this->resize.step_height - FONT_HEIGHT_NORMAL) / 2, STR_TOWN_DIRECTORY_TOWN);
|
||||
DrawString(text_left, text_right, y + (this->resize.step_height - FONT_HEIGHT_NORMAL) / 2, GetTownString(t));
|
||||
|
||||
y += this->resize.step_height;
|
||||
if (++n == this->vscroll->GetCapacity()) break; // max number of towns in 1 window
|
||||
@@ -830,7 +840,7 @@ public:
|
||||
|
||||
SetDParam(0, t->index);
|
||||
SetDParamMaxDigits(1, 8);
|
||||
d = maxdim(d, GetStringBoundingBox(STR_TOWN_DIRECTORY_TOWN));
|
||||
d = maxdim(d, GetStringBoundingBox(GetTownString(t)));
|
||||
}
|
||||
Dimension icon_size = GetSpriteSize(SPR_TOWN_RATING_GOOD);
|
||||
d.width += icon_size.width + 2;
|
||||
|
@@ -692,4 +692,25 @@ static inline bool IsUphillTrackdir(Slope slope, Trackdir dir)
|
||||
return HasBit(_uphill_trackdirs[RemoveHalftileSlope(slope)], dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the side in which the vehicle will leave the tile
|
||||
*
|
||||
* @param direction vehicle direction
|
||||
* @param track vehicle track bits
|
||||
* @return side of tile the vehicle will leave
|
||||
*/
|
||||
static inline DiagDirection VehicleExitDir(Direction direction, TrackBits track)
|
||||
{
|
||||
static const TrackBits state_dir_table[DIAGDIR_END] = { TRACK_BIT_RIGHT, TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER };
|
||||
|
||||
DiagDirection diagdir = DirToDiagDir(direction);
|
||||
|
||||
/* Determine the diagonal direction in which we will exit this tile */
|
||||
if (!HasBit(direction, 0) && track != state_dir_table[diagdir]) {
|
||||
diagdir = ChangeDiagDir(diagdir, DIAGDIRDIFF_90LEFT);
|
||||
}
|
||||
|
||||
return diagdir;
|
||||
}
|
||||
|
||||
#endif /* TRACK_FUNC_H */
|
||||
|
@@ -69,27 +69,6 @@ bool IsValidImageIndex<VEH_TRAIN>(uint8 image_index)
|
||||
return image_index < lengthof(_engine_sprite_base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the side in which the train will leave the tile
|
||||
*
|
||||
* @param direction vehicle direction
|
||||
* @param track vehicle track bits
|
||||
* @return side of tile the train will leave
|
||||
*/
|
||||
static inline DiagDirection TrainExitDir(Direction direction, TrackBits track)
|
||||
{
|
||||
static const TrackBits state_dir_table[DIAGDIR_END] = { TRACK_BIT_RIGHT, TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER };
|
||||
|
||||
DiagDirection diagdir = DirToDiagDir(direction);
|
||||
|
||||
/* Determine the diagonal direction in which we will exit this tile */
|
||||
if (!HasBit(direction, 0) && track != state_dir_table[diagdir]) {
|
||||
diagdir = ChangeDiagDir(diagdir, DIAGDIRDIFF_90LEFT);
|
||||
}
|
||||
|
||||
return diagdir;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the cargo weight multiplier to use for a rail vehicle
|
||||
@@ -469,7 +448,7 @@ int GetTrainStopLocation(StationID station_id, TileIndex tile, Train *v, int *st
|
||||
|
||||
if (TrainCanLeaveTile(front)) {
|
||||
/* Determine the non-diagonal direction in which we will exit this tile */
|
||||
DiagDirection dir = TrainExitDir(front->direction, front->track);
|
||||
DiagDirection dir = VehicleExitDir(front->direction, front->track);
|
||||
/* Calculate next tile */
|
||||
TileIndex tile = front->tile + TileOffsByDiagDir(dir);
|
||||
|
||||
@@ -2213,9 +2192,9 @@ void ReverseTrainDirection(Train *v)
|
||||
return;
|
||||
}
|
||||
|
||||
/* TrainExitDir does not always produce the desired dir for depots and
|
||||
/* VehicleExitDir does not always produce the desired dir for depots and
|
||||
* tunnels/bridges that is needed for UpdateSignalsOnSegment. */
|
||||
DiagDirection dir = TrainExitDir(v->direction, v->track);
|
||||
DiagDirection dir = VehicleExitDir(v->direction, v->track);
|
||||
if (IsRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)) dir = INVALID_DIAGDIR;
|
||||
|
||||
if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
|
||||
@@ -3634,7 +3613,7 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data)
|
||||
/* not front engine of a train, inside wormhole or depot, crashed */
|
||||
if (!t->IsFrontEngine() || !(t->track & TRACK_BIT_MASK)) return NULL;
|
||||
|
||||
if (t->cur_speed > 5 || TrainExitDir(t->direction, t->track) != exitdir) return NULL;
|
||||
if (t->cur_speed > 5 || VehicleExitDir(t->direction, t->track) != exitdir) return NULL;
|
||||
|
||||
return t;
|
||||
}
|
||||
@@ -4573,7 +4552,7 @@ static TileIndex TrainApproachingCrossingTile(const Train *v)
|
||||
|
||||
if (!TrainCanLeaveTile(v)) return INVALID_TILE;
|
||||
|
||||
DiagDirection dir = TrainExitDir(v->direction, v->track);
|
||||
DiagDirection dir = VehicleExitDir(v->direction, v->track);
|
||||
TileIndex tile = v->tile + TileOffsByDiagDir(dir);
|
||||
|
||||
/* not a crossing || wrong axis || unusable rail (wrong type or owner) */
|
||||
@@ -4606,7 +4585,7 @@ static bool TrainCheckIfLineEnds(Train *v, bool reverse)
|
||||
if (!TrainCanLeaveTile(v)) return true;
|
||||
|
||||
/* Determine the non-diagonal direction in which we will exit this tile */
|
||||
DiagDirection dir = TrainExitDir(v->direction, v->track);
|
||||
DiagDirection dir = VehicleExitDir(v->direction, v->track);
|
||||
/* Calculate next tile */
|
||||
TileIndex tile = v->tile + TileOffsByDiagDir(dir);
|
||||
|
||||
@@ -4689,7 +4668,7 @@ static bool TrainLocoHandler(Train *v, bool mode)
|
||||
/* Try to reserve a path when leaving the station as we
|
||||
* might not be marked as wanting a reservation, e.g.
|
||||
* when an overlength train gets turned around in a station. */
|
||||
DiagDirection dir = TrainExitDir(v->direction, v->track);
|
||||
DiagDirection dir = VehicleExitDir(v->direction, v->track);
|
||||
if (IsRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)) dir = INVALID_DIAGDIR;
|
||||
|
||||
if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
|
||||
|
Reference in New Issue
Block a user