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)
endif()
project(${BINARY_NAME})
project(${BINARY_NAME}
VERSION 1.12.0
)
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.")
@ -92,6 +94,9 @@ if(OPTION_TOOLS_ONLY)
${CMAKE_COMMAND}
-DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated
-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>
-P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
@ -463,6 +468,9 @@ add_custom_target(find_version
${CMAKE_COMMAND}
-DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated
-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}"
$<$<PLATFORM_ID:Windows>:-DWIN32=TRUE>
-P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake"

@ -2,27 +2,28 @@
## 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
- liblzo2: (de)compressing of old (pre 0.3.0) savegames
- liblzma: (de)compressing of savegames (1.1.0 and later)
- libzstd: (de)compressing of savegames (1.11.0 and later)
- libpng: making screenshots and loading heightmaps
- (encouraged) liblzma: (de)compressing of savegames (1.1.0 and later)
- (encouraged) libpng: making screenshots and loading heightmaps
- (optional) liblzo2: (de)compressing of old (pre 0.3.0) savegames
- (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
- libfontconfig: searching for fonts, resolving font names to actual fonts
- libicu: handling of right-to-left scripts (e.g. Arabic and Persian) and
natural sorting of strings (Linux only)
- libSDL2: hardware access (video, sound, mouse) (not required for Windows or macOS)
natural sorting of strings
OpenTTD does not require any of the libraries to be present, but without
liblzma you cannot open most recent savegames and without zlib you cannot
open most older savegames or use the content downloading system.
Without libSDL/liballegro on non-Windows and non-macOS 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.
@ -79,6 +80,8 @@ files himself via the `ZERO_CHECK` project.
## All other platforms
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
mkdir build
@ -90,6 +93,25 @@ make
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).
## 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
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)
- Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5)
- José Soler (Terkhen) - General coding (since 1.0)
- Leif Linse (Zuu) - AI/Game Script (since 1.2)
### Inactive Developers:
@ -28,6 +27,7 @@
- Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5)
- Patric Stout (TrueBrain) - NoProgrammer (0.3 - 1.2), sys op (active)
- Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3)
- Leif Linse (Zuu) - AI/Game Script (1.2 - 1.6)
### 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.
The currently working platforms are:
The currently supported platforms are:
- FreeBSD (SDL)
- Haiku (SDL)
- Linux (SDL)
- macOS (universal) (Cocoa video and sound drivers)
- OpenBSD (SDL)
- OS/2 (SDL)
- Windows (Win32 GDI (faster) or SDL)
- Linux (SDL (OpenGL and non-OpenGL))
- macOS (universal) (Cocoa)
- Windows (Win32 GDI / OpenGL)
Other platforms may also work (in particular various BSD systems), but we don't actively test or maintain these.
### 1.3.1) Legacy support
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
OpenMSX for music can be found at:
- https://www.openttd.org/download-opengfx for OpenGFX
- https://www.openttd.org/download-opensfx for OpenSFX
- https://www.openttd.org/download-openmsx for OpenMSX
- https://www.openttd.org/downloads/opengfx-releases/ for OpenGFX
- https://www.openttd.org/downloads/opensfx-releases/ for OpenSFX
- https://www.openttd.org/downloads/openmsx-releases/ for OpenMSX
Please follow the readme of these packages about the installation procedure.
The Windows installer can optionally download and install these packages.

@ -1,5 +1,15 @@
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.
#

@ -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) {
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()) {
this->selected = sel;
this->SetDirty();
@ -795,6 +795,7 @@ struct AIConfigWindow : public Window {
case WID_AIC_LIST:
this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
resize->height = this->line_height;
size->height = 8 * this->line_height;
break;
@ -906,7 +907,7 @@ struct AIConfigWindow : public Window {
}
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();
if (click_count > 1 && this->selected_slot != INVALID_COMPANY) ShowAIListWindow((CompanyID)this->selected_slot);
break;

@ -11,6 +11,7 @@
#include "command_func.h"
#include "group.h"
#include "autoreplace_base.h"
#include "core/bitmath_func.hpp"
#include "core/pool_func.hpp"
#include "safeguards.h"
@ -64,7 +65,7 @@ void RemoveAllEngineReplacement(EngineRenewList *erl)
EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, bool *replace_when_old)
{
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 */
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 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 */
Vehicle *w = v;
bool any_replacements = false;

@ -375,8 +375,15 @@ public:
break;
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: {
const Company *c = Company::Get(_local_company);
SetDParam(0, c->settings.renew_keep_length ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
bool remove_wagon;
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;
}
@ -528,9 +535,16 @@ public:
}
break;
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: // 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);
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: {
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;
}
case WID_RV_START_REPLACE: { // Start replacing
if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {

@ -228,7 +228,7 @@ CommandProc CmdCreateGroupFromList;
CommandProc CmdAddVehicleGroup;
CommandProc CmdAddSharedVehicleGroup;
CommandProc CmdRemoveAllVehiclesGroup;
CommandProc CmdSetGroupReplaceProtection;
CommandProc CmdSetGroupFlag;
CommandProc CmdSetGroupLivery;
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(CmdAddSharedVehicleGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ADD_SHARE_VEHICLE_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(CmdMoveOrder, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_MOVE_ORDER
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_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_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_MOVE_ORDER, ///< move an order

@ -246,10 +246,10 @@ static const NWidgetPart _nested_company_finances_widgets[] = {
EndContainer(),
NWidget(NWID_SPACER), SetFill(0, 0), SetMinimalSize(30, 0),
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_LOAN_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_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_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(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_MAXLOAN),
NWidget(NWID_HORIZONTAL),
@ -299,6 +299,24 @@ struct CompanyFinancesWindow : Window {
SetDParam(1, (CompanyID)this->window_number);
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:
SetDParam(0, _economy.max_loan);
break;
@ -357,27 +375,6 @@ struct CompanyFinancesWindow : Window {
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:
GfxFillRect(r.left, r.top, r.right, r.top, PC_BLACK);
break;
@ -1018,7 +1015,7 @@ public:
break;
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 (this->livery_class < LC_GROUP_RAIL) {
@ -1262,70 +1259,82 @@ static const NWidgetPart _nested_select_company_manager_face_widgets[] = {
EndContainer(),
NWidget(NWID_SPACER), SetMinimalSize(0, 4),
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),
EndContainer(),
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),
EndContainer(),
NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0),
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_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),
EndContainer(),
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_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),
EndContainer(),
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_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),
EndContainer(),
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_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),
EndContainer(),
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_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),
EndContainer(),
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_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),
EndContainer(),
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_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),
EndContainer(),
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_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),
EndContainer(),
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_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),
EndContainer(),
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_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),
@ -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 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
*
@ -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->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->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:
@ -1452,41 +1462,27 @@ public:
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
switch (widget) {
case WID_SCMF_FACE: {
Dimension face_size = GetSpriteSize(SPR_GRADIENT);
size->width = std::max(size->width, face_size.width);
size->height = std::max(size->height, face_size.height);
case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT:
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING));
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
break;
}
case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT:
case WID_SCMF_TIE_EARRING_TEXT: {
int offset = (widget - WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT) * 2;
*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;
case WID_SCMF_TIE_EARRING_TEXT:
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING));
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_TIE));
break;
}
case WID_SCMF_LIPS_MOUSTACHE_TEXT:
*size = maxdim(GetStringBoundingBox(STR_FACE_LIPS), GetStringBoundingBox(STR_FACE_MOUSTACHE));
size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_LIPS));
*size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
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:
*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;
case WID_SCMF_FACE: {
Dimension face_size = GetSpriteSize(SPR_GRADIENT);
size->width = std::max(size->width, face_size.width);
size->height = std::max(size->height, face_size.height);
break;
}
case WID_SCMF_HAS_MOUSTACHE_EARRING:
case WID_SCMF_HAS_GLASSES:
@ -1572,30 +1568,6 @@ public:
void DrawWidget(const Rect &r, int widget) const override
{
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:
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);
@ -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 */
static WindowDesc _select_company_manager_face_desc(
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
/* console parser */
IConsoleCmd *_iconsole_cmds; ///< list of registered commands
IConsoleAlias *_iconsole_aliases; ///< list of registered aliases
/* static */ IConsole::CommandList &IConsole::Commands()
{
static IConsole::CommandList cmds;
return cmds;
}
/* static */ IConsole::AliasList &IConsole::Aliases()
{
static IConsole::AliasList aliases;
return aliases;
}
FILE *_iconsole_output_file;
@ -195,49 +204,13 @@ bool GetArgumentInteger(uint32 *value, const char *arg)
}
/**
* Add an item to an alphabetically sorted list.
* @param base first item of the list
* @param item_new the item to add
*/
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.
* Creates a copy of a string with underscores removed from it
* @param name String to remove the underscores from.
* @return A copy of \a name, without underscores.
*/
char *RemoveUnderscores(char *name)
std::string RemoveUnderscores(std::string name)
{
char *q = name;
for (const char *p = name; *p != '\0'; p++) {
if (*p != '_') *q++ = *p;
}
*q = '\0';
name.erase(std::remove(name.begin(), name.end(), '_'), name.end());
return name;
}
@ -246,16 +219,9 @@ char *RemoveUnderscores(char *name)
* @param name name of the command that will be used
* @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);
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);
IConsole::Commands().try_emplace(RemoveUnderscores(name), name, proc, hook, unlisted);
}
/**
@ -263,13 +229,10 @@ void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *
* @param name command to be found
* @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;
for (item = _iconsole_cmds; item != nullptr; item = item->next) {
if (strcmp(item->name, name) == 0) return item;
}
auto item = IConsole::Commands().find(RemoveUnderscores(name));
if (item != IConsole::Commands().end()) return &item->second;
return nullptr;
}
@ -278,22 +241,10 @@ IConsoleCmd *IConsoleCmdGet(const char *name)
* @param name name of the alias that will be used
* @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) {
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);
auto result = IConsole::Aliases().try_emplace(RemoveUnderscores(name), name, cmd);
if (!result.second) IConsoleError("an alias with this name already exists; insertion aborted");
}
/**
@ -301,16 +252,13 @@ void IConsoleAliasRegister(const char *name, const char *cmd)
* @param name alias to be found
* @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;
for (item = _iconsole_aliases; item != nullptr; item = item->next) {
if (strcmp(item->name, name) == 0) return item;
}
auto item = IConsole::Aliases().find(RemoveUnderscores(name));
if (item != IConsole::Aliases().end()) return &item->second;
return nullptr;
}
/**
* An alias is just another name for a command, or for more commands
* Execute it as well.
@ -330,7 +278,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char
return;
}
for (const char *cmdptr = alias->cmdline; *cmdptr != '\0'; cmdptr++) {
for (const char *cmdptr = alias->cmdline.c_str(); *cmdptr != '\0'; cmdptr++) {
switch (*cmdptr) {
case '\'': // ' will double for ""
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) {
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;
}
@ -501,8 +449,7 @@ void IConsoleCmdExecTokens(uint token_count, char *tokens[], const uint recurse_
* First try commands, then aliases. Execute
* the found action taking into account its hooking code
*/
RemoveUnderscores(tokens[0]);
IConsoleCmd *cmd = IConsoleCmdGet(tokens[0]);
IConsoleCmd *cmd = IConsole::CmdGet(tokens[0]);
if (cmd != nullptr) {
ConsoleHookResult chr = (cmd->hook == nullptr ? CHR_ALLOW : cmd->hook(true));
switch (chr) {
@ -518,7 +465,7 @@ void IConsoleCmdExecTokens(uint token_count, char *tokens[], const uint recurse_
}
token_count--;
IConsoleAlias *alias = IConsoleAliasGet(tokens[0]);
IConsoleAlias *alias = IConsole::AliasGet(tokens[0]);
if (alias != nullptr) {
IConsoleAliasExec(alias, token_count, &tokens[1], recurse_count + 1);
return;

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

@ -524,11 +524,9 @@ static void IConsoleTabCompletion()
return;
}
}
size_t length = cmdptr - input;
char *prefix = (char*)alloca(length + 1);
strecpy(prefix, input, prefix + length);
RemoveUnderscores(prefix);
size_t prefix_length = strlen(prefix);
extern std::string RemoveUnderscores(std::string name);
std::string prefix = RemoveUnderscores(std::string(input, cmdptr - input));
size_t prefix_length = prefix.size();
if (prefix_length == 0) return;
@ -536,26 +534,28 @@ static void IConsoleTabCompletion()
char *b = buffer;
uint matches = 0;
std::string common_prefix;
for (const IConsoleCmd *cmd = _iconsole_cmds; cmd != nullptr; cmd = cmd->next) {
if (strncmp(cmd->name, prefix, prefix_length) == 0) {
for (auto &it : IConsole::Commands()) {
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 (matches == 0) {
common_prefix = cmd->name;
common_prefix = it.first;
} else {
const char *cp = common_prefix.c_str();
const char *cmdp = cmd->name;
const char *cmdp = cmd_name;
while (true) {
const char *end = cmdp;
WChar a = Utf8Consume(cp);
WChar b = Utf8Consume(cmdp);
if (a == 0 || b == 0 || a != b) {
common_prefix.resize(end - cmd->name);
common_prefix.resize(end - cmd_name);
break;
}
}
}
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
#include "gfx_type.h"
#include <map>
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
@ -33,9 +34,9 @@ enum ConsoleHookResult {
typedef bool IConsoleCmdProc(byte argc, char *argv[]);
typedef ConsoleHookResult IConsoleHook(bool echo);
struct IConsoleCmd {
char *name; ///< name of command
IConsoleCmd *next; ///< next command in list
IConsoleCmd(const std::string &name, IConsoleCmdProc *proc, IConsoleHook *hook, bool unlisted) : name(name), proc(proc), hook(hook), unlisted(unlisted) {}
std::string name; ///< name of command
IConsoleCmdProc *proc; ///< process executed when command is typed
IConsoleHook *hook; ///< any special trigger action that needs executing
bool unlisted;
@ -54,25 +55,31 @@ struct IConsoleCmd {
* - ";" allows for combining commands (see example 'ng')
*/
struct IConsoleAlias {
char *name; ///< name of the alias
IConsoleAlias *next; ///< next alias in list
IConsoleAlias(const std::string &name, const std::string &cmdline) : name(name), cmdline(cmdline) {}
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 */
extern IConsoleCmd *_iconsole_cmds; ///< List of registered commands.
extern IConsoleAlias *_iconsole_aliases; ///< List of registered aliases.
struct IConsole
{
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 */
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) */
void IConsoleStdLibRegister();
@ -82,6 +89,5 @@ bool GetArgumentInteger(uint32 *value, const char *arg);
void IConsoleGUIInit();
void IConsoleGUIFree();
void IConsoleGUIPrint(TextColour colour_code, char *string);
char *RemoveUnderscores(char *name);
#endif /* CONSOLE_INTERNAL_H */

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

@ -682,7 +682,7 @@ struct FramerateWindow : Window {
case WID_FRW_TIMES_AVERAGE: {
/* Open time graph windows when clicking detail measurement lines */
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) {
line++;
/* 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 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, 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);

@ -324,4 +324,22 @@ enum Support8bpp {
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 */

@ -992,7 +992,7 @@ struct PaymentRatesGraphWindow : BaseGraphWindow {
}
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;
const CargoSpec *cs;

@ -62,13 +62,19 @@ struct GroupStatistics {
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. */
struct Group : GroupPool::PoolItem<&_group_pool> {
std::string name; ///< Group Name
Owner owner; ///< Group Owner
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
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) {
Group *g = new Group(_current_company);
g->replace_protection = false;
g->vehicle_type = vt;
g->parent = INVALID_GROUP;
@ -332,10 +331,12 @@ CommandCost CmdCreateGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
const Company *c = Company::Get(_current_company);
g->livery.colour1 = c->livery[LS_DEFAULT].colour1;
g->livery.colour2 = c->livery[LS_DEFAULT].colour2;
if (c->settings.renew_keep_length) SetBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL);
} else {
g->parent = pg->index;
g->livery.colour1 = pg->livery.colour1;
g->livery.colour2 = pg->livery.colour2;
g->flags = pg->flags;
}
_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 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()) {
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 flags type of operation
* @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
* - p2 bit 0 : 1 to set or 0 to clear protection.
* - p2 bit 1 : 1 to apply to sub-groups.
* @param text unused
* @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;
/* 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 (HasBit(p2, 1)) {
SetGroupReplaceProtection(g, HasBit(p2, 0));
} else {
g->replace_protection = HasBit(p2, 0);
}
SetGroupFlag(g, flag, HasBit(p2, 0), HasBit(p2, 1));
SetWindowDirty(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack());
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. */
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;
/* Set text of "group by" dropdown widget. */
@ -675,7 +675,7 @@ public:
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;
}
@ -755,7 +755,7 @@ public:
break;
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 (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: {
const Group *g = Group::GetIfValid(this->vli.index);
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;
}
@ -921,7 +921,7 @@ public:
break;
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;
if (this->group_sel != new_g && g->parent != new_g) {
@ -954,7 +954,7 @@ public:
this->group_over = INVALID_GROUP;
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;
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;
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;
break;
}

@ -11,6 +11,7 @@ set(LANG_SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/brazilian_portuguese.txt
${CMAKE_CURRENT_SOURCE_DIR}/bulgarian.txt
${CMAKE_CURRENT_SOURCE_DIR}/catalan.txt
${CMAKE_CURRENT_SOURCE_DIR}/chuvash.txt
${CMAKE_CURRENT_SOURCE_DIR}/croatian.txt
${CMAKE_CURRENT_SOURCE_DIR}/czech.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}/finnish.txt
${CMAKE_CURRENT_SOURCE_DIR}/french.txt
${CMAKE_CURRENT_SOURCE_DIR}/frisian.txt
${CMAKE_CURRENT_SOURCE_DIR}/gaelic.txt
${CMAKE_CURRENT_SOURCE_DIR}/galician.txt
${CMAKE_CURRENT_SOURCE_DIR}/german.txt
${CMAKE_CURRENT_SOURCE_DIR}/greek.txt
${CMAKE_CURRENT_SOURCE_DIR}/hebrew.txt
${CMAKE_CURRENT_SOURCE_DIR}/hindi.txt
${CMAKE_CURRENT_SOURCE_DIR}/hungarian.txt
${CMAKE_CURRENT_SOURCE_DIR}/icelandic.txt
${CMAKE_CURRENT_SOURCE_DIR}/ido.txt
${CMAKE_CURRENT_SOURCE_DIR}/indonesian.txt
${CMAKE_CURRENT_SOURCE_DIR}/irish.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}/lithuanian.txt
${CMAKE_CURRENT_SOURCE_DIR}/luxembourgish.txt
${CMAKE_CURRENT_SOURCE_DIR}/macedonian.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_nynorsk.txt
${CMAKE_CURRENT_SOURCE_DIR}/persian.txt
${CMAKE_CURRENT_SOURCE_DIR}/polish.txt
${CMAKE_CURRENT_SOURCE_DIR}/portuguese.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}/traditional_chinese.txt
${CMAKE_CURRENT_SOURCE_DIR}/turkish.txt
${CMAKE_CURRENT_SOURCE_DIR}/urdu.txt
${CMAKE_CURRENT_SOURCE_DIR}/ukrainian.txt
${CMAKE_CURRENT_SOURCE_DIR}/vietnamese.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_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_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_EXPAND_ALL :{BLACK}Alles uitvouwen
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_DEFAULT_VALUE :{LTBLUE}Standaardwaarde: {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_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_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_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_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_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
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_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_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_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_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_PASSWORD :{WHITE}Wrong password
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_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_INVALID_CLIENT_NAME :{WHITE}Your player name is not valid
############ Leave those lines in this order!!
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_MAP :downloading 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
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_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_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_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_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_TOOLTIP :{BLACK}사용할 언어를 선택하세요
STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% 번역됨)
STR_GAME_OPTIONS_FULLSCREEN :{BLACK}전체화면
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_HELPTEXT :선로의 어느 쪽에 신호기를 설치할 지 선택합니다.
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_SHOWFINANCES :연말에 자동으로 재정 창을 띄우기: {STRING}
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_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_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_TOOLTIP :{BLACK}Язык пользовательского интерфейса
STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% перевед{P ён ено ено})
STR_GAME_OPTIONS_FULLSCREEN :{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_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_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_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_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_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_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_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_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_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
bool IsValid() const;
bool IsReasonablyFinished() const;
/**
* 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" Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5)",
u8" Jos\u00e9 Soler (Terkhen) - General coding (since 1.0)",
u8" Leif Linse (Zuu) - AI/Game Script (since 1.2)",
u8"",
u8"Inactive Developers:",
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" 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" Leif Linse (Zuu) - AI/Game Script (1.2 - 1.6)",
u8"",
u8"Retired Developers:",
u8" Tam\u00e1s Farag\u00f3 (Darkvater) - Ex-Lead coder (0.3 - 0.5)",

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

@ -340,6 +340,7 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err)
STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER,
STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP,
STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN,
STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME,
};
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 (!NetworkValidateClientName()) return;
strecpy(_settings_client.network.last_host, address.GetHostname(), lastof(_settings_client.network.last_host));
_settings_client.network.last_port = address.GetPort();
_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. */
struct NetworkChatWindow : public Window {
DestType dtype; ///< The type of destination.
StringID dest_string; ///< String representation of the destination.
int dest; ///< The identifier of the destination.
QueryString message_editbox; ///< Message editbox.
@ -307,9 +306,10 @@ struct NetworkChatWindow : public Window {
STR_NULL,
};
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);
InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height);
@ -464,27 +464,13 @@ struct NetworkChatWindow : public Window {
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 (this->dtype == DESTTYPE_CLIENT) {
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
@ -532,7 +518,7 @@ static const NWidgetPart _nested_chat_window_widgets[] = {
NWidget(WWT_CLOSEBOX, COLOUR_GREY, WID_NC_CLOSE),
NWidget(WWT_PANEL, COLOUR_GREY, WID_NC_BACKGROUND),
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),
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),

@ -736,6 +736,10 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac
if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
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);
if (ci != nullptr) {
@ -781,26 +785,27 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p)
{
static const StringID network_error_strings[] = {
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_GENERAL
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_DESYNC
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_SAVEGAME_FAILED
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_CONNECTION_LOST
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_ILLEGAL_PACKET
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_EXPECTED
STR_NETWORK_ERROR_WRONG_REVISION, // NETWORK_ERROR_WRONG_REVISION
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NAME_IN_USE
STR_NETWORK_ERROR_WRONG_PASSWORD, // NETWORK_ERROR_WRONG_PASSWORD
STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_COMPANY_MISMATCH
STR_NETWORK_ERROR_KICKED, // NETWORK_ERROR_KICKED
STR_NETWORK_ERROR_CHEATER, // NETWORK_ERROR_CHEATER
STR_NETWORK_ERROR_SERVER_FULL, // NETWORK_ERROR_FULL
STR_NETWORK_ERROR_TOO_MANY_COMMANDS, // NETWORK_ERROR_TOO_MANY_COMMANDS
STR_NETWORK_ERROR_TIMEOUT_PASSWORD, // NETWORK_ERROR_TIMEOUT_PASSWORD
STR_NETWORK_ERROR_TIMEOUT_COMPUTER, // NETWORK_ERROR_TIMEOUT_COMPUTER
STR_NETWORK_ERROR_TIMEOUT_MAP, // NETWORK_ERROR_TIMEOUT_MAP
STR_NETWORK_ERROR_TIMEOUT_JOIN, // NETWORK_ERROR_TIMEOUT_JOIN
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_GENERAL
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_DESYNC
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_SAVEGAME_FAILED
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_CONNECTION_LOST
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_ILLEGAL_PACKET
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_EXPECTED
STR_NETWORK_ERROR_WRONG_REVISION, // NETWORK_ERROR_WRONG_REVISION
STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NAME_IN_USE
STR_NETWORK_ERROR_WRONG_PASSWORD, // NETWORK_ERROR_WRONG_PASSWORD
STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_COMPANY_MISMATCH
STR_NETWORK_ERROR_KICKED, // NETWORK_ERROR_KICKED
STR_NETWORK_ERROR_CHEATER, // NETWORK_ERROR_CHEATER
STR_NETWORK_ERROR_SERVER_FULL, // NETWORK_ERROR_FULL
STR_NETWORK_ERROR_TOO_MANY_COMMANDS, // NETWORK_ERROR_TOO_MANY_COMMANDS
STR_NETWORK_ERROR_TIMEOUT_PASSWORD, // NETWORK_ERROR_TIMEOUT_PASSWORD
STR_NETWORK_ERROR_TIMEOUT_COMPUTER, // NETWORK_ERROR_TIMEOUT_COMPUTER
STR_NETWORK_ERROR_TIMEOUT_MAP, // NETWORK_ERROR_TIMEOUT_MAP
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);
@ -1414,6 +1419,56 @@ void NetworkClientsToSpectators(CompanyID cid)
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.
*/
@ -1422,6 +1477,11 @@ void NetworkUpdateClientName()
NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(_network_own_client_id);
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 */
if (strcmp(ci->client_name, _settings_client.network.client_name) != 0) {

@ -863,55 +863,31 @@ public:
EventState OnKeyPress(WChar key, uint16 keycode) override
{
switch (keycode) {
case WKC_UP:
/* scroll up by one */
if (this->list_pos > 0) this->list_pos--;
break;
case WKC_DOWN:
/* scroll down by one */
if (this->list_pos < (int)this->content.size() - 1) this->list_pos++;
break;
case WKC_PAGEUP:
/* scroll up a page */
this->list_pos = (this->list_pos < this->vscroll->GetCapacity()) ? 0 : this->list_pos - this->vscroll->GetCapacity();
break;
case WKC_PAGEDOWN:
/* scroll down a page */
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();
if (this->vscroll->UpdateListPositionOnKeyPress(this->list_pos, keycode) == ES_NOT_HANDLED) {
switch (keycode) {
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;
}
return ES_HANDLED;
}
/* space is pressed and filter is focused. */
FALLTHROUGH;
/* space is pressed and filter is focused. */
FALLTHROUGH;
default:
return ES_NOT_HANDLED;
default:
return ES_NOT_HANDLED;
}
}
if (this->content.size() == 0) {
this->list_pos = 0; // above stuff may result in "-1".
if (this->UpdateFilterState()) {
this->content.ForceRebuild();
this->InvalidateData();

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

@ -68,8 +68,8 @@ void UpdateNetworkGameWindow()
}
typedef GUIList<NetworkGameList*, StringFilter&> GUIGameServerList;
typedef uint16 ServerListPosition;
static const ServerListPosition SLP_INVALID = 0xFFFF;
typedef int ServerListPosition;
static const ServerListPosition SLP_INVALID = -1;
/** Full blown container to make it behave exactly as we want :) */
class NWidgetServerListHeader : public NWidgetContainer {
@ -786,39 +786,8 @@ public:
EventState state = ES_NOT_HANDLED;
/* 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->servers.size() == 0) 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();
}
if (this->vscroll->UpdateListPositionOnKeyPress(this->list_pos, keycode) == ES_HANDLED) {
if (this->list_pos == SLP_INVALID) return ES_HANDLED;
this->server = this->servers[this->list_pos];
@ -854,12 +823,9 @@ public:
}
case WID_NG_CLIENT:
/* Make sure the name does not start with a space, so TAB completion works */
if (!StrEmpty(this->name_editbox.text.buf) && this->name_editbox.text.buf[0] != ' ') {
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));
}
/* Validation of the name will happen once the user tries to join or start a game, as getting
* 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));
break;
}
}
@ -1292,6 +1258,8 @@ static WindowDesc _network_start_server_window_desc(
static void ShowNetworkStartServerWindow()
{
if (!NetworkValidateClientName()) return;
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME);
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY);
@ -1584,6 +1552,8 @@ static WindowDesc _network_lobby_window_desc(
*/
static void ShowNetworkLobbyWindow(NetworkGameList *ngl)
{
if (!NetworkValidateClientName()) return;
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START);
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME);

@ -987,8 +987,12 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p)
break;
}
/* We need a valid name.. make it Player */
if (StrEmpty(name)) strecpy(name, "Player", lastof(name));
if (!NetworkIsValidClientName(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
/* 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 (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 */
if (NetworkFindName(client_name, lastof(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_MAP,
NETWORK_ERROR_TIMEOUT_JOIN,
NETWORK_ERROR_INVALID_CLIENT_NAME,
NETWORK_ERROR_END,
};

@ -1327,42 +1327,8 @@ struct NewGRFWindow : public Window, NewGRFScanCallback {
{
if (!this->editable) return ES_NOT_HANDLED;
switch (keycode) {
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->vscroll2->UpdateListPositionOnKeyPress(this->avail_pos, keycode) == ES_NOT_HANDLED) return ES_NOT_HANDLED;
if (this->avails.size() == 0) this->avail_pos = -1;
if (this->avail_pos >= 0) {
this->active_sel = nullptr;
DeleteWindowByClass(WC_GRF_PARAMETERS);

@ -422,7 +422,7 @@ struct NewsWindow : Window {
{
switch (widget) {
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;
case WID_N_PANEL:
@ -1205,7 +1205,7 @@ struct MessageHistoryWindow : Window {
NewsItem *ni = _latest_news;
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;
if (ni == nullptr) return;
}

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

@ -1578,7 +1578,7 @@ public:
break;
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;
StationClassID station_class_id = this->station_classes[y];
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.
*/
/** ${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. */
Station::RecomputeCatchmentForAll();

@ -21,7 +21,7 @@ static const SaveLoad _group_desc[] = {
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), // num_vehicle
SLE_VAR(Group, owner, 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.colour1, 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_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_GROUP_REPLACE_WAGON_REMOVAL, ///< 291 PR#7441 Per-group wagon removal flag.
SL_MAX_VERSION, ///< Highest possible saveload version

@ -89,14 +89,14 @@
{
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)
{
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)

@ -36,6 +36,7 @@
#include "querystring_gui.h"
#include "fontcache.h"
#include "zoom_func.h"
#include "rev.h"
#include "video/video_driver.hpp"
#include "music/music_driver.hpp"
@ -220,7 +221,10 @@ struct GameOptionsWindow : Window {
case WID_GO_LANG_DROPDOWN: { // Setup interface language dropdown
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) {
*selected_index = i;
item->SetParamStr(0, _languages[i].own_name);
@ -232,6 +236,7 @@ struct GameOptionsWindow : Window {
* entries in the dropdown list. */
item->SetParamStr(0, _languages[i].name);
}
item->SetParam(1, (LANGUAGE_TOTAL_STRINGS - _languages[i].missing) * 100 / LANGUAGE_TOTAL_STRINGS);
list.emplace_back(item);
}
std::sort(list.begin(), list.end(), DropDownListStringItem::NatSortFunc);

@ -156,9 +156,6 @@ void CheckRedrawStationCoverage(Window *w)
* @param type Cargo type
* @param amount Cargo amount
* @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)
{
@ -168,32 +165,33 @@ static void StationsWndShowStationRating(int left, int right, int y, CargoID typ
const CargoSpec *cs = CargoSpec::Get(type);
if (!cs->IsValid()) return;
int padding = ScaleFontTrad(1);
int width = right - left;
int colour = cs->rating_colour;
TextColour tc = GetContrastColour(colour);
uint w = (std::min(amount, units_full) + 5) / 36;
int height = GetCharacterHeight(FS_SMALL);
uint w = std::min(amount + 5, units_full) * width / units_full;
/* Draw total cargo (limited) on station (fits into 16 pixels) */
if (w != 0) GfxFillRect(left, y, left + w - 1, y + height, colour);
int height = GetCharacterHeight(FS_SMALL) + padding - 1;
/* Draw a one pixel-wide bar of additional cargo meter, useful
* for stations with only a small amount (<=30) */
if (w == 0) {
uint rest = amount / 5;
if (amount > 30) {
/* Draw total cargo (limited) on station */
GfxFillRect(left, y, left + w - 1, y + height, colour);
} 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) {
w += left;
GfxFillRect(w, y + height - rest, w, y + height, colour);
GfxFillRect(left, y + height - rest, left + padding - 1, 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) */
y += height + 2;
GfxFillRect(left + 1, y, left + 14, y, PC_RED);
rating = std::min<uint>(rating, rating_full) / 16;
if (rating != 0) GfxFillRect(left + 1, y, left + rating, y, PC_GREEN);
/* Draw green/red ratings bar (fits under the waiting bar) */
y += height + padding + 1;
GfxFillRect(left + padding, y, right - padding - 1, y + padding - 1, PC_RED);
w = std::min<uint>(rating, rating_full) * (width - padding - padding) / rating_full;
if (w != 0) GfxFillRect(left + padding, y, left + w - 1, y + padding - 1, PC_GREEN);
}
typedef GUIList<const Station*> GUIStationList;
@ -217,6 +215,7 @@ protected:
GUIStationList stations;
Scrollbar *vscroll;
uint rating_width;
/**
* (Re)Build station list
@ -395,16 +394,17 @@ public:
}
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;
break;
case WID_STL_TRAIN:
case WID_STL_TRUCK:
case WID_STL_BUS:
case WID_STL_AIRPLANE:
case WID_STL_SHIP:
size->height = std::max<uint>(FONT_HEIGHT_SMALL, 10) + padding.height;
/* Determine appropriate width for mini station rating graph */
this->rating_width = 0;
const CargoSpec *cs;
FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
this->rating_width = std::max(this->rating_width, GetStringBoundingBox(cs->abbrev).width);
}
/* Approximately match original 16 pixel wide rating bars by multiplying string width by 1.6 */
this->rating_width = this->rating_width * 16 / 10;
break;
case WID_STL_CARGOALL:
@ -448,6 +448,12 @@ public:
bool rtl = _current_text_dir == TD_RTL;
int max = std::min<size_t>(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->stations.size());
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
const Station *st = this->stations[i];
assert(st->xy != INVALID_TILE);
@ -458,8 +464,8 @@ public:
SetDParam(0, st->index);
SetDParam(1, st->facilities);
int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_STATION);
x += rtl ? -5 : 5;
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 ? -text_spacing : text_spacing;
/* show cargo waiting and station ratings */
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
* the space. */
if (rtl) {
x -= 20;
x -= rating_width + rating_spacing;
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) {
x += 20;
x += rating_width + rating_spacing;
if (x > r.right - WD_FRAMERECT_RIGHT) break;
}
}
}
y += FONT_HEIGHT_NORMAL;
y += line_height;
}
if (this->vscroll->GetCount() == 0) { // company has no stations
@ -491,30 +497,30 @@ public:
}
case WID_STL_NOCARGOWAITING: {
int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_HOR_CENTER);
int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0;
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;
}
case WID_STL_CARGOALL: {
int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER);
int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0;
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;
}
case WID_STL_FACILALL: {
int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER);
int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0;
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;
}
default:
if (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;
GfxFillRect(r.left + cg_ofst, r.top + cg_ofst, r.right - 2 + cg_ofst, r.bottom - 2 + cg_ofst, cs->rating_colour);
int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 1 : 0;
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);
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;
}
@ -532,7 +538,7 @@ public:
{
switch (widget) {
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
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++) {
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->SetFill(0, 1);
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),
EndContainer(),
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_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_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_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_AIRPLANE), SetMinimalSize(14, 11), 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_PANEL, COLOUR_GREY), SetMinimalSize(5, 11), SetFill(0, 1), EndContainer(),
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, 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, 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, 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, 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, 0), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1),
NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 0), SetFill(0, 1), EndContainer(),
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_PUSHBTN, COLOUR_GREY, WID_STL_CARGOALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1),
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, 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(),
EndContainer(),
NWidget(NWID_HORIZONTAL),
@ -1922,7 +1929,7 @@ struct StationViewWindow : public Window {
{
switch (widget) {
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;
case WID_SV_CATCHMENT:

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

@ -334,6 +334,66 @@ bool StrValid(const char *str, const char *last)
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 */
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 StrValid(const char *str, const char *last);
void StrTrimInPlace(char *str);
/**
* 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));
}
/**
* 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.
* @param lang The metadata about the language.
@ -2312,6 +2321,10 @@ void InitializeLanguagePacks()
}
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, 2) == 0) language_fallback = &lng;
}

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

@ -14,8 +14,9 @@
#include "station_type.h"
#include "company_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);
bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st);
void RebuildSubsidisedSourceAndDestinationCache();

@ -3458,7 +3458,7 @@ public:
break;
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;
this->slot_sel = this->vli.index = this->slots[id_s]->index;
@ -3526,7 +3526,7 @@ public:
this->slot_over = INVALID_GROUP;
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 (_ctrl_pressed) {
@ -3627,7 +3627,7 @@ public:
break;
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;
break;
}
@ -3959,7 +3959,7 @@ public:
{
switch (widget) {
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;
this->selected = this->ctrs[id_s]->index;

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

@ -464,16 +464,17 @@ void SetupDebugOutput()
/**
* Create and initialize the singleton back-end class.
* @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.
*/
/* 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();
GetOGLProcAddress = get_proc;
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.
* @param screen_res Current display resolution.
* @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";
@ -546,6 +548,12 @@ const char *OpenGLBackend::Init()
_gl_major_ver = atoi(ver);
_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.";
SetupDebugOutput();
@ -581,6 +589,11 @@ const char *OpenGLBackend::Init()
}
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. */
GLint max_tex_units = 0;
_glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_tex_units);

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

@ -121,7 +121,7 @@ const char *VideoDriver_SDL_OpenGL::AllocateContext()
ToggleVsync(_video_vsync);
return OpenGLBackend::Create(&GetOGLProcAddressCallback);
return OpenGLBackend::Create(&GetOGLProcAddressCallback, this->GetScreenSize());
}
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. */
if (_wglCreateContextAttribsARB != nullptr) {
/* Try for OpenGL 4.5 first. */
int attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 2,
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
WGL_CONTEXT_MINOR_VERSION_ARB, 5,
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.
0
};
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) {
@ -1388,7 +1396,7 @@ const char *VideoDriver_Win32OpenGL::AllocateContext()
this->ToggleVsync(_video_vsync);
this->gl_rc = rc;
return OpenGLBackend::Create(&GetOGLProcAddressCallback);
return OpenGLBackend::Create(&GetOGLProcAddressCallback, this->GetScreenSize());
}
bool VideoDriver_Win32OpenGL::ToggleFullscreen(bool full_screen)

@ -24,6 +24,33 @@
#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
* @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 clicked Button is lowered.
* @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);
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.
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 type Widget type (#WWT_TEXTBTN, #WWT_TEXTBTN_2, or #WWT_LABEL).
* @param clicked Label is rendered lowered.
* @param colour Colour of the text.
* @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 ((type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) 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
DrawString(r.left + clicked, r.right + clicked, r.top + offset + clicked, str, TC_FROMSTRING, SA_HOR_CENTER);
Point p = GetAlignedPosition(r, d, align);
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 colour Colour of the text.
* @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);
int offset = std::max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered
if (str != STR_NULL) DrawString(r.left, r.right, r.top + offset, str, colour);
Point p = GetAlignedPosition(r, d, align);
if (str != STR_NULL) DrawString(r.left, r.right, p.y, str, colour, align);
}
/**
* Draw an inset widget.
* @param r Rectangle of the background.
* @param colour Colour of the inset.
* @param str Text to draw.
* @param r Rectangle of the background.
* @param colour Colour of the inset.
* @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);
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.
* @param r Rectangle of the frame.
* @param colour Colour of the frame.
* @param str Text of the frame.
* @param r Rectangle of the frame.
* @param colour Colour 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
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 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)
{
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)
{
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)
{
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)
{
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.
* @param r Rectangle of the bar.
* @param colour Colour of the window.
* @param owner 'Owner' of the window.
* @param str Text to draw in the bar.
* @param r Rectangle of the bar.
* @param colour Colour of the window.
* @param owner 'Owner' of the window.
* @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;
@ -548,8 +586,8 @@ void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str)
if (str != STR_NULL) {
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
DrawString(r.left + WD_CAPTIONTEXT_LEFT, r.right - WD_CAPTIONTEXT_RIGHT, r.top + offset, str, TC_FROMSTRING, SA_HOR_CENTER);
Point p = GetAlignedPosition(r, d, align);
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_dropdown The drop-down part is lowered.
* @param str Text of the button.
* @param align Alignment of the text within the dropdown.
*
* @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
@ -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.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);
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 {
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);
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 clicked The widget is lowered.
* @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->tool_tip = tool_tip;
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;
}
/**
* 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.
* @param tool_tip Tool tip string to use.
@ -903,6 +954,15 @@ void NWidgetCore::SetToolTip(StringID 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)
{
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);
if (index >= 0) this->SetIndex(index);
this->child = child;
this->SetAlignment(SA_TOP | SA_LEFT);
}
NWidgetBackground::~NWidgetBackground()
@ -1910,12 +1971,12 @@ void NWidgetBackground::Draw(const Window *w)
case WWT_FRAME:
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;
case WWT_INSET:
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;
default:
@ -2030,16 +2091,75 @@ void NWidgetViewport::UpdateViewportCoordinates(Window *w)
* @param w The window the click was 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 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.
*/
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();
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.
* @param w Window.
@ -2202,6 +2322,11 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data,
case WWT_EMPTY:
break;
case WWT_TEXT:
this->SetFill(0, 0);
this->SetAlignment(SA_LEFT | SA_VERT_CENTER);
break;
case WWT_PUSHBTN:
case WWT_IMGBTN:
case WWT_PUSHIMGBTN:
@ -2210,7 +2335,6 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data,
case WWT_PUSHTXTBTN:
case WWT_TEXTBTN_2:
case WWT_LABEL:
case WWT_TEXT:
case WWT_MATRIX:
case NWID_BUTTON_DROPDOWN:
case NWID_PUSHBUTTON_DROPDOWN:
@ -2269,6 +2393,7 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data,
case WWT_DROPDOWN:
this->SetFill(0, 0);
this->min_y = WD_DROPDOWN_HEIGHT;
this->SetAlignment(SA_TOP | SA_LEFT);
break;
default:
@ -2506,7 +2631,7 @@ void NWidgetLeaf::Draw(const Window *w)
case WWT_IMGBTN:
case WWT_PUSHIMGBTN:
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;
case WWT_TEXTBTN:
@ -2514,7 +2639,7 @@ void NWidgetLeaf::Draw(const Window *w)
case WWT_TEXTBTN_2:
if (this->index >= 0) w->SetStringParameters(this->index);
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;
case WWT_ARROWBTN:
@ -2527,18 +2652,18 @@ void NWidgetLeaf::Draw(const Window *w)
case AWV_RIGHT: sprite = SPR_ARROW_RIGHT; break;
default: NOT_REACHED();
}
DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite);
DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite, this->align);
break;
}
case WWT_LABEL:
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;
case WWT_TEXT:
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;
case WWT_MATRIX:
@ -2553,7 +2678,7 @@ void NWidgetLeaf::Draw(const Window *w)
case WWT_CAPTION:
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;
case WWT_SHADEBOX:
@ -2586,13 +2711,13 @@ void NWidgetLeaf::Draw(const Window *w)
case WWT_DROPDOWN:
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;
case NWID_BUTTON_DROPDOWN:
case NWID_PUSHBUTTON_DROPDOWN:
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;
default:
@ -2731,6 +2856,22 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest,
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: {
NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest);
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_PADDING, ///< Widget part for specifying a padding.
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_FUNCTION, ///< Widget part for calling a user function.
WPT_SCROLLBAR, ///< Widget part for attaching a scrollbar.
@ -316,6 +318,8 @@ public:
void SetIndex(int index);
void SetDataTip(uint32 widget_data, StringID tool_tip);
void SetToolTip(StringID tool_tip);
void SetTextColour(TextColour colour);
void SetAlignment(StringAlignment align);
inline void SetLowered(bool lowered);
inline bool IsLowered() const;
@ -336,6 +340,8 @@ public:
StringID tool_tip; ///< Tooltip of the widget. @see Widget::tootips
int scrollbar_index; ///< Index of an attached scrollbar.
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.
};
/**
* 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.
* @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.
NWidgetPartPIP pip; ///< Part with pre/inter/post spaces.
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.
NWidContainerFlags cont_flags; ///< Part with container flags.
} u;
@ -1009,6 +1034,36 @@ static inline NWidgetPart SetMinimalTextLines(uint8 lines, uint8 spacing, FontSi
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.
* @param fill_x Horizontal filling step from minimal size.

@ -140,7 +140,7 @@ enum WidgetDrawDistances {
/* widget.cpp */
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 */
extern WindowBase *_z_front_window;

Loading…
Cancel
Save