Merge branch 'master' into jgrpp

# Conflicts:
#	CMakeLists.txt
#	COMPILING.md
#	src/console.cpp
#	src/console_cmds.cpp
#	src/console_internal.h
#	src/rev.cpp.in
pull/251/head
Jonathan G Rennison 3 years ago
commit 253772e9af

@ -1,7 +0,0 @@
## Version of OpenTTD
## Expected result
## Actual result
## Steps to reproduce

@ -0,0 +1,17 @@
---
name: Bugs
about: Found a bug in OpenTTD?
title: "Bug Report"
---
## Version of OpenTTD
<!-- Indicate what version of OpenTTD you are using, including your OS. -->
## Expected result
<!-- Describe in a few words what you expected to happen. -->
## Actual result
<!-- Descibe in a few words what actually happens. -->
## Steps to reproduce
<!-- As detailed as possible, please tell us how we can reproduce this. Feel free to attach a savegame (zip it first) to make it more clear. -->

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Suggestions and ideas?
url: https://www.tt-forums.net/viewforum.php?f=32
about: Have a suggestion or an idea for a cool new feature? Post them on our forum!

@ -0,0 +1,12 @@
---
name: Crash
about: Did OpenTTD crash?
title: "Crash Report"
---
<!-- Please zip the crash.log, crash.dmp and crash.sav and attach it to this crash report. -->
## Version of OpenTTD
<!-- Indicate what version of OpenTTD you are using, including your OS. -->
## Steps to reproduce
<!-- Please spend a few words if you can reproduce this problem. -->

@ -4,7 +4,9 @@ if(NOT BINARY_NAME)
set(BINARY_NAME openttd) set(BINARY_NAME openttd)
endif() endif()
project(${BINARY_NAME}) project(${BINARY_NAME}
VERSION 1.12.0
)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "In-source builds not allowed. Please run \"cmake ..\" from the build directory. You may need to delete \"${CMAKE_SOURCE_DIR}/CMakeCache.txt\" first.") message(FATAL_ERROR "In-source builds not allowed. Please run \"cmake ..\" from the build directory. You may need to delete \"${CMAKE_SOURCE_DIR}/CMakeCache.txt\" first.")
@ -92,6 +94,9 @@ if(OPTION_TOOLS_ONLY)
${CMAKE_COMMAND} ${CMAKE_COMMAND}
-DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated -DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated
-DCPACK_BINARY_DIR=${CMAKE_BINARY_DIR} -DCPACK_BINARY_DIR=${CMAKE_BINARY_DIR}
-DREV_MAJOR=${CMAKE_PROJECT_VERSION_MAJOR}
-DREV_MINOR=${CMAKE_PROJECT_VERSION_MINOR}
-DREV_BUILD=${CMAKE_PROJECT_VERSION_PATCH}
$<$<PLATFORM_ID:Windows>:-DWIN32=TRUE> $<$<PLATFORM_ID:Windows>:-DWIN32=TRUE>
-P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake" -P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
@ -463,6 +468,9 @@ add_custom_target(find_version
${CMAKE_COMMAND} ${CMAKE_COMMAND}
-DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated -DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated
-DCPACK_BINARY_DIR=${CMAKE_BINARY_DIR} -DCPACK_BINARY_DIR=${CMAKE_BINARY_DIR}
-DREV_MAJOR=${CMAKE_PROJECT_VERSION_MAJOR}
-DREV_MINOR=${CMAKE_PROJECT_VERSION_MINOR}
-DREV_BUILD=${CMAKE_PROJECT_VERSION_PATCH}
-DCONFIGURE_DEFINES="${CFG_DEFS}" -DCONFIGURE_DEFINES="${CFG_DEFS}"
$<$<PLATFORM_ID:Windows>:-DWIN32=TRUE> $<$<PLATFORM_ID:Windows>:-DWIN32=TRUE>
-P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake" -P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake"

@ -2,27 +2,28 @@
## Required/optional libraries ## Required/optional libraries
The following libraries are used by OpenTTD for: OpenTTD makes use of the following external libraries:
- zlib: (de)compressing of old (0.3.0-1.0.5) savegames, content downloads, - (encouraged) zlib: (de)compressing of old (0.3.0-1.0.5) savegames, content downloads,
heightmaps heightmaps
- liblzo2: (de)compressing of old (pre 0.3.0) savegames - (encouraged) liblzma: (de)compressing of savegames (1.1.0 and later)
- liblzma: (de)compressing of savegames (1.1.0 and later) - (encouraged) libpng: making screenshots and loading heightmaps
- libzstd: (de)compressing of savegames (1.11.0 and later) - (optional) liblzo2: (de)compressing of old (pre 0.3.0) savegames
- libpng: making screenshots and loading heightmaps - (optional) libzstd: (de)compressing of multiplayer join savegames, if available
For Linux, the following additional libraries are used (for non-dedicated only):
- libSDL2: hardware access (video, sound, mouse)
- libfreetype: loading generic fonts and rendering them - libfreetype: loading generic fonts and rendering them
- libfontconfig: searching for fonts, resolving font names to actual fonts - libfontconfig: searching for fonts, resolving font names to actual fonts
- libicu: handling of right-to-left scripts (e.g. Arabic and Persian) and - libicu: handling of right-to-left scripts (e.g. Arabic and Persian) and
natural sorting of strings (Linux only) natural sorting of strings
- libSDL2: hardware access (video, sound, mouse) (not required for Windows or macOS)
OpenTTD does not require any of the libraries to be present, but without 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 liblzma you cannot open most recent savegames and without zlib you cannot
open most older savegames or use the content downloading system. open most older savegames or use the content downloading system.
Without libSDL/liballegro on non-Windows and non-macOS machines you have
no graphical user interface; you would be building a dedicated server.
## Windows: ## Windows
You need Microsoft Visual Studio 2017 or more recent. You need Microsoft Visual Studio 2017 or more recent.
@ -79,6 +80,8 @@ files himself via the `ZERO_CHECK` project.
## All other platforms ## All other platforms
Minimum required version of CMake is 3.9. Minimum required version of CMake is 3.9.
By default this produces a Debug build with assertations enabled.
This is a far slower build than release builds.
```bash ```bash
mkdir build mkdir build
@ -90,6 +93,25 @@ make
For more information on how to use CMake (including how to make Release builds), For more information on how to use CMake (including how to make Release builds),
we urge you to read [their excellent manual](https://cmake.org/cmake/help/latest/guide/user-interaction/index.html). we urge you to read [their excellent manual](https://cmake.org/cmake/help/latest/guide/user-interaction/index.html).
## CMake Options
Via CMake, several options can be influenced to get different types of
builds.
- `-DCMAKE_BUILD_TYPE=RelWithDebInfo`: build a release build. This is
significant faster than a debug build, but has far less useful information
in case of a crash.
- `-DOPTION_DEDICATED=ON`: build OpenTTD without a GUI. Useful if you are
running a headless server, as it requires less libraries to operate.
- `-DOPTION_USE_ASSERTS=OFF`: disable asserts. Use with care, as assert
statements capture early signs of trouble. Release builds have them
disabled by default.
- `-DOPTION_USE_THREADS=OFF`: disable the use of threads. This will block
the interface in many places, and in general gives a worse experience of
the game. Use with care.
- `-DOPTION_TOOLS_ONLY=ON`: only build tools like `strgen`. Does not build
the game itself. Useful for cross-compiling.
## Supported compilers ## Supported compilers
Every compiler that is supported by CMake and supports C++17, should be Every compiler that is supported by CMake and supports C++17, should be

@ -14,7 +14,6 @@
- Ingo von Borstel (planetmaker) - General coding, Support (since 1.1) - Ingo von Borstel (planetmaker) - General coding, Support (since 1.1)
- Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5) - Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5)
- José Soler (Terkhen) - General coding (since 1.0) - José Soler (Terkhen) - General coding (since 1.0)
- Leif Linse (Zuu) - AI/Game Script (since 1.2)
### Inactive Developers: ### Inactive Developers:
@ -28,6 +27,7 @@
- Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5) - Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5)
- Patric Stout (TrueBrain) - NoProgrammer (0.3 - 1.2), sys op (active) - Patric Stout (TrueBrain) - NoProgrammer (0.3 - 1.2), sys op (active)
- Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3) - Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3)
- Leif Linse (Zuu) - AI/Game Script (1.2 - 1.6)
### Retired Developers: ### Retired Developers:

@ -367,15 +367,13 @@ OpenTTD has a [community-maintained wiki](https://wiki.openttd.org/), including
OpenTTD has been ported to several platforms and operating systems. OpenTTD has been ported to several platforms and operating systems.
The currently working platforms are: The currently supported platforms are:
- FreeBSD (SDL) - Linux (SDL (OpenGL and non-OpenGL))
- Haiku (SDL) - macOS (universal) (Cocoa)
- Linux (SDL) - Windows (Win32 GDI / OpenGL)
- macOS (universal) (Cocoa video and sound drivers)
- OpenBSD (SDL) Other platforms may also work (in particular various BSD systems), but we don't actively test or maintain these.
- OS/2 (SDL)
- Windows (Win32 GDI (faster) or SDL)
### 1.3.1) Legacy support ### 1.3.1) Legacy support
Platforms, languages and compilers change. Platforms, languages and compilers change.
@ -400,9 +398,9 @@ For some platforms, you will need to refer to [the installation guide](https://w
The free data files, split into OpenGFX for graphics, OpenSFX for sounds and The free data files, split into OpenGFX for graphics, OpenSFX for sounds and
OpenMSX for music can be found at: OpenMSX for music can be found at:
- https://www.openttd.org/download-opengfx for OpenGFX - https://www.openttd.org/downloads/opengfx-releases/ for OpenGFX
- https://www.openttd.org/download-opensfx for OpenSFX - https://www.openttd.org/downloads/opensfx-releases/ for OpenSFX
- https://www.openttd.org/download-openmsx for OpenMSX - https://www.openttd.org/downloads/openmsx-releases/ for OpenMSX
Please follow the readme of these packages about the installation procedure. Please follow the readme of these packages about the installation procedure.
The Windows installer can optionally download and install these packages. The Windows installer can optionally download and install these packages.

@ -1,5 +1,15 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
if(NOT REV_MAJOR)
set(REV_MAJOR 0)
endif()
if(NOT REV_MINOR)
set(REV_MINOR 0)
endif()
if(NOT REV_BUILD)
set(REV_BUILD 0)
endif()
# #
# Finds the current version of the current folder. # Finds the current version of the current folder.
# #

@ -0,0 +1,557 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<!-- derived from Iron Horse branch at https://github.com/andythenorth/iron-horse/tree/company_colour_indexes -->
<head>
<title>OpenTTD Company Colour Indexes</title>
<meta charset="UTF-8" />
<style>
th {
vertical-align: top;
border-bottom: solid 1px #ddd;
padding-top: 30px;
padding-right: 15px;
text-align: right;
}
tr.top td {
padding-top: 20px;
}
tr.bottom td {
border-bottom: solid 1px #ddd;
padding-bottom: 10px;
}
td {
text-align: right;
}
span {
border: solid 1px #000;
width: 32px;
height: 32px;
display: inline-block;
vertical-align: top;
margin-left: 0px;
}
</style>
</head>
<body>
<h1>Company Colour Indexes</h1>
<p>Hex / dec indexes into the DOS palette</p>
<p>
Visual representation of values derived from <a href="https://github.com/frosch123/TTDViewer/blob/master/src/recolor.xml#L186">https://github.com/frosch123/TTDViewer/blob/master/src/recolor.xml#L186</a>
</p>
<table style="margin-top:30px;">
<tbody>
<tr class="top">
<th rowspan="3">COLOUR_DARK_BLUE</th>
<td><span style="background-color:rgb( 8, 24, 88)"></span></td>
<td><span style="background-color:rgb( 12, 36, 104)"></span></td>
<td><span style="background-color:rgb( 20, 52, 124)"></span></td>
<td><span style="background-color:rgb( 28, 68, 140)"></span></td>
<td><span style="background-color:rgb( 40, 92, 164)"></span></td>
<td><span style="background-color:rgb( 56, 120, 188)"></span></td>
<td><span style="background-color:rgb( 72, 152, 216)"></span></td>
<td><span style="background-color:rgb(100, 172, 224)"></span></td>
</tr>
<tr>
<td>0xc6</td>
<td>0xc7</td>
<td>0xc8</td>
<td>0xc9</td>
<td>0xca</td>
<td>0xcb</td>
<td>0xcc</td>
<td>0xcd</td>
</tr>
<tr class="bottom">
<td>198</td>
<td>199</td>
<td>200</td>
<td>201</td>
<td>202</td>
<td>203</td>
<td>204</td>
<td>205</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_PALE_GREEN</th>
<td><span style="background-color:rgb( 16, 52, 24)"></span></td>
<td><span style="background-color:rgb( 32, 72, 44)"></span></td>
<td><span style="background-color:rgb( 56, 96, 72)"></span></td>
<td><span style="background-color:rgb( 76, 116, 88)"></span></td>
<td><span style="background-color:rgb( 96, 136, 108)"></span></td>
<td><span style="background-color:rgb(120, 164, 136)"></span></td>
<td><span style="background-color:rgb(152, 192, 168)"></span></td>
<td><span style="background-color:rgb(184, 220, 200)"></span></td>
</tr>
<tr>
<td>0x60</td>
<td>0x61</td>
<td>0x62</td>
<td>0x63</td>
<td>0x64</td>
<td>0x65</td>
<td>0x66</td>
<td>0x67</td>
</tr>
<tr class="bottom">
<td>96</td>
<td>97</td>
<td>98</td>
<td>99</td>
<td>100</td>
<td>101</td>
<td>102</td>
<td>103</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_PINK</th>
<td><span style="background-color:rgb(112, 16, 32)"></span></td>
<td><span style="background-color:rgb(136, 32, 52)"></span></td>
<td><span style="background-color:rgb(160, 56, 76)"></span></td>
<td><span style="background-color:rgb(188, 84, 108)"></span></td>
<td><span style="background-color:rgb(204, 104, 124)"></span></td>
<td><span style="background-color:rgb(220, 132, 144)"></span></td>
<td><span style="background-color:rgb(236, 156, 164)"></span></td>
<td><span style="background-color:rgb(252, 188, 192)"></span></td>
</tr>
<tr>
<td>0x2a</td>
<td>0x2b</td>
<td>0x2c</td>
<td>0x2d</td>
<td>0x2e</td>
<td>0x2f</td>
<td>0x30</td>
<td>0x31</td>
</tr>
<tr class="bottom">
<td>42</td>
<td>43</td>
<td>44</td>
<td>45</td>
<td>46</td>
<td>47</td>
<td>48</td>
<td>49</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_YELLOW</th>
<td><span style="background-color:rgb(128, 68, 8)"></span></td>
<td><span style="background-color:rgb(156, 96, 16)"></span></td>
<td><span style="background-color:rgb(184, 120, 24)"></span></td>
<td><span style="background-color:rgb(212, 156, 32)"></span></td>
<td><span style="background-color:rgb(232, 184, 16)"></span></td>
<td><span style="background-color:rgb(252, 212, 0)"></span></td>
<td><span style="background-color:rgb(252, 248, 128)"></span></td>
<td><span style="background-color:rgb(252, 252, 192)"></span></td>
</tr>
<tr>
<td>0x3e</td>
<td>0x3f</td>
<td>0x40</td>
<td>0x41</td>
<td>0x42</td>
<td>0x43</td>
<td>0x44</td>
<td>0x45</td>
</tr>
<tr class="bottom">
<td>62</td>
<td>63</td>
<td>64</td>
<td>65</td>
<td>66</td>
<td>67</td>
<td>68</td>
<td>69</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_RED</th>
<td><span style="background-color:rgb( 92, 0, 0)"></span></td>
<td><span style="background-color:rgb(128, 0, 0)"></span></td>
<td><span style="background-color:rgb(160, 0, 0)"></span></td>
<td><span style="background-color:rgb(196, 0, 0)"></span></td>
<td><span style="background-color:rgb(224, 0, 0)"></span></td>
<td><span style="background-color:rgb(252, 52, 52)"></span></td>
<td><span style="background-color:rgb(252, 100, 88)"></span></td>
<td><span style="background-color:rgb(252, 144, 124)"></span></td>
</tr>
<tr>
<td>0xb3</td>
<td>0xb4</td>
<td>0xb5</td>
<td>0xb6</td>
<td>0xb7</td>
<td>0xa4</td>
<td>0xa5</td>
<td>0xa6</td>
</tr>
<tr class="bottom">
<td>179</td>
<td>180</td>
<td>181</td>
<td>182</td>
<td>183</td>
<td>164</td>
<td>165</td>
<td>166</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_LIGHT_BLUE</th>
<td><span style="background-color:rgb( 16, 64, 96)"></span></td>
<td><span style="background-color:rgb( 24, 80, 108)"></span></td>
<td><span style="background-color:rgb( 40, 96, 120)"></span></td>
<td><span style="background-color:rgb( 52, 112, 132)"></span></td>
<td><span style="background-color:rgb( 80, 140, 160)"></span></td>
<td><span style="background-color:rgb(116, 172, 192)"></span></td>
<td><span style="background-color:rgb(156, 204, 220)"></span></td>
<td><span style="background-color:rgb(204, 240, 252)"></span></td>
</tr>
<tr>
<td>0x9a</td>
<td>0x9b</td>
<td>0x9c</td>
<td>0x9d</td>
<td>0x9e</td>
<td>0x9f</td>
<td>0xa0</td>
<td>0xa1</td>
</tr>
<tr class="bottom">
<td>154</td>
<td>155</td>
<td>156</td>
<td>157</td>
<td>158</td>
<td>159</td>
<td>160</td>
<td>161</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_GREEN</th>
<td><span style="background-color:rgb( 32, 80, 4)"></span></td>
<td><span style="background-color:rgb( 48, 96, 4)"></span></td>
<td><span style="background-color:rgb( 64, 112, 12)"></span></td>
<td><span style="background-color:rgb( 84, 132, 20)"></span></td>
<td><span style="background-color:rgb( 92, 156, 52)"></span></td>
<td><span style="background-color:rgb(108, 176, 64)"></span></td>
<td><span style="background-color:rgb(124, 200, 76)"></span></td>
<td><span style="background-color:rgb(144, 224, 92)"></span></td>
</tr>
<tr>
<td>0x52</td>
<td>0x53</td>
<td>0x54</td>
<td>0x55</td>
<td>0xce</td>
<td>0xcf</td>
<td>0xd0</td>
<td>0xd1</td>
</tr>
<tr class="bottom">
<td>82</td>
<td>83</td>
<td>84</td>
<td>85</td>
<td>206</td>
<td>207</td>
<td>208</td>
<td>209</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_DARK_GREEN</th>
<td><span style="background-color:rgb( 28, 52, 24)"></span></td>
<td><span style="background-color:rgb( 44, 68, 32)"></span></td>
<td><span style="background-color:rgb( 60, 88, 48)"></span></td>
<td><span style="background-color:rgb( 80, 104, 60)"></span></td>
<td><span style="background-color:rgb(104, 124, 76)"></span></td>
<td><span style="background-color:rgb(128, 148, 92)"></span></td>
<td><span style="background-color:rgb(152, 176, 108)"></span></td>
<td><span style="background-color:rgb(180, 204, 124)"></span></td>
</tr>
<tr>
<td>0x58</td>
<td>0x59</td>
<td>0x5a</td>
<td>0x5b</td>
<td>0x5c</td>
<td>0x5d</td>
<td>0x5e</td>
<td>0x5f</td>
</tr>
<tr class="bottom">
<td>88</td>
<td>89</td>
<td>90</td>
<td>91</td>
<td>92</td>
<td>93</td>
<td>94</td>
<td>95</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_BLUE</th>
<td><span style="background-color:rgb( 0, 52, 160)"></span></td>
<td><span style="background-color:rgb( 0, 72, 184)"></span></td>
<td><span style="background-color:rgb( 0, 96, 212)"></span></td>
<td><span style="background-color:rgb( 24, 120, 220)"></span></td>
<td><span style="background-color:rgb( 56, 144, 232)"></span></td>
<td><span style="background-color:rgb( 88, 168, 240)"></span></td>
<td><span style="background-color:rgb(128, 196, 252)"></span></td>
<td><span style="background-color:rgb(188, 224, 252)"></span></td>
</tr>
<tr>
<td>0x92</td>
<td>0x93</td>
<td>0x94</td>
<td>0x95</td>
<td>0x96</td>
<td>0x97</td>
<td>0x98</td>
<td>0x99</td>
</tr>
<tr class="bottom">
<td>146</td>
<td>147</td>
<td>148</td>
<td>149</td>
<td>150</td>
<td>151</td>
<td>152</td>
<td>153</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_CREAM</th>
<td><span style="background-color:rgb(116, 68, 40)"></span></td>
<td><span style="background-color:rgb(136, 84, 56)"></span></td>
<td><span style="background-color:rgb(164, 96, 64)"></span></td>
<td><span style="background-color:rgb(184, 112, 80)"></span></td>
<td><span style="background-color:rgb(204, 128, 96)"></span></td>
<td><span style="background-color:rgb(212, 148, 112)"></span></td>
<td><span style="background-color:rgb(224, 168, 128)"></span></td>
<td><span style="background-color:rgb(236, 188, 148)"></span></td>
</tr>
<tr>
<td>0x72</td>
<td>0x73</td>
<td>0x74</td>
<td>0x75</td>
<td>0x76</td>
<td>0x77</td>
<td>0x78</td>
<td>0x79</td>
</tr>
<tr class="bottom">
<td>114</td>
<td>115</td>
<td>116</td>
<td>117</td>
<td>118</td>
<td>119</td>
<td>120</td>
<td>121</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_MAUVE</th>
<td><span style="background-color:rgb( 36, 40, 68)"></span></td>
<td><span style="background-color:rgb( 48, 52, 84)"></span></td>
<td><span style="background-color:rgb( 64, 64, 100)"></span></td>
<td><span style="background-color:rgb( 80, 80, 116)"></span></td>
<td><span style="background-color:rgb(100, 100, 136)"></span></td>
<td><span style="background-color:rgb(132, 132, 164)"></span></td>
<td><span style="background-color:rgb(172, 172, 192)"></span></td>
<td><span style="background-color:rgb(212, 212, 224)"></span></td>
</tr>
<tr>
<td>0x80</td>
<td>0x81</td>
<td>0x82</td>
<td>0x83</td>
<td>0x84</td>
<td>0x85</td>
<td>0x86</td>
<td>0x87</td>
</tr>
<tr class="bottom">
<td>128</td>
<td>129</td>
<td>130</td>
<td>131</td>
<td>132</td>
<td>133</td>
<td>134</td>
<td>135</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_PURPLE</th>
<td><span style="background-color:rgb( 40, 20, 112)"></span></td>
<td><span style="background-color:rgb( 64, 44, 144)"></span></td>
<td><span style="background-color:rgb( 88, 64, 172)"></span></td>
<td><span style="background-color:rgb(104, 76, 196)"></span></td>
<td><span style="background-color:rgb(120, 88, 224)"></span></td>
<td><span style="background-color:rgb(140, 104, 252)"></span></td>
<td><span style="background-color:rgb(160, 136, 252)"></span></td>
<td><span style="background-color:rgb(188, 168, 252)"></span></td>
</tr>
<tr>
<td>0x88</td>
<td>0x89</td>
<td>0x8a</td>
<td>0x8b</td>
<td>0x8c</td>
<td>0x8d</td>
<td>0x8e</td>
<td>0x8f</td>
</tr>
<tr class="bottom">
<td>136</td>
<td>137</td>
<td>138</td>
<td>139</td>
<td>140</td>
<td>141</td>
<td>142</td>
<td>143</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_ORANGE</th>
<td><span style="background-color:rgb(184, 120, 24)"></span></td>
<td><span style="background-color:rgb(204, 136, 8)"></span></td>
<td><span style="background-color:rgb(228, 144, 4)"></span></td>
<td><span style="background-color:rgb(252, 156, 0)"></span></td>
<td><span style="background-color:rgb(252, 176, 48)"></span></td>
<td><span style="background-color:rgb(252, 196, 100)"></span></td>
<td><span style="background-color:rgb(252, 216, 152)"></span></td>
<td><span style="background-color:rgb(244, 220, 176)"></span></td>
</tr>
<tr>
<td>0x40</td>
<td>0xc0</td>
<td>0xc1</td>
<td>0xc2</td>
<td>0xc3</td>
<td>0xc4</td>
<td>0xc5</td>
<td>0x27</td>
</tr>
<tr class="bottom">
<td>64</td>
<td>192</td>
<td>193</td>
<td>194</td>
<td>195</td>
<td>196</td>
<td>197</td>
<td>39</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_BROWN</th>
<td><span style="background-color:rgb( 72, 44, 4)"></span></td>
<td><span style="background-color:rgb( 88, 60, 20)"></span></td>
<td><span style="background-color:rgb(104, 80, 44)"></span></td>
<td><span style="background-color:rgb(124, 104, 72)"></span></td>
<td><span style="background-color:rgb(152, 132, 92)"></span></td>
<td><span style="background-color:rgb(184, 160, 120)"></span></td>
<td><span style="background-color:rgb(212, 188, 148)"></span></td>
<td><span style="background-color:rgb(244, 220, 176)"></span></td>
</tr>
<tr>
<td>0x20</td>
<td>0x21</td>
<td>0x22</td>
<td>0x23</td>
<td>0x24</td>
<td>0x25</td>
<td>0x26</td>
<td>0x27</td>
</tr>
<tr class="bottom">
<td>32</td>
<td>33</td>
<td>34</td>
<td>35</td>
<td>36</td>
<td>37</td>
<td>38</td>
<td>39</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_GREY</th>
<td><span style="background-color:rgb( 64, 64, 64)"></span></td>
<td><span style="background-color:rgb( 80, 80, 80)"></span></td>
<td><span style="background-color:rgb(100, 100, 100)"></span></td>
<td><span style="background-color:rgb(116, 116, 116)"></span></td>
<td><span style="background-color:rgb(132, 132, 132)"></span></td>
<td><span style="background-color:rgb(148, 148, 148)"></span></td>
<td><span style="background-color:rgb(168, 168, 168)"></span></td>
<td><span style="background-color:rgb(184, 184, 184)"></span></td>
</tr>
<tr>
<td>0x4</td>
<td>0x5</td>
<td>0x6</td>
<td>0x7</td>
<td>0x8</td>
<td>0x9</td>
<td>0xa</td>
<td>0xb</td>
</tr>
<tr class="bottom">
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
</tr>
<tr class="top">
<th rowspan="3">COLOUR_WHITE</th>
<td><span style="background-color:rgb(132, 132, 132)"></span></td>
<td><span style="background-color:rgb(148, 148, 148)"></span></td>
<td><span style="background-color:rgb(168, 168, 168)"></span></td>
<td><span style="background-color:rgb(184, 184, 184)"></span></td>
<td><span style="background-color:rgb(200, 200, 200)"></span></td>
<td><span style="background-color:rgb(216, 216, 216)"></span></td>
<td><span style="background-color:rgb(232, 232, 232)"></span></td>
<td><span style="background-color:rgb(252, 252, 252)"></span></td>
</tr>
<tr>
<td>0x8</td>
<td>0x9</td>
<td>0xa</td>
<td>0xb</td>
<td>0xc</td>
<td>0xd</td>
<td>0xe</td>
<td>0xf</td>
</tr>
<tr class="bottom">
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
</tr>
</tbody>
</table>
</body>
</html>

@ -201,7 +201,7 @@ struct AIListWindow : public Window {
{ {
switch (widget) { switch (widget) {
case WID_AIL_LIST: { // Select one of the AIs case WID_AIL_LIST: { // Select one of the AIs
int sel = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_AIL_LIST, 0, this->line_height) - 1; int sel = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_AIL_LIST) - 1;
if (sel < (int)this->info_list->size()) { if (sel < (int)this->info_list->size()) {
this->selected = sel; this->selected = sel;
this->SetDirty(); this->SetDirty();
@ -795,6 +795,7 @@ struct AIConfigWindow : public Window {
case WID_AIC_LIST: case WID_AIC_LIST:
this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM; this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
resize->height = this->line_height;
size->height = 8 * this->line_height; size->height = 8 * this->line_height;
break; break;
@ -906,7 +907,7 @@ struct AIConfigWindow : public Window {
} }
case WID_AIC_LIST: { // Select a slot case WID_AIC_LIST: { // Select a slot
this->selected_slot = (CompanyID)this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget, 0, this->line_height); this->selected_slot = (CompanyID)this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget);
this->InvalidateData(); this->InvalidateData();
if (click_count > 1 && this->selected_slot != INVALID_COMPANY) ShowAIListWindow((CompanyID)this->selected_slot); if (click_count > 1 && this->selected_slot != INVALID_COMPANY) ShowAIListWindow((CompanyID)this->selected_slot);
break; break;

@ -11,6 +11,7 @@
#include "command_func.h" #include "command_func.h"
#include "group.h" #include "group.h"
#include "autoreplace_base.h" #include "autoreplace_base.h"
#include "core/bitmath_func.hpp"
#include "core/pool_func.hpp" #include "core/pool_func.hpp"
#include "safeguards.h" #include "safeguards.h"
@ -64,7 +65,7 @@ void RemoveAllEngineReplacement(EngineRenewList *erl)
EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, bool *replace_when_old) EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, bool *replace_when_old)
{ {
const EngineRenew *er = GetEngineReplacement(erl, engine, group); const EngineRenew *er = GetEngineReplacement(erl, engine, group);
if (er == nullptr && (group == DEFAULT_GROUP || (Group::IsValidID(group) && !Group::Get(group)->replace_protection))) { if (er == nullptr && (group == DEFAULT_GROUP || (Group::IsValidID(group) && !HasBit(Group::Get(group)->flags, GroupFlags::GF_REPLACE_PROTECTION)))) {
/* We didn't find anything useful in the vehicle's own group so we will try ALL_GROUP */ /* We didn't find anything useful in the vehicle's own group so we will try ALL_GROUP */
er = GetEngineReplacement(erl, engine, ALL_GROUP); er = GetEngineReplacement(erl, engine, ALL_GROUP);
} }

@ -760,6 +760,9 @@ CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1
bool wagon_removal = c->settings.renew_keep_length; bool wagon_removal = c->settings.renew_keep_length;
bool same_type_only = HasBit(p2, 0); bool same_type_only = HasBit(p2, 0);
const Group *g = Group::GetIfValid(v->group_id);
if (g != nullptr) wagon_removal = HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL);
/* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */ /* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */
Vehicle *w = v; Vehicle *w = v;
bool any_replacements = false; bool any_replacements = false;

@ -375,8 +375,15 @@ public:
break; break;
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: { case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: {
const Company *c = Company::Get(_local_company); bool remove_wagon;
SetDParam(0, c->settings.renew_keep_length ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); const Group *g = Group::GetIfValid(this->sel_group);
if (g != nullptr) {
remove_wagon = HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL);
} else {
const Company *c = Company::Get(_local_company);
remove_wagon = c->settings.renew_keep_length;
}
SetDParam(0, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
break; break;
} }
@ -528,9 +535,16 @@ public:
} }
break; break;
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: // toggle renew_keep_length case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: {
DoCommandP(0, GetCompanySettingIndex("company.renew_keep_length"), Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, CMD_CHANGE_COMPANY_SETTING); const Group *g = Group::GetIfValid(this->sel_group);
if (g != nullptr) {
DoCommandP(0, this->sel_group | (GroupFlags::GF_REPLACE_WAGON_REMOVAL << 16), (HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL) ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_FLAG);
} else {
// toggle renew_keep_length
DoCommandP(0, GetCompanySettingIndex("company.renew_keep_length"), Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, CMD_CHANGE_COMPANY_SETTING);
}
break; break;
}
case WID_RV_START_REPLACE: { // Start replacing case WID_RV_START_REPLACE: { // Start replacing
if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) { if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {

@ -228,7 +228,7 @@ CommandProc CmdCreateGroupFromList;
CommandProc CmdAddVehicleGroup; CommandProc CmdAddVehicleGroup;
CommandProc CmdAddSharedVehicleGroup; CommandProc CmdAddSharedVehicleGroup;
CommandProc CmdRemoveAllVehiclesGroup; CommandProc CmdRemoveAllVehiclesGroup;
CommandProc CmdSetGroupReplaceProtection; CommandProc CmdSetGroupFlag;
CommandProc CmdSetGroupLivery; CommandProc CmdSetGroupLivery;
CommandProc CmdMoveOrder; CommandProc CmdMoveOrder;
@ -462,7 +462,7 @@ static const Command _command_proc_table[] = {
DEF_CMD(CmdAddVehicleGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ADD_VEHICLE_GROUP DEF_CMD(CmdAddVehicleGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ADD_VEHICLE_GROUP
DEF_CMD(CmdAddSharedVehicleGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ADD_SHARE_VEHICLE_GROUP DEF_CMD(CmdAddSharedVehicleGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ADD_SHARE_VEHICLE_GROUP
DEF_CMD(CmdRemoveAllVehiclesGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_REMOVE_ALL_VEHICLES_GROUP DEF_CMD(CmdRemoveAllVehiclesGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_REMOVE_ALL_VEHICLES_GROUP
DEF_CMD(CmdSetGroupReplaceProtection, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_REPLACE_PROTECTION DEF_CMD(CmdSetGroupFlag, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_FLAG
DEF_CMD(CmdSetGroupLivery, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_LIVERY DEF_CMD(CmdSetGroupLivery, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_LIVERY
DEF_CMD(CmdMoveOrder, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_MOVE_ORDER DEF_CMD(CmdMoveOrder, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_MOVE_ORDER
DEF_CMD(CmdReverseOrderList, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_REVERSE_ORDER_LIST DEF_CMD(CmdReverseOrderList, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_REVERSE_ORDER_LIST

@ -410,7 +410,7 @@ enum Commands {
CMD_ADD_VEHICLE_GROUP, ///< add a vehicle to a group CMD_ADD_VEHICLE_GROUP, ///< add a vehicle to a group
CMD_ADD_SHARED_VEHICLE_GROUP, ///< add all other shared vehicles to a group which are missing CMD_ADD_SHARED_VEHICLE_GROUP, ///< add all other shared vehicles to a group which are missing
CMD_REMOVE_ALL_VEHICLES_GROUP, ///< remove all vehicles from a group CMD_REMOVE_ALL_VEHICLES_GROUP, ///< remove all vehicles from a group
CMD_SET_GROUP_REPLACE_PROTECTION, ///< set the autoreplace-protection for a group CMD_SET_GROUP_FLAG, ///< set/clear a flag for a group
CMD_SET_GROUP_LIVERY, ///< set the livery for a group CMD_SET_GROUP_LIVERY, ///< set the livery for a group
CMD_MOVE_ORDER, ///< move an order CMD_MOVE_ORDER, ///< move an order

@ -246,10 +246,10 @@ static const NWidgetPart _nested_company_finances_widgets[] = {
EndContainer(), EndContainer(),
NWidget(NWID_SPACER), SetFill(0, 0), SetMinimalSize(30, 0), NWidget(NWID_SPACER), SetFill(0, 0), SetMinimalSize(30, 0),
NWidget(NWID_VERTICAL), // Vertical column with bank balance amount, loan amount, and total. NWidget(NWID_VERTICAL), // Vertical column with bank balance amount, loan amount, and total.
NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_BALANCE_VALUE), SetDataTip(STR_NULL, STR_NULL), NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_BALANCE_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_LOAN_VALUE), SetDataTip(STR_NULL, STR_NULL), NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_LOAN_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_LOAN_LINE), SetMinimalSize(0, 2), SetFill(1, 0), NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_LOAN_LINE), SetMinimalSize(0, 2), SetFill(1, 0),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_TOTAL_VALUE), SetDataTip(STR_NULL, STR_NULL), NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_TOTAL_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
EndContainer(), EndContainer(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_MAXLOAN), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_MAXLOAN),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
@ -299,6 +299,24 @@ struct CompanyFinancesWindow : Window {
SetDParam(1, (CompanyID)this->window_number); SetDParam(1, (CompanyID)this->window_number);
break; break;
case WID_CF_BALANCE_VALUE: {
const Company *c = Company::Get((CompanyID)this->window_number);
SetDParam(0, c->money);
break;
}
case WID_CF_LOAN_VALUE: {
const Company *c = Company::Get((CompanyID)this->window_number);
SetDParam(0, c->current_loan);
break;
}
case WID_CF_TOTAL_VALUE: {
const Company *c = Company::Get((CompanyID)this->window_number);
SetDParam(0, c->money - c->current_loan);
break;
}
case WID_CF_MAXLOAN_VALUE: case WID_CF_MAXLOAN_VALUE:
SetDParam(0, _economy.max_loan); SetDParam(0, _economy.max_loan);
break; break;
@ -357,27 +375,6 @@ struct CompanyFinancesWindow : Window {
break; break;
} }
case WID_CF_BALANCE_VALUE: {
const Company *c = Company::Get((CompanyID)this->window_number);
SetDParam(0, c->money);
DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT);
break;
}
case WID_CF_LOAN_VALUE: {
const Company *c = Company::Get((CompanyID)this->window_number);
SetDParam(0, c->current_loan);
DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT);
break;
}
case WID_CF_TOTAL_VALUE: {
const Company *c = Company::Get((CompanyID)this->window_number);
SetDParam(0, c->money - c->current_loan);
DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT);
break;
}
case WID_CF_LOAN_LINE: case WID_CF_LOAN_LINE:
GfxFillRect(r.left, r.top, r.right, r.top, PC_BLACK); GfxFillRect(r.left, r.top, r.right, r.top, PC_BLACK);
break; break;
@ -1018,7 +1015,7 @@ public:
break; break;
case WID_SCL_MATRIX: { case WID_SCL_MATRIX: {
uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SCL_MATRIX, 0, this->line_height); uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SCL_MATRIX);
if (row >= this->rows) return; if (row >= this->rows) return;
if (this->livery_class < LC_GROUP_RAIL) { if (this->livery_class < LC_GROUP_RAIL) {
@ -1262,70 +1259,82 @@ static const NWidgetPart _nested_select_company_manager_face_widgets[] = {
EndContainer(), EndContainer(),
NWidget(NWID_SPACER), SetMinimalSize(0, 4), NWidget(NWID_SPACER), SetMinimalSize(0, 4),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_EYECOLOUR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_MOUSTACHE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_MOUSTACHE_EARRING_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_MOUSTACHE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_MOUSTACHE_EARRING_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAS_GLASSES_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_GLASSES_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_GLASSES, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0), NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAIR_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAIR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_HAIR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_L), SetDataTip(AWV_DECREASE, STR_FACE_HAIR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_L), SetDataTip(AWV_DECREASE, STR_FACE_HAIR_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAIR), SetDataTip(STR_EMPTY, STR_FACE_HAIR_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAIR), SetDataTip(STR_EMPTY, STR_FACE_HAIR_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_R), SetDataTip(AWV_INCREASE, STR_FACE_HAIR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_R), SetDataTip(AWV_INCREASE, STR_FACE_HAIR_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_EYEBROWS_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYEBROWS_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_EYEBROWS, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_L), SetDataTip(AWV_DECREASE, STR_FACE_EYEBROWS_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_L), SetDataTip(AWV_DECREASE, STR_FACE_EYEBROWS_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYEBROWS), SetDataTip(STR_EMPTY, STR_FACE_EYEBROWS_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYEBROWS), SetDataTip(STR_EMPTY, STR_FACE_EYEBROWS_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_R), SetDataTip(AWV_INCREASE, STR_FACE_EYEBROWS_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_R), SetDataTip(AWV_INCREASE, STR_FACE_EYEBROWS_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_EYECOLOUR_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYECOLOUR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_EYECOLOUR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_L), SetDataTip(AWV_DECREASE, STR_FACE_EYECOLOUR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_L), SetDataTip(AWV_DECREASE, STR_FACE_EYECOLOUR_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR), SetDataTip(STR_EMPTY, STR_FACE_EYECOLOUR_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR), SetDataTip(STR_EMPTY, STR_FACE_EYECOLOUR_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_R), SetDataTip(AWV_INCREASE, STR_FACE_EYECOLOUR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_R), SetDataTip(AWV_INCREASE, STR_FACE_EYECOLOUR_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_GLASSES_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_GLASSES_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_GLASSES, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_L), SetDataTip(AWV_DECREASE, STR_FACE_GLASSES_TOOLTIP_2), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_L), SetDataTip(AWV_DECREASE, STR_FACE_GLASSES_TOOLTIP_2),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP_2), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP_2),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_R), SetDataTip(AWV_INCREASE, STR_FACE_GLASSES_TOOLTIP_2), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_R), SetDataTip(AWV_INCREASE, STR_FACE_GLASSES_TOOLTIP_2),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_NOSE_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_NOSE_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_NOSE, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_L), SetDataTip(AWV_DECREASE, STR_FACE_NOSE_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_L), SetDataTip(AWV_DECREASE, STR_FACE_NOSE_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_NOSE), SetDataTip(STR_EMPTY, STR_FACE_NOSE_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_NOSE), SetDataTip(STR_EMPTY, STR_FACE_NOSE_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_R), SetDataTip(AWV_INCREASE, STR_FACE_NOSE_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_R), SetDataTip(AWV_INCREASE, STR_FACE_NOSE_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_LIPS_MOUSTACHE_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_LIPS_MOUSTACHE_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_MOUSTACHE, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_L), SetDataTip(AWV_DECREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_L), SetDataTip(AWV_DECREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE), SetDataTip(STR_EMPTY, STR_FACE_LIPS_MOUSTACHE_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE), SetDataTip(STR_EMPTY, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_R), SetDataTip(AWV_INCREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_R), SetDataTip(AWV_INCREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_CHIN_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_CHIN_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_CHIN, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_L), SetDataTip(AWV_DECREASE, STR_FACE_CHIN_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_L), SetDataTip(AWV_DECREASE, STR_FACE_CHIN_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CHIN), SetDataTip(STR_EMPTY, STR_FACE_CHIN_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CHIN), SetDataTip(STR_EMPTY, STR_FACE_CHIN_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_R), SetDataTip(AWV_INCREASE, STR_FACE_CHIN_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_R), SetDataTip(AWV_INCREASE, STR_FACE_CHIN_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_JACKET_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_JACKET_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_JACKET, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_L), SetDataTip(AWV_DECREASE, STR_FACE_JACKET_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_L), SetDataTip(AWV_DECREASE, STR_FACE_JACKET_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_JACKET), SetDataTip(STR_EMPTY, STR_FACE_JACKET_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_JACKET), SetDataTip(STR_EMPTY, STR_FACE_JACKET_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_R), SetDataTip(AWV_INCREASE, STR_FACE_JACKET_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_R), SetDataTip(AWV_INCREASE, STR_FACE_JACKET_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_COLLAR_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_COLLAR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_COLLAR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_L), SetDataTip(AWV_DECREASE, STR_FACE_COLLAR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_L), SetDataTip(AWV_DECREASE, STR_FACE_COLLAR_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_COLLAR), SetDataTip(STR_EMPTY, STR_FACE_COLLAR_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_COLLAR), SetDataTip(STR_EMPTY, STR_FACE_COLLAR_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_R), SetDataTip(AWV_INCREASE, STR_FACE_COLLAR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_R), SetDataTip(AWV_INCREASE, STR_FACE_COLLAR_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_TIE_EARRING_TEXT), SetFill(1, 0), NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_TIE_EARRING_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT),
SetDataTip(STR_FACE_EARRING, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_L), SetDataTip(AWV_DECREASE, STR_FACE_TIE_EARRING_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_L), SetDataTip(AWV_DECREASE, STR_FACE_TIE_EARRING_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_TIE_EARRING_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_TIE_EARRING_TOOLTIP),
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_R), SetDataTip(AWV_INCREASE, STR_FACE_TIE_EARRING_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_R), SetDataTip(AWV_INCREASE, STR_FACE_TIE_EARRING_TOOLTIP),
@ -1356,9 +1365,6 @@ class SelectCompanyManagerFaceWindow : public Window
Dimension yesno_dim; ///< Dimension of a yes/no button of a part in the advanced face window. Dimension yesno_dim; ///< Dimension of a yes/no button of a part in the advanced face window.
Dimension number_dim; ///< Dimension of a number widget of a part in the advanced face window. Dimension number_dim; ///< Dimension of a number widget of a part in the advanced face window.
static const StringID PART_TEXTS_IS_FEMALE[]; ///< Strings depending on #is_female, used to describe parts (2 entries for a part).
static const StringID PART_TEXTS[]; ///< Fixed strings to describe parts of the face.
/** /**
* Draw dynamic a label to the left of the button and a value in the button * Draw dynamic a label to the left of the button and a value in the button
* *
@ -1391,6 +1397,10 @@ class SelectCompanyManagerFaceWindow : public Window
this->ge = (GenderEthnicity)GB(this->face, _cmf_info[CMFV_GEN_ETHN].offset, _cmf_info[CMFV_GEN_ETHN].length); // get the gender and ethnicity this->ge = (GenderEthnicity)GB(this->face, _cmf_info[CMFV_GEN_ETHN].offset, _cmf_info[CMFV_GEN_ETHN].length); // get the gender and ethnicity
this->is_female = HasBit(this->ge, GENDER_FEMALE); // get the gender: 0 == male and 1 == female this->is_female = HasBit(this->ge, GENDER_FEMALE); // get the gender: 0 == male and 1 == female
this->is_moust_male = !is_female && GetCompanyManagerFaceBits(this->face, CMFV_HAS_MOUSTACHE, this->ge) != 0; // is a male face with moustache this->is_moust_male = !is_female && GetCompanyManagerFaceBits(this->face, CMFV_HAS_MOUSTACHE, this->ge) != 0; // is a male face with moustache
this->GetWidget<NWidgetCore>(WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT)->widget_data = this->is_female ? STR_FACE_EARRING : STR_FACE_MOUSTACHE;
this->GetWidget<NWidgetCore>(WID_SCMF_TIE_EARRING_TEXT)->widget_data = this->is_female ? STR_FACE_EARRING : STR_FACE_TIE;
this->GetWidget<NWidgetCore>(WID_SCMF_LIPS_MOUSTACHE_TEXT)->widget_data = this->is_moust_male ? STR_FACE_MOUSTACHE : STR_FACE_LIPS;
} }
public: public:
@ -1452,41 +1462,27 @@ public:
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{ {
switch (widget) { switch (widget) {
case WID_SCMF_FACE: { case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT:
Dimension face_size = GetSpriteSize(SPR_GRADIENT); *size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING));
size->width = std::max(size->width, face_size.width); *size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
size->height = std::max(size->height, face_size.height);
break; break;
}
case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT: case WID_SCMF_TIE_EARRING_TEXT:
case WID_SCMF_TIE_EARRING_TEXT: { *size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING));
int offset = (widget - WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT) * 2; *size = maxdim(*size, GetStringBoundingBox(STR_FACE_TIE));
*size = maxdim(GetStringBoundingBox(PART_TEXTS_IS_FEMALE[offset]), GetStringBoundingBox(PART_TEXTS_IS_FEMALE[offset + 1]));
size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
break; break;
}
case WID_SCMF_LIPS_MOUSTACHE_TEXT: case WID_SCMF_LIPS_MOUSTACHE_TEXT:
*size = maxdim(GetStringBoundingBox(STR_FACE_LIPS), GetStringBoundingBox(STR_FACE_MOUSTACHE)); *size = maxdim(*size, GetStringBoundingBox(STR_FACE_LIPS));
size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; *size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
break; break;
case WID_SCMF_HAS_GLASSES_TEXT: case WID_SCMF_FACE: {
case WID_SCMF_HAIR_TEXT: Dimension face_size = GetSpriteSize(SPR_GRADIENT);
case WID_SCMF_EYEBROWS_TEXT: size->width = std::max(size->width, face_size.width);
case WID_SCMF_EYECOLOUR_TEXT: size->height = std::max(size->height, face_size.height);
case WID_SCMF_GLASSES_TEXT:
case WID_SCMF_NOSE_TEXT:
case WID_SCMF_CHIN_TEXT:
case WID_SCMF_JACKET_TEXT:
case WID_SCMF_COLLAR_TEXT:
*size = GetStringBoundingBox(PART_TEXTS[widget - WID_SCMF_HAS_GLASSES_TEXT]);
size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
break; break;
}
case WID_SCMF_HAS_MOUSTACHE_EARRING: case WID_SCMF_HAS_MOUSTACHE_EARRING:
case WID_SCMF_HAS_GLASSES: case WID_SCMF_HAS_GLASSES:
@ -1572,30 +1568,6 @@ public:
void DrawWidget(const Rect &r, int widget) const override void DrawWidget(const Rect &r, int widget) const override
{ {
switch (widget) { switch (widget) {
case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT:
case WID_SCMF_TIE_EARRING_TEXT: {
StringID str = PART_TEXTS_IS_FEMALE[(widget - WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT) * 2 + this->is_female];
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_GOLD, SA_RIGHT);
break;
}
case WID_SCMF_LIPS_MOUSTACHE_TEXT:
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, (this->is_moust_male) ? STR_FACE_MOUSTACHE : STR_FACE_LIPS, TC_GOLD, SA_RIGHT);
break;
case WID_SCMF_HAS_GLASSES_TEXT:
case WID_SCMF_HAIR_TEXT:
case WID_SCMF_EYEBROWS_TEXT:
case WID_SCMF_EYECOLOUR_TEXT:
case WID_SCMF_GLASSES_TEXT:
case WID_SCMF_NOSE_TEXT:
case WID_SCMF_CHIN_TEXT:
case WID_SCMF_JACKET_TEXT:
case WID_SCMF_COLLAR_TEXT:
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, PART_TEXTS[widget - WID_SCMF_HAS_GLASSES_TEXT], TC_GOLD, SA_RIGHT);
break;
case WID_SCMF_HAS_MOUSTACHE_EARRING: case WID_SCMF_HAS_MOUSTACHE_EARRING:
if (this->is_female) { // Only for female faces if (this->is_female) { // Only for female faces
this->DrawFaceStringLabel(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_TIE_EARRING, this->ge), true); this->DrawFaceStringLabel(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_TIE_EARRING, this->ge), true);
@ -1783,25 +1755,6 @@ public:
} }
}; };
/** Both text values of parts of the face that depend on the #is_female boolean value. */
const StringID SelectCompanyManagerFaceWindow::PART_TEXTS_IS_FEMALE[] = {
STR_FACE_MOUSTACHE, STR_FACE_EARRING, // WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT
STR_FACE_TIE, STR_FACE_EARRING, // WID_SCMF_TIE_EARRING_TEXT
};
/** Textual names for parts of the face. */
const StringID SelectCompanyManagerFaceWindow::PART_TEXTS[] = {
STR_FACE_GLASSES, // WID_SCMF_HAS_GLASSES_TEXT
STR_FACE_HAIR, // WID_SCMF_HAIR_TEXT
STR_FACE_EYEBROWS, // WID_SCMF_EYEBROWS_TEXT
STR_FACE_EYECOLOUR, // WID_SCMF_EYECOLOUR_TEXT
STR_FACE_GLASSES, // WID_SCMF_GLASSES_TEXT
STR_FACE_NOSE, // WID_SCMF_NOSE_TEXT
STR_FACE_CHIN, // WID_SCMF_CHIN_TEXT
STR_FACE_JACKET, // WID_SCMF_JACKET_TEXT
STR_FACE_COLLAR, // WID_SCMF_COLLAR_TEXT
};
/** Company manager face selection window description */ /** Company manager face selection window description */
static WindowDesc _select_company_manager_face_desc( static WindowDesc _select_company_manager_face_desc(
WDP_AUTO, "company_face", 0, 0, WDP_AUTO, "company_face", 0, 0,

@ -24,8 +24,17 @@ static const uint ICON_TOKEN_COUNT = 20; ///< Maximum number of tokens in on
static const uint ICON_MAX_RECURSE = 10; ///< Maximum number of recursion static const uint ICON_MAX_RECURSE = 10; ///< Maximum number of recursion
/* console parser */ /* console parser */
IConsoleCmd *_iconsole_cmds; ///< list of registered commands /* static */ IConsole::CommandList &IConsole::Commands()
IConsoleAlias *_iconsole_aliases; ///< list of registered aliases {
static IConsole::CommandList cmds;
return cmds;
}
/* static */ IConsole::AliasList &IConsole::Aliases()
{
static IConsole::AliasList aliases;
return aliases;
}
FILE *_iconsole_output_file; FILE *_iconsole_output_file;
@ -195,49 +204,13 @@ bool GetArgumentInteger(uint32 *value, const char *arg)
} }
/** /**
* Add an item to an alphabetically sorted list. * Creates a copy of a string with underscores removed from it
* @param base first item of the list * @param name String to remove the underscores from.
* @param item_new the item to add * @return A copy of \a name, without underscores.
*/
template<class T>
void IConsoleAddSorted(T **base, T *item_new)
{
if (*base == nullptr) {
*base = item_new;
return;
}
T *item_before = nullptr;
T *item = *base;
/* The list is alphabetically sorted, insert the new item at the correct location */
while (item != nullptr) {
if (strcmp(item->name, item_new->name) > 0) break; // insert here
item_before = item;
item = item->next;
}
if (item_before == nullptr) {
*base = item_new;
} else {
item_before->next = item_new;
}
item_new->next = item;
}
/**
* Remove underscores from a string; the string will be modified!
* @param[in,out] name String to remove the underscores from.
* @return \a name, with its contents modified.
*/ */
char *RemoveUnderscores(char *name) std::string RemoveUnderscores(std::string name)
{ {
char *q = name; name.erase(std::remove(name.begin(), name.end(), '_'), name.end());
for (const char *p = name; *p != '\0'; p++) {
if (*p != '_') *q++ = *p;
}
*q = '\0';
return name; return name;
} }
@ -246,16 +219,9 @@ char *RemoveUnderscores(char *name)
* @param name name of the command that will be used * @param name name of the command that will be used
* @param proc function that will be called upon execution of command * @param proc function that will be called upon execution of command
*/ */
void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook, bool unlisted) /* static */ void IConsole::CmdRegister(const std::string &name, IConsoleCmdProc *proc, IConsoleHook *hook, bool unlisted)
{ {
IConsoleCmd *item_new = MallocT<IConsoleCmd>(1); IConsole::Commands().try_emplace(RemoveUnderscores(name), name, proc, hook, unlisted);
item_new->name = RemoveUnderscores(stredup(name));
item_new->next = nullptr;
item_new->proc = proc;
item_new->hook = hook;
item_new->unlisted = unlisted;
IConsoleAddSorted(&_iconsole_cmds, item_new);
} }
/** /**
@ -263,13 +229,10 @@ void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *
* @param name command to be found * @param name command to be found
* @return return Cmdstruct of the found command, or nullptr on failure * @return return Cmdstruct of the found command, or nullptr on failure
*/ */
IConsoleCmd *IConsoleCmdGet(const char *name) /* static */ IConsoleCmd *IConsole::CmdGet(const std::string &name)
{ {
IConsoleCmd *item; auto item = IConsole::Commands().find(RemoveUnderscores(name));
if (item != IConsole::Commands().end()) return &item->second;
for (item = _iconsole_cmds; item != nullptr; item = item->next) {
if (strcmp(item->name, name) == 0) return item;
}
return nullptr; return nullptr;
} }
@ -278,22 +241,10 @@ IConsoleCmd *IConsoleCmdGet(const char *name)
* @param name name of the alias that will be used * @param name name of the alias that will be used
* @param cmd name of the command that 'name' will be alias of * @param cmd name of the command that 'name' will be alias of
*/ */
void IConsoleAliasRegister(const char *name, const char *cmd) /* static */ void IConsole::AliasRegister(const std::string &name, const std::string &cmd)
{ {
if (IConsoleAliasGet(name) != nullptr) { auto result = IConsole::Aliases().try_emplace(RemoveUnderscores(name), name, cmd);
IConsoleError("an alias with this name already exists; insertion aborted"); if (!result.second) IConsoleError("an alias with this name already exists; insertion aborted");
return;
}
char *new_alias = RemoveUnderscores(stredup(name));
char *cmd_aliased = stredup(cmd);
IConsoleAlias *item_new = MallocT<IConsoleAlias>(1);
item_new->next = nullptr;
item_new->cmdline = cmd_aliased;
item_new->name = new_alias;
IConsoleAddSorted(&_iconsole_aliases, item_new);
} }
/** /**
@ -301,16 +252,13 @@ void IConsoleAliasRegister(const char *name, const char *cmd)
* @param name alias to be found * @param name alias to be found
* @return return Aliasstruct of the found alias, or nullptr on failure * @return return Aliasstruct of the found alias, or nullptr on failure
*/ */
IConsoleAlias *IConsoleAliasGet(const char *name) /* static */ IConsoleAlias *IConsole::AliasGet(const std::string &name)
{ {
IConsoleAlias *item; auto item = IConsole::Aliases().find(RemoveUnderscores(name));
if (item != IConsole::Aliases().end()) return &item->second;
for (item = _iconsole_aliases; item != nullptr; item = item->next) {
if (strcmp(item->name, name) == 0) return item;
}
return nullptr; return nullptr;
} }
/** /**
* An alias is just another name for a command, or for more commands * An alias is just another name for a command, or for more commands
* Execute it as well. * Execute it as well.
@ -330,7 +278,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char
return; return;
} }
for (const char *cmdptr = alias->cmdline; *cmdptr != '\0'; cmdptr++) { for (const char *cmdptr = alias->cmdline.c_str(); *cmdptr != '\0'; cmdptr++) {
switch (*cmdptr) { switch (*cmdptr) {
case '\'': // ' will double for "" case '\'': // ' will double for ""
alias_stream = strecpy(alias_stream, "\"", lastof(alias_buffer)); alias_stream = strecpy(alias_stream, "\"", lastof(alias_buffer));
@ -373,7 +321,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char
if (param < 0 || param >= tokencount) { if (param < 0 || param >= tokencount) {
IConsoleError("too many or wrong amount of parameters passed to alias, aborting"); IConsoleError("too many or wrong amount of parameters passed to alias, aborting");
IConsolePrintF(CC_WARNING, "Usage of alias '%s': %s", alias->name, alias->cmdline); IConsolePrintF(CC_WARNING, "Usage of alias '%s': %s", alias->name.c_str(), alias->cmdline.c_str());
return; return;
} }
@ -501,8 +449,7 @@ void IConsoleCmdExecTokens(uint token_count, char *tokens[], const uint recurse_
* First try commands, then aliases. Execute * First try commands, then aliases. Execute
* the found action taking into account its hooking code * the found action taking into account its hooking code
*/ */
RemoveUnderscores(tokens[0]); IConsoleCmd *cmd = IConsole::CmdGet(tokens[0]);
IConsoleCmd *cmd = IConsoleCmdGet(tokens[0]);
if (cmd != nullptr) { if (cmd != nullptr) {
ConsoleHookResult chr = (cmd->hook == nullptr ? CHR_ALLOW : cmd->hook(true)); ConsoleHookResult chr = (cmd->hook == nullptr ? CHR_ALLOW : cmd->hook(true));
switch (chr) { switch (chr) {
@ -518,7 +465,7 @@ void IConsoleCmdExecTokens(uint token_count, char *tokens[], const uint recurse_
} }
token_count--; token_count--;
IConsoleAlias *alias = IConsoleAliasGet(tokens[0]); IConsoleAlias *alias = IConsole::AliasGet(tokens[0]);
if (alias != nullptr) { if (alias != nullptr) {
IConsoleAliasExec(alias, token_count, &tokens[1], recurse_count + 1); IConsoleAliasExec(alias, token_count, &tokens[1], recurse_count + 1);
return; return;

@ -792,7 +792,14 @@ DEF_CONSOLE_CMD(ConClientNickChange)
return true; return true;
} }
if (!NetworkServerChangeClientName(client_id, argv[2])) { char *client_name = argv[2];
StrTrimInPlace(client_name);
if (!NetworkIsValidClientName(client_name)) {
IConsoleError("Cannot give a client an empty name");
return true;
}
if (!NetworkServerChangeClientName(client_id, client_name)) {
IConsoleError("Cannot give a client a duplicate name"); IConsoleError("Cannot give a client a duplicate name");
} }
@ -1477,12 +1484,11 @@ DEF_CONSOLE_CMD(ConAlias)
if (argc < 3) return false; if (argc < 3) return false;
alias = IConsoleAliasGet(argv[1]); alias = IConsole::AliasGet(argv[1]);
if (alias == nullptr) { if (alias == nullptr) {
IConsoleAliasRegister(argv[1], argv[2]); IConsole::AliasRegister(argv[1], argv[2]);
} else { } else {
free(alias->cmdline); alias->cmdline = argv[2];
alias->cmdline = stredup(argv[2]);
} }
return true; return true;
} }
@ -1603,13 +1609,13 @@ DEF_CONSOLE_CMD(ConInfoCmd)
if (argc < 2) return false; if (argc < 2) return false;
const IConsoleCmd *cmd = IConsoleCmdGet(argv[1]); const IConsoleCmd *cmd = IConsole::CmdGet(argv[1]);
if (cmd == nullptr) { if (cmd == nullptr) {
IConsoleError("the given command was not found"); IConsoleError("the given command was not found");
return true; return true;
} }
IConsolePrintF(CC_DEFAULT, "command name: %s", cmd->name); IConsolePrintF(CC_DEFAULT, "command name: %s", cmd->name.c_str());
IConsolePrintF(CC_DEFAULT, "command proc: %p", cmd->proc); IConsolePrintF(CC_DEFAULT, "command proc: %p", cmd->proc);
if (cmd->hook != nullptr) IConsoleWarning("command is hooked"); if (cmd->hook != nullptr) IConsoleWarning("command is hooked");
@ -1668,21 +1674,20 @@ DEF_CONSOLE_CMD(ConHelp)
const IConsoleCmd *cmd; const IConsoleCmd *cmd;
const IConsoleAlias *alias; const IConsoleAlias *alias;
RemoveUnderscores(argv[1]); cmd = IConsole::CmdGet(argv[1]);
cmd = IConsoleCmdGet(argv[1]);
if (cmd != nullptr) { if (cmd != nullptr) {
cmd->proc(0, nullptr); cmd->proc(0, nullptr);
return true; return true;
} }
alias = IConsoleAliasGet(argv[1]); alias = IConsole::AliasGet(argv[1]);
if (alias != nullptr) { if (alias != nullptr) {
cmd = IConsoleCmdGet(alias->cmdline); cmd = IConsole::CmdGet(alias->cmdline);
if (cmd != nullptr) { if (cmd != nullptr) {
cmd->proc(0, nullptr); cmd->proc(0, nullptr);
return true; return true;
} }
IConsolePrintF(CC_ERROR, "ERROR: alias is of special type, please see its execution-line: '%s'", alias->cmdline); IConsolePrintF(CC_ERROR, "ERROR: alias is of special type, please see its execution-line: '%s'", alias->cmdline.c_str());
return true; return true;
} }
@ -1709,9 +1714,10 @@ DEF_CONSOLE_CMD(ConListCommands)
return true; return true;
} }
for (const IConsoleCmd *cmd = _iconsole_cmds; cmd != nullptr; cmd = cmd->next) { for (auto &it : IConsole::Commands()) {
if (argv[1] == nullptr || strstr(cmd->name, argv[1]) != nullptr) { const IConsoleCmd *cmd = &it.second;
if ((_settings_client.gui.console_show_unlisted || !cmd->unlisted) && (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE)) IConsolePrintF(CC_DEFAULT, "%s", cmd->name); if (argv[1] == nullptr || cmd->name.find(argv[1]) != std::string::npos) {
if ((_settings_client.gui.console_show_unlisted || !cmd->unlisted) && (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE)) IConsolePrintF(CC_DEFAULT, "%s", cmd->name.c_str());
} }
} }
@ -1725,9 +1731,10 @@ DEF_CONSOLE_CMD(ConListAliases)
return true; return true;
} }
for (const IConsoleAlias *alias = _iconsole_aliases; alias != nullptr; alias = alias->next) { for (auto &it : IConsole::Aliases()) {
if (argv[1] == nullptr || strstr(alias->name, argv[1]) != nullptr) { const IConsoleAlias *alias = &it.second;
IConsolePrintF(CC_DEFAULT, "%s => %s", alias->name, alias->cmdline); if (argv[1] == nullptr || alias->name.find(argv[1]) != std::string::npos) {
IConsolePrintF(CC_DEFAULT, "%s => %s", alias->name.c_str(), alias->cmdline.c_str());
} }
} }
@ -3185,9 +3192,9 @@ DEF_CONSOLE_CMD(ConIfHourMinute)
static void IConsoleDebugLibRegister() static void IConsoleDebugLibRegister()
{ {
IConsoleCmdRegister("resettile", ConResetTile); IConsole::CmdRegister("resettile", ConResetTile);
IConsoleAliasRegister("dbg_echo", "echo %A; echo %B"); IConsole::AliasRegister("dbg_echo", "echo %A; echo %B");
IConsoleAliasRegister("dbg_echo2", "echo %!"); IConsole::AliasRegister("dbg_echo2", "echo %!");
} }
#endif #endif
@ -3284,199 +3291,199 @@ DEF_CONSOLE_CMD(ConDumpInfo)
void IConsoleStdLibRegister() void IConsoleStdLibRegister()
{ {
IConsoleCmdRegister("debug_level", ConDebugLevel); IConsole::CmdRegister("debug_level", ConDebugLevel);
IConsoleCmdRegister("echo", ConEcho); IConsole::CmdRegister("echo", ConEcho);
IConsoleCmdRegister("echoc", ConEchoC); IConsole::CmdRegister("echoc", ConEchoC);
IConsoleCmdRegister("exec", ConExec); IConsole::CmdRegister("exec", ConExec);
IConsoleCmdRegister("exit", ConExit); IConsole::CmdRegister("exit", ConExit);
IConsoleCmdRegister("part", ConPart); IConsole::CmdRegister("part", ConPart);
IConsoleCmdRegister("help", ConHelp); IConsole::CmdRegister("help", ConHelp);
IConsoleCmdRegister("info_cmd", ConInfoCmd); IConsole::CmdRegister("info_cmd", ConInfoCmd);
IConsoleCmdRegister("list_cmds", ConListCommands); IConsole::CmdRegister("list_cmds", ConListCommands);
IConsoleCmdRegister("list_aliases", ConListAliases); IConsole::CmdRegister("list_aliases", ConListAliases);
IConsoleCmdRegister("newgame", ConNewGame); IConsole::CmdRegister("newgame", ConNewGame);
IConsoleCmdRegister("restart", ConRestart); IConsole::CmdRegister("restart", ConRestart);
IConsoleCmdRegister("reload", ConReload); IConsole::CmdRegister("reload", ConReload);
IConsoleCmdRegister("getseed", ConGetSeed); IConsole::CmdRegister("getseed", ConGetSeed);
IConsoleCmdRegister("getdate", ConGetDate); IConsole::CmdRegister("getdate", ConGetDate);
IConsoleCmdRegister("getsysdate", ConGetSysDate); IConsole::CmdRegister("getsysdate", ConGetSysDate);
IConsoleCmdRegister("quit", ConExit); IConsole::CmdRegister("quit", ConExit);
IConsoleCmdRegister("resetengines", ConResetEngines, ConHookNoNetwork); IConsole::CmdRegister("resetengines", ConResetEngines, ConHookNoNetwork);
IConsoleCmdRegister("reset_enginepool", ConResetEnginePool, ConHookNoNetwork); IConsole::CmdRegister("reset_enginepool", ConResetEnginePool, ConHookNoNetwork);
IConsoleCmdRegister("return", ConReturn); IConsole::CmdRegister("return", ConReturn);
IConsoleCmdRegister("screenshot", ConScreenShot); IConsole::CmdRegister("screenshot", ConScreenShot);
IConsoleCmdRegister("minimap", ConMinimap); IConsole::CmdRegister("minimap", ConMinimap);
IConsoleCmdRegister("script", ConScript); IConsole::CmdRegister("script", ConScript);
IConsoleCmdRegister("scrollto", ConScrollToTile); IConsole::CmdRegister("scrollto", ConScrollToTile);
IConsoleCmdRegister("highlight_tile", ConHighlightTile); IConsole::CmdRegister("highlight_tile", ConHighlightTile);
IConsoleAliasRegister("scrollto_highlight", "scrollto %+; highlight_tile %+"); IConsole::AliasRegister("scrollto_highlight", "scrollto %+; highlight_tile %+");
IConsoleCmdRegister("alias", ConAlias); IConsole::CmdRegister("alias", ConAlias);
IConsoleCmdRegister("load", ConLoad); IConsole::CmdRegister("load", ConLoad);
IConsoleCmdRegister("rm", ConRemove); IConsole::CmdRegister("rm", ConRemove);
IConsoleCmdRegister("save", ConSave); IConsole::CmdRegister("save", ConSave);
IConsoleCmdRegister("saveconfig", ConSaveConfig); IConsole::CmdRegister("saveconfig", ConSaveConfig);
IConsoleCmdRegister("ls", ConListFiles); IConsole::CmdRegister("ls", ConListFiles);
IConsoleCmdRegister("cd", ConChangeDirectory); IConsole::CmdRegister("cd", ConChangeDirectory);
IConsoleCmdRegister("pwd", ConPrintWorkingDirectory); IConsole::CmdRegister("pwd", ConPrintWorkingDirectory);
IConsoleCmdRegister("clear", ConClearBuffer); IConsole::CmdRegister("clear", ConClearBuffer);
IConsoleCmdRegister("setting", ConSetting); IConsole::CmdRegister("setting", ConSetting);
IConsoleCmdRegister("setting_newgame", ConSettingNewgame); IConsole::CmdRegister("setting_newgame", ConSettingNewgame);
IConsoleCmdRegister("list_settings",ConListSettings); IConsole::CmdRegister("list_settings", ConListSettings);
IConsoleCmdRegister("gamelog", ConGamelogPrint); IConsole::CmdRegister("gamelog", ConGamelogPrint);
IConsoleCmdRegister("rescan_newgrf", ConRescanNewGRF); IConsole::CmdRegister("rescan_newgrf", ConRescanNewGRF);
IConsoleAliasRegister("dir", "ls"); IConsole::AliasRegister("dir", "ls");
IConsoleAliasRegister("del", "rm %+"); IConsole::AliasRegister("del", "rm %+");
IConsoleAliasRegister("newmap", "newgame"); IConsole::AliasRegister("newmap", "newgame");
IConsoleAliasRegister("patch", "setting %+"); IConsole::AliasRegister("patch", "setting %+");
IConsoleAliasRegister("set", "setting %+"); IConsole::AliasRegister("set", "setting %+");
IConsoleAliasRegister("set_newgame", "setting_newgame %+"); IConsole::AliasRegister("set_newgame", "setting_newgame %+");
IConsoleAliasRegister("list_patches", "list_settings %+"); IConsole::AliasRegister("list_patches", "list_settings %+");
IConsoleAliasRegister("developer", "setting developer %+"); IConsole::AliasRegister("developer", "setting developer %+");
IConsoleCmdRegister("list_ai_libs", ConListAILibs); IConsole::CmdRegister("list_ai_libs", ConListAILibs);
IConsoleCmdRegister("list_ai", ConListAI); IConsole::CmdRegister("list_ai", ConListAI);
IConsoleCmdRegister("reload_ai", ConReloadAI); IConsole::CmdRegister("reload_ai", ConReloadAI);
IConsoleCmdRegister("rescan_ai", ConRescanAI); IConsole::CmdRegister("rescan_ai", ConRescanAI);
IConsoleCmdRegister("start_ai", ConStartAI); IConsole::CmdRegister("start_ai", ConStartAI);
IConsoleCmdRegister("stop_ai", ConStopAI); IConsole::CmdRegister("stop_ai", ConStopAI);
IConsoleCmdRegister("list_game", ConListGame); IConsole::CmdRegister("list_game", ConListGame);
IConsoleCmdRegister("list_game_libs", ConListGameLibs); IConsole::CmdRegister("list_game_libs", ConListGameLibs);
IConsoleCmdRegister("rescan_game", ConRescanGame); IConsole::CmdRegister("rescan_game", ConRescanGame);
IConsoleCmdRegister("companies", ConCompanies); IConsole::CmdRegister("companies", ConCompanies);
IConsoleAliasRegister("players", "companies"); IConsole::AliasRegister("players", "companies");
/* networking functions */ /* networking functions */
/* Content downloading is only available with ZLIB */ /* Content downloading is only available with ZLIB */
#if defined(WITH_ZLIB) #if defined(WITH_ZLIB)
IConsoleCmdRegister("content", ConContent); IConsole::CmdRegister("content", ConContent);
#endif /* defined(WITH_ZLIB) */ #endif /* defined(WITH_ZLIB) */
/*** Networking commands ***/ /*** Networking commands ***/
IConsoleCmdRegister("say", ConSay, ConHookNeedNetwork); IConsole::CmdRegister("say", ConSay, ConHookNeedNetwork);
IConsoleCmdRegister("say_company", ConSayCompany, ConHookNeedNetwork); IConsole::CmdRegister("say_company", ConSayCompany, ConHookNeedNetwork);
IConsoleAliasRegister("say_player", "say_company %+"); IConsole::AliasRegister("say_player", "say_company %+");
IConsoleCmdRegister("say_client", ConSayClient, ConHookNeedNetwork); IConsole::CmdRegister("say_client", ConSayClient, ConHookNeedNetwork);
IConsoleCmdRegister("connect", ConNetworkConnect, ConHookClientOnly); IConsole::CmdRegister("connect", ConNetworkConnect, ConHookClientOnly);
IConsoleCmdRegister("clients", ConNetworkClients, ConHookNeedNetwork); IConsole::CmdRegister("clients", ConNetworkClients, ConHookNeedNetwork);
IConsoleCmdRegister("status", ConStatus, ConHookServerOnly); IConsole::CmdRegister("status", ConStatus, ConHookServerOnly);
IConsoleCmdRegister("server_info", ConServerInfo, ConHookServerOnly); IConsole::CmdRegister("server_info", ConServerInfo, ConHookServerOnly);
IConsoleAliasRegister("info", "server_info"); IConsole::AliasRegister("info", "server_info");
IConsoleCmdRegister("reconnect", ConNetworkReconnect, ConHookClientOnly); IConsole::CmdRegister("reconnect", ConNetworkReconnect, ConHookClientOnly);
IConsoleCmdRegister("rcon", ConRcon, ConHookNeedNetwork); IConsole::CmdRegister("rcon", ConRcon, ConHookNeedNetwork);
IConsoleCmdRegister("settings_access", ConSettingsAccess, ConHookNeedNetwork); IConsole::CmdRegister("settings_access", ConSettingsAccess, ConHookNeedNetwork);
IConsoleCmdRegister("join", ConJoinCompany, ConHookNeedNetwork); IConsole::CmdRegister("join", ConJoinCompany, ConHookNeedNetwork);
IConsoleAliasRegister("spectate", "join 255"); IConsole::AliasRegister("spectate", "join 255");
IConsoleCmdRegister("move", ConMoveClient, ConHookServerOnly); IConsole::CmdRegister("move", ConMoveClient, ConHookServerOnly);
IConsoleCmdRegister("reset_company", ConResetCompany, ConHookServerOnly); IConsole::CmdRegister("reset_company", ConResetCompany, ConHookServerOnly);
IConsoleAliasRegister("clean_company", "reset_company %A"); IConsole::AliasRegister("clean_company", "reset_company %A");
IConsoleCmdRegister("client_name", ConClientNickChange, ConHookServerOnly); IConsole::CmdRegister("client_name", ConClientNickChange, ConHookServerOnly);
IConsoleCmdRegister("kick", ConKick, ConHookServerOnly); IConsole::CmdRegister("kick", ConKick, ConHookServerOnly);
IConsoleCmdRegister("ban", ConBan, ConHookServerOnly); IConsole::CmdRegister("ban", ConBan, ConHookServerOnly);
IConsoleCmdRegister("unban", ConUnBan, ConHookServerOnly); IConsole::CmdRegister("unban", ConUnBan, ConHookServerOnly);
IConsoleCmdRegister("banlist", ConBanList, ConHookServerOnly); IConsole::CmdRegister("banlist", ConBanList, ConHookServerOnly);
IConsoleCmdRegister("pause", ConPauseGame, ConHookServerOnly); IConsole::CmdRegister("pause", ConPauseGame, ConHookServerOnly);
IConsoleCmdRegister("unpause", ConUnpauseGame, ConHookServerOnly); IConsole::CmdRegister("unpause", ConUnpauseGame, ConHookServerOnly);
IConsoleCmdRegister("company_pw", ConCompanyPassword, ConHookNeedNetwork); IConsole::CmdRegister("company_pw", ConCompanyPassword, ConHookNeedNetwork);
IConsoleAliasRegister("company_password", "company_pw %+"); IConsole::AliasRegister("company_password", "company_pw %+");
IConsoleCmdRegister("company_pw_hash", ConCompanyPasswordHash, ConHookServerOnly); IConsole::CmdRegister("company_pw_hash", ConCompanyPasswordHash, ConHookServerOnly);
IConsoleAliasRegister("company_password_hash", "company_pw %+"); IConsole::AliasRegister("company_password_hash", "company_pw %+");
IConsoleCmdRegister("company_pw_hashes", ConCompanyPasswordHashes, ConHookServerOnly); IConsole::CmdRegister("company_pw_hashes", ConCompanyPasswordHashes, ConHookServerOnly);
IConsoleAliasRegister("company_password_hashes", "company_pw_hashes"); IConsole::AliasRegister("company_password_hashes", "company_pw_hashes");
IConsoleAliasRegister("net_frame_freq", "setting frame_freq %+"); IConsole::AliasRegister("net_frame_freq", "setting frame_freq %+");
IConsoleAliasRegister("net_sync_freq", "setting sync_freq %+"); IConsole::AliasRegister("net_sync_freq", "setting sync_freq %+");
IConsoleAliasRegister("server_pw", "setting server_password %+"); IConsole::AliasRegister("server_pw", "setting server_password %+");
IConsoleAliasRegister("server_password", "setting server_password %+"); IConsole::AliasRegister("server_password", "setting server_password %+");
IConsoleAliasRegister("rcon_pw", "setting rcon_password %+"); IConsole::AliasRegister("rcon_pw", "setting rcon_password %+");
IConsoleAliasRegister("rcon_password", "setting rcon_password %+"); IConsole::AliasRegister("rcon_password", "setting rcon_password %+");
IConsoleAliasRegister("settings_pw", "setting settings_password %+"); IConsole::AliasRegister("settings_pw", "setting settings_password %+");
IConsoleAliasRegister("settings_password", "setting settings_password %+"); IConsole::AliasRegister("settings_password", "setting settings_password %+");
IConsoleAliasRegister("name", "setting client_name %+"); IConsole::AliasRegister("name", "setting client_name %+");
IConsoleAliasRegister("server_name", "setting server_name %+"); IConsole::AliasRegister("server_name", "setting server_name %+");
IConsoleAliasRegister("server_port", "setting server_port %+"); IConsole::AliasRegister("server_port", "setting server_port %+");
IConsoleAliasRegister("server_advertise", "setting server_advertise %+"); IConsole::AliasRegister("server_advertise", "setting server_advertise %+");
IConsoleAliasRegister("max_clients", "setting max_clients %+"); IConsole::AliasRegister("max_clients", "setting max_clients %+");
IConsoleAliasRegister("max_companies", "setting max_companies %+"); IConsole::AliasRegister("max_companies", "setting max_companies %+");
IConsoleAliasRegister("max_spectators", "setting max_spectators %+"); IConsole::AliasRegister("max_spectators", "setting max_spectators %+");
IConsoleAliasRegister("max_join_time", "setting max_join_time %+"); IConsole::AliasRegister("max_join_time", "setting max_join_time %+");
IConsoleAliasRegister("pause_on_join", "setting pause_on_join %+"); IConsole::AliasRegister("pause_on_join", "setting pause_on_join %+");
IConsoleAliasRegister("autoclean_companies", "setting autoclean_companies %+"); IConsole::AliasRegister("autoclean_companies", "setting autoclean_companies %+");
IConsoleAliasRegister("autoclean_protected", "setting autoclean_protected %+"); IConsole::AliasRegister("autoclean_protected", "setting autoclean_protected %+");
IConsoleAliasRegister("autoclean_unprotected", "setting autoclean_unprotected %+"); IConsole::AliasRegister("autoclean_unprotected", "setting autoclean_unprotected %+");
IConsoleAliasRegister("restart_game_year", "setting restart_game_year %+"); IConsole::AliasRegister("restart_game_year", "setting restart_game_year %+");
IConsoleAliasRegister("min_players", "setting min_active_clients %+"); IConsole::AliasRegister("min_players", "setting min_active_clients %+");
IConsoleAliasRegister("reload_cfg", "setting reload_cfg %+"); IConsole::AliasRegister("reload_cfg", "setting reload_cfg %+");
/* conditionals */ /* conditionals */
IConsoleCmdRegister("if_year", ConIfYear); IConsole::CmdRegister("if_year", ConIfYear);
IConsoleCmdRegister("if_month", ConIfMonth); IConsole::CmdRegister("if_month", ConIfMonth);
IConsoleCmdRegister("if_day", ConIfDay); IConsole::CmdRegister("if_day", ConIfDay);
IConsoleCmdRegister("if_hour", ConIfHour); IConsole::CmdRegister("if_hour", ConIfHour);
IConsoleCmdRegister("if_minute", ConIfMinute); IConsole::CmdRegister("if_minute", ConIfMinute);
IConsoleCmdRegister("if_hour_minute", ConIfHourMinute); IConsole::CmdRegister("if_hour_minute", ConIfHourMinute);
/* debugging stuff */ /* debugging stuff */
#ifdef _DEBUG #ifdef _DEBUG
IConsoleDebugLibRegister(); IConsoleDebugLibRegister();
#endif #endif
IConsoleCmdRegister("fps", ConFramerate); IConsole::CmdRegister("fps", ConFramerate);
IConsoleCmdRegister("fps_wnd", ConFramerateWindow); IConsole::CmdRegister("fps_wnd", ConFramerateWindow);
IConsoleCmdRegister("find_non_realistic_braking_signal", ConFindNonRealisticBrakingSignal); IConsole::CmdRegister("find_non_realistic_braking_signal", ConFindNonRealisticBrakingSignal);
IConsoleCmdRegister("getfulldate", ConGetFullDate, nullptr, true); IConsole::CmdRegister("getfulldate", ConGetFullDate, nullptr, true);
IConsoleCmdRegister("dump_command_log", ConDumpCommandLog, nullptr, true); IConsole::CmdRegister("dump_command_log", ConDumpCommandLog, nullptr, true);
IConsoleCmdRegister("dump_desync_msgs", ConDumpDesyncMsgLog, nullptr, true); IConsole::CmdRegister("dump_desync_msgs", ConDumpDesyncMsgLog, nullptr, true);
IConsoleCmdRegister("dump_inflation", ConDumpInflation, nullptr, true); IConsole::CmdRegister("dump_inflation", ConDumpInflation, nullptr, true);
IConsoleCmdRegister("dump_cpdp_stats", ConDumpCpdpStats, nullptr, true); IConsole::CmdRegister("dump_cpdp_stats", ConDumpCpdpStats, nullptr, true);
IConsoleCmdRegister("dump_veh_stats", ConVehicleStats, nullptr, true); IConsole::CmdRegister("dump_veh_stats", ConVehicleStats, nullptr, true);
IConsoleCmdRegister("dump_map_stats", ConMapStats, nullptr, true); IConsole::CmdRegister("dump_map_stats", ConMapStats, nullptr, true);
IConsoleCmdRegister("dump_st_flow_stats", ConStFlowStats, nullptr, true); IConsole::CmdRegister("dump_st_flow_stats", ConStFlowStats, nullptr, true);
IConsoleCmdRegister("dump_game_events", ConDumpGameEvents, nullptr, true); IConsole::CmdRegister("dump_game_events", ConDumpGameEvents, nullptr, true);
IConsoleCmdRegister("dump_load_debug_log", ConDumpLoadDebugLog, nullptr, true); IConsole::CmdRegister("dump_load_debug_log", ConDumpLoadDebugLog, nullptr, true);
IConsoleCmdRegister("dump_load_debug_config", ConDumpLoadDebugConfig, nullptr, true); IConsole::CmdRegister("dump_load_debug_config", ConDumpLoadDebugConfig, nullptr, true);
IConsoleCmdRegister("dump_linkgraph_jobs", ConDumpLinkgraphJobs, nullptr, true); IConsole::CmdRegister("dump_linkgraph_jobs", ConDumpLinkgraphJobs, nullptr, true);
IConsoleCmdRegister("dump_road_types", ConDumpRoadTypes, nullptr, true); IConsole::CmdRegister("dump_road_types", ConDumpRoadTypes, nullptr, true);
IConsoleCmdRegister("dump_rail_types", ConDumpRailTypes, nullptr, true); IConsole::CmdRegister("dump_rail_types", ConDumpRailTypes, nullptr, true);
IConsoleCmdRegister("dump_bridge_types", ConDumpBridgeTypes, nullptr, true); IConsole::CmdRegister("dump_bridge_types", ConDumpBridgeTypes, nullptr, true);
IConsoleCmdRegister("dump_cargo_types", ConDumpCargoTypes, nullptr, true); IConsole::CmdRegister("dump_cargo_types", ConDumpCargoTypes, nullptr, true);
IConsoleCmdRegister("dump_tile", ConDumpTile, nullptr, true); IConsole::CmdRegister("dump_tile", ConDumpTile, nullptr, true);
IConsoleCmdRegister("check_caches", ConCheckCaches, nullptr, true); IConsole::CmdRegister("check_caches", ConCheckCaches, nullptr, true);
IConsoleCmdRegister("show_town_window", ConShowTownWindow, nullptr, true); IConsole::CmdRegister("show_town_window", ConShowTownWindow, nullptr, true);
IConsoleCmdRegister("show_station_window", ConShowStationWindow, nullptr, true); IConsole::CmdRegister("show_station_window", ConShowStationWindow, nullptr, true);
IConsoleCmdRegister("show_industry_window", ConShowIndustryWindow, nullptr, true); IConsole::CmdRegister("show_industry_window", ConShowIndustryWindow, nullptr, true);
IConsoleCmdRegister("viewport_debug", ConViewportDebug, nullptr, true); IConsole::CmdRegister("viewport_debug", ConViewportDebug, nullptr, true);
IConsoleCmdRegister("viewport_mark_dirty", ConViewportMarkDirty, nullptr, true); IConsole::CmdRegister("viewport_mark_dirty", ConViewportMarkDirty, nullptr, true);
IConsoleCmdRegister("viewport_mark_dirty_st_overlay", ConViewportMarkStationOverlayDirty, nullptr, true); IConsole::CmdRegister("viewport_mark_dirty_st_overlay", ConViewportMarkStationOverlayDirty, nullptr, true);
IConsoleCmdRegister("gfx_debug", ConGfxDebug, nullptr, true); IConsole::CmdRegister("gfx_debug", ConGfxDebug, nullptr, true);
IConsoleCmdRegister("csleep", ConCSleep, nullptr, true); IConsole::CmdRegister("csleep", ConCSleep, nullptr, true);
IConsoleCmdRegister("recalculate_road_cached_one_way_states", ConRecalculateRoadCachedOneWayStates, ConHookNoNetwork, true); IConsole::CmdRegister("recalculate_road_cached_one_way_states", ConRecalculateRoadCachedOneWayStates, ConHookNoNetwork, true);
IConsoleCmdRegister("misc_debug", ConMiscDebug, nullptr, true); IConsole::CmdRegister("misc_debug", ConMiscDebug, nullptr, true);
/* NewGRF development stuff */ /* NewGRF development stuff */
IConsoleCmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool); IConsole::CmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool);
IConsoleCmdRegister("newgrf_profile", ConNewGRFProfile, ConHookNewGRFDeveloperTool); IConsole::CmdRegister("newgrf_profile", ConNewGRFProfile, ConHookNewGRFDeveloperTool);
IConsoleCmdRegister("dump_info", ConDumpInfo); IConsole::CmdRegister("dump_info", ConDumpInfo);
IConsoleCmdRegister("do_disaster", ConDoDisaster, ConHookNewGRFDeveloperTool, true); IConsole::CmdRegister("do_disaster", ConDoDisaster, ConHookNewGRFDeveloperTool, true);
IConsoleCmdRegister("bankrupt_company", ConBankruptCompany, ConHookNewGRFDeveloperTool, true); IConsole::CmdRegister("bankrupt_company", ConBankruptCompany, ConHookNewGRFDeveloperTool, true);
IConsoleCmdRegister("delete_company", ConDeleteCompany, ConHookNewGRFDeveloperTool, true); IConsole::CmdRegister("delete_company", ConDeleteCompany, ConHookNewGRFDeveloperTool, true);
IConsoleCmdRegister("road_type_flag_ctl", ConRoadTypeFlagCtl, ConHookNewGRFDeveloperTool, true); IConsole::CmdRegister("road_type_flag_ctl", ConRoadTypeFlagCtl, ConHookNewGRFDeveloperTool, true);
IConsoleCmdRegister("rail_type_map_colour_ctl", ConRailTypeMapColourCtl, ConHookNewGRFDeveloperTool, true); IConsole::CmdRegister("rail_type_map_colour_ctl", ConRailTypeMapColourCtl, ConHookNewGRFDeveloperTool, true);
IConsoleCmdRegister("switch_baseset", ConSwitchBaseset, ConHookNewGRFDeveloperTool, true); IConsole::CmdRegister("switch_baseset", ConSwitchBaseset, ConHookNewGRFDeveloperTool, true);
/* Bug workarounds */ /* Bug workarounds */
IConsoleCmdRegister("jgrpp_bug_workaround_unblock_heliports", ConResetBlockedHeliports, ConHookNoNetwork, true); IConsole::CmdRegister("jgrpp_bug_workaround_unblock_heliports", ConResetBlockedHeliports, ConHookNoNetwork, true);
IConsoleCmdRegister("merge_linkgraph_jobs_asap", ConMergeLinkgraphJobsAsap, ConHookNoNetwork, true); IConsole::CmdRegister("merge_linkgraph_jobs_asap", ConMergeLinkgraphJobsAsap, ConHookNoNetwork, true);
#ifdef _DEBUG #ifdef _DEBUG
IConsoleCmdRegister("delete_vehicle_id", ConDeleteVehicleID, ConHookNoNetwork, true); IConsole::CmdRegister("delete_vehicle_id", ConDeleteVehicleID, ConHookNoNetwork, true);
#endif #endif
} }

@ -524,11 +524,9 @@ static void IConsoleTabCompletion()
return; return;
} }
} }
size_t length = cmdptr - input; extern std::string RemoveUnderscores(std::string name);
char *prefix = (char*)alloca(length + 1); std::string prefix = RemoveUnderscores(std::string(input, cmdptr - input));
strecpy(prefix, input, prefix + length); size_t prefix_length = prefix.size();
RemoveUnderscores(prefix);
size_t prefix_length = strlen(prefix);
if (prefix_length == 0) return; if (prefix_length == 0) return;
@ -536,26 +534,28 @@ static void IConsoleTabCompletion()
char *b = buffer; char *b = buffer;
uint matches = 0; uint matches = 0;
std::string common_prefix; std::string common_prefix;
for (const IConsoleCmd *cmd = _iconsole_cmds; cmd != nullptr; cmd = cmd->next) { for (auto &it : IConsole::Commands()) {
if (strncmp(cmd->name, prefix, prefix_length) == 0) { const char *cmd_name = it.first.c_str();
const IConsoleCmd *cmd = &it.second;
if (strncmp(cmd_name, prefix.c_str(), prefix_length) == 0) {
if ((_settings_client.gui.console_show_unlisted || !cmd->unlisted) && (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE)) { if ((_settings_client.gui.console_show_unlisted || !cmd->unlisted) && (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE)) {
if (matches == 0) { if (matches == 0) {
common_prefix = cmd->name; common_prefix = it.first;
} else { } else {
const char *cp = common_prefix.c_str(); const char *cp = common_prefix.c_str();
const char *cmdp = cmd->name; const char *cmdp = cmd_name;
while (true) { while (true) {
const char *end = cmdp; const char *end = cmdp;
WChar a = Utf8Consume(cp); WChar a = Utf8Consume(cp);
WChar b = Utf8Consume(cmdp); WChar b = Utf8Consume(cmdp);
if (a == 0 || b == 0 || a != b) { if (a == 0 || b == 0 || a != b) {
common_prefix.resize(end - cmd->name); common_prefix.resize(end - cmd_name);
break; break;
} }
} }
} }
matches++; matches++;
b += seprintf(b, lastof(buffer), "%s ", cmd->name); b += seprintf(b, lastof(buffer), "%s ", cmd_name);
} }
} }
} }

@ -11,6 +11,7 @@
#define CONSOLE_INTERNAL_H #define CONSOLE_INTERNAL_H
#include "gfx_type.h" #include "gfx_type.h"
#include <map>
static const uint ICON_CMDLN_SIZE = 1024; ///< maximum length of a typed in command static const uint ICON_CMDLN_SIZE = 1024; ///< maximum length of a typed in command
static const uint ICON_MAX_STREAMSIZE = 2048; ///< maximum length of a totally expanded command static const uint ICON_MAX_STREAMSIZE = 2048; ///< maximum length of a totally expanded command
@ -33,9 +34,9 @@ enum ConsoleHookResult {
typedef bool IConsoleCmdProc(byte argc, char *argv[]); typedef bool IConsoleCmdProc(byte argc, char *argv[]);
typedef ConsoleHookResult IConsoleHook(bool echo); typedef ConsoleHookResult IConsoleHook(bool echo);
struct IConsoleCmd { struct IConsoleCmd {
char *name; ///< name of command IConsoleCmd(const std::string &name, IConsoleCmdProc *proc, IConsoleHook *hook, bool unlisted) : name(name), proc(proc), hook(hook), unlisted(unlisted) {}
IConsoleCmd *next; ///< next command in list
std::string name; ///< name of command
IConsoleCmdProc *proc; ///< process executed when command is typed IConsoleCmdProc *proc; ///< process executed when command is typed
IConsoleHook *hook; ///< any special trigger action that needs executing IConsoleHook *hook; ///< any special trigger action that needs executing
bool unlisted; bool unlisted;
@ -54,25 +55,31 @@ struct IConsoleCmd {
* - ";" allows for combining commands (see example 'ng') * - ";" allows for combining commands (see example 'ng')
*/ */
struct IConsoleAlias { struct IConsoleAlias {
char *name; ///< name of the alias IConsoleAlias(const std::string &name, const std::string &cmdline) : name(name), cmdline(cmdline) {}
IConsoleAlias *next; ///< next alias in list
char *cmdline; ///< command(s) that is/are being aliased std::string name; ///< name of the alias
std::string cmdline; ///< command(s) that is/are being aliased
}; };
/* console parser */ struct IConsole
extern IConsoleCmd *_iconsole_cmds; ///< List of registered commands. {
extern IConsoleAlias *_iconsole_aliases; ///< List of registered aliases. typedef std::map<std::string, IConsoleCmd> CommandList;
typedef std::map<std::string, IConsoleAlias> AliasList;
/* console parser */
static CommandList &Commands();
static AliasList &Aliases();
/* Commands */
static void CmdRegister(const std::string &name, IConsoleCmdProc *proc, IConsoleHook *hook = nullptr, bool unlisted = false);
static IConsoleCmd *CmdGet(const std::string &name);
static void AliasRegister(const std::string &name, const std::string &cmd);
static IConsoleAlias *AliasGet(const std::string &name);
};
/* console functions */ /* console functions */
void IConsoleClearBuffer(); void IConsoleClearBuffer();
/* Commands */
void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook = nullptr, bool unlisted = false);
void IConsoleAliasRegister(const char *name, const char *cmd);
IConsoleCmd *IConsoleCmdGet(const char *name);
IConsoleAlias *IConsoleAliasGet(const char *name);
/* console std lib (register ingame commands/aliases) */ /* console std lib (register ingame commands/aliases) */
void IConsoleStdLibRegister(); void IConsoleStdLibRegister();
@ -82,6 +89,5 @@ bool GetArgumentInteger(uint32 *value, const char *arg);
void IConsoleGUIInit(); void IConsoleGUIInit();
void IConsoleGUIFree(); void IConsoleGUIFree();
void IConsoleGUIPrint(TextColour colour_code, char *string); void IConsoleGUIPrint(TextColour colour_code, char *string);
char *RemoveUnderscores(char *name);
#endif /* CONSOLE_INTERNAL_H */ #endif /* CONSOLE_INTERNAL_H */

@ -80,10 +80,4 @@ struct PointDimension {
int height; int height;
}; };
/** A pair of two integers */
struct Pair {
int a;
int b;
};
#endif /* GEOMETRY_TYPE_HPP */ #endif /* GEOMETRY_TYPE_HPP */

@ -682,7 +682,7 @@ struct FramerateWindow : Window {
case WID_FRW_TIMES_AVERAGE: { case WID_FRW_TIMES_AVERAGE: {
/* Open time graph windows when clicking detail measurement lines */ /* Open time graph windows when clicking detail measurement lines */
const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR); const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR);
int line = sb->GetScrolledRowFromWidget(pt.y - FONT_HEIGHT_NORMAL - VSPACING, this, widget, VSPACING, FONT_HEIGHT_NORMAL); int line = sb->GetScrolledRowFromWidget(pt.y, this, widget, VSPACING + FONT_HEIGHT_NORMAL);
if (line != INT_MAX) { if (line != INT_MAX) {
line++; line++;
/* Find the visible line that was clicked */ /* Find the visible line that was clicked */

@ -92,24 +92,6 @@ Dimension GetSpriteSize(SpriteID sprid, Point *offset = nullptr, ZoomLevel zoom
void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr); void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr);
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI); void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI);
/** How to align the to-be drawn text. */
enum StringAlignment {
SA_LEFT = 0 << 0, ///< Left align the text.
SA_HOR_CENTER = 1 << 0, ///< Horizontally center the text.
SA_RIGHT = 2 << 0, ///< Right align the text (must be a single bit).
SA_HOR_MASK = 3 << 0, ///< Mask for horizontal alignment.
SA_TOP = 0 << 2, ///< Top align the text.
SA_VERT_CENTER = 1 << 2, ///< Vertically center the text.
SA_BOTTOM = 2 << 2, ///< Bottom align the text.
SA_VERT_MASK = 3 << 2, ///< Mask for vertical alignment.
SA_CENTER = SA_HOR_CENTER | SA_VERT_CENTER, ///< Center both horizontally and vertically.
SA_FORCE = 1 << 4, ///< Force the alignment, i.e. don't swap for RTL languages.
};
DECLARE_ENUM_AS_BIT_SET(StringAlignment)
int DrawString(int left, int right, int top, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL); int DrawString(int left, int right, int top, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL);
int DrawString(int left, int right, int top, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL); int DrawString(int left, int right, int top, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL);
int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL);

@ -324,4 +324,22 @@ enum Support8bpp {
S8BPP_HARDWARE, ///< Full 8bpp support by OS and hardware. S8BPP_HARDWARE, ///< Full 8bpp support by OS and hardware.
}; };
/** How to align the to-be drawn text. */
enum StringAlignment {
SA_LEFT = 0 << 0, ///< Left align the text.
SA_HOR_CENTER = 1 << 0, ///< Horizontally center the text.
SA_RIGHT = 2 << 0, ///< Right align the text (must be a single bit).
SA_HOR_MASK = 3 << 0, ///< Mask for horizontal alignment.
SA_TOP = 0 << 2, ///< Top align the text.
SA_VERT_CENTER = 1 << 2, ///< Vertically center the text.
SA_BOTTOM = 2 << 2, ///< Bottom align the text.
SA_VERT_MASK = 3 << 2, ///< Mask for vertical alignment.
SA_CENTER = SA_HOR_CENTER | SA_VERT_CENTER, ///< Center both horizontally and vertically.
SA_FORCE = 1 << 4, ///< Force the alignment, i.e. don't swap for RTL languages.
};
DECLARE_ENUM_AS_BIT_SET(StringAlignment)
#endif /* GFX_TYPE_H */ #endif /* GFX_TYPE_H */

@ -992,7 +992,7 @@ struct PaymentRatesGraphWindow : BaseGraphWindow {
} }
case WID_CPR_MATRIX: { case WID_CPR_MATRIX: {
uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_CPR_MATRIX, 0, this->line_height); uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_CPR_MATRIX);
if (row >= this->vscroll->GetCount()) return; if (row >= this->vscroll->GetCount()) return;
const CargoSpec *cs; const CargoSpec *cs;

@ -62,13 +62,19 @@ struct GroupStatistics {
static void UpdateAutoreplace(CompanyID company); static void UpdateAutoreplace(CompanyID company);
}; };
enum GroupFlags : uint8 {
GF_REPLACE_PROTECTION, ///< If set to true, the global autoreplace has no effect on the group
GF_REPLACE_WAGON_REMOVAL, ///< If set, autoreplace will perform wagon removal on vehicles in this group.
GF_END,
};
/** Group data. */ /** Group data. */
struct Group : GroupPool::PoolItem<&_group_pool> { struct Group : GroupPool::PoolItem<&_group_pool> {
std::string name; ///< Group Name std::string name; ///< Group Name
Owner owner; ///< Group Owner Owner owner; ///< Group Owner
VehicleType vehicle_type; ///< Vehicle type of the group VehicleType vehicle_type; ///< Vehicle type of the group
bool replace_protection; ///< If set to true, the global autoreplace have no effect on the group uint8 flags; ///< Group flags
Livery livery; ///< Custom colour scheme for vehicles in this group Livery livery; ///< Custom colour scheme for vehicles in this group
GroupStatistics statistics; ///< NOSAVE: Statistics and caches on the vehicles in the group. GroupStatistics statistics; ///< NOSAVE: Statistics and caches on the vehicles in the group.

@ -324,7 +324,6 @@ CommandCost CmdCreateGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
Group *g = new Group(_current_company); Group *g = new Group(_current_company);
g->replace_protection = false;
g->vehicle_type = vt; g->vehicle_type = vt;
g->parent = INVALID_GROUP; g->parent = INVALID_GROUP;
@ -332,10 +331,12 @@ CommandCost CmdCreateGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
const Company *c = Company::Get(_current_company); const Company *c = Company::Get(_current_company);
g->livery.colour1 = c->livery[LS_DEFAULT].colour1; g->livery.colour1 = c->livery[LS_DEFAULT].colour1;
g->livery.colour2 = c->livery[LS_DEFAULT].colour2; g->livery.colour2 = c->livery[LS_DEFAULT].colour2;
if (c->settings.renew_keep_length) SetBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL);
} else { } else {
g->parent = pg->index; g->parent = pg->index;
g->livery.colour1 = pg->livery.colour1; g->livery.colour1 = pg->livery.colour1;
g->livery.colour2 = pg->livery.colour2; g->livery.colour2 = pg->livery.colour2;
g->flags = pg->flags;
} }
_new_group_id = g->index; _new_group_id = g->index;
@ -728,42 +729,50 @@ CommandCost CmdSetGroupLivery(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
} }
/** /**
* Set replace protection for a group and its sub-groups. * Set group flag for a group and its sub-groups.
* @param g initial group. * @param g initial group.
* @param protect 1 to set or 0 to clear protection. * @param set 1 to set or 0 to clear protection.
*/ */
static void SetGroupReplaceProtection(Group *g, bool protect) static void SetGroupFlag(Group *g, GroupFlags flag, bool set, bool children)
{ {
g->replace_protection = protect; if (set) {
SetBit(g->flags, flag);
} else {
ClrBit(g->flags, flag);
}
if (!children) return;
for (Group *pg : Group::Iterate()) { for (Group *pg : Group::Iterate()) {
if (pg->parent == g->index) SetGroupReplaceProtection(pg, protect); if (pg->parent == g->index) SetGroupFlag(pg, flag, set, true);
} }
} }
/** /**
* (Un)set global replace protection from a group * (Un)set group flag from a group
* @param tile unused * @param tile unused
* @param flags type of operation * @param flags type of operation
* @param p1 index of group array * @param p1 index of group array
* - p1 bit 0-15 : GroupID * - p1 bit 0-15 : GroupID
* - p1 bit 16-18 : Flag to set, by value not bit.
* @param p2 * @param p2
* - p2 bit 0 : 1 to set or 0 to clear protection. * - p2 bit 0 : 1 to set or 0 to clear protection.
* - p2 bit 1 : 1 to apply to sub-groups. * - p2 bit 1 : 1 to apply to sub-groups.
* @param text unused * @param text unused
* @return the cost of this operation or an error * @return the cost of this operation or an error
*/ */
CommandCost CmdSetGroupReplaceProtection(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) CommandCost CmdSetGroupFlag(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{ {
Group *g = Group::GetIfValid(p1); Group *g = Group::GetIfValid(GB(p1, 0, 16));
if (g == nullptr || g->owner != _current_company) return CMD_ERROR; if (g == nullptr || g->owner != _current_company) return CMD_ERROR;
/* GroupFlags are stored in as an 8 bit bitfield but passed here by value,
* so 3 bits is sufficient to cover each possible value. */
GroupFlags flag = (GroupFlags)GB(p1, 16, 3);
if (flag >= GroupFlags::GF_END) return CMD_ERROR;
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
if (HasBit(p2, 1)) { SetGroupFlag(g, flag, HasBit(p2, 0), HasBit(p2, 1));
SetGroupReplaceProtection(g, HasBit(p2, 0));
} else {
g->replace_protection = HasBit(p2, 0);
}
SetWindowDirty(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack()); SetWindowDirty(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack());
InvalidateWindowData(WC_REPLACE_VEHICLE, g->vehicle_type); InvalidateWindowData(WC_REPLACE_VEHICLE, g->vehicle_type);

@ -618,7 +618,7 @@ public:
/* If not a default group and the group has replace protection, show an enabled replace sprite. */ /* If not a default group and the group has replace protection, show an enabled replace sprite. */
uint16 protect_sprite = SPR_GROUP_REPLACE_OFF_TRAIN; uint16 protect_sprite = SPR_GROUP_REPLACE_OFF_TRAIN;
if (!IsDefaultGroupID(this->vli.index) && !IsAllGroupID(this->vli.index) && Group::Get(this->vli.index)->replace_protection) protect_sprite = SPR_GROUP_REPLACE_ON_TRAIN; if (!IsDefaultGroupID(this->vli.index) && !IsAllGroupID(this->vli.index) && HasBit(Group::Get(this->vli.index)->flags, GroupFlags::GF_REPLACE_PROTECTION)) protect_sprite = SPR_GROUP_REPLACE_ON_TRAIN;
this->GetWidget<NWidgetCore>(WID_GL_REPLACE_PROTECTION)->widget_data = protect_sprite + this->vli.vtype; this->GetWidget<NWidgetCore>(WID_GL_REPLACE_PROTECTION)->widget_data = protect_sprite + this->vli.vtype;
/* Set text of "group by" dropdown widget. */ /* Set text of "group by" dropdown widget. */
@ -675,7 +675,7 @@ public:
assert(g->owner == this->owner); assert(g->owner == this->owner);
DrawGroupInfo(y1, r.left, r.right, g->index, this->indents[i] * LEVEL_WIDTH, g->replace_protection, g->folded || (i + 1 < (int)this->groups.size() && indents[i + 1] > this->indents[i])); DrawGroupInfo(y1, r.left, r.right, g->index, this->indents[i] * LEVEL_WIDTH, HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION), g->folded || (i + 1 < (int)this->groups.size() && indents[i + 1] > this->indents[i]));
y1 += this->tiny_step_height; y1 += this->tiny_step_height;
} }
@ -755,7 +755,7 @@ public:
break; break;
case WID_GL_LIST_GROUP: { // Matrix Group case WID_GL_LIST_GROUP: { // Matrix Group
uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP);
if (id_g >= this->groups.size()) return; if (id_g >= this->groups.size()) return;
if (groups[id_g]->folded || (id_g + 1 < this->groups.size() && this->indents[id_g + 1] > this->indents[id_g])) { if (groups[id_g]->folded || (id_g + 1 < this->groups.size() && this->indents[id_g + 1] > this->indents[id_g])) {
@ -891,7 +891,7 @@ public:
case WID_GL_REPLACE_PROTECTION: { case WID_GL_REPLACE_PROTECTION: {
const Group *g = Group::GetIfValid(this->vli.index); const Group *g = Group::GetIfValid(this->vli.index);
if (g != nullptr) { if (g != nullptr) {
DoCommandP(0, this->vli.index, (g->replace_protection ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_REPLACE_PROTECTION); DoCommandP(0, this->vli.index | (GroupFlags::GF_REPLACE_PROTECTION << 16), (HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION) ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_FLAG);
} }
break; break;
} }
@ -921,7 +921,7 @@ public:
break; break;
case WID_GL_LIST_GROUP: { // Matrix group case WID_GL_LIST_GROUP: { // Matrix group
uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP);
GroupID new_g = id_g >= this->groups.size() ? INVALID_GROUP : this->groups[id_g]->index; GroupID new_g = id_g >= this->groups.size() ? INVALID_GROUP : this->groups[id_g]->index;
if (this->group_sel != new_g && g->parent != new_g) { if (this->group_sel != new_g && g->parent != new_g) {
@ -954,7 +954,7 @@ public:
this->group_over = INVALID_GROUP; this->group_over = INVALID_GROUP;
this->SetDirty(); this->SetDirty();
uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP);
GroupID new_g = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index; GroupID new_g = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index;
DoCommandP(0, new_g, vindex | (_ctrl_pressed || this->grouping == GB_SHARED_ORDERS ? 1 << 31 : 0), CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE), new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr); DoCommandP(0, new_g, vindex | (_ctrl_pressed || this->grouping == GB_SHARED_ORDERS ? 1 << 31 : 0), CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE), new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr);
@ -1124,7 +1124,7 @@ public:
break; break;
case WID_GL_LIST_GROUP: { // ... the list of custom groups. case WID_GL_LIST_GROUP: { // ... the list of custom groups.
uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP);
new_group_over = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index; new_group_over = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index;
break; break;
} }

@ -11,6 +11,7 @@ set(LANG_SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/brazilian_portuguese.txt ${CMAKE_CURRENT_SOURCE_DIR}/brazilian_portuguese.txt
${CMAKE_CURRENT_SOURCE_DIR}/bulgarian.txt ${CMAKE_CURRENT_SOURCE_DIR}/bulgarian.txt
${CMAKE_CURRENT_SOURCE_DIR}/catalan.txt ${CMAKE_CURRENT_SOURCE_DIR}/catalan.txt
${CMAKE_CURRENT_SOURCE_DIR}/chuvash.txt
${CMAKE_CURRENT_SOURCE_DIR}/croatian.txt ${CMAKE_CURRENT_SOURCE_DIR}/croatian.txt
${CMAKE_CURRENT_SOURCE_DIR}/czech.txt ${CMAKE_CURRENT_SOURCE_DIR}/czech.txt
${CMAKE_CURRENT_SOURCE_DIR}/danish.txt ${CMAKE_CURRENT_SOURCE_DIR}/danish.txt
@ -22,13 +23,16 @@ set(LANG_SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/faroese.txt ${CMAKE_CURRENT_SOURCE_DIR}/faroese.txt
${CMAKE_CURRENT_SOURCE_DIR}/finnish.txt ${CMAKE_CURRENT_SOURCE_DIR}/finnish.txt
${CMAKE_CURRENT_SOURCE_DIR}/french.txt ${CMAKE_CURRENT_SOURCE_DIR}/french.txt
${CMAKE_CURRENT_SOURCE_DIR}/frisian.txt
${CMAKE_CURRENT_SOURCE_DIR}/gaelic.txt ${CMAKE_CURRENT_SOURCE_DIR}/gaelic.txt
${CMAKE_CURRENT_SOURCE_DIR}/galician.txt ${CMAKE_CURRENT_SOURCE_DIR}/galician.txt
${CMAKE_CURRENT_SOURCE_DIR}/german.txt ${CMAKE_CURRENT_SOURCE_DIR}/german.txt
${CMAKE_CURRENT_SOURCE_DIR}/greek.txt ${CMAKE_CURRENT_SOURCE_DIR}/greek.txt
${CMAKE_CURRENT_SOURCE_DIR}/hebrew.txt ${CMAKE_CURRENT_SOURCE_DIR}/hebrew.txt
${CMAKE_CURRENT_SOURCE_DIR}/hindi.txt
${CMAKE_CURRENT_SOURCE_DIR}/hungarian.txt ${CMAKE_CURRENT_SOURCE_DIR}/hungarian.txt
${CMAKE_CURRENT_SOURCE_DIR}/icelandic.txt ${CMAKE_CURRENT_SOURCE_DIR}/icelandic.txt
${CMAKE_CURRENT_SOURCE_DIR}/ido.txt
${CMAKE_CURRENT_SOURCE_DIR}/indonesian.txt ${CMAKE_CURRENT_SOURCE_DIR}/indonesian.txt
${CMAKE_CURRENT_SOURCE_DIR}/irish.txt ${CMAKE_CURRENT_SOURCE_DIR}/irish.txt
${CMAKE_CURRENT_SOURCE_DIR}/italian.txt ${CMAKE_CURRENT_SOURCE_DIR}/italian.txt
@ -38,9 +42,13 @@ set(LANG_SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/latvian.txt ${CMAKE_CURRENT_SOURCE_DIR}/latvian.txt
${CMAKE_CURRENT_SOURCE_DIR}/lithuanian.txt ${CMAKE_CURRENT_SOURCE_DIR}/lithuanian.txt
${CMAKE_CURRENT_SOURCE_DIR}/luxembourgish.txt ${CMAKE_CURRENT_SOURCE_DIR}/luxembourgish.txt
${CMAKE_CURRENT_SOURCE_DIR}/macedonian.txt
${CMAKE_CURRENT_SOURCE_DIR}/malay.txt ${CMAKE_CURRENT_SOURCE_DIR}/malay.txt
${CMAKE_CURRENT_SOURCE_DIR}/maltese.txt
${CMAKE_CURRENT_SOURCE_DIR}/marathi.txt
${CMAKE_CURRENT_SOURCE_DIR}/norwegian_bokmal.txt ${CMAKE_CURRENT_SOURCE_DIR}/norwegian_bokmal.txt
${CMAKE_CURRENT_SOURCE_DIR}/norwegian_nynorsk.txt ${CMAKE_CURRENT_SOURCE_DIR}/norwegian_nynorsk.txt
${CMAKE_CURRENT_SOURCE_DIR}/persian.txt
${CMAKE_CURRENT_SOURCE_DIR}/polish.txt ${CMAKE_CURRENT_SOURCE_DIR}/polish.txt
${CMAKE_CURRENT_SOURCE_DIR}/portuguese.txt ${CMAKE_CURRENT_SOURCE_DIR}/portuguese.txt
${CMAKE_CURRENT_SOURCE_DIR}/romanian.txt ${CMAKE_CURRENT_SOURCE_DIR}/romanian.txt
@ -56,6 +64,7 @@ set(LANG_SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/thai.txt ${CMAKE_CURRENT_SOURCE_DIR}/thai.txt
${CMAKE_CURRENT_SOURCE_DIR}/traditional_chinese.txt ${CMAKE_CURRENT_SOURCE_DIR}/traditional_chinese.txt
${CMAKE_CURRENT_SOURCE_DIR}/turkish.txt ${CMAKE_CURRENT_SOURCE_DIR}/turkish.txt
${CMAKE_CURRENT_SOURCE_DIR}/urdu.txt
${CMAKE_CURRENT_SOURCE_DIR}/ukrainian.txt ${CMAKE_CURRENT_SOURCE_DIR}/ukrainian.txt
${CMAKE_CURRENT_SOURCE_DIR}/vietnamese.txt ${CMAKE_CURRENT_SOURCE_DIR}/vietnamese.txt
${CMAKE_CURRENT_SOURCE_DIR}/welsh.txt ${CMAKE_CURRENT_SOURCE_DIR}/welsh.txt

@ -1007,6 +1007,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardware
STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Selecteer dit vakje om OpenTTD hardwareversnelling te laten gebruiken. De gewijzigde instelling wordt pas van kracht nadat het spel opnieuw is gestart. STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Selecteer dit vakje om OpenTTD hardwareversnelling te laten gebruiken. De gewijzigde instelling wordt pas van kracht nadat het spel opnieuw is gestart.
STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}De instelling wordt pas van kracht als het spel opnieuw is gestart STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}De instelling wordt pas van kracht als het spel opnieuw is gestart
STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync
STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Selecteer dit vakje om het scherm verticaal te synchroniseren. De wijziging gaat pas in nadat je het spel opnieuw hebt opgestart. Werkt alleen als hardwareversnelling is ingeschakeld
STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Menupuntgrootte STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Menupuntgrootte
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Kiest de grootte van bedieningselementen STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Kiest de grootte van bedieningselementen
@ -1140,6 +1142,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Instelli
STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtertekst: STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtertekst:
STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Alles uitvouwen STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Alles uitvouwen
STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Alles inklappen STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Alles inklappen
STR_CONFIG_SETTING_RESET_ALL :{BLACK}Alle waarden terugstellen
STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(geen uitleg beschikbaar) STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(geen uitleg beschikbaar)
STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Standaardwaarde: {ORANGE}{STRING} STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Standaardwaarde: {ORANGE}{STRING}
STR_CONFIG_SETTING_TYPE :{LTBLUE}Instellingstype: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Instellingstype: {ORANGE}{STRING}
@ -1148,6 +1151,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Spelinstellinge
STR_CONFIG_SETTING_TYPE_GAME_INGAME :Spelinstellingen (opgeslagen in bestand; alleen van invloed op huidig spel) STR_CONFIG_SETTING_TYPE_GAME_INGAME :Spelinstellingen (opgeslagen in bestand; alleen van invloed op huidig spel)
STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Bedrijfsinstellingen (opgeslagen in bestand; alleen van invloed op nieuwe spellen) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Bedrijfsinstellingen (opgeslagen in bestand; alleen van invloed op nieuwe spellen)
STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Bedrijfsinstellingen (opgeslagen in bestand; alleen van invloed op huidig bedrijf) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Bedrijfsinstellingen (opgeslagen in bestand; alleen van invloed op huidig bedrijf)
STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Voorzichtig!
STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Met deze actie herstel je alle spelinstellingen naar hun standaardwaarden.{}Weet je zeker dat je wilt doorgaan?
STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Categorie: STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Categorie:
STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Type: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Type:
@ -2493,7 +2498,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Haven bo
STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Boei plaatsen, deze kan gebruikt worden voor extra tussenstops. Shift schakelt tussen bouwen/inschatting van de kosten STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Boei plaatsen, deze kan gebruikt worden voor extra tussenstops. Shift schakelt tussen bouwen/inschatting van de kosten
STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Aquaduct bouwen. Shift schakelt tussen bouwen/inschatting van de kosten. STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Aquaduct bouwen. Shift schakelt tussen bouwen/inschatting van de kosten.
STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Hiermee plaats je watermassa's.{}Maakt een kanaal, tenzij je Ctrl vasthoudt op zeeniveau, dan overstroomt de omgeving. STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Hiermee plaats je watermassa's.{}Maakt een kanaal, tenzij je Ctrl vasthoudt op zeeniveau, dan overstroomt de omgeving.
STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Hiermee maak je rivieren STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Hiermee maak je rivieren. Ctrl selecteert het gebied diagonaal
# Ship depot construction window # Ship depot construction window
STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Richting van dok STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Richting van dok

@ -1006,6 +1006,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Every 12 months
STR_GAME_OPTIONS_LANGUAGE :{BLACK}Language STR_GAME_OPTIONS_LANGUAGE :{BLACK}Language
STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Select the interface language to use STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Select the interface language to use
STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{RAW_STRING} ({NUM}% completed)
STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Fullscreen STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Fullscreen
STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Check this box to play OpenTTD fullscreen mode STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Check this box to play OpenTTD fullscreen mode
@ -2553,6 +2554,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Could no
STR_NETWORK_ERROR_CLIENT_START :{WHITE}Could not connect STR_NETWORK_ERROR_CLIENT_START :{WHITE}Could not connect
STR_NETWORK_ERROR_TIMEOUT :{WHITE}Connection #{NUM} timed out STR_NETWORK_ERROR_TIMEOUT :{WHITE}Connection #{NUM} timed out
STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}A protocol error was detected and the connection was closed STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}A protocol error was detected and the connection was closed
STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Your player name has not been set. The name can be set at the top of the Multiplayer window
STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}The revision of this client does not match the server's revision STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}The revision of this client does not match the server's revision
STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Wrong password STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Wrong password
STR_NETWORK_ERROR_SERVER_FULL :{WHITE}The server is full STR_NETWORK_ERROR_SERVER_FULL :{WHITE}The server is full
@ -2565,6 +2567,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}You took
STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Your computer is too slow to keep up with the server STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Your computer is too slow to keep up with the server
STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Your computer took too long to download the map STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Your computer took too long to download the map
STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Your computer took too long to join the server STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Your computer took too long to join the server
STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Your player name is not valid
############ Leave those lines in this order!! ############ Leave those lines in this order!!
STR_NETWORK_ERROR_CLIENT_GENERAL :general error STR_NETWORK_ERROR_CLIENT_GENERAL :general error
@ -2587,6 +2590,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :received no pas
STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :general timeout STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :general timeout
STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :downloading map took too long STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :downloading map took too long
STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :processing map took too long STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :processing map took too long
STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :invalid client name
############ End of leave-in-this-order ############ End of leave-in-this-order
STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possible connection loss STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possible connection loss

@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Every 12 months
STR_GAME_OPTIONS_LANGUAGE :{BLACK}Language STR_GAME_OPTIONS_LANGUAGE :{BLACK}Language
STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Select the interface language to use STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Select the interface language to use
STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% completed)
STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Fullscreen STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Fullscreen
STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Check this box to play OpenTTD fullscreen mode STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Check this box to play OpenTTD fullscreen mode

@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Kerran vuodessa
STR_GAME_OPTIONS_LANGUAGE :{BLACK}Kieli STR_GAME_OPTIONS_LANGUAGE :{BLACK}Kieli
STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Valitse käyttöliittymän kieli STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Valitse käyttöliittymän kieli
STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}{NBSP}% valmiina)
STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Koko näyttö STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Koko näyttö
STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Valitse tämä pelataksesi kokoruututilassa STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Valitse tämä pelataksesi kokoruututilassa

File diff suppressed because it is too large Load Diff

@ -1006,6 +1006,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :12개월마다
STR_GAME_OPTIONS_LANGUAGE :{BLACK}언어 STR_GAME_OPTIONS_LANGUAGE :{BLACK}언어
STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}사용할 언어를 선택하세요 STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}사용할 언어를 선택하세요
STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% 번역됨)
STR_GAME_OPTIONS_FULLSCREEN :{BLACK}전체화면 STR_GAME_OPTIONS_FULLSCREEN :{BLACK}전체화면
STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}OpenTTD를 전체화면으로 플레이하려면 클릭하세요. STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}OpenTTD를 전체화면으로 플레이하려면 클릭하세요.
@ -1298,7 +1299,7 @@ STR_CONFIG_SETTING_MULTIPINDTOWN_HELPTEXT :일반적으로
STR_CONFIG_SETTING_SIGNALSIDE :신호기 보이기: {STRING} STR_CONFIG_SETTING_SIGNALSIDE :신호기 보이기: {STRING}
STR_CONFIG_SETTING_SIGNALSIDE_HELPTEXT :선로의 어느 쪽에 신호기를 설치할 지 선택합니다. STR_CONFIG_SETTING_SIGNALSIDE_HELPTEXT :선로의 어느 쪽에 신호기를 설치할 지 선택합니다.
STR_CONFIG_SETTING_SIGNALSIDE_LEFT :왼쪽에 STR_CONFIG_SETTING_SIGNALSIDE_LEFT :왼쪽에
STR_CONFIG_SETTING_SIGNALSIDE_DRIVING_SIDE :행 방향에 STR_CONFIG_SETTING_SIGNALSIDE_DRIVING_SIDE :자동차 통행 방향에
STR_CONFIG_SETTING_SIGNALSIDE_RIGHT :오른쪽에 STR_CONFIG_SETTING_SIGNALSIDE_RIGHT :오른쪽에
STR_CONFIG_SETTING_SHOWFINANCES :연말에 자동으로 재정 창을 띄우기: {STRING} STR_CONFIG_SETTING_SHOWFINANCES :연말에 자동으로 재정 창을 띄우기: {STRING}
STR_CONFIG_SETTING_SHOWFINANCES_HELPTEXT :이 설정을 켜면. 회사의 재정 상태를 확인하기 쉽도록 매년 말에 재정 창이 자동으로 뜹니다. STR_CONFIG_SETTING_SHOWFINANCES_HELPTEXT :이 설정을 켜면. 회사의 재정 상태를 확인하기 쉽도록 매년 말에 재정 창이 자동으로 뜹니다.

@ -1374,6 +1374,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Co 12 miesięcy
STR_GAME_OPTIONS_LANGUAGE :{BLACK}Język STR_GAME_OPTIONS_LANGUAGE :{BLACK}Język
STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Wybierz język interfejsu STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Wybierz język interfejsu
STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} (ukończono {NUM}%)
STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pełny ekran STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pełny ekran
STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Zaznacz, jeśli chcesz grać w OpenTTD w trybie pełnoekranowym STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Zaznacz, jeśli chcesz grać w OpenTTD w trybie pełnoekranowym

@ -1140,6 +1140,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Каждый г
STR_GAME_OPTIONS_LANGUAGE :{BLACK}Язык STR_GAME_OPTIONS_LANGUAGE :{BLACK}Язык
STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Язык пользовательского интерфейса STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Язык пользовательского интерфейса
STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% перевед{P ён ено ено})
STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Полноэкранный режим STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Полноэкранный режим
STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Включить/выключить полноэкранный режим STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Включить/выключить полноэкранный режим
@ -1150,11 +1151,11 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :Другое
STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM}
STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Аппаратное ускорение STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Аппаратное ускорение
STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Нажмите здесь, чтобы включить/выключить аппаратное ускорение в OpenTTD. После этого игру потребуется перезапустить. STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Включить/выключить аппаратное ускорение. После этого игру потребуется перезапустить.
STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Эта настройка будет применена только после перезапуска игры STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Эта настройка будет применена только после перезапуска игры
STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}Вертикальная синхронизация STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}Вертикальная синхронизация
STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Нажмите здесь, чтобы включить/выключить вертикальную синхронизацию. После этого игру потребуется перезапустить. Работает только при включённом аппаратном ускорении. STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Включить/выключить вертикальную синхронизацию. После этого игру потребуется перезапустить. Работает только при включённом аппаратном ускорении.
STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Размер элементов интерфейса STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Размер элементов интерфейса
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Выберите размер элементов интерфейса STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Выберите размер элементов интерфейса

@ -995,6 +995,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Cada 12 meses
STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma
STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Selecciona el idioma a emplear en interfaz del juego STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Selecciona el idioma a emplear en interfaz del juego
STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% completo)
STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pantalla completa STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pantalla completa
STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Marca esta opción para jugar OpenTTD a pantalla completa STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Marca esta opción para jugar OpenTTD a pantalla completa

@ -995,6 +995,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Cada 12 meses
STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma
STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Elegir el idioma de la interfaz STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Elegir el idioma de la interfaz
STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% completado)
STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pantalla completa STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pantalla completa
STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Jugar OpenTTD en pantalla completa STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Jugar OpenTTD en pantalla completa
@ -1008,6 +1009,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Acelerac
STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Activar esta casilla para intentar emplear la aceleración por hardware. Este cambio solo tiene efecto tras reiniciar el juego STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Activar esta casilla para intentar emplear la aceleración por hardware. Este cambio solo tiene efecto tras reiniciar el juego
STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Esta configuración solo tendrá efecto después de reiniciar el juego STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Esta configuración solo tendrá efecto después de reiniciar el juego
STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync
STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Activar esta casilla para realizar sincronización vertical de pantalla (VSync). Este cambio solo tiene efecto tras reiniciar el juego y si la aceleración por hardware está activada.
STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamaño de la interfaz STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamaño de la interfaz
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Elegir el tamaño de los elementos de la interfaz STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Elegir el tamaño de los elementos de la interfaz

@ -58,6 +58,7 @@ struct LanguagePackHeader {
char cases[MAX_NUM_CASES][CASE_GENDER_LEN]; ///< the cases used by this translation char cases[MAX_NUM_CASES][CASE_GENDER_LEN]; ///< the cases used by this translation
bool IsValid() const; bool IsValid() const;
bool IsReasonablyFinished() const;
/** /**
* Get the index for the given gender. * Get the index for the given gender.

@ -483,7 +483,6 @@ static const char * const _credits[] = {
u8" Ingo von Borstel (planetmaker) - General, Support (since 1.1)", u8" Ingo von Borstel (planetmaker) - General, Support (since 1.1)",
u8" Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5)", u8" Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5)",
u8" Jos\u00e9 Soler (Terkhen) - General coding (since 1.0)", u8" Jos\u00e9 Soler (Terkhen) - General coding (since 1.0)",
u8" Leif Linse (Zuu) - AI/Game Script (since 1.2)",
u8"", u8"",
u8"Inactive Developers:", u8"Inactive Developers:",
u8" Jean-Fran\u00e7ois Claeys (Belugas) - GUI, NewGRF and more (0.4.5 - 1.0)", u8" Jean-Fran\u00e7ois Claeys (Belugas) - GUI, NewGRF and more (0.4.5 - 1.0)",
@ -496,6 +495,7 @@ static const char * const _credits[] = {
u8" Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5)", u8" Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5)",
u8" Patric Stout (TrueBrain) - NoAI, NoGo, Network (0.3 - 1.2), sys op (active)", u8" Patric Stout (TrueBrain) - NoAI, NoGo, Network (0.3 - 1.2), sys op (active)",
u8" Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3)", u8" Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3)",
u8" Leif Linse (Zuu) - AI/Game Script (1.2 - 1.6)",
u8"", u8"",
u8"Retired Developers:", u8"Retired Developers:",
u8" Tam\u00e1s Farag\u00f3 (Darkvater) - Ex-Lead coder (0.3 - 0.5)", u8" Tam\u00e1s Farag\u00f3 (Darkvater) - Ex-Lead coder (0.3 - 0.5)",

@ -1142,7 +1142,7 @@ static void RegisterConsoleMidiCommands()
{ {
static bool registered = false; static bool registered = false;
if (!registered) { if (!registered) {
IConsoleCmdRegister("dumpsmf", CmdDumpSMF); IConsole::CmdRegister("dumpsmf", CmdDumpSMF);
registered = true; registered = true;
} }
} }

@ -340,6 +340,7 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err)
STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER, STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER,
STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP, STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP,
STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN, STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN,
STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME,
}; };
static_assert(lengthof(network_error_strings) == NETWORK_ERROR_END); static_assert(lengthof(network_error_strings) == NETWORK_ERROR_END);
@ -722,6 +723,8 @@ void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const c
if (address.GetPort() == 0) return; if (address.GetPort() == 0) return;
if (!NetworkValidateClientName()) return;
strecpy(_settings_client.network.last_host, address.GetHostname(), lastof(_settings_client.network.last_host)); strecpy(_settings_client.network.last_host, address.GetHostname(), lastof(_settings_client.network.last_host));
_settings_client.network.last_port = address.GetPort(); _settings_client.network.last_port = address.GetPort();
_network_join_as = join_as; _network_join_as = join_as;

@ -282,7 +282,6 @@ static void SendChat(const char *buf, DestType type, int dest)
/** Window to enter the chat message in. */ /** Window to enter the chat message in. */
struct NetworkChatWindow : public Window { struct NetworkChatWindow : public Window {
DestType dtype; ///< The type of destination. DestType dtype; ///< The type of destination.
StringID dest_string; ///< String representation of the destination.
int dest; ///< The identifier of the destination. int dest; ///< The identifier of the destination.
QueryString message_editbox; ///< Message editbox. QueryString message_editbox; ///< Message editbox.
@ -307,9 +306,10 @@ struct NetworkChatWindow : public Window {
STR_NULL, STR_NULL,
}; };
assert((uint)this->dtype < lengthof(chat_captions)); assert((uint)this->dtype < lengthof(chat_captions));
this->dest_string = chat_captions[this->dtype];
this->InitNested(type); this->CreateNestedTree();
this->GetWidget<NWidgetCore>(WID_NC_DESTINATION)->widget_data = chat_captions[this->dtype];
this->FinishInitNested(type);
this->SetFocusedWidget(WID_NC_TEXTBOX); this->SetFocusedWidget(WID_NC_TEXTBOX);
InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height); InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height);
@ -464,27 +464,13 @@ struct NetworkChatWindow : public Window {
return pt; return pt;
} }
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override void SetStringParameters(int widget) const override
{ {
if (widget != WID_NC_DESTINATION) return; if (widget != WID_NC_DESTINATION) return;
if (this->dtype == DESTTYPE_CLIENT) { if (this->dtype == DESTTYPE_CLIENT) {
SetDParamStr(0, NetworkClientInfo::GetByClientID((ClientID)this->dest)->client_name); SetDParamStr(0, NetworkClientInfo::GetByClientID((ClientID)this->dest)->client_name);
} }
Dimension d = GetStringBoundingBox(this->dest_string);
d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
*size = maxdim(*size, d);
}
void DrawWidget(const Rect &r, int widget) const override
{
if (widget != WID_NC_DESTINATION) return;
if (this->dtype == DESTTYPE_CLIENT) {
SetDParamStr(0, NetworkClientInfo::GetByClientID((ClientID)this->dest)->client_name);
}
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, this->dest_string, TC_BLACK, SA_RIGHT);
} }
void OnClick(Point pt, int widget, int click_count) override void OnClick(Point pt, int widget, int click_count) override
@ -532,7 +518,7 @@ static const NWidgetPart _nested_chat_window_widgets[] = {
NWidget(WWT_CLOSEBOX, COLOUR_GREY, WID_NC_CLOSE), NWidget(WWT_CLOSEBOX, COLOUR_GREY, WID_NC_CLOSE),
NWidget(WWT_PANEL, COLOUR_GREY, WID_NC_BACKGROUND), NWidget(WWT_PANEL, COLOUR_GREY, WID_NC_BACKGROUND),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_TEXT, COLOUR_GREY, WID_NC_DESTINATION), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetDataTip(STR_NULL, STR_NULL), NWidget(WWT_TEXT, COLOUR_GREY, WID_NC_DESTINATION), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetTextColour(TC_BLACK), SetAlignment(SA_TOP | SA_RIGHT), SetDataTip(STR_NULL, STR_NULL),
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_NC_TEXTBOX), SetMinimalSize(100, 12), SetPadding(1, 0, 1, 0), SetResize(1, 0), NWidget(WWT_EDITBOX, COLOUR_GREY, WID_NC_TEXTBOX), SetMinimalSize(100, 12), SetPadding(1, 0, 1, 0), SetResize(1, 0),
SetDataTip(STR_NETWORK_CHAT_OSKTITLE, STR_NULL), SetDataTip(STR_NETWORK_CHAT_OSKTITLE, STR_NULL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_NC_SENDBUTTON), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetDataTip(STR_NETWORK_CHAT_SEND, STR_NULL), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_NC_SENDBUTTON), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetDataTip(STR_NETWORK_CHAT_SEND, STR_NULL),

@ -736,6 +736,10 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac
if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET; if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST; if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST;
/* The server validates the name when receiving it from clients, so when it is wrong
* here something went really wrong. In the best case the packet got malformed on its
* way too us, in the worst case the server is broken or compromised. */
if (!NetworkIsValidClientName(name)) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
ci = NetworkClientInfo::GetByClientID(client_id); ci = NetworkClientInfo::GetByClientID(client_id);
if (ci != nullptr) { if (ci != nullptr) {
@ -781,26 +785,27 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p) NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p)
{ {
static const StringID network_error_strings[] = { static const StringID network_error_strings[] = {
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_GENERAL STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_GENERAL
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_DESYNC STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_DESYNC
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_SAVEGAME_FAILED STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_SAVEGAME_FAILED
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_CONNECTION_LOST STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_CONNECTION_LOST
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_ILLEGAL_PACKET STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_ILLEGAL_PACKET
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NEWGRF_MISMATCH STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NEWGRF_MISMATCH
STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_AUTHORIZED STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_AUTHORIZED
STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_EXPECTED STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_EXPECTED
STR_NETWORK_ERROR_WRONG_REVISION, // NETWORK_ERROR_WRONG_REVISION STR_NETWORK_ERROR_WRONG_REVISION, // NETWORK_ERROR_WRONG_REVISION
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NAME_IN_USE STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NAME_IN_USE
STR_NETWORK_ERROR_WRONG_PASSWORD, // NETWORK_ERROR_WRONG_PASSWORD STR_NETWORK_ERROR_WRONG_PASSWORD, // NETWORK_ERROR_WRONG_PASSWORD
STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_COMPANY_MISMATCH STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_COMPANY_MISMATCH
STR_NETWORK_ERROR_KICKED, // NETWORK_ERROR_KICKED STR_NETWORK_ERROR_KICKED, // NETWORK_ERROR_KICKED
STR_NETWORK_ERROR_CHEATER, // NETWORK_ERROR_CHEATER STR_NETWORK_ERROR_CHEATER, // NETWORK_ERROR_CHEATER
STR_NETWORK_ERROR_SERVER_FULL, // NETWORK_ERROR_FULL STR_NETWORK_ERROR_SERVER_FULL, // NETWORK_ERROR_FULL
STR_NETWORK_ERROR_TOO_MANY_COMMANDS, // NETWORK_ERROR_TOO_MANY_COMMANDS STR_NETWORK_ERROR_TOO_MANY_COMMANDS, // NETWORK_ERROR_TOO_MANY_COMMANDS
STR_NETWORK_ERROR_TIMEOUT_PASSWORD, // NETWORK_ERROR_TIMEOUT_PASSWORD STR_NETWORK_ERROR_TIMEOUT_PASSWORD, // NETWORK_ERROR_TIMEOUT_PASSWORD
STR_NETWORK_ERROR_TIMEOUT_COMPUTER, // NETWORK_ERROR_TIMEOUT_COMPUTER STR_NETWORK_ERROR_TIMEOUT_COMPUTER, // NETWORK_ERROR_TIMEOUT_COMPUTER
STR_NETWORK_ERROR_TIMEOUT_MAP, // NETWORK_ERROR_TIMEOUT_MAP STR_NETWORK_ERROR_TIMEOUT_MAP, // NETWORK_ERROR_TIMEOUT_MAP
STR_NETWORK_ERROR_TIMEOUT_JOIN, // NETWORK_ERROR_TIMEOUT_JOIN STR_NETWORK_ERROR_TIMEOUT_JOIN, // NETWORK_ERROR_TIMEOUT_JOIN
STR_NETWORK_ERROR_INVALID_CLIENT_NAME, // NETWORK_ERROR_INVALID_CLIENT_NAME
}; };
static_assert(lengthof(network_error_strings) == NETWORK_ERROR_END); static_assert(lengthof(network_error_strings) == NETWORK_ERROR_END);
@ -1414,6 +1419,56 @@ void NetworkClientsToSpectators(CompanyID cid)
cur_company.Restore(); cur_company.Restore();
} }
/**
* Check whether the given client name is deemed valid for use in network games.
* An empty name (null or '') is not valid as that is essentially no name at all.
* A name starting with white space is not valid for tab completion purposes.
* @param client_name The client name to check for validity.
* @return True iff the name is valid.
*/
bool NetworkIsValidClientName(const char *client_name)
{
if (StrEmpty(client_name)) return false;
if (*client_name == ' ') return false;
return true;
}
/**
* Trim the given client name in place, i.e. remove leading and trailing spaces.
* After the trim check whether the client name is valid. A client name is valid
* whenever the name is not empty and does not start with spaces. This check is
* done via \c NetworkIsValidClientName.
* When the client name is valid, this function returns true.
* When the client name is not valid a GUI error message is shown telling the
* user to set the client name and this function returns false.
*
* This function is not suitable for ensuring a valid client name at the server
* as the error message will then be shown to the host instead of the client.
* @param client_name The client name to validate. It will be trimmed of leading
* and trailing spaces.
* @return True iff the client name is valid.
*/
bool NetworkValidateClientName(char *client_name)
{
StrTrimInPlace(client_name);
if (NetworkIsValidClientName(client_name)) return true;
ShowErrorMessage(STR_NETWORK_ERROR_BAD_PLAYER_NAME, INVALID_STRING_ID, WL_ERROR);
return false;
}
/**
* Convenience method for NetworkValidateClientName on _settings_client.network.client_name.
* It trims the client name and checks whether it is empty. When it is empty
* an error message is shown to the GUI user.
* See \c NetworkValidateClientName(char*) for details about the functionality.
* @return True iff the client name is valid.
*/
bool NetworkValidateClientName()
{
return NetworkValidateClientName(_settings_client.network.client_name);
}
/** /**
* Send the server our name. * Send the server our name.
*/ */
@ -1422,6 +1477,11 @@ void NetworkUpdateClientName()
NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(_network_own_client_id); NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(_network_own_client_id);
if (ci == nullptr) return; if (ci == nullptr) return;
/* There is no validation on string settings, it is actually a post change callback.
* This method is called from that post change callback. So, when the client name is
* changed via the console there is no easy way to prevent an invalid name. Though,
* we can prevent it getting sent here. */
if (!NetworkValidateClientName()) return;
/* Don't change the name if it is the same as the old name */ /* Don't change the name if it is the same as the old name */
if (strcmp(ci->client_name, _settings_client.network.client_name) != 0) { if (strcmp(ci->client_name, _settings_client.network.client_name) != 0) {

@ -863,55 +863,31 @@ public:
EventState OnKeyPress(WChar key, uint16 keycode) override EventState OnKeyPress(WChar key, uint16 keycode) override
{ {
switch (keycode) { if (this->vscroll->UpdateListPositionOnKeyPress(this->list_pos, keycode) == ES_NOT_HANDLED) {
case WKC_UP: switch (keycode) {
/* scroll up by one */ case WKC_SPACE:
if (this->list_pos > 0) this->list_pos--; case WKC_RETURN:
break; if (keycode == WKC_RETURN || !IsWidgetFocused(WID_NCL_FILTER)) {
case WKC_DOWN: if (this->selected != nullptr) {
/* scroll down by one */ _network_content_client.ToggleSelectedState(this->selected);
if (this->list_pos < (int)this->content.size() - 1) this->list_pos++; this->content.ForceResort();
break; this->InvalidateData();
case WKC_PAGEUP: }
/* scroll up a page */ if (this->filter_data.types.any()) {
this->list_pos = (this->list_pos < this->vscroll->GetCapacity()) ? 0 : this->list_pos - this->vscroll->GetCapacity(); this->content.ForceRebuild();
break; this->InvalidateData();
case WKC_PAGEDOWN: }
/* scroll down a page */ return ES_HANDLED;
this->list_pos = std::min(this->list_pos + this->vscroll->GetCapacity(), (int)this->content.size() - 1);
break;
case WKC_HOME:
/* jump to beginning */
this->list_pos = 0;
break;
case WKC_END:
/* jump to end */
this->list_pos = (int)this->content.size() - 1;
break;
case WKC_SPACE:
case WKC_RETURN:
if (keycode == WKC_RETURN || !IsWidgetFocused(WID_NCL_FILTER)) {
if (this->selected != nullptr) {
_network_content_client.ToggleSelectedState(this->selected);
this->content.ForceResort();
this->InvalidateData();
}
if (this->filter_data.types.any()) {
this->content.ForceRebuild();
this->InvalidateData();
} }
return ES_HANDLED; /* space is pressed and filter is focused. */
} FALLTHROUGH;
/* space is pressed and filter is focused. */
FALLTHROUGH;
default: default:
return ES_NOT_HANDLED; return ES_NOT_HANDLED;
}
} }
if (this->content.size() == 0) { if (this->content.size() == 0) {
this->list_pos = 0; // above stuff may result in "-1".
if (this->UpdateFilterState()) { if (this->UpdateFilterState()) {
this->content.ForceRebuild(); this->content.ForceRebuild();
this->InvalidateData(); this->InvalidateData();

@ -36,6 +36,9 @@ extern StringList _network_host_list;
extern StringList _network_ban_list; extern StringList _network_ban_list;
byte NetworkSpectatorCount(); byte NetworkSpectatorCount();
bool NetworkIsValidClientName(const char *client_name);
bool NetworkValidateClientName();
bool NetworkValidateClientName(char *client_name);
void NetworkUpdateClientName(); void NetworkUpdateClientName();
bool NetworkCompanyHasClients(CompanyID company); bool NetworkCompanyHasClients(CompanyID company);
const char *NetworkChangeCompanyPassword(CompanyID company_id, const char *password); const char *NetworkChangeCompanyPassword(CompanyID company_id, const char *password);

@ -68,8 +68,8 @@ void UpdateNetworkGameWindow()
} }
typedef GUIList<NetworkGameList*, StringFilter&> GUIGameServerList; typedef GUIList<NetworkGameList*, StringFilter&> GUIGameServerList;
typedef uint16 ServerListPosition; typedef int ServerListPosition;
static const ServerListPosition SLP_INVALID = 0xFFFF; static const ServerListPosition SLP_INVALID = -1;
/** Full blown container to make it behave exactly as we want :) */ /** Full blown container to make it behave exactly as we want :) */
class NWidgetServerListHeader : public NWidgetContainer { class NWidgetServerListHeader : public NWidgetContainer {
@ -786,39 +786,8 @@ public:
EventState state = ES_NOT_HANDLED; EventState state = ES_NOT_HANDLED;
/* handle up, down, pageup, pagedown, home and end */ /* handle up, down, pageup, pagedown, home and end */
if (keycode == WKC_UP || keycode == WKC_DOWN || keycode == WKC_PAGEUP || keycode == WKC_PAGEDOWN || keycode == WKC_HOME || keycode == WKC_END) { if (this->vscroll->UpdateListPositionOnKeyPress(this->list_pos, keycode) == ES_HANDLED) {
if (this->servers.size() == 0) return ES_HANDLED; if (this->list_pos == SLP_INVALID) return ES_HANDLED;
switch (keycode) {
case WKC_UP:
/* scroll up by one */
if (this->list_pos == SLP_INVALID) return ES_HANDLED;
if (this->list_pos > 0) this->list_pos--;
break;
case WKC_DOWN:
/* scroll down by one */
if (this->list_pos == SLP_INVALID) return ES_HANDLED;
if (this->list_pos < this->servers.size() - 1) this->list_pos++;
break;
case WKC_PAGEUP:
/* scroll up a page */
if (this->list_pos == SLP_INVALID) return ES_HANDLED;
this->list_pos = (this->list_pos < this->vscroll->GetCapacity()) ? 0 : this->list_pos - this->vscroll->GetCapacity();
break;
case WKC_PAGEDOWN:
/* scroll down a page */
if (this->list_pos == SLP_INVALID) return ES_HANDLED;
this->list_pos = std::min(this->list_pos + this->vscroll->GetCapacity(), (int)this->servers.size() - 1);
break;
case WKC_HOME:
/* jump to beginning */
this->list_pos = 0;
break;
case WKC_END:
/* jump to end */
this->list_pos = (ServerListPosition)this->servers.size() - 1;
break;
default: NOT_REACHED();
}
this->server = this->servers[this->list_pos]; this->server = this->servers[this->list_pos];
@ -854,12 +823,9 @@ public:
} }
case WID_NG_CLIENT: case WID_NG_CLIENT:
/* Make sure the name does not start with a space, so TAB completion works */ /* Validation of the name will happen once the user tries to join or start a game, as getting
if (!StrEmpty(this->name_editbox.text.buf) && this->name_editbox.text.buf[0] != ' ') { * error messages while typing (e.g. when you clear the name) defeats the purpose of the check. */
strecpy(_settings_client.network.client_name, this->name_editbox.text.buf, lastof(_settings_client.network.client_name)); strecpy(_settings_client.network.client_name, this->name_editbox.text.buf, lastof(_settings_client.network.client_name));
} else {
strecpy(_settings_client.network.client_name, "Player", lastof(_settings_client.network.client_name));
}
break; break;
} }
} }
@ -1292,6 +1258,8 @@ static WindowDesc _network_start_server_window_desc(
static void ShowNetworkStartServerWindow() static void ShowNetworkStartServerWindow()
{ {
if (!NetworkValidateClientName()) return;
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME);
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY);
@ -1584,6 +1552,8 @@ static WindowDesc _network_lobby_window_desc(
*/ */
static void ShowNetworkLobbyWindow(NetworkGameList *ngl) static void ShowNetworkLobbyWindow(NetworkGameList *ngl)
{ {
if (!NetworkValidateClientName()) return;
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START);
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME);

@ -987,8 +987,12 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p)
break; break;
} }
/* We need a valid name.. make it Player */ if (!NetworkIsValidClientName(name)) {
if (StrEmpty(name)) strecpy(name, "Player", lastof(name)); /* An invalid client name was given. However, the client ensures the name
* is valid before it is sent over the network, so something went horribly
* wrong. This is probably someone trying to troll us. */
return this->SendError(NETWORK_ERROR_INVALID_CLIENT_NAME);
}
if (!NetworkFindName(name, lastof(name))) { // Change name if duplicate if (!NetworkFindName(name, lastof(name))) { // Change name if duplicate
/* We could not create a name for this client */ /* We could not create a name for this client */
@ -1558,6 +1562,13 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet
if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST; if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST;
if (ci != nullptr) { if (ci != nullptr) {
if (!NetworkIsValidClientName(client_name)) {
/* An invalid client name was given. However, the client ensures the name
* is valid before it is sent over the network, so something went horribly
* wrong. This is probably someone trying to troll us. */
return this->SendError(NETWORK_ERROR_INVALID_CLIENT_NAME);
}
/* Display change */ /* Display change */
if (NetworkFindName(client_name, lastof(client_name))) { if (NetworkFindName(client_name, lastof(client_name))) {
NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, CC_DEFAULT, false, ci->client_name, client_name); NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, CC_DEFAULT, false, ci->client_name, client_name);

@ -133,6 +133,7 @@ enum NetworkErrorCode {
NETWORK_ERROR_TIMEOUT_COMPUTER, NETWORK_ERROR_TIMEOUT_COMPUTER,
NETWORK_ERROR_TIMEOUT_MAP, NETWORK_ERROR_TIMEOUT_MAP,
NETWORK_ERROR_TIMEOUT_JOIN, NETWORK_ERROR_TIMEOUT_JOIN,
NETWORK_ERROR_INVALID_CLIENT_NAME,
NETWORK_ERROR_END, NETWORK_ERROR_END,
}; };

@ -1327,42 +1327,8 @@ struct NewGRFWindow : public Window, NewGRFScanCallback {
{ {
if (!this->editable) return ES_NOT_HANDLED; if (!this->editable) return ES_NOT_HANDLED;
switch (keycode) { if (this->vscroll2->UpdateListPositionOnKeyPress(this->avail_pos, keycode) == ES_NOT_HANDLED) return ES_NOT_HANDLED;
case WKC_UP:
/* scroll up by one */
if (this->avail_pos > 0) this->avail_pos--;
break;
case WKC_DOWN:
/* scroll down by one */
if (this->avail_pos < (int)this->avails.size() - 1) this->avail_pos++;
break;
case WKC_PAGEUP:
/* scroll up a page */
this->avail_pos = (this->avail_pos < this->vscroll2->GetCapacity()) ? 0 : this->avail_pos - this->vscroll2->GetCapacity();
break;
case WKC_PAGEDOWN:
/* scroll down a page */
this->avail_pos = std::min(this->avail_pos + this->vscroll2->GetCapacity(), (int)this->avails.size() - 1);
break;
case WKC_HOME:
/* jump to beginning */
this->avail_pos = 0;
break;
case WKC_END:
/* jump to end */
this->avail_pos = (uint)this->avails.size() - 1;
break;
default:
return ES_NOT_HANDLED;
}
if (this->avails.size() == 0) this->avail_pos = -1;
if (this->avail_pos >= 0) { if (this->avail_pos >= 0) {
this->active_sel = nullptr; this->active_sel = nullptr;
DeleteWindowByClass(WC_GRF_PARAMETERS); DeleteWindowByClass(WC_GRF_PARAMETERS);

@ -422,7 +422,7 @@ struct NewsWindow : Window {
{ {
switch (widget) { switch (widget) {
case WID_N_CAPTION: case WID_N_CAPTION:
DrawCaption(r, COLOUR_LIGHT_BLUE, this->owner, STR_NEWS_MESSAGE_CAPTION); DrawCaption(r, COLOUR_LIGHT_BLUE, this->owner, TC_FROMSTRING, STR_NEWS_MESSAGE_CAPTION, SA_HOR_CENTER);
break; break;
case WID_N_PANEL: case WID_N_PANEL:
@ -1205,7 +1205,7 @@ struct MessageHistoryWindow : Window {
NewsItem *ni = _latest_news; NewsItem *ni = _latest_news;
if (ni == nullptr) return; if (ni == nullptr) return;
for (int n = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_MH_BACKGROUND, WD_FRAMERECT_TOP, this->line_height); n > 0; n--) { for (int n = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_MH_BACKGROUND, WD_FRAMERECT_TOP); n > 0; n--) {
ni = ni->prev; ni = ni->prev;
if (ni == nullptr) return; if (ni == nullptr) return;
} }

@ -77,8 +77,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,11,0,${REV_ISODATE} FILEVERSION ${REV_MAJOR},${REV_MINOR},${REV_BUILD},${REV_ISODATE}
PRODUCTVERSION 1,11,0,${REV_ISODATE} PRODUCTVERSION ${REV_MAJOR},${REV_MINOR},${REV_BUILD},${REV_ISODATE}
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L

@ -1578,7 +1578,7 @@ public:
break; break;
case WID_BRAS_NEWST_LIST: { case WID_BRAS_NEWST_LIST: {
int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BRAS_NEWST_LIST, 0, this->line_height); int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BRAS_NEWST_LIST);
if (y >= (int)this->station_classes.size()) return; if (y >= (int)this->station_classes.size()) return;
StationClassID station_class_id = this->station_classes[y]; StationClassID station_class_id = this->station_classes[y];
if (_railstation.station_class != station_class_id) { if (_railstation.station_class != station_class_id) {

@ -91,4 +91,4 @@ const byte _openttd_revision_tagged = ${REV_ISTAG};
* version, thus making comparisons on specific revisions easy. * version, thus making comparisons on specific revisions easy.
*/ */
/** ${REV_ISSTABLETAG} removed */ /** ${REV_ISSTABLETAG} removed */
const uint32 _openttd_newgrf_version = 1 << 28 | 12 << 24 | 0 << 20 | 0 << 19 | 28004; const uint32 _openttd_newgrf_version = ${REV_MAJOR} << 28 | ${REV_MINOR} << 24 | ${REV_BUILD} << 20 | 0 << 19 | 28004;

@ -3743,6 +3743,23 @@ bool AfterLoadGame()
} }
} }
if (IsSavegameVersionBefore(SLV_GROUP_REPLACE_WAGON_REMOVAL)) {
/* Propagate wagon removal flag for compatibility */
/* Temporary bitmask of company wagon removal setting */
uint16 wagon_removal = 0;
for (const Company *c : Company::Iterate()) {
if (c->settings.renew_keep_length) SetBit(wagon_removal, c->index);
}
for (Group *g : Group::Iterate()) {
if (g->flags != 0) {
/* Convert old replace_protection value to flag. */
g->flags = 0;
SetBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION);
}
if (HasBit(wagon_removal, g->owner)) SetBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL);
}
}
/* Compute station catchment areas. This is needed here in case UpdateStationAcceptance is called below. */ /* Compute station catchment areas. This is needed here in case UpdateStationAcceptance is called below. */
Station::RecomputeCatchmentForAll(); Station::RecomputeCatchmentForAll();

@ -21,7 +21,7 @@ static const SaveLoad _group_desc[] = {
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), // num_vehicle SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), // num_vehicle
SLE_VAR(Group, owner, SLE_UINT8), SLE_VAR(Group, owner, SLE_UINT8),
SLE_VAR(Group, vehicle_type, SLE_UINT8), SLE_VAR(Group, vehicle_type, SLE_UINT8),
SLE_VAR(Group, replace_protection, SLE_BOOL), SLE_VAR(Group, flags, SLE_UINT8),
SLE_CONDVAR(Group, livery.in_use, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION), SLE_CONDVAR(Group, livery.in_use, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION),
SLE_CONDVAR(Group, livery.colour1, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION), SLE_CONDVAR(Group, livery.colour1, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION),
SLE_CONDVAR(Group, livery.colour2, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION), SLE_CONDVAR(Group, livery.colour2, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION),

@ -329,6 +329,7 @@ enum SaveLoadVersion : uint16 {
SLV_VEH_MOTION_COUNTER, ///< 288 PR#8591 Desync safe motion counter SLV_VEH_MOTION_COUNTER, ///< 288 PR#8591 Desync safe motion counter
SLV_INDUSTRY_TEXT, ///< 289 PR#8576 v1.11.0-RC1 Additional GS text for industries. SLV_INDUSTRY_TEXT, ///< 289 PR#8576 v1.11.0-RC1 Additional GS text for industries.
SLV_MAPGEN_SETTINGS_REVAMP, ///< 290 PR#8891 v1.11 Revamp of some mapgen settings (snow coverage, desert coverage, heightmap height, custom terrain type). SLV_MAPGEN_SETTINGS_REVAMP, ///< 290 PR#8891 v1.11 Revamp of some mapgen settings (snow coverage, desert coverage, heightmap height, custom terrain type).
SLV_GROUP_REPLACE_WAGON_REMOVAL, ///< 291 PR#7441 Per-group wagon removal flag.
SL_MAX_VERSION, ///< Highest possible saveload version SL_MAX_VERSION, ///< Highest possible saveload version

@ -89,14 +89,14 @@
{ {
EnforcePrecondition(false, IsValidGroup(group_id)); EnforcePrecondition(false, IsValidGroup(group_id));
return ScriptObject::DoCommand(0, group_id, enable ? 1 : 0, CMD_SET_GROUP_REPLACE_PROTECTION); return ScriptObject::DoCommand(0, group_id | GroupFlags::GF_REPLACE_PROTECTION, enable ? 1 : 0, CMD_SET_GROUP_FLAG);
} }
/* static */ bool ScriptGroup::GetAutoReplaceProtection(GroupID group_id) /* static */ bool ScriptGroup::GetAutoReplaceProtection(GroupID group_id)
{ {
if (!IsValidGroup(group_id)) return false; if (!IsValidGroup(group_id)) return false;
return ::Group::Get(group_id)->replace_protection; return HasBit(::Group::Get(group_id)->flags, GroupFlags::GF_REPLACE_PROTECTION);
} }
/* static */ int32 ScriptGroup::GetNumEngines(GroupID group_id, EngineID engine_id) /* static */ int32 ScriptGroup::GetNumEngines(GroupID group_id, EngineID engine_id)

@ -36,6 +36,7 @@
#include "querystring_gui.h" #include "querystring_gui.h"
#include "fontcache.h" #include "fontcache.h"
#include "zoom_func.h" #include "zoom_func.h"
#include "rev.h"
#include "video/video_driver.hpp" #include "video/video_driver.hpp"
#include "music/music_driver.hpp" #include "music/music_driver.hpp"
@ -220,7 +221,10 @@ struct GameOptionsWindow : Window {
case WID_GO_LANG_DROPDOWN: { // Setup interface language dropdown case WID_GO_LANG_DROPDOWN: { // Setup interface language dropdown
for (uint i = 0; i < _languages.size(); i++) { for (uint i = 0; i < _languages.size(); i++) {
auto item = new DropDownListParamStringItem(STR_JUST_RAW_STRING, i, false); bool hide_language = IsReleasedVersion() && !_languages[i].IsReasonablyFinished();
if (hide_language) continue;
bool hide_percentage = IsReleasedVersion() || _languages[i].missing < _settings_client.gui.missing_strings_threshold;
auto item = new DropDownListParamStringItem(hide_percentage ? STR_JUST_RAW_STRING : STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE, i, false);
if (&_languages[i] == _current_language) { if (&_languages[i] == _current_language) {
*selected_index = i; *selected_index = i;
item->SetParamStr(0, _languages[i].own_name); item->SetParamStr(0, _languages[i].own_name);
@ -232,6 +236,7 @@ struct GameOptionsWindow : Window {
* entries in the dropdown list. */ * entries in the dropdown list. */
item->SetParamStr(0, _languages[i].name); item->SetParamStr(0, _languages[i].name);
} }
item->SetParam(1, (LANGUAGE_TOTAL_STRINGS - _languages[i].missing) * 100 / LANGUAGE_TOTAL_STRINGS);
list.emplace_back(item); list.emplace_back(item);
} }
std::sort(list.begin(), list.end(), DropDownListStringItem::NatSortFunc); std::sort(list.begin(), list.end(), DropDownListStringItem::NatSortFunc);

@ -156,9 +156,6 @@ void CheckRedrawStationCoverage(Window *w)
* @param type Cargo type * @param type Cargo type
* @param amount Cargo amount * @param amount Cargo amount
* @param rating ratings data for that particular cargo * @param rating ratings data for that particular cargo
*
* @note Each cargo-bar is 16 pixels wide and 6 pixels high
* @note Each rating 14 pixels wide and 1 pixel high and is 1 pixel below the cargo-bar
*/ */
static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, byte rating) static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, byte rating)
{ {
@ -168,32 +165,33 @@ static void StationsWndShowStationRating(int left, int right, int y, CargoID typ
const CargoSpec *cs = CargoSpec::Get(type); const CargoSpec *cs = CargoSpec::Get(type);
if (!cs->IsValid()) return; if (!cs->IsValid()) return;
int padding = ScaleFontTrad(1);
int width = right - left;
int colour = cs->rating_colour; int colour = cs->rating_colour;
TextColour tc = GetContrastColour(colour); TextColour tc = GetContrastColour(colour);
uint w = (std::min(amount, units_full) + 5) / 36; uint w = std::min(amount + 5, units_full) * width / units_full;
int height = GetCharacterHeight(FS_SMALL);
/* Draw total cargo (limited) on station (fits into 16 pixels) */ int height = GetCharacterHeight(FS_SMALL) + padding - 1;
if (w != 0) GfxFillRect(left, y, left + w - 1, y + height, colour);
/* Draw a one pixel-wide bar of additional cargo meter, useful if (amount > 30) {
* for stations with only a small amount (<=30) */ /* Draw total cargo (limited) on station */
if (w == 0) { GfxFillRect(left, y, left + w - 1, y + height, colour);
uint rest = amount / 5; } else {
/* Draw a (scaled) one pixel-wide bar of additional cargo meter, useful
* for stations with only a small amount (<=30) */
uint rest = ScaleFontTrad(amount) / 5;
if (rest != 0) { if (rest != 0) {
w += left; GfxFillRect(left, y + height - rest, left + padding - 1, y + height, colour);
GfxFillRect(w, y + height - rest, w, y + height, colour);
} }
} }
DrawString(left + 1, right, y, cs->abbrev, tc); DrawString(left + padding, right, y, cs->abbrev, tc);
/* Draw green/red ratings bar (fits into 14 pixels) */ /* Draw green/red ratings bar (fits under the waiting bar) */
y += height + 2; y += height + padding + 1;
GfxFillRect(left + 1, y, left + 14, y, PC_RED); GfxFillRect(left + padding, y, right - padding - 1, y + padding - 1, PC_RED);
rating = std::min<uint>(rating, rating_full) / 16; w = std::min<uint>(rating, rating_full) * (width - padding - padding) / rating_full;
if (rating != 0) GfxFillRect(left + 1, y, left + rating, y, PC_GREEN); if (w != 0) GfxFillRect(left + padding, y, left + w - 1, y + padding - 1, PC_GREEN);
} }
typedef GUIList<const Station*> GUIStationList; typedef GUIList<const Station*> GUIStationList;
@ -217,6 +215,7 @@ protected:
GUIStationList stations; GUIStationList stations;
Scrollbar *vscroll; Scrollbar *vscroll;
uint rating_width;
/** /**
* (Re)Build station list * (Re)Build station list
@ -395,16 +394,17 @@ public:
} }
case WID_STL_LIST: case WID_STL_LIST:
resize->height = FONT_HEIGHT_NORMAL; resize->height = std::max(FONT_HEIGHT_NORMAL, FONT_HEIGHT_SMALL + ScaleFontTrad(3));
size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM; size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
break;
case WID_STL_TRAIN: /* Determine appropriate width for mini station rating graph */
case WID_STL_TRUCK: this->rating_width = 0;
case WID_STL_BUS: const CargoSpec *cs;
case WID_STL_AIRPLANE: FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
case WID_STL_SHIP: this->rating_width = std::max(this->rating_width, GetStringBoundingBox(cs->abbrev).width);
size->height = std::max<uint>(FONT_HEIGHT_SMALL, 10) + padding.height; }
/* Approximately match original 16 pixel wide rating bars by multiplying string width by 1.6 */
this->rating_width = this->rating_width * 16 / 10;
break; break;
case WID_STL_CARGOALL: case WID_STL_CARGOALL:
@ -448,6 +448,12 @@ public:
bool rtl = _current_text_dir == TD_RTL; bool rtl = _current_text_dir == TD_RTL;
int max = std::min<size_t>(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->stations.size()); int max = std::min<size_t>(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->stations.size());
int y = r.top + WD_FRAMERECT_TOP; int y = r.top + WD_FRAMERECT_TOP;
uint line_height = this->GetWidget<NWidgetBase>(widget)->resize_y;
/* Spacing between station name and first rating graph. */
int text_spacing = ScaleFontTrad(5);
/* Spacing between additional rating graphs. */
int rating_spacing = ScaleFontTrad(4);
for (int i = this->vscroll->GetPosition(); i < max; ++i) { // do until max number of stations of owner for (int i = this->vscroll->GetPosition(); i < max; ++i) { // do until max number of stations of owner
const Station *st = this->stations[i]; const Station *st = this->stations[i];
assert(st->xy != INVALID_TILE); assert(st->xy != INVALID_TILE);
@ -458,8 +464,8 @@ public:
SetDParam(0, st->index); SetDParam(0, st->index);
SetDParam(1, st->facilities); SetDParam(1, st->facilities);
int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_STATION); int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y + (line_height - FONT_HEIGHT_NORMAL) / 2, STR_STATION_LIST_STATION);
x += rtl ? -5 : 5; x += rtl ? -text_spacing : text_spacing;
/* show cargo waiting and station ratings */ /* show cargo waiting and station ratings */
for (uint j = 0; j < _sorted_standard_cargo_specs_size; j++) { for (uint j = 0; j < _sorted_standard_cargo_specs_size; j++) {
@ -470,17 +476,17 @@ public:
* instead of drawing to the left and then incrementing * instead of drawing to the left and then incrementing
* the space. */ * the space. */
if (rtl) { if (rtl) {
x -= 20; x -= rating_width + rating_spacing;
if (x < r.left + WD_FRAMERECT_LEFT) break; if (x < r.left + WD_FRAMERECT_LEFT) break;
} }
StationsWndShowStationRating(x, x + 16, y, cid, st->goods[cid].cargo.TotalCount(), st->goods[cid].rating); StationsWndShowStationRating(x, x + rating_width, y, cid, st->goods[cid].cargo.TotalCount(), st->goods[cid].rating);
if (!rtl) { if (!rtl) {
x += 20; x += rating_width + rating_spacing;
if (x > r.right - WD_FRAMERECT_RIGHT) break; if (x > r.right - WD_FRAMERECT_RIGHT) break;
} }
} }
} }
y += FONT_HEIGHT_NORMAL; y += line_height;
} }
if (this->vscroll->GetCount() == 0) { // company has no stations if (this->vscroll->GetCount() == 0) { // company has no stations
@ -491,30 +497,30 @@ public:
} }
case WID_STL_NOCARGOWAITING: { case WID_STL_NOCARGOWAITING: {
int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1; int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0;
DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_HOR_CENTER); DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_HOR_CENTER);
break; break;
} }
case WID_STL_CARGOALL: { case WID_STL_CARGOALL: {
int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1; int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0;
DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER); DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER);
break; break;
} }
case WID_STL_FACILALL: { case WID_STL_FACILALL: {
int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1; int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0;
DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER); DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER);
break; break;
} }
default: default:
if (widget >= WID_STL_CARGOSTART) { if (widget >= WID_STL_CARGOSTART) {
const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART]; const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART];
int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 2 : 1; int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 1 : 0;
GfxFillRect(r.left + cg_ofst, r.top + cg_ofst, r.right - 2 + cg_ofst, r.bottom - 2 + cg_ofst, cs->rating_colour); GfxFillRect(r.left + cg_ofst + 1, r.top + cg_ofst + 1, r.right - 1 + cg_ofst, r.bottom - 1 + cg_ofst, cs->rating_colour);
TextColour tc = GetContrastColour(cs->rating_colour); TextColour tc = GetContrastColour(cs->rating_colour);
DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, cs->abbrev, tc, SA_HOR_CENTER); DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, cs->abbrev, tc, SA_HOR_CENTER);
} }
break; break;
} }
@ -532,7 +538,7 @@ public:
{ {
switch (widget) { switch (widget) {
case WID_STL_LIST: { case WID_STL_LIST: {
uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_STL_LIST, 0, FONT_HEIGHT_NORMAL); uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_STL_LIST);
if (id_v >= this->stations.size()) return; // click out of list bound if (id_v >= this->stations.size()) return; // click out of list bound
const Station *st = this->stations[id_v]; const Station *st = this->stations[id_v];
@ -723,7 +729,8 @@ static NWidgetBase *CargoWidgets(int *biggest_index)
for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) { for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) {
NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, WID_STL_CARGOSTART + i); NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, WID_STL_CARGOSTART + i);
panel->SetMinimalSize(14, 11); panel->SetMinimalSize(14, 0);
panel->SetMinimalTextLines(1, 0, FS_NORMAL);
panel->SetResize(0, 0); panel->SetResize(0, 0);
panel->SetFill(0, 1); panel->SetFill(0, 1);
panel->SetDataTip(0, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE); panel->SetDataTip(0, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE);
@ -742,16 +749,16 @@ static const NWidgetPart _nested_company_stations_widgets[] = {
NWidget(WWT_STICKYBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRAIN), SetMinimalSize(14, 11), SetDataTip(STR_TRAIN, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRAIN), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_TRAIN, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRUCK), SetMinimalSize(14, 11), SetDataTip(STR_LORRY, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRUCK), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_LORRY, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_BUS), SetMinimalSize(14, 11), SetDataTip(STR_BUS, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_BUS), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_BUS, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_SHIP), SetMinimalSize(14, 11), SetDataTip(STR_SHIP, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_SHIP), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_SHIP, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_AIRPLANE), SetMinimalSize(14, 11), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_AIRPLANE), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_FACILALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1), NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_FACILALL), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1),
NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 11), SetFill(0, 1), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 0), SetFill(0, 1), EndContainer(),
NWidgetFunction(CargoWidgets), NWidgetFunction(CargoWidgets),
NWidget(WWT_PANEL, COLOUR_GREY, WID_STL_NOCARGOWAITING), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY, WID_STL_NOCARGOWAITING), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(),
NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_CARGOALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1), NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_CARGOALL), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1),
NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
@ -1922,7 +1929,7 @@ struct StationViewWindow : public Window {
{ {
switch (widget) { switch (widget) {
case WID_SV_WAITING: case WID_SV_WAITING:
this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SV_WAITING, WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL) - this->vscroll->GetPosition()); this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SV_WAITING, WD_FRAMERECT_TOP) - this->vscroll->GetPosition());
break; break;
case WID_SV_CATCHMENT: case WID_SV_CATCHMENT:

@ -273,13 +273,14 @@ struct HeaderFileWriter : HeaderWriter, FileWriter {
const char *real_filename; const char *real_filename;
/** The previous string ID that was printed. */ /** The previous string ID that was printed. */
int prev; int prev;
uint total_strings;
/** /**
* Open a file to write to. * Open a file to write to.
* @param filename The file to open. * @param filename The file to open.
*/ */
HeaderFileWriter(const char *filename) : FileWriter("tmp.xxx"), HeaderFileWriter(const char *filename) : FileWriter("tmp.xxx"),
real_filename(stredup(filename)), prev(0) real_filename(stredup(filename)), prev(0), total_strings(0)
{ {
fprintf(this->fh, "/* This file is automatically generated. Do not modify */\n\n"); fprintf(this->fh, "/* This file is automatically generated. Do not modify */\n\n");
fprintf(this->fh, "#ifndef TABLE_STRINGS_H\n"); fprintf(this->fh, "#ifndef TABLE_STRINGS_H\n");
@ -297,6 +298,7 @@ struct HeaderFileWriter : HeaderWriter, FileWriter {
if (prev + 1 != stringid) fprintf(this->fh, "\n"); if (prev + 1 != stringid) fprintf(this->fh, "\n");
fprintf(this->fh, "static const StringID %s = 0x%X;\n", name, stringid); fprintf(this->fh, "static const StringID %s = 0x%X;\n", name, stringid);
prev = stringid; prev = stringid;
total_strings++;
} }
void Finalise(const StringData &data) void Finalise(const StringData &data)
@ -311,8 +313,10 @@ struct HeaderFileWriter : HeaderWriter, FileWriter {
"\n" "\n"
"static const uint LANGUAGE_PACK_VERSION = 0x%X;\n" "static const uint LANGUAGE_PACK_VERSION = 0x%X;\n"
"static const uint LANGUAGE_MAX_PLURAL = %u;\n" "static const uint LANGUAGE_MAX_PLURAL = %u;\n"
"static const uint LANGUAGE_MAX_PLURAL_FORMS = %d;\n\n", "static const uint LANGUAGE_MAX_PLURAL_FORMS = %d;\n"
(uint)data.Version(), (uint)lengthof(_plural_forms), max_plural_forms "static const uint LANGUAGE_TOTAL_STRINGS = %u;\n"
"\n",
(uint)data.Version(), (uint)lengthof(_plural_forms), max_plural_forms, total_strings
); );
fprintf(this->fh, "#endif /* TABLE_STRINGS_H */\n"); fprintf(this->fh, "#endif /* TABLE_STRINGS_H */\n");

@ -334,6 +334,66 @@ bool StrValid(const char *str, const char *last)
return *str == '\0'; return *str == '\0';
} }
/**
* Trim the spaces from the begin of given string in place, i.e. the string buffer
* that is passed will be modified whenever spaces exist in the given string.
* When there are spaces at the begin, the whole string is moved forward.
* @param str The string to perform the in place left trimming on.
*/
static void StrLeftTrimInPlace(char *str)
{
if (StrEmpty(str)) return;
char *first_non_space = str;
while (*first_non_space == ' ') first_non_space++;
if (first_non_space == str) return;
/* The source will reach '\0' first, but set the '\0' on the destination afterwards. */
char *dst = str;
for (char *src = first_non_space; *src != '\0'; dst++, src++) *dst = *src;
*dst = '\0';
}
/**
* Trim the spaces from the end of given string in place, i.e. the string buffer
* that is passed will be modified whenever spaces exist in the given string.
* When there are spaces at the end, the '\0' will be moved forward.
* @param str The string to perform the in place left trimming on.
*/
static void StrRightTrimInPlace(char *str)
{
if (StrEmpty(str)) return;
char *end = str;
while (*end != '\0') end++;
char *last_non_space = end - 1;
while (last_non_space >= str && *last_non_space == ' ') last_non_space--;
/* The last non space points to the last character of the string that is not
* a space. For a string with only spaces or an empty string this would be
* the position before the begin of the string. The previous search ensures
* that this location before the string is not read.
* In any case, the character after the last non space character will be
* either a space or the existing termination, so it can be set to '\0'.
*/
last_non_space[1] = '\0';
}
/**
* Trim the spaces from given string in place, i.e. the string buffer that
* is passed will be modified whenever spaces exist in the given string.
* When there are spaces at the begin, the whole string is moved forward
* and when there are spaces at the back the '\0' termination is moved.
* @param str The string to perform the in place trimming on.
*/
void StrTrimInPlace(char *str)
{
StrLeftTrimInPlace(str);
StrRightTrimInPlace(str);
}
/** Scans the string for colour codes and strips them */ /** Scans the string for colour codes and strips them */
void str_strip_colours(char *str) void str_strip_colours(char *str)
{ {

@ -54,6 +54,7 @@ bool strtolower(char *str);
bool strtolower(std::string &str, std::string::size_type offs = 0); bool strtolower(std::string &str, std::string::size_type offs = 0);
bool StrValid(const char *str, const char *last); bool StrValid(const char *str, const char *last);
void StrTrimInPlace(char *str);
/** /**
* Check if a string buffer is empty. * Check if a string buffer is empty.

@ -2058,6 +2058,15 @@ bool LanguagePackHeader::IsValid() const
StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator)); StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
} }
/**
* Check whether a translation is sufficiently finished to offer it to the public.
*/
bool LanguagePackHeader::IsReasonablyFinished() const
{
/* "Less than 25% missing" is "sufficiently finished". */
return 4 * this->missing < LANGUAGE_TOTAL_STRINGS;
}
/** /**
* Read a particular language. * Read a particular language.
* @param lang The metadata about the language. * @param lang The metadata about the language.
@ -2312,6 +2321,10 @@ void InitializeLanguagePacks()
} }
if (strcmp (lng.isocode, "en_GB") == 0) en_GB_fallback = &lng; if (strcmp (lng.isocode, "en_GB") == 0) en_GB_fallback = &lng;
/* Only auto-pick finished translations */
if (!lng.IsReasonablyFinished()) continue;
if (strncmp(lng.isocode, lang, 5) == 0) chosen_language = &lng; if (strncmp(lng.isocode, lang, 5) == 0) chosen_language = &lng;
if (strncmp(lng.isocode, lang, 2) == 0) language_fallback = &lng; if (strncmp(lng.isocode, lang, 2) == 0) language_fallback = &lng;
} }

@ -50,14 +50,14 @@ void Subsidy::AwardTo(CompanyID company)
char *cn = stredup(company_name); char *cn = stredup(company_name);
/* Add a news item */ /* Add a news item */
Pair reftype = SetupSubsidyDecodeParam(this, false); std::pair<NewsReferenceType, NewsReferenceType> reftype = SetupSubsidyDecodeParam(this, false);
InjectDParam(1); InjectDParam(1);
SetDParamStr(0, cn); SetDParamStr(0, cn);
AddNewsItem( AddNewsItem(
STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier, STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier,
NT_SUBSIDIES, NF_NORMAL, NT_SUBSIDIES, NF_NORMAL,
(NewsReferenceType)reftype.a, this->src, (NewsReferenceType)reftype.b, this->dst, reftype.first, this->src, reftype.second, this->dst,
cn cn
); );
AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index)); AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index));
@ -72,7 +72,7 @@ void Subsidy::AwardTo(CompanyID company)
* @param mode Unit of cargo used, \c true means general name, \c false means singular form. * @param mode Unit of cargo used, \c true means general name, \c false means singular form.
* @return Reference of the subsidy in the news system. * @return Reference of the subsidy in the news system.
*/ */
Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode) std::pair<NewsReferenceType, NewsReferenceType> SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
{ {
NewsReferenceType reftype1 = NR_NONE; NewsReferenceType reftype1 = NR_NONE;
NewsReferenceType reftype2 = NR_NONE; NewsReferenceType reftype2 = NR_NONE;
@ -107,10 +107,7 @@ Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
} }
SetDParam(5, s->dst); SetDParam(5, s->dst);
Pair p; return std::pair<NewsReferenceType, NewsReferenceType>(reftype1, reftype2);
p.a = reftype1;
p.b = reftype2;
return p;
} }
/** /**
@ -219,8 +216,8 @@ void CreateSubsidy(CargoID cid, SourceType src_type, SourceID src, SourceType ds
s->remaining = SUBSIDY_OFFER_MONTHS; s->remaining = SUBSIDY_OFFER_MONTHS;
s->awarded = INVALID_COMPANY; s->awarded = INVALID_COMPANY;
Pair reftype = SetupSubsidyDecodeParam(s, false); std::pair<NewsReferenceType, NewsReferenceType> reftype = SetupSubsidyDecodeParam(s, false);
AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst);
SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC); SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST); SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index)); AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index));
@ -494,14 +491,14 @@ void SubsidyMonthlyLoop()
for (Subsidy *s : Subsidy::Iterate()) { for (Subsidy *s : Subsidy::Iterate()) {
if (--s->remaining == 0) { if (--s->remaining == 0) {
if (!s->IsAwarded()) { if (!s->IsAwarded()) {
Pair reftype = SetupSubsidyDecodeParam(s, true); std::pair<NewsReferenceType, NewsReferenceType> reftype = SetupSubsidyDecodeParam(s, true);
AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst);
AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index)); AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));
Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index)); Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index));
} else { } else {
if (s->awarded == _local_company) { if (s->awarded == _local_company) {
Pair reftype = SetupSubsidyDecodeParam(s, true); std::pair<NewsReferenceType, NewsReferenceType> reftype = SetupSubsidyDecodeParam(s, true);
AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst);
} }
AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index)); AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));
Game::NewEvent(new ScriptEventSubsidyExpired(s->index)); Game::NewEvent(new ScriptEventSubsidyExpired(s->index));

@ -14,8 +14,9 @@
#include "station_type.h" #include "station_type.h"
#include "company_type.h" #include "company_type.h"
#include "cargo_type.h" #include "cargo_type.h"
#include "news_type.h"
Pair SetupSubsidyDecodeParam(const struct Subsidy *s, bool mode); std::pair<NewsReferenceType, NewsReferenceType> SetupSubsidyDecodeParam(const struct Subsidy *s, bool mode);
void DeleteSubsidyWith(SourceType type, SourceID index); void DeleteSubsidyWith(SourceType type, SourceID index);
bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st); bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st);
void RebuildSubsidisedSourceAndDestinationCache(); void RebuildSubsidisedSourceAndDestinationCache();

@ -3458,7 +3458,7 @@ public:
break; break;
case WID_TRSL_LIST_SLOTS: { // Matrix Slot case WID_TRSL_LIST_SLOTS: { // Matrix Slot
uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0, this->tiny_step_height); uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0);
if (id_s >= this->slots.size()) return; if (id_s >= this->slots.size()) return;
this->slot_sel = this->vli.index = this->slots[id_s]->index; this->slot_sel = this->vli.index = this->slots[id_s]->index;
@ -3526,7 +3526,7 @@ public:
this->slot_over = INVALID_GROUP; this->slot_over = INVALID_GROUP;
this->SetDirty(); this->SetDirty();
uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0, this->tiny_step_height); uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0);
if (id_s >= this->slots.size()) return; // click out of list bound if (id_s >= this->slots.size()) return; // click out of list bound
if (_ctrl_pressed) { if (_ctrl_pressed) {
@ -3627,7 +3627,7 @@ public:
break; break;
case WID_TRSL_LIST_SLOTS: { // ... the list of slots. case WID_TRSL_LIST_SLOTS: { // ... the list of slots.
uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0, this->tiny_step_height); uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0);
if (id_s < this->slots.size()) new_slot_over = this->slots[id_s]->index; if (id_s < this->slots.size()) new_slot_over = this->slots[id_s]->index;
break; break;
} }
@ -3959,7 +3959,7 @@ public:
{ {
switch (widget) { switch (widget) {
case WID_TRCL_LIST_COUNTERS: { // Matrix case WID_TRCL_LIST_COUNTERS: { // Matrix
uint id_s = this->sb->GetScrolledRowFromWidget(pt.y, this, WID_TRCL_LIST_COUNTERS, 0, this->tiny_step_height); uint id_s = this->sb->GetScrolledRowFromWidget(pt.y, this, WID_TRCL_LIST_COUNTERS, 0);
if (id_s >= this->ctrs.size()) return; if (id_s >= this->ctrs.size()) return;
this->selected = this->ctrs[id_s]->index; this->selected = this->ctrs[id_s]->index;

@ -254,7 +254,7 @@ const char *VideoDriver_CocoaOpenGL::AllocateContext(bool allow_software)
CGLSetCurrentContext(this->gl_context); CGLSetCurrentContext(this->gl_context);
return OpenGLBackend::Create(&GetOGLProcAddressCallback); return OpenGLBackend::Create(&GetOGLProcAddressCallback, this->GetScreenSize());
} }
NSView *VideoDriver_CocoaOpenGL::AllocateDrawView() NSView *VideoDriver_CocoaOpenGL::AllocateDrawView()

@ -464,16 +464,17 @@ void SetupDebugOutput()
/** /**
* Create and initialize the singleton back-end class. * Create and initialize the singleton back-end class.
* @param get_proc Callback to get an OpenGL function from the OS driver. * @param get_proc Callback to get an OpenGL function from the OS driver.
* @param screen_res Current display resolution.
* @return nullptr on success, error message otherwise. * @return nullptr on success, error message otherwise.
*/ */
/* static */ const char *OpenGLBackend::Create(GetOGLProcAddressProc get_proc) /* static */ const char *OpenGLBackend::Create(GetOGLProcAddressProc get_proc, const Dimension &screen_res)
{ {
if (OpenGLBackend::instance != nullptr) OpenGLBackend::Destroy(); if (OpenGLBackend::instance != nullptr) OpenGLBackend::Destroy();
GetOGLProcAddress = get_proc; GetOGLProcAddress = get_proc;
OpenGLBackend::instance = new OpenGLBackend(); OpenGLBackend::instance = new OpenGLBackend();
return OpenGLBackend::instance->Init(); return OpenGLBackend::instance->Init(screen_res);
} }
/** /**
@ -521,9 +522,10 @@ OpenGLBackend::~OpenGLBackend()
/** /**
* Check for the needed OpenGL functionality and allocate all resources. * Check for the needed OpenGL functionality and allocate all resources.
* @param screen_res Current display resolution.
* @return Error string or nullptr if successful. * @return Error string or nullptr if successful.
*/ */
const char *OpenGLBackend::Init() const char *OpenGLBackend::Init(const Dimension &screen_res)
{ {
if (!BindBasicInfoProcs()) return "OpenGL not supported"; if (!BindBasicInfoProcs()) return "OpenGL not supported";
@ -546,6 +548,12 @@ const char *OpenGLBackend::Init()
_gl_major_ver = atoi(ver); _gl_major_ver = atoi(ver);
_gl_minor_ver = minor != nullptr ? atoi(minor + 1) : 0; _gl_minor_ver = minor != nullptr ? atoi(minor + 1) : 0;
#ifdef _WIN32
/* Old drivers on Windows (especially if made by Intel) seem to be
* unstable, so cull the oldest stuff here. */
if (!IsOpenGLVersionAtLeast(3, 2)) return "Need at least OpenGL version 3.2 on Windows";
#endif
if (!BindBasicOpenGLProcs()) return "Failed to bind basic OpenGL functions."; if (!BindBasicOpenGLProcs()) return "Failed to bind basic OpenGL functions.";
SetupDebugOutput(); SetupDebugOutput();
@ -581,6 +589,11 @@ const char *OpenGLBackend::Init()
} }
if (this->persistent_mapping_supported) DEBUG(driver, 3, "OpenGL: Using persistent buffer mapping"); if (this->persistent_mapping_supported) DEBUG(driver, 3, "OpenGL: Using persistent buffer mapping");
/* Check maximum texture size against screen resolution. */
GLint max_tex_size = 0;
_glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
if (std::max(screen_res.width, screen_res.height) > (uint)max_tex_size) return "Max supported texture size is too small";
/* Check available texture units. */ /* Check available texture units. */
GLint max_tex_units = 0; GLint max_tex_units = 0;
_glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_tex_units); _glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_tex_units);

@ -74,7 +74,7 @@ private:
OpenGLBackend(); OpenGLBackend();
~OpenGLBackend(); ~OpenGLBackend();
const char *Init(); const char *Init(const Dimension &screen_res);
bool InitShaders(); bool InitShaders();
void InternalClearCursorCache(); void InternalClearCursorCache();
@ -87,7 +87,7 @@ public:
{ {
return OpenGLBackend::instance; return OpenGLBackend::instance;
} }
static const char *Create(GetOGLProcAddressProc get_proc); static const char *Create(GetOGLProcAddressProc get_proc, const Dimension &screen_res);
static void Destroy(); static void Destroy();
void PrepareContext(); void PrepareContext();

@ -121,7 +121,7 @@ const char *VideoDriver_SDL_OpenGL::AllocateContext()
ToggleVsync(_video_vsync); ToggleVsync(_video_vsync);
return OpenGLBackend::Create(&GetOGLProcAddressCallback); return OpenGLBackend::Create(&GetOGLProcAddressCallback, this->GetScreenSize());
} }
void VideoDriver_SDL_OpenGL::PopulateSystemSprites() void VideoDriver_SDL_OpenGL::PopulateSystemSprites()

@ -1368,14 +1368,22 @@ const char *VideoDriver_Win32OpenGL::AllocateContext()
/* Create OpenGL device context. Try to get an 3.2+ context if possible. */ /* Create OpenGL device context. Try to get an 3.2+ context if possible. */
if (_wglCreateContextAttribsARB != nullptr) { if (_wglCreateContextAttribsARB != nullptr) {
/* Try for OpenGL 4.5 first. */
int attribs[] = { int attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
WGL_CONTEXT_MINOR_VERSION_ARB, 2, WGL_CONTEXT_MINOR_VERSION_ARB, 5,
WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0, WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
_hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, // Terminate list if WGL_ARB_create_context_profile isn't supported. _hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, // Terminate list if WGL_ARB_create_context_profile isn't supported.
0 0
}; };
rc = _wglCreateContextAttribsARB(this->dc, nullptr, attribs); rc = _wglCreateContextAttribsARB(this->dc, nullptr, attribs);
if (rc == nullptr) {
/* Try again for a 3.2 context. */
attribs[1] = 3;
attribs[3] = 2;
rc = _wglCreateContextAttribsARB(this->dc, nullptr, attribs);
}
} }
if (rc == nullptr) { if (rc == nullptr) {
@ -1388,7 +1396,7 @@ const char *VideoDriver_Win32OpenGL::AllocateContext()
this->ToggleVsync(_video_vsync); this->ToggleVsync(_video_vsync);
this->gl_rc = rc; this->gl_rc = rc;
return OpenGLBackend::Create(&GetOGLProcAddressCallback); return OpenGLBackend::Create(&GetOGLProcAddressCallback, this->GetScreenSize());
} }
bool VideoDriver_Win32OpenGL::ToggleFullscreen(bool full_screen) bool VideoDriver_Win32OpenGL::ToggleFullscreen(bool full_screen)

@ -24,6 +24,33 @@
#include "safeguards.h" #include "safeguards.h"
/**
* Calculate x and y coordinates for an aligned object within a window.
* @param r Rectangle of the widget to be drawn in.
* @param d Dimension of the object to be drawn.
* @param align Alignment of the object.
* @return A point containing the position at which to draw.
*/
static inline Point GetAlignedPosition(const Rect &r, const Dimension &d, StringAlignment align)
{
Point p;
/* In case we have a RTL language we swap the alignment. */
if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
switch (align & SA_HOR_MASK) {
case SA_LEFT: p.x = r.left; break;
case SA_HOR_CENTER: p.x = CenterBounds(r.left, r.right, d.width); break;
case SA_RIGHT: p.x = r.right - d.width; break;
default: NOT_REACHED();
}
switch (align & SA_VERT_MASK) {
case SA_TOP: p.y = r.top; break;
case SA_VERT_CENTER: p.y = CenterBounds(r.top, r.bottom, d.height); break;
case SA_BOTTOM: p.y = r.bottom - d.height; break;
default: NOT_REACHED();
}
return p;
}
/** /**
* Compute the vertical position of the draggable part of scrollbar * Compute the vertical position of the draggable part of scrollbar
* @param sb Scrollbar list data * @param sb Scrollbar list data
@ -212,15 +239,17 @@ void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, Fra
* @param colour Colour of the button. * @param colour Colour of the button.
* @param clicked Button is lowered. * @param clicked Button is lowered.
* @param img Sprite to draw. * @param img Sprite to draw.
* @param align Alignment of the sprite.
*/ */
static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img) static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align)
{ {
assert(img != 0); assert(img != 0);
DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE); DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
if ((type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; // Show different image when clicked for #WWT_IMGBTN_2. if ((type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; // Show different image when clicked for #WWT_IMGBTN_2.
Dimension d = GetSpriteSize(img); Dimension d = GetSpriteSize(img);
DrawSprite(img, PAL_NONE, CenterBounds(r.left, r.right, d.width) + clicked, CenterBounds(r.top, r.bottom, d.height) + clicked); Point p = GetAlignedPosition(r, d, align);
DrawSprite(img, PAL_NONE, p.x + clicked, p.y + clicked);
} }
/** /**
@ -228,15 +257,17 @@ static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colo
* @param r Rectangle of the label background. * @param r Rectangle of the label background.
* @param type Widget type (#WWT_TEXTBTN, #WWT_TEXTBTN_2, or #WWT_LABEL). * @param type Widget type (#WWT_TEXTBTN, #WWT_TEXTBTN_2, or #WWT_LABEL).
* @param clicked Label is rendered lowered. * @param clicked Label is rendered lowered.
* @param colour Colour of the text.
* @param str Text to draw. * @param str Text to draw.
* @param align Alignment of the text.
*/ */
static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, StringID str) static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, TextColour colour, StringID str, StringAlignment align)
{ {
if (str == STR_NULL) return; if (str == STR_NULL) return;
if ((type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++; if ((type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++;
Dimension d = GetStringBoundingBox(str); Dimension d = GetStringBoundingBox(str);
int offset = std::max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered Point p = GetAlignedPosition(r, d, align);
DrawString(r.left + clicked, r.right + clicked, r.top + offset + clicked, str, TC_FROMSTRING, SA_HOR_CENTER); DrawString(r.left + clicked, r.right + clicked, p.y + clicked, str, colour, align);
} }
/** /**
@ -244,24 +275,27 @@ static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, Strin
* @param r Rectangle of the background. * @param r Rectangle of the background.
* @param colour Colour of the text. * @param colour Colour of the text.
* @param str Text to draw. * @param str Text to draw.
* @param align Alignment of the text.
*/ */
static inline void DrawText(const Rect &r, TextColour colour, StringID str) static inline void DrawText(const Rect &r, TextColour colour, StringID str, StringAlignment align)
{ {
Dimension d = GetStringBoundingBox(str); Dimension d = GetStringBoundingBox(str);
int offset = std::max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered Point p = GetAlignedPosition(r, d, align);
if (str != STR_NULL) DrawString(r.left, r.right, r.top + offset, str, colour); if (str != STR_NULL) DrawString(r.left, r.right, p.y, str, colour, align);
} }
/** /**
* Draw an inset widget. * Draw an inset widget.
* @param r Rectangle of the background. * @param r Rectangle of the background.
* @param colour Colour of the inset. * @param colour Colour of the inset.
* @param str Text to draw. * @param text_colour Colour of the text.
* @param str Text to draw.
* @param align Alignment of the text.
*/ */
static inline void DrawInset(const Rect &r, Colours colour, StringID str) static inline void DrawInset(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align)
{ {
DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_LOWERED | FR_DARKENED); DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_LOWERED | FR_DARKENED);
if (str != STR_NULL) DrawString(r.left + WD_INSET_LEFT, r.right - WD_INSET_RIGHT, r.top + WD_INSET_TOP, str); if (str != STR_NULL) DrawString(r.left + WD_INSET_LEFT, r.right - WD_INSET_RIGHT, r.top + WD_INSET_TOP, str, text_colour, align);
} }
/** /**
@ -402,15 +436,17 @@ static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool l
/** /**
* Draw a frame widget. * Draw a frame widget.
* @param r Rectangle of the frame. * @param r Rectangle of the frame.
* @param colour Colour of the frame. * @param colour Colour of the frame.
* @param str Text of the frame. * @param text_colour Colour of the text.
* @param str Text of the frame.
* @param align Alignment of the text in the frame.
*/ */
static inline void DrawFrame(const Rect &r, Colours colour, StringID str) static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align)
{ {
int x2 = r.left; // by default the left side is the left side of the widget int x2 = r.left; // by default the left side is the left side of the widget
if (str != STR_NULL) x2 = DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top, str); if (str != STR_NULL) x2 = DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top, str, text_colour, align);
int c1 = _colour_gradient[colour][3]; int c1 = _colour_gradient[colour][3];
int c2 = _colour_gradient[colour][7]; int c2 = _colour_gradient[colour][7];
@ -458,7 +494,7 @@ static inline void DrawFrame(const Rect &r, Colours colour, StringID str)
*/ */
static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked) static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
{ {
DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE); DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE, SA_CENTER);
} }
/** /**
@ -469,7 +505,7 @@ static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
*/ */
static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked) static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
{ {
DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN); DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN, SA_CENTER);
} }
/** /**
@ -480,7 +516,7 @@ static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
*/ */
static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked) static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
{ {
DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE); DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE, SA_CENTER);
} }
/** /**
@ -491,7 +527,7 @@ static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
*/ */
static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked) static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
{ {
DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG); DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG, SA_CENTER);
} }
/** /**
@ -530,12 +566,14 @@ static inline void DrawCloseBox(const Rect &r, Colours colour)
/** /**
* Draw a caption bar. * Draw a caption bar.
* @param r Rectangle of the bar. * @param r Rectangle of the bar.
* @param colour Colour of the window. * @param colour Colour of the window.
* @param owner 'Owner' of the window. * @param owner 'Owner' of the window.
* @param str Text to draw in the bar. * @param text_colour Colour of the text.
* @param str Text to draw in the bar.
* @param align Alignment of the text.
*/ */
void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str) void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align)
{ {
bool company_owned = owner < MAX_COMPANIES; bool company_owned = owner < MAX_COMPANIES;
@ -548,8 +586,8 @@ void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str)
if (str != STR_NULL) { if (str != STR_NULL) {
Dimension d = GetStringBoundingBox(str); Dimension d = GetStringBoundingBox(str);
int offset = std::max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered Point p = GetAlignedPosition(r, d, align);
DrawString(r.left + WD_CAPTIONTEXT_LEFT, r.right - WD_CAPTIONTEXT_RIGHT, r.top + offset, str, TC_FROMSTRING, SA_HOR_CENTER); DrawString(r.left + WD_CAPTIONTEXT_LEFT, r.right - WD_CAPTIONTEXT_RIGHT, p.y, str, text_colour, align);
} }
} }
@ -560,10 +598,11 @@ void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str)
* @param clicked_button The button-part is lowered. * @param clicked_button The button-part is lowered.
* @param clicked_dropdown The drop-down part is lowered. * @param clicked_dropdown The drop-down part is lowered.
* @param str Text of the button. * @param str Text of the button.
* @param align Alignment of the text within the dropdown.
* *
* @note Magic constants are also used in #NWidgetLeaf::ButtonHit. * @note Magic constants are also used in #NWidgetLeaf::ButtonHit.
*/ */
static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str) static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str, StringAlignment align)
{ {
int text_offset = std::max(0, ((int)(r.bottom - r.top + 1) - FONT_HEIGHT_NORMAL) / 2); // Offset for rendering the text vertically centered int text_offset = std::max(0, ((int)(r.bottom - r.top + 1) - FONT_HEIGHT_NORMAL) / 2); // Offset for rendering the text vertically centered
@ -575,12 +614,12 @@ static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicke
DrawFrameRect(r.left, r.top, r.right - dd_width, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE); DrawFrameRect(r.left, r.top, r.right - dd_width, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
DrawFrameRect(r.right + 1 - dd_width, r.top, r.right, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE); DrawFrameRect(r.right + 1 - dd_width, r.top, r.right, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE);
DrawSprite(SPR_ARROW_DOWN, PAL_NONE, r.right - (dd_width - 2) + clicked_dropdown, r.top + image_offset + clicked_dropdown); DrawSprite(SPR_ARROW_DOWN, PAL_NONE, r.right - (dd_width - 2) + clicked_dropdown, r.top + image_offset + clicked_dropdown);
if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - dd_width - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK); if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - dd_width - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK, align);
} else { } else {
DrawFrameRect(r.left + dd_width, r.top, r.right, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE); DrawFrameRect(r.left + dd_width, r.top, r.right, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
DrawFrameRect(r.left, r.top, r.left + dd_width - 1, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE); DrawFrameRect(r.left, r.top, r.left + dd_width - 1, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE);
DrawSprite(SPR_ARROW_DOWN, PAL_NONE, r.left + 1 + clicked_dropdown, r.top + image_offset + clicked_dropdown); DrawSprite(SPR_ARROW_DOWN, PAL_NONE, r.left + 1 + clicked_dropdown, r.top + image_offset + clicked_dropdown);
if (str != STR_NULL) DrawString(r.left + dd_width + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK); if (str != STR_NULL) DrawString(r.left + dd_width + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK, align);
} }
} }
@ -590,10 +629,11 @@ static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicke
* @param colour Background colour of the widget. * @param colour Background colour of the widget.
* @param clicked The widget is lowered. * @param clicked The widget is lowered.
* @param str Text of the button. * @param str Text of the button.
* @param align Alignment of the text.
*/ */
static inline void DrawDropdown(const Rect &r, Colours colour, bool clicked, StringID str) static inline void DrawDropdown(const Rect &r, Colours colour, bool clicked, StringID str, StringAlignment align)
{ {
DrawButtonDropdown(r, colour, false, clicked, str); DrawButtonDropdown(r, colour, false, clicked, str, align);
} }
/** /**
@ -871,6 +911,8 @@ NWidgetCore::NWidgetCore(WidgetType tp, Colours colour, uint fill_x, uint fill_y
this->widget_data = widget_data; this->widget_data = widget_data;
this->tool_tip = tool_tip; this->tool_tip = tool_tip;
this->scrollbar_index = -1; this->scrollbar_index = -1;
this->text_colour = TC_FROMSTRING;
this->align = SA_CENTER;
} }
/** /**
@ -894,6 +936,15 @@ void NWidgetCore::SetDataTip(uint32 widget_data, StringID tool_tip)
this->tool_tip = tool_tip; this->tool_tip = tool_tip;
} }
/**
* Set the text colour of the nested widget.
* @param colour TextColour to use.
*/
void NWidgetCore::SetTextColour(TextColour colour)
{
this->text_colour = colour;
}
/** /**
* Set the tool tip of the nested widget. * Set the tool tip of the nested widget.
* @param tool_tip Tool tip string to use. * @param tool_tip Tool tip string to use.
@ -903,6 +954,15 @@ void NWidgetCore::SetToolTip(StringID tool_tip)
this->tool_tip = tool_tip; this->tool_tip = tool_tip;
} }
/**
* Set the text/image alignment of the nested widget.
* @param align Alignment to use.
*/
void NWidgetCore::SetAlignment(StringAlignment align)
{
this->align = align;
}
void NWidgetCore::FillNestedArray(NWidgetBase **array, uint length) void NWidgetCore::FillNestedArray(NWidgetBase **array, uint length)
{ {
if (this->index >= 0 && (uint)(this->index) < length) array[this->index] = this; if (this->index >= 0 && (uint)(this->index) < length) array[this->index] = this;
@ -1774,6 +1834,7 @@ NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, int index, N
assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME); assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME);
if (index >= 0) this->SetIndex(index); if (index >= 0) this->SetIndex(index);
this->child = child; this->child = child;
this->SetAlignment(SA_TOP | SA_LEFT);
} }
NWidgetBackground::~NWidgetBackground() NWidgetBackground::~NWidgetBackground()
@ -1910,12 +1971,12 @@ void NWidgetBackground::Draw(const Window *w)
case WWT_FRAME: case WWT_FRAME:
if (this->index >= 0) w->SetStringParameters(this->index); if (this->index >= 0) w->SetStringParameters(this->index);
DrawFrame(r, this->colour, this->widget_data); DrawFrame(r, this->colour, this->text_colour, this->widget_data, this->align);
break; break;
case WWT_INSET: case WWT_INSET:
if (this->index >= 0) w->SetStringParameters(this->index); if (this->index >= 0) w->SetStringParameters(this->index);
DrawInset(r, this->colour, this->widget_data); DrawInset(r, this->colour, this->text_colour, this->widget_data, this->align);
break; break;
default: default:
@ -2030,16 +2091,75 @@ void NWidgetViewport::UpdateViewportCoordinates(Window *w)
* @param w The window the click was in. * @param w The window the click was in.
* @param widget Widget number of the widget clicked in. * @param widget Widget number of the widget clicked in.
* @param padding Amount of empty space between the widget edge and the top of the first row. Default value is \c 0. * @param padding Amount of empty space between the widget edge and the top of the first row. Default value is \c 0.
* @param line_height Height of a single row. A negative value means using the vertical resize step of the widget.
* @return Row number clicked at. If clicked at a wrong position, #INT_MAX is returned. * @return Row number clicked at. If clicked at a wrong position, #INT_MAX is returned.
*/ */
int Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding, int line_height) const int Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding) const
{ {
uint pos = w->GetRowFromWidget(clickpos, widget, padding, line_height); uint pos = w->GetRowFromWidget(clickpos, widget, padding, -1);
if (pos != INT_MAX) pos += this->GetPosition(); if (pos != INT_MAX) pos += this->GetPosition();
return (pos >= this->GetCount()) ? INT_MAX : pos; return (pos >= this->GetCount()) ? INT_MAX : pos;
} }
/**
* Update the given list position as if it were on this scroll bar when the given keycode was pressed.
* This does not update the actual position of this scroll bar, that is left to the caller. It does,
* however use the capacity and count of the scroll bar for the bounds and amount to scroll.
*
* When the count is 0 or the return is ES_NOT_HANDLED, then the position is not updated.
* With WKC_UP and WKC_DOWN the position goes one up or down respectively.
* With WKC_PAGEUP and WKC_PAGEDOWN the position goes one capacity up or down respectively.
* With WKC_HOME the first position is selected and with WKC_END the last position is selected.
* This function ensures that pos is in the range [0..count).
* @param list_position The current position in the list.
* @param key_code The pressed key code.
* @return ES_NOT_HANDLED when another key than the 6 specific keys was pressed, otherwise ES_HANDLED.
*/
EventState Scrollbar::UpdateListPositionOnKeyPress(int &list_position, uint16 keycode) const
{
int new_pos = list_position;
switch (keycode) {
case WKC_UP:
/* scroll up by one */
new_pos--;
break;
case WKC_DOWN:
/* scroll down by one */
new_pos++;
break;
case WKC_PAGEUP:
/* scroll up a page */
new_pos -= this->GetCapacity();
break;
case WKC_PAGEDOWN:
/* scroll down a page */
new_pos += this->GetCapacity();
break;
case WKC_HOME:
/* jump to beginning */
new_pos = 0;
break;
case WKC_END:
/* jump to end */
new_pos = this->GetCount() - 1;
break;
default:
return ES_NOT_HANDLED;
}
/* If there are no elements, there is nothing to scroll/update. */
if (this->GetCount() != 0) {
list_position = Clamp(new_pos, 0, this->GetCount() - 1);
}
return ES_HANDLED;
}
/** /**
* Set capacity of visible elements from the size and resize properties of a widget. * Set capacity of visible elements from the size and resize properties of a widget.
* @param w Window. * @param w Window.
@ -2202,6 +2322,11 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data,
case WWT_EMPTY: case WWT_EMPTY:
break; break;
case WWT_TEXT:
this->SetFill(0, 0);
this->SetAlignment(SA_LEFT | SA_VERT_CENTER);
break;
case WWT_PUSHBTN: case WWT_PUSHBTN:
case WWT_IMGBTN: case WWT_IMGBTN:
case WWT_PUSHIMGBTN: case WWT_PUSHIMGBTN:
@ -2210,7 +2335,6 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data,
case WWT_PUSHTXTBTN: case WWT_PUSHTXTBTN:
case WWT_TEXTBTN_2: case WWT_TEXTBTN_2:
case WWT_LABEL: case WWT_LABEL:
case WWT_TEXT:
case WWT_MATRIX: case WWT_MATRIX:
case NWID_BUTTON_DROPDOWN: case NWID_BUTTON_DROPDOWN:
case NWID_PUSHBUTTON_DROPDOWN: case NWID_PUSHBUTTON_DROPDOWN:
@ -2269,6 +2393,7 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data,
case WWT_DROPDOWN: case WWT_DROPDOWN:
this->SetFill(0, 0); this->SetFill(0, 0);
this->min_y = WD_DROPDOWN_HEIGHT; this->min_y = WD_DROPDOWN_HEIGHT;
this->SetAlignment(SA_TOP | SA_LEFT);
break; break;
default: default:
@ -2506,7 +2631,7 @@ void NWidgetLeaf::Draw(const Window *w)
case WWT_IMGBTN: case WWT_IMGBTN:
case WWT_PUSHIMGBTN: case WWT_PUSHIMGBTN:
case WWT_IMGBTN_2: case WWT_IMGBTN_2:
DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data); DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data, this->align);
break; break;
case WWT_TEXTBTN: case WWT_TEXTBTN:
@ -2514,7 +2639,7 @@ void NWidgetLeaf::Draw(const Window *w)
case WWT_TEXTBTN_2: case WWT_TEXTBTN_2:
if (this->index >= 0) w->SetStringParameters(this->index); if (this->index >= 0) w->SetStringParameters(this->index);
DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE); DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE);
DrawLabel(r, this->type, clicked, this->widget_data); DrawLabel(r, this->type, clicked, this->text_colour, this->widget_data, this->align);
break; break;
case WWT_ARROWBTN: case WWT_ARROWBTN:
@ -2527,18 +2652,18 @@ void NWidgetLeaf::Draw(const Window *w)
case AWV_RIGHT: sprite = SPR_ARROW_RIGHT; break; case AWV_RIGHT: sprite = SPR_ARROW_RIGHT; break;
default: NOT_REACHED(); default: NOT_REACHED();
} }
DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite); DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite, this->align);
break; break;
} }
case WWT_LABEL: case WWT_LABEL:
if (this->index >= 0) w->SetStringParameters(this->index); if (this->index >= 0) w->SetStringParameters(this->index);
DrawLabel(r, this->type, clicked, this->widget_data); DrawLabel(r, this->type, clicked, this->text_colour, this->widget_data, this->align);
break; break;
case WWT_TEXT: case WWT_TEXT:
if (this->index >= 0) w->SetStringParameters(this->index); if (this->index >= 0) w->SetStringParameters(this->index);
DrawText(r, (TextColour)this->colour, this->widget_data); DrawText(r, this->text_colour, this->widget_data, this->align);
break; break;
case WWT_MATRIX: case WWT_MATRIX:
@ -2553,7 +2678,7 @@ void NWidgetLeaf::Draw(const Window *w)
case WWT_CAPTION: case WWT_CAPTION:
if (this->index >= 0) w->SetStringParameters(this->index); if (this->index >= 0) w->SetStringParameters(this->index);
DrawCaption(r, this->colour, w->owner, this->widget_data); DrawCaption(r, this->colour, w->owner, this->text_colour, this->widget_data, this->align);
break; break;
case WWT_SHADEBOX: case WWT_SHADEBOX:
@ -2586,13 +2711,13 @@ void NWidgetLeaf::Draw(const Window *w)
case WWT_DROPDOWN: case WWT_DROPDOWN:
if (this->index >= 0) w->SetStringParameters(this->index); if (this->index >= 0) w->SetStringParameters(this->index);
DrawDropdown(r, this->colour, clicked, this->widget_data); DrawDropdown(r, this->colour, clicked, this->widget_data, this->align);
break; break;
case NWID_BUTTON_DROPDOWN: case NWID_BUTTON_DROPDOWN:
case NWID_PUSHBUTTON_DROPDOWN: case NWID_PUSHBUTTON_DROPDOWN:
if (this->index >= 0) w->SetStringParameters(this->index); if (this->index >= 0) w->SetStringParameters(this->index);
DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data); DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data, this->align);
break; break;
default: default:
@ -2731,6 +2856,22 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest,
break; break;
} }
case WPT_TEXTCOLOUR: {
NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(*dest);
if (nwc != nullptr) {
nwc->SetTextColour(parts->u.colour.colour);
}
break;
}
case WPT_ALIGNMENT: {
NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(*dest);
if (nwc != nullptr) {
nwc->SetAlignment(parts->u.align.align);
}
break;
}
case WPT_FILL: { case WPT_FILL: {
NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest); NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest);
if (nwrb != nullptr) nwrb->SetFill(parts->u.xy.x, parts->u.xy.y); if (nwrb != nullptr) nwrb->SetFill(parts->u.xy.x, parts->u.xy.y);

@ -92,6 +92,8 @@ enum WidgetType : uint8 {
WPT_DATATIP, ///< Widget part for specifying data and tooltip. WPT_DATATIP, ///< Widget part for specifying data and tooltip.
WPT_PADDING, ///< Widget part for specifying a padding. WPT_PADDING, ///< Widget part for specifying a padding.
WPT_PIPSPACE, ///< Widget part for specifying pre/inter/post space for containers. WPT_PIPSPACE, ///< Widget part for specifying pre/inter/post space for containers.
WPT_TEXTCOLOUR, ///< Widget part for specifying text colour.
WPT_ALIGNMENT, ///< Widget part for specifying text/image alignment.
WPT_ENDCONTAINER, ///< Widget part to denote end of a container. WPT_ENDCONTAINER, ///< Widget part to denote end of a container.
WPT_FUNCTION, ///< Widget part for calling a user function. WPT_FUNCTION, ///< Widget part for calling a user function.
WPT_SCROLLBAR, ///< Widget part for attaching a scrollbar. WPT_SCROLLBAR, ///< Widget part for attaching a scrollbar.
@ -316,6 +318,8 @@ public:
void SetIndex(int index); void SetIndex(int index);
void SetDataTip(uint32 widget_data, StringID tool_tip); void SetDataTip(uint32 widget_data, StringID tool_tip);
void SetToolTip(StringID tool_tip); void SetToolTip(StringID tool_tip);
void SetTextColour(TextColour colour);
void SetAlignment(StringAlignment align);
inline void SetLowered(bool lowered); inline void SetLowered(bool lowered);
inline bool IsLowered() const; inline bool IsLowered() const;
@ -336,6 +340,8 @@ public:
StringID tool_tip; ///< Tooltip of the widget. @see Widget::tootips StringID tool_tip; ///< Tooltip of the widget. @see Widget::tootips
int scrollbar_index; ///< Index of an attached scrollbar. int scrollbar_index; ///< Index of an attached scrollbar.
TextColour highlight_colour; ///< Colour of highlight. TextColour highlight_colour; ///< Colour of highlight.
TextColour text_colour; ///< Colour of text within widget.
StringAlignment align; ///< Alignment of text/image within widget.
}; };
/** /**
@ -774,7 +780,8 @@ public:
} }
} }
int GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding = 0, int line_height = -1) const; int GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding = 0) const;
EventState UpdateListPositionOnKeyPress(int &list_position, uint16 keycode) const;
}; };
/** /**
@ -930,6 +937,22 @@ struct NWidgetPartTextLines {
FontSize size; ///< Font size of text lines. FontSize size; ///< Font size of text lines.
}; };
/**
* Widget part for storing text colour.
* @ingroup NestedWidgetParts
*/
struct NWidgetPartTextColour {
TextColour colour; ///< TextColour for DrawString.
};
/**
* Widget part for setting text/image alignment within a widget.
* @ingroup NestedWidgetParts
*/
struct NWidgetPartAlignment {
StringAlignment align; ///< Alignment of text/image.
};
/** /**
* Pointer to function returning a nested widget. * Pointer to function returning a nested widget.
* @param biggest_index Pointer to storage for collecting the biggest index used in the nested widget. * @param biggest_index Pointer to storage for collecting the biggest index used in the nested widget.
@ -951,6 +974,8 @@ struct NWidgetPart {
NWidgetPartPaddings padding; ///< Part with paddings. NWidgetPartPaddings padding; ///< Part with paddings.
NWidgetPartPIP pip; ///< Part with pre/inter/post spaces. NWidgetPartPIP pip; ///< Part with pre/inter/post spaces.
NWidgetPartTextLines text_lines; ///< Part with text line data. NWidgetPartTextLines text_lines; ///< Part with text line data.
NWidgetPartTextColour colour; ///< Part with text colour data.
NWidgetPartAlignment align; ///< Part with internal alignment.
NWidgetFunctionType *func_ptr; ///< Part with a function call. NWidgetFunctionType *func_ptr; ///< Part with a function call.
NWidContainerFlags cont_flags; ///< Part with container flags. NWidContainerFlags cont_flags; ///< Part with container flags.
} u; } u;
@ -1009,6 +1034,36 @@ static inline NWidgetPart SetMinimalTextLines(uint8 lines, uint8 spacing, FontSi
return part; return part;
} }
/**
* Widget part function for setting the text colour.
* @param colour Colour to draw string within widget.
* @ingroup NestedWidgetParts
*/
static inline NWidgetPart SetTextColour(TextColour colour)
{
NWidgetPart part;
part.type = WPT_TEXTCOLOUR;
part.u.colour.colour = colour;
return part;
}
/**
* Widget part function for setting the alignment of text/images.
* @param align Alignment of text/image within widget.
* @ingroup NestedWidgetParts
*/
static inline NWidgetPart SetAlignment(StringAlignment align)
{
NWidgetPart part;
part.type = WPT_ALIGNMENT;
part.u.align.align = align;
return part;
}
/** /**
* Widget part function for setting filling. * Widget part function for setting filling.
* @param fill_x Horizontal filling step from minimal size. * @param fill_x Horizontal filling step from minimal size.

@ -140,7 +140,7 @@ enum WidgetDrawDistances {
/* widget.cpp */ /* widget.cpp */
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags); void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags);
void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str); void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align);
/* window.cpp */ /* window.cpp */
extern WindowBase *_z_front_window; extern WindowBase *_z_front_window;

Loading…
Cancel
Save