0
|
1 |
#include "stdafx.h"
|
|
2 |
#include "ttd.h"
|
|
3 |
#include "station.h"
|
|
4 |
#include "gfx.h"
|
|
5 |
#include "window.h"
|
|
6 |
#include "viewport.h"
|
|
7 |
#include "command.h"
|
|
8 |
#include "town.h"
|
|
9 |
#include "vehicle.h"
|
|
10 |
#include "news.h"
|
|
11 |
#include "saveload.h"
|
|
12 |
#include "economy.h"
|
|
13 |
#include "player.h"
|
|
14 |
#include "airport.h"
|
|
15 |
|
|
16 |
// FIXME -- need to be embedded into Airport variable. Is dynamically
|
|
17 |
// deducteable from graphics-tile array, so will not be needed
|
|
18 |
const byte _airport_size_x[5] = {4, 6, 1, 6, 7 };
|
|
19 |
const byte _airport_size_y[5] = {3, 6, 1, 6, 7 };
|
|
20 |
|
|
21 |
void ShowAircraftDepotWindow(uint tile);
|
|
22 |
extern void UpdateAirplanesOnNewStation(Station *st);
|
|
23 |
|
|
24 |
static void MarkStationDirty(Station *st)
|
|
25 |
{
|
|
26 |
if (st->sign.width_1 != 0) {
|
|
27 |
InvalidateWindowWidget(WC_STATION_VIEW, st->index, 1);
|
|
28 |
|
|
29 |
MarkAllViewportsDirty(
|
|
30 |
st->sign.left - 6,
|
|
31 |
st->sign.top,
|
|
32 |
st->sign.left + (st->sign.width_1 << 2) + 12,
|
|
33 |
st->sign.top + 48);
|
|
34 |
}
|
|
35 |
}
|
|
36 |
|
|
37 |
#define CHECK_STATIONS_ERR ((Station*)-1)
|
|
38 |
|
|
39 |
static Station *GetStationAround(uint tile, int w, int h, int closest_station)
|
|
40 |
{
|
|
41 |
// check around to see if there's any stations there
|
|
42 |
BEGIN_TILE_LOOP(tile_cur, w + 2, h + 2, tile - TILE_XY(1,1))
|
|
43 |
if (IS_TILETYPE(tile_cur, MP_STATION)) {
|
|
44 |
int t;
|
|
45 |
t = _map2[tile_cur];
|
|
46 |
if (closest_station == -1) {
|
|
47 |
closest_station = t;
|
|
48 |
} else if (closest_station != t) {
|
|
49 |
_error_message = STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING;
|
|
50 |
return CHECK_STATIONS_ERR;
|
|
51 |
}
|
|
52 |
}
|
|
53 |
END_TILE_LOOP(tile_cur, w + 2, h + 2, tile - TILE_XY(1,1))
|
|
54 |
return (closest_station == -1) ? NULL : DEREF_STATION(closest_station);
|
|
55 |
}
|
|
56 |
|
|
57 |
|
|
58 |
static bool CheckStationSpreadOut(Station *st, uint tile, int w, int h)
|
|
59 |
{
|
|
60 |
byte station_index = st->index;
|
|
61 |
uint i;
|
|
62 |
uint x1 = GET_TILE_X(tile);
|
|
63 |
uint y1 = GET_TILE_Y(tile);
|
|
64 |
uint x2 = x1 + w - 1;
|
|
65 |
uint y2 = y1 + h - 1;
|
|
66 |
uint t;
|
|
67 |
|
|
68 |
for(i=0; i!=TILES_X*TILES_Y; i++) {
|
|
69 |
if (IS_TILETYPE(i, MP_STATION) && _map2[i] == station_index) {
|
|
70 |
t = GET_TILE_X(i);
|
|
71 |
if (t < x1) x1 = t;
|
|
72 |
if (t > x2) x2 = t;
|
|
73 |
|
|
74 |
t = GET_TILE_Y(i);
|
|
75 |
if (t < y1) y1 = t;
|
|
76 |
if (t > y2) y2 = t;
|
|
77 |
}
|
|
78 |
}
|
|
79 |
|
|
80 |
if (y2-y1 >= _patches.station_spread || x2-x1 >= _patches.station_spread) {
|
|
81 |
_error_message = STR_306C_STATION_TOO_SPREAD_OUT;
|
|
82 |
return false;
|
|
83 |
}
|
|
84 |
|
|
85 |
return true;
|
|
86 |
}
|
|
87 |
|
|
88 |
static Station *AllocateStation()
|
|
89 |
{
|
|
90 |
Station *st, *a_free = NULL;
|
|
91 |
int count;
|
|
92 |
int num_free = 0;
|
|
93 |
int i;
|
|
94 |
|
|
95 |
for(st = _stations, count = lengthof(_stations); count != 0; count--, st++) {
|
|
96 |
if (st->xy == 0) {
|
|
97 |
num_free++;
|
|
98 |
if (a_free==NULL)
|
|
99 |
a_free = st;
|
|
100 |
}
|
|
101 |
}
|
|
102 |
|
|
103 |
if (a_free == NULL ||
|
|
104 |
(num_free < 30 && IS_HUMAN_PLAYER(_current_player))) {
|
|
105 |
_error_message = STR_3008_TOO_MANY_STATIONS_LOADING;
|
|
106 |
return NULL;
|
|
107 |
}
|
|
108 |
|
|
109 |
i = a_free->index;
|
|
110 |
memset(a_free, 0, sizeof(Station));
|
|
111 |
a_free->index = i;
|
|
112 |
return a_free;
|
|
113 |
}
|
|
114 |
|
|
115 |
|
|
116 |
static int CountMapSquareAround(uint tile, byte type, byte min, byte max) {
|
|
117 |
static const TileIndexDiff _count_square_table[7*7+1] = {
|
|
118 |
TILE_XY(-3,-3), 1, 1, 1, 1, 1, 1,
|
|
119 |
TILE_XY(-6,1), 1, 1, 1, 1, 1, 1,
|
|
120 |
TILE_XY(-6,1), 1, 1, 1, 1, 1, 1,
|
|
121 |
TILE_XY(-6,1), 1, 1, 1, 1, 1, 1,
|
|
122 |
TILE_XY(-6,1), 1, 1, 1, 1, 1, 1,
|
|
123 |
TILE_XY(-6,1), 1, 1, 1, 1, 1, 1,
|
|
124 |
TILE_XY(-6,1), 1, 1, 1, 1, 1, 1,
|
|
125 |
0,
|
|
126 |
};
|
|
127 |
int j;
|
|
128 |
const TileIndexDiff *p = _count_square_table;
|
|
129 |
int num = 0;
|
|
130 |
|
|
131 |
while ( (j=*p++) != 0 ) {
|
|
132 |
tile = TILE_MASK(tile + j);
|
|
133 |
|
|
134 |
if (IS_TILETYPE(tile, type) && _map5[tile] >= min && _map5[tile] <= max)
|
|
135 |
num++;
|
|
136 |
}
|
|
137 |
|
|
138 |
return num;
|
|
139 |
}
|
|
140 |
|
|
141 |
#define M(x) ((x) - STR_SV_STNAME)
|
|
142 |
|
|
143 |
static bool GenerateStationName(Station *st, uint tile, int flag)
|
|
144 |
{
|
|
145 |
static const uint32 _gen_station_name_bits[] = {
|
|
146 |
0, /* 0 */
|
|
147 |
1 << M(STR_SV_STNAME_AIRPORT), /* 1 */
|
|
148 |
1 << M(STR_SV_STNAME_OILFIELD), /* 2 */
|
|
149 |
1 << M(STR_SV_STNAME_DOCKS), /* 3 */
|
|
150 |
0x1FF << M(STR_SV_STNAME_BUOY_1), /* 4 */
|
|
151 |
1 << M(STR_SV_STNAME_HELIPORT), /* 5 */
|
|
152 |
};
|
|
153 |
|
|
154 |
Town *t = st->town;
|
|
155 |
uint32 free_names = (uint32)-1;
|
|
156 |
int found;
|
|
157 |
uint z,z2;
|
|
158 |
unsigned long tmp;
|
|
159 |
|
|
160 |
{
|
|
161 |
Station *s;
|
|
162 |
|
|
163 |
FOR_ALL_STATIONS(s) {
|
|
164 |
if (s != st && s->xy != 0 && s->town==t) {
|
|
165 |
uint str = M(s->string_id);
|
|
166 |
if (str <= 0x20) {
|
|
167 |
if (str == M(STR_SV_STNAME_FOREST))
|
|
168 |
str = M(STR_SV_STNAME_WOODS);
|
|
169 |
CLRBIT(free_names, str);
|
|
170 |
}
|
|
171 |
}
|
|
172 |
}
|
|
173 |
}
|
|
174 |
|
|
175 |
/* check default names */
|
|
176 |
tmp = free_names & _gen_station_name_bits[flag];
|
|
177 |
if (tmp != 0) {
|
|
178 |
found = FindFirstBit(tmp);
|
|
179 |
goto done;
|
|
180 |
}
|
|
181 |
|
|
182 |
/* check mine? */
|
|
183 |
if (HASBIT(free_names, M(STR_SV_STNAME_MINES))) {
|
|
184 |
if (CountMapSquareAround(tile, MP_INDUSTRY, 0, 6) >= 2 ||
|
|
185 |
CountMapSquareAround(tile, MP_INDUSTRY, 0x64, 0x73) >= 2 ||
|
|
186 |
CountMapSquareAround(tile, MP_INDUSTRY, 0x2F, 0x33) >= 2 ||
|
|
187 |
CountMapSquareAround(tile, MP_INDUSTRY, 0x48, 0x58) >= 2 ||
|
|
188 |
CountMapSquareAround(tile, MP_INDUSTRY, 0x5B, 0x63) >= 2) {
|
|
189 |
found = M(STR_SV_STNAME_MINES);
|
|
190 |
goto done;
|
|
191 |
}
|
|
192 |
}
|
|
193 |
|
|
194 |
/* check close enough to town to get central as name? */
|
|
195 |
if (GetTileDist1D(tile,t->xy) < 8) {
|
|
196 |
found = M(STR_SV_STNAME);
|
|
197 |
if (HASBIT(free_names, M(STR_SV_STNAME))) goto done;
|
|
198 |
|
|
199 |
found = M(STR_SV_STNAME_CENTRAL);
|
|
200 |
if (HASBIT(free_names, M(STR_SV_STNAME_CENTRAL))) goto done;
|
|
201 |
}
|
|
202 |
|
|
203 |
/* Check lakeside */
|
|
204 |
if (HASBIT(free_names, M(STR_SV_STNAME_LAKESIDE)) &&
|
|
205 |
!CheckDistanceFromEdge(tile, 20) &&
|
|
206 |
CountMapSquareAround(tile, MP_WATER, 0, 0) >= 5) {
|
|
207 |
found = M(STR_SV_STNAME_LAKESIDE);
|
|
208 |
goto done;
|
|
209 |
}
|
|
210 |
|
|
211 |
/* Check woods */
|
|
212 |
if (HASBIT(free_names, M(STR_SV_STNAME_WOODS)) && (
|
|
213 |
CountMapSquareAround(tile, MP_TREES, 0, 255) >= 8 ||
|
|
214 |
CountMapSquareAround(tile, MP_INDUSTRY, 0x10, 0x11) >= 2 )) {
|
|
215 |
found = (_opt.landscape==LT_DESERT) ? M(STR_SV_STNAME_FOREST) : M(STR_SV_STNAME_WOODS);
|
|
216 |
goto done;
|
|
217 |
}
|
|
218 |
|
|
219 |
/* check elevation compared to town */
|
|
220 |
z = GetTileZ(tile);
|
|
221 |
z2 = GetTileZ(t->xy);
|
|
222 |
if (z < z2) {
|
|
223 |
found = M(STR_SV_STNAME_VALLEY);
|
|
224 |
if (HASBIT(free_names, M(STR_SV_STNAME_VALLEY))) goto done;
|
|
225 |
} else if (z > z2) {
|
|
226 |
found = M(STR_SV_STNAME_HEIGHTS);
|
|
227 |
if (HASBIT(free_names, M(STR_SV_STNAME_HEIGHTS))) goto done;
|
|
228 |
}
|
|
229 |
|
|
230 |
/* check direction compared to town */
|
|
231 |
{
|
|
232 |
static const int8 _direction_and_table[] = {
|
|
233 |
~( (1<<M(STR_SV_STNAME_WEST)) | (1<<M(STR_SV_STNAME_EAST)) | (1<<M(STR_SV_STNAME_NORTH)) ),
|
|
234 |
~( (1<<M(STR_SV_STNAME_SOUTH)) | (1<<M(STR_SV_STNAME_WEST)) | (1<<M(STR_SV_STNAME_NORTH)) ),
|
|
235 |
~( (1<<M(STR_SV_STNAME_SOUTH)) | (1<<M(STR_SV_STNAME_EAST)) | (1<<M(STR_SV_STNAME_NORTH)) ),
|
|
236 |
~( (1<<M(STR_SV_STNAME_SOUTH)) | (1<<M(STR_SV_STNAME_WEST)) | (1<<M(STR_SV_STNAME_EAST)) ),
|
|
237 |
};
|
|
238 |
|
|
239 |
free_names &= _direction_and_table[
|
|
240 |
(GET_TILE_X(tile) < GET_TILE_X(t->xy)) +
|
|
241 |
(GET_TILE_Y(tile) < GET_TILE_Y(t->xy))*2];
|
|
242 |
}
|
|
243 |
|
|
244 |
tmp = free_names & ((1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<6)|(1<<7)|(1<<12)|(1<<26)|(1<<27)|(1<<28)|(1<<29)|(1<<30));
|
|
245 |
if (tmp == 0) {
|
|
246 |
_error_message = STR_3007_TOO_MANY_STATIONS_LOADING;
|
|
247 |
return false;
|
|
248 |
}
|
|
249 |
found = FindFirstBit(tmp);
|
|
250 |
|
|
251 |
done:
|
|
252 |
st->string_id = found + STR_SV_STNAME;
|
|
253 |
return true;
|
|
254 |
}
|
|
255 |
#undef M
|
|
256 |
|
|
257 |
static Station *GetClosestStationFromTile(uint tile, uint threshold, byte owner)
|
|
258 |
{
|
|
259 |
Station *st, *best_station = NULL;
|
|
260 |
uint cur_dist;
|
|
261 |
|
|
262 |
FOR_ALL_STATIONS(st) {
|
|
263 |
cur_dist = GetTileDist(tile, st->xy);
|
|
264 |
if (cur_dist < threshold && (owner == 0xFF || st->owner == owner)) {
|
|
265 |
threshold = cur_dist;
|
|
266 |
best_station = st;
|
|
267 |
}
|
|
268 |
}
|
|
269 |
|
|
270 |
return best_station;
|
|
271 |
}
|
|
272 |
|
|
273 |
static void StationInitialize(Station *st, TileIndex tile)
|
|
274 |
{
|
|
275 |
int i;
|
|
276 |
GoodsEntry *ge;
|
|
277 |
|
|
278 |
st->xy = tile;
|
|
279 |
st->bus_tile = st->lorry_tile = st->airport_tile = st->dock_tile = st->train_tile = 0;
|
|
280 |
st->had_vehicle_of_type = 0;
|
|
281 |
st->time_since_load = 255;
|
|
282 |
st->time_since_unload = 255;
|
|
283 |
st->delete_ctr = 0;
|
|
284 |
st->facilities = 0;
|
|
285 |
|
|
286 |
st->blocked_months = 0;
|
|
287 |
st->last_vehicle = INVALID_VEHICLE;
|
|
288 |
|
|
289 |
for(i=0,ge=st->goods; i!=NUM_CARGO; i++, ge++) {
|
|
290 |
ge->waiting_acceptance = 0;
|
|
291 |
ge->days_since_pickup = 0;
|
|
292 |
ge->enroute_from = 0xFF;
|
|
293 |
ge->rating = 175;
|
|
294 |
ge->last_speed = 0;
|
|
295 |
ge->last_age = 0xFF;
|
|
296 |
}
|
|
297 |
_station_sort_dirty = true;
|
|
298 |
}
|
|
299 |
|
|
300 |
// Update the virtual coords needed to draw the station sign.
|
|
301 |
// st = Station to update for.
|
|
302 |
static void UpdateStationVirtCoord(Station *st)
|
|
303 |
{
|
|
304 |
Point pt = RemapCoords2(GET_TILE_X(st->xy) * 16, GET_TILE_Y(st->xy) * 16);
|
|
305 |
pt.y -= 32;
|
|
306 |
if (st->facilities&FACIL_AIRPORT && st->airport_type==AT_OILRIG) pt.y -= 16;
|
|
307 |
|
|
308 |
SET_DPARAM16(0, st->index);
|
|
309 |
SET_DPARAM8(1, st->facilities);
|
|
310 |
UpdateViewportSignPos(&st->sign, pt.x, pt.y, STR_305C_0);
|
|
311 |
}
|
|
312 |
|
|
313 |
// Update the virtual coords needed to draw the station sign for all stations.
|
|
314 |
void UpdateAllStationVirtCoord()
|
|
315 |
{
|
|
316 |
Station *st;
|
|
317 |
FOR_ALL_STATIONS(st) {
|
|
318 |
if (st->xy != 0)
|
|
319 |
UpdateStationVirtCoord(st);
|
|
320 |
}
|
|
321 |
}
|
|
322 |
|
|
323 |
// Update the station virt coords while making the modified parts dirty.
|
|
324 |
static void UpdateStationVirtCoordDirty(Station *st)
|
|
325 |
{
|
|
326 |
MarkStationDirty(st);
|
|
327 |
UpdateStationVirtCoord(st);
|
|
328 |
MarkStationDirty(st);
|
|
329 |
}
|
|
330 |
|
|
331 |
// Get a mask of the cargo types that the station accepts.
|
|
332 |
static uint GetAcceptanceMask(Station *st)
|
|
333 |
{
|
|
334 |
uint mask = 0;
|
|
335 |
uint cur_mask = 1;
|
|
336 |
int i;
|
|
337 |
for(i=0; i!=NUM_CARGO; i++,cur_mask*=2) {
|
|
338 |
if (st->goods[i].waiting_acceptance & 0x8000)
|
|
339 |
mask |= cur_mask;
|
|
340 |
}
|
|
341 |
return mask;
|
|
342 |
}
|
|
343 |
|
|
344 |
// Items contains the two cargo names that are to be accepted or rejected.
|
|
345 |
// msg is the string id of the message to display.
|
|
346 |
static void ShowRejectOrAcceptNews(Station *st, uint32 items, StringID msg)
|
|
347 |
{
|
|
348 |
if (items) {
|
|
349 |
SET_DPARAM32(2, items >> 16);
|
|
350 |
SET_DPARAM32(1, items & 0xFFFF);
|
|
351 |
SET_DPARAM16(0, st->index);
|
|
352 |
AddNewsItem(msg + ((items >> 16)?1:0), NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_TILE, NT_ACCEPTANCE, 0), st->xy, 0);
|
|
353 |
}
|
|
354 |
}
|
|
355 |
|
|
356 |
// Get a list of the cargo types being produced around the tile.
|
|
357 |
void GetProductionAroundTiles(uint *produced, uint tile, int w, int h)
|
|
358 |
{
|
|
359 |
int x,y;
|
|
360 |
int x1,y1,x2,y2;
|
|
361 |
int xc,yc;
|
|
362 |
byte cargos[2];
|
|
363 |
|
|
364 |
memset(produced, 0, NUM_CARGO * sizeof(uint));
|
|
365 |
|
|
366 |
x = GET_TILE_X(tile);
|
|
367 |
y = GET_TILE_Y(tile);
|
|
368 |
|
|
369 |
// expand the region by 4 tiles on each side
|
|
370 |
// while making sure that we remain inside the board.
|
|
371 |
x2 = min(x + w+4, TILE_X_MAX+1);
|
|
372 |
x1 = max(x-4, 0);
|
|
373 |
|
|
374 |
y2 = min(y + h+4, TILE_Y_MAX+1);
|
|
375 |
y1 = max(y-4, 0);
|
|
376 |
|
|
377 |
assert(x1 < x2);
|
|
378 |
assert(y1 < y2);
|
|
379 |
assert(w > 0);
|
|
380 |
assert(h > 0);
|
|
381 |
|
|
382 |
yc = y1;
|
|
383 |
do {
|
|
384 |
xc = x1;
|
|
385 |
do {
|
|
386 |
if (!(IS_INSIDE_1D(xc, x, w) && IS_INSIDE_1D(yc, y, h))) {
|
|
387 |
GetProducedCargoProc *gpc;
|
|
388 |
uint tile = TILE_XY(xc, yc);
|
|
389 |
gpc = _tile_type_procs[GET_TILETYPE(tile)]->get_produced_cargo_proc;
|
|
390 |
if (gpc != NULL) {
|
|
391 |
cargos[0] = cargos[1] = 0xFF;
|
|
392 |
gpc(tile, cargos);
|
|
393 |
if (cargos[0] != 0xFF) {
|
|
394 |
produced[cargos[0]]++;
|
|
395 |
if (cargos[1] != 0xFF) {
|
|
396 |
produced[cargos[1]]++;
|
|
397 |
}
|
|
398 |
}
|
|
399 |
}
|
|
400 |
}
|
|
401 |
} while (++xc != x2);
|
|
402 |
} while (++yc != y2);
|
|
403 |
}
|
|
404 |
|
|
405 |
// Get a list of the cargo types that are accepted around the tile.
|
|
406 |
void GetAcceptanceAroundTiles(uint *accepts, uint tile, int w, int h)
|
|
407 |
{
|
|
408 |
int x,y;
|
|
409 |
int x1,y1,x2,y2;
|
|
410 |
int xc,yc;
|
|
411 |
AcceptedCargo ac;
|
|
412 |
|
|
413 |
memset(accepts, 0, NUM_CARGO * sizeof(uint));
|
|
414 |
|
|
415 |
x = GET_TILE_X(tile);
|
|
416 |
y = GET_TILE_Y(tile);
|
|
417 |
|
|
418 |
// expand the region by 4 tiles on each side
|
|
419 |
// while making sure that we remain inside the board.
|
|
420 |
x2 = min(x + w + 4, TILE_X_MAX+1);
|
|
421 |
y2 = min(y + h + 4, TILE_Y_MAX+1);
|
|
422 |
x1 = max(x-4, 0);
|
|
423 |
y1 = max(y-4, 0);
|
|
424 |
|
|
425 |
assert(x1 < x2);
|
|
426 |
assert(y1 < y2);
|
|
427 |
assert(w > 0);
|
|
428 |
assert(h > 0);
|
|
429 |
|
|
430 |
yc = y1;
|
|
431 |
do {
|
|
432 |
xc = x1;
|
|
433 |
do {
|
|
434 |
uint tile = TILE_XY(xc, yc);
|
|
435 |
if (!IS_TILETYPE(tile, MP_STATION)) {
|
|
436 |
GetAcceptedCargo(tile, &ac);
|
|
437 |
accepts[ac.type_1] += ac.amount_1;
|
|
438 |
accepts[ac.type_2] += ac.amount_2;
|
|
439 |
accepts[ac.type_3] += ac.amount_3;
|
|
440 |
}
|
|
441 |
} while (++xc != x2);
|
|
442 |
} while (++yc != y2);
|
|
443 |
}
|
|
444 |
|
|
445 |
// Update the acceptance for a station.
|
|
446 |
// show_msg controls whether to display a message that acceptance was changed.
|
|
447 |
static void UpdateStationAcceptance(Station *st, bool show_msg)
|
|
448 |
{
|
|
449 |
uint old_acc, new_acc;
|
|
450 |
TileIndex span[1+1+2+2+1];
|
|
451 |
int i;
|
|
452 |
int min_x, min_y, max_x, max_y;
|
|
453 |
uint accepts[NUM_CARGO];
|
|
454 |
|
|
455 |
// Don't update acceptance for a buoy
|
|
456 |
if (st->had_vehicle_of_type & HVOT_BUOY)
|
|
457 |
return;
|
|
458 |
|
|
459 |
/* old accepted goods types */
|
|
460 |
old_acc = GetAcceptanceMask(st);
|
|
461 |
|
|
462 |
// Put all the tiles that span an area in the table.
|
|
463 |
span[3] = span[5] = 0;
|
|
464 |
span[0] = st->bus_tile;
|
|
465 |
span[1] = st->lorry_tile;
|
|
466 |
span[2] = st->train_tile;
|
|
467 |
if (st->train_tile != 0) {
|
|
468 |
span[3] = st->train_tile + TILE_XY(st->trainst_w-1, st->trainst_h-1);
|
|
469 |
}
|
|
470 |
span[4] = st->airport_tile;
|
|
471 |
if (st->airport_tile != 0) {
|
|
472 |
span[5] = st->airport_tile + TILE_XY(_airport_size_x[st->airport_type]-1, _airport_size_y[st->airport_type]-1);
|
|
473 |
}
|
|
474 |
span[6] = st->dock_tile;
|
|
475 |
|
|
476 |
// Construct a rectangle from those points
|
|
477 |
min_x = min_y = 0x7FFFFFFF;
|
|
478 |
max_x = max_y = 0;
|
|
479 |
|
|
480 |
for(i=0; i!=7; i++) {
|
|
481 |
uint tile = span[i];
|
|
482 |
if (tile) {
|
|
483 |
min_x = min(min_x,GET_TILE_X(tile));
|
|
484 |
max_x = max(max_x,GET_TILE_X(tile));
|
|
485 |
min_y = min(min_y,GET_TILE_Y(tile));
|
|
486 |
max_y = max(max_y,GET_TILE_Y(tile));
|
|
487 |
}
|
|
488 |
}
|
|
489 |
|
|
490 |
// And retrieve the acceptance.
|
|
491 |
if (max_x != 0) {
|
|
492 |
GetAcceptanceAroundTiles(accepts, TILE_XY(min_x, min_y), max_x - min_x + 1, max_y-min_y+1);
|
|
493 |
} else {
|
|
494 |
memset(accepts, 0, sizeof(accepts));
|
|
495 |
}
|
|
496 |
|
|
497 |
// Adjust in case our station only accepts fewer kinds of goods
|
|
498 |
for(i=0; i!=NUM_CARGO; i++) {
|
|
499 |
uint amt = min(accepts[i], 15);
|
|
500 |
|
|
501 |
// Make sure the station can accept the goods type.
|
|
502 |
if ((i != CT_PASSENGERS && !(st->facilities & (byte)~FACIL_BUS_STOP)) ||
|
|
503 |
(i == CT_PASSENGERS && !(st->facilities & (byte)~FACIL_TRUCK_STOP)))
|
|
504 |
amt = 0;
|
|
505 |
|
|
506 |
st->goods[i].waiting_acceptance = (st->goods[i].waiting_acceptance & ~0xF000) + (amt << 12);
|
|
507 |
}
|
|
508 |
|
|
509 |
// Only show a message in case the acceptance was actually changed.
|
|
510 |
new_acc = GetAcceptanceMask(st);
|
|
511 |
if (old_acc == new_acc)
|
|
512 |
return;
|
|
513 |
|
|
514 |
// show a message to report that the acceptance was changed?
|
|
515 |
if (show_msg && st->owner == _local_player && st->facilities) {
|
|
516 |
uint32 accept=0, reject=0; /* these contain two string ids each */
|
|
517 |
const StringID *str = _cargoc.names_s;
|
|
518 |
|
|
519 |
do {
|
|
520 |
if (new_acc & 1) {
|
|
521 |
if (!(old_acc & 1)) accept = (accept << 16) | *str;
|
|
522 |
} else {
|
|
523 |
if (old_acc & 1) reject = (reject << 16) | *str;
|
|
524 |
}
|
|
525 |
} while (str++,(new_acc>>=1) != (old_acc>>=1));
|
|
526 |
|
|
527 |
ShowRejectOrAcceptNews(st, accept, STR_3040_NOW_ACCEPTS);
|
|
528 |
ShowRejectOrAcceptNews(st, reject, STR_303E_NO_LONGER_ACCEPTS);
|
|
529 |
}
|
|
530 |
|
|
531 |
// redraw the station view since acceptance changed
|
|
532 |
InvalidateWindowWidget(WC_STATION_VIEW, st->index, 4);
|
|
533 |
}
|
|
534 |
|
|
535 |
// This is called right after a station was deleted.
|
|
536 |
// It checks if the whole station is free of substations, and if so, the station will be
|
|
537 |
// deleted after a little while.
|
|
538 |
static void DeleteStationIfEmpty(Station *st) {
|
|
539 |
if (st->facilities == 0) {
|
|
540 |
st->delete_ctr = 0;
|
|
541 |
InvalidateWindow(WC_STATION_LIST, st->owner);
|
|
542 |
}
|
|
543 |
}
|
|
544 |
|
|
545 |
// Tries to clear the given area. Returns the cost in case of success.
|
|
546 |
// Or an error code if it failed.
|
|
547 |
int32 CheckFlatLandBelow(uint tile, uint w, uint h, uint flags, uint invalid_dirs, int *station)
|
|
548 |
{
|
|
549 |
int32 cost = 0, ret;
|
|
550 |
|
|
551 |
uint tileh;
|
|
552 |
int z, allowed_z = -1, flat_z;
|
|
553 |
|
|
554 |
BEGIN_TILE_LOOP(tile_cur, w, h, tile)
|
|
555 |
if (!EnsureNoVehicle(tile_cur))
|
|
556 |
return CMD_ERROR;
|
|
557 |
|
|
558 |
tileh = GetTileSlope(tile_cur, &z);
|
|
559 |
|
|
560 |
// steep slopes are completely prohibited
|
|
561 |
if (tileh & 0x10 || ((_is_ai_player || !_patches.build_on_slopes) && tileh != 0)) {
|
|
562 |
_error_message = STR_0007_FLAT_LAND_REQUIRED;
|
|
563 |
return CMD_ERROR;
|
|
564 |
}
|
|
565 |
|
|
566 |
flat_z = z;
|
|
567 |
if (tileh) {
|
|
568 |
// need to check so the entrance to the station is not pointing at a slope.
|
|
569 |
if ((invalid_dirs&1 && !(tileh & 0xC) && (uint)w_cur == w) ||
|
|
570 |
(invalid_dirs&2 && !(tileh & 6) && h_cur == 1) ||
|
|
571 |
(invalid_dirs&4 && !(tileh & 3) && w_cur == 1) ||
|
|
572 |
(invalid_dirs&8 && !(tileh & 9) && (uint)h_cur == h)) {
|
|
573 |
_error_message = STR_0007_FLAT_LAND_REQUIRED;
|
|
574 |
return CMD_ERROR;
|
|
575 |
}
|
|
576 |
cost += _price.terraform;
|
|
577 |
flat_z += 8;
|
|
578 |
}
|
|
579 |
|
|
580 |
// get corresponding flat level and make sure that all parts of the station have the same level.
|
|
581 |
if (allowed_z == -1) {
|
|
582 |
// first tile
|
|
583 |
allowed_z = flat_z;
|
|
584 |
} else if (allowed_z != flat_z) {
|
|
585 |
_error_message = STR_0007_FLAT_LAND_REQUIRED;
|
|
586 |
return CMD_ERROR;
|
|
587 |
}
|
|
588 |
|
|
589 |
// if station is set, then we have special handling to allow building on top of already existing stations.
|
|
590 |
// so station points to -1 if we can build on any station. or it points to a station if we're only allowed to build
|
|
591 |
// on exactly that station.
|
|
592 |
if (station && IS_TILETYPE(tile_cur, MP_STATION)) {
|
|
593 |
if (_map5[tile] >= 8) {
|
|
594 |
_error_message = STR_0007_FLAT_LAND_REQUIRED;
|
|
595 |
return CMD_ERROR;
|
|
596 |
} else {
|
|
597 |
int st = _map2[tile_cur];
|
|
598 |
if (*station == -1)
|
|
599 |
*station = st;
|
|
600 |
else if (*station != st) {
|
|
601 |
_error_message = STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING;
|
|
602 |
return CMD_ERROR;
|
|
603 |
}
|
|
604 |
}
|
|
605 |
} else {
|
|
606 |
ret = DoCommandByTile(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
|
607 |
if (ret == CMD_ERROR) return CMD_ERROR;
|
|
608 |
cost += ret;
|
|
609 |
}
|
|
610 |
END_TILE_LOOP(tile_cur, w, h, tile)
|
|
611 |
|
|
612 |
return cost;
|
|
613 |
}
|
|
614 |
|
|
615 |
static bool CanExpandRailroadStation(Station *st, uint *fin, int direction)
|
|
616 |
{
|
|
617 |
uint curw = st->trainst_w, curh = st->trainst_h;
|
|
618 |
uint tile = fin[0];
|
|
619 |
uint w = fin[1];
|
|
620 |
uint h = fin[2];
|
|
621 |
|
|
622 |
if (_patches.nonuniform_stations) {
|
|
623 |
// determine new size of train station region..
|
|
624 |
int x = min(GET_TILE_X(st->train_tile), GET_TILE_X(tile));
|
|
625 |
int y = min(GET_TILE_Y(st->train_tile), GET_TILE_Y(tile));
|
|
626 |
curw = max(GET_TILE_X(st->train_tile) + curw, GET_TILE_X(tile) + w) - x;
|
|
627 |
curh = max(GET_TILE_Y(st->train_tile) + curh, GET_TILE_Y(tile) + h) - y;
|
|
628 |
tile = TILE_XY(x,y);
|
|
629 |
} else {
|
|
630 |
// check so the direction is the same
|
|
631 |
if ((_map5[st->train_tile] & 1) != direction) return false;
|
|
632 |
|
|
633 |
// check if the new station adjoins the old station in either direction
|
|
634 |
if (curw == w && st->train_tile == tile + TILE_XY(0, h)) {
|
|
635 |
// above
|
|
636 |
curh += h;
|
|
637 |
} else if (curw == w && st->train_tile == tile - TILE_XY(0, curh)) {
|
|
638 |
// below
|
|
639 |
tile -= TILE_XY(0, curh);
|
|
640 |
curh += h;
|
|
641 |
} else if (curh == h && st->train_tile == tile + TILE_XY(w, 0)) {
|
|
642 |
// to the left
|
|
643 |
curw += w;
|
|
644 |
} else if (curh == h && st->train_tile == tile - TILE_XY(curw, 0)) {
|
|
645 |
// to the right
|
|
646 |
tile -= TILE_XY(curw, 0);
|
|
647 |
curw += w;
|
|
648 |
} else
|
|
649 |
return false;
|
|
650 |
}
|
|
651 |
// make sure the final size is not too big.
|
|
652 |
if (curw > 15 || curh > 15) return false;
|
|
653 |
|
|
654 |
// now tile contains the new value for st->train_tile
|
|
655 |
// curw, curh contain the new value for width and height
|
|
656 |
fin[0] = tile;
|
|
657 |
fin[1] = curw;
|
|
658 |
fin[2] = curh;
|
|
659 |
return true;
|
|
660 |
}
|
|
661 |
|
|
662 |
static byte FORCEINLINE *CreateSingle(byte *layout, int n)
|
|
663 |
{
|
|
664 |
int i = n;
|
|
665 |
do *layout++ = 0; while (--i);
|
|
666 |
layout[((n-1) >> 1)-n] = 2;
|
|
667 |
return layout;
|
|
668 |
}
|
|
669 |
|
|
670 |
static byte FORCEINLINE *CreateMulti(byte *layout, int n, byte b)
|
|
671 |
{
|
|
672 |
int i = n;
|
|
673 |
do *layout++ = b; while (--i);
|
|
674 |
if (n > 4) {
|
|
675 |
layout[0-n] = 0;
|
|
676 |
layout[n-1-n] = 0;
|
|
677 |
}
|
|
678 |
return layout;
|
|
679 |
}
|
|
680 |
|
|
681 |
// stolen from TTDPatch
|
|
682 |
static void GetStationLayout(byte *layout, int numtracks, int plat_len)
|
|
683 |
{
|
|
684 |
if (plat_len == 1) {
|
|
685 |
CreateSingle(layout, numtracks);
|
|
686 |
} else {
|
|
687 |
if (numtracks & 1)
|
|
688 |
layout = CreateSingle(layout, plat_len);
|
|
689 |
numtracks>>=1;
|
|
690 |
|
|
691 |
while (--numtracks >= 0) {
|
|
692 |
layout = CreateMulti(layout, plat_len, 4);
|
|
693 |
layout = CreateMulti(layout, plat_len, 6);
|
|
694 |
}
|
|
695 |
}
|
|
696 |
}
|
|
697 |
|
|
698 |
/* build railroad station
|
|
699 |
* p1 & 1 - orientation
|
|
700 |
* (p1 >> 8) & 0xFF - numtracks
|
|
701 |
* (p1 >> 16) & 0xFF - platform length
|
|
702 |
* p2 - railtype
|
|
703 |
*/
|
|
704 |
|
|
705 |
int32 CmdBuildRailroadStation(int x_org, int y_org, uint32 flags, uint32 p1, uint32 p2)
|
|
706 |
{
|
|
707 |
/* unpack params */
|
|
708 |
int w_org,h_org;
|
|
709 |
uint tile_org;
|
|
710 |
int32 cost, ret;
|
|
711 |
Station *st;
|
|
712 |
int est;
|
|
713 |
int plat_len, numtracks;
|
|
714 |
int direction;
|
|
715 |
uint finalvalues[3];
|
|
716 |
|
|
717 |
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
|
718 |
|
|
719 |
tile_org = TILE_FROM_XY(x_org,y_org);
|
|
720 |
|
|
721 |
/* Does the authority allow this? */
|
|
722 |
if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile_org))
|
|
723 |
return CMD_ERROR;
|
|
724 |
|
|
725 |
{
|
|
726 |
/* unpack parameters */
|
|
727 |
direction = p1 & 1;
|
|
728 |
plat_len = (p1 >> 16) & 0xFF;
|
|
729 |
numtracks = (p1 >> 8) & 0xFF;
|
|
730 |
/* w = length, h = num_tracks */
|
|
731 |
if (direction) {
|
|
732 |
h_org = plat_len;
|
|
733 |
w_org = numtracks;
|
|
734 |
} else {
|
|
735 |
w_org = plat_len;
|
|
736 |
h_org = numtracks;
|
|
737 |
}
|
|
738 |
}
|
|
739 |
|
|
740 |
// these values are those that will be stored in train_tile and station_platforms
|
|
741 |
finalvalues[0] = tile_org;
|
|
742 |
finalvalues[1] = w_org;
|
|
743 |
finalvalues[2] = h_org;
|
|
744 |
|
|
745 |
// Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station)
|
|
746 |
est = -1;
|
|
747 |
if ((ret=CheckFlatLandBelow(tile_org, w_org, h_org, flags, 5 << direction, _patches.nonuniform_stations ? &est : NULL)) == CMD_ERROR) return CMD_ERROR;
|
|
748 |
cost = ret + (numtracks * _price.train_station_track + _price.train_station_length) * plat_len;
|
|
749 |
|
|
750 |
// Make sure there are no similar stations around us.
|
|
751 |
st = GetStationAround(tile_org, w_org, h_org, est);
|
|
752 |
if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
|
|
753 |
|
|
754 |
// See if there is a deleted station close to us.
|
|
755 |
if (st == NULL) {
|
|
756 |
st = GetClosestStationFromTile(tile_org, 8, _current_player);
|
|
757 |
if (st != NULL && st->facilities) st = NULL;
|
|
758 |
}
|
|
759 |
|
|
760 |
if (st != NULL) {
|
|
761 |
// Reuse an existing station.
|
|
762 |
if (st->owner != OWNER_NONE && st->owner != _current_player)
|
|
763 |
return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
|
|
764 |
|
|
765 |
if (st->train_tile != 0) {
|
|
766 |
// check if we want to expanding an already existing station? Only human players can do this.
|
|
767 |
if (_is_ai_player || !_patches.join_stations || !CanExpandRailroadStation(st, finalvalues, direction))
|
|
768 |
return_cmd_error(STR_3005_TOO_CLOSE_TO_ANOTHER_RAILROAD);
|
|
769 |
}
|
|
770 |
|
|
771 |
if (!CheckStationSpreadOut(st, tile_org, w_org, h_org))
|
|
772 |
return CMD_ERROR;
|
|
773 |
|
|
774 |
} else {
|
|
775 |
// Create a new station
|
|
776 |
st = AllocateStation();
|
|
777 |
if (st == NULL)
|
|
778 |
return CMD_ERROR;
|
|
779 |
|
|
780 |
st->town = ClosestTownFromTile(tile_org, (uint)-1);
|
|
781 |
if (_current_player < MAX_PLAYERS && flags&DC_EXEC)
|
|
782 |
SETBIT(st->town->have_ratings, _current_player);
|
|
783 |
|
|
784 |
if (!GenerateStationName(st, tile_org, 0))
|
|
785 |
return CMD_ERROR;
|
|
786 |
|
|
787 |
if (flags & DC_EXEC)
|
|
788 |
StationInitialize(st, tile_org);
|
|
789 |
}
|
|
790 |
|
|
791 |
if (flags & DC_EXEC) {
|
|
792 |
int tile_delta;
|
|
793 |
byte *layout_ptr;
|
|
794 |
uint station_index = st->index;
|
|
795 |
|
|
796 |
st->train_tile = finalvalues[0];
|
|
797 |
if (!st->facilities) st->xy = finalvalues[0];
|
|
798 |
st->facilities |= FACIL_TRAIN;
|
|
799 |
st->owner = _current_player;
|
|
800 |
|
|
801 |
st->trainst_w = finalvalues[1];
|
|
802 |
st->trainst_h = finalvalues[2];
|
|
803 |
|
|
804 |
tile_delta = direction ? TILE_XY(0,1) : TILE_XY(1,0);
|
|
805 |
|
|
806 |
layout_ptr = alloca(numtracks * plat_len);
|
|
807 |
GetStationLayout(layout_ptr, numtracks, plat_len);
|
|
808 |
|
|
809 |
do {
|
|
810 |
int tile = tile_org;
|
|
811 |
int w = plat_len;
|
|
812 |
do {
|
|
813 |
|
|
814 |
ModifyTile(tile,
|
|
815 |
MP_SETTYPE(MP_STATION) | MP_MAPOWNER_CURRENT |
|
|
816 |
MP_MAP2 | MP_MAP5 | MP_MAP3LO | MP_MAP3HI_CLEAR,
|
|
817 |
station_index, /* map2 parameter */
|
|
818 |
p2, /* map3lo parameter */
|
|
819 |
(*layout_ptr++) + direction /* map5 parameter */
|
|
820 |
);
|
|
821 |
|
|
822 |
tile += tile_delta;
|
|
823 |
} while (--w);
|
|
824 |
tile_org += tile_delta ^ TILE_XY(1,1);
|
|
825 |
} while (--numtracks);
|
|
826 |
|
|
827 |
UpdateStationVirtCoordDirty(st);
|
|
828 |
UpdateStationAcceptance(st, false);
|
|
829 |
InvalidateWindow(WC_STATION_LIST, st->owner);
|
|
830 |
}
|
|
831 |
|
|
832 |
return cost;
|
|
833 |
}
|
|
834 |
|
|
835 |
static bool TileBelongsToRailStation(Station *st, uint tile)
|
|
836 |
{
|
|
837 |
return IS_TILETYPE(tile, MP_STATION) && _map2[tile] == st->index && _map5[tile] < 8;
|
|
838 |
}
|
|
839 |
|
|
840 |
static void MakeRailwayStationAreaSmaller(Station *st)
|
|
841 |
{
|
|
842 |
uint w = st->trainst_w;
|
|
843 |
uint h = st->trainst_h;
|
|
844 |
uint tile = st->train_tile;
|
|
845 |
uint i;
|
|
846 |
|
|
847 |
restart:
|
|
848 |
|
|
849 |
// too small?
|
|
850 |
if (w != 0 && h != 0) {
|
|
851 |
// check the left side, x = constant, y changes
|
|
852 |
for(i=0; !TileBelongsToRailStation(st, tile + TILE_XY(0,i)) ;)
|
|
853 |
// the left side is unused?
|
|
854 |
if (++i==h) { tile += TILE_XY(1, 0); w--; goto restart; }
|
|
855 |
|
|
856 |
// check the right side, x = constant, y changes
|
|
857 |
for(i=0; !TileBelongsToRailStation(st, tile + TILE_XY(w-1,i)) ;)
|
|
858 |
// the right side is unused?
|
|
859 |
if (++i==h) { w--; goto restart; }
|
|
860 |
|
|
861 |
// check the upper side, y = constant, x changes
|
|
862 |
for(i=0; !TileBelongsToRailStation(st, tile + TILE_XY(i,0)) ;)
|
|
863 |
// the left side is unused?
|
|
864 |
if (++i==w) { tile += TILE_XY(0, 1); h--; goto restart; }
|
|
865 |
|
|
866 |
// check the lower side, y = constant, x changes
|
|
867 |
for(i=0; !TileBelongsToRailStation(st, tile + TILE_XY(i,h-1)) ;)
|
|
868 |
// the left side is unused?
|
|
869 |
if (++i==w) { h--; goto restart; }
|
|
870 |
} else {
|
|
871 |
tile = 0;
|
|
872 |
}
|
|
873 |
|
|
874 |
st->trainst_w = w;
|
|
875 |
st->trainst_h = h;
|
|
876 |
st->train_tile = tile;
|
|
877 |
}
|
|
878 |
|
|
879 |
// remove a single tile from a railroad station
|
|
880 |
int32 CmdRemoveFromRailroadStation(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|
881 |
{
|
|
882 |
uint tile = TILE_FROM_XY(x, y);
|
|
883 |
Station *st;
|
|
884 |
|
|
885 |
// make sure the specified tile belongs to the current player, and that it is a railroad station.
|
|
886 |
if (!IS_TILETYPE(tile, MP_STATION) || _map5[tile] >= 8 || !_patches.nonuniform_stations) return CMD_ERROR;
|
|
887 |
st = DEREF_STATION(_map2[tile]);
|
|
888 |
if (!CheckOwnership(st->owner) || !EnsureNoVehicle(tile)) return CMD_ERROR;
|
|
889 |
|
|
890 |
// if we reached here, it means we can actually delete it. do that.
|
|
891 |
if (flags & DC_EXEC) {
|
|
892 |
DoClearSquare(tile);
|
|
893 |
// now we need to make the "spanned" area of the railway station smaller if we deleted something at the edges.
|
|
894 |
// we also need to adjust train_tile.
|
|
895 |
MakeRailwayStationAreaSmaller(st);
|
|
896 |
|
|
897 |
// if we deleted the whole station, delete the train facility.
|
|
898 |
if (st->train_tile == 0) {
|
|
899 |
st->facilities &= ~FACIL_TRAIN;
|
|
900 |
UpdateStationVirtCoordDirty(st);
|
|
901 |
DeleteStationIfEmpty(st);
|
|
902 |
}
|
|
903 |
}
|
|
904 |
return _price.remove_rail_station;
|
|
905 |
}
|
|
906 |
|
|
907 |
// determine the number of platforms for the station
|
|
908 |
uint GetStationPlatforms(Station *st, uint tile)
|
|
909 |
{
|
|
910 |
uint t;
|
|
911 |
int dir,delta;
|
|
912 |
int len;
|
|
913 |
assert(TileBelongsToRailStation(st, tile));
|
|
914 |
|
|
915 |
len = 0;
|
|
916 |
dir = _map5[tile]&1;
|
|
917 |
delta = dir ? TILE_XY(0,1) : TILE_XY(1,0);
|
|
918 |
|
|
919 |
// find starting tile..
|
|
920 |
t = tile;
|
|
921 |
do { t -= delta; len++; } while (TileBelongsToRailStation(st, t) && (_map5[t]&1) == dir);
|
|
922 |
|
|
923 |
// find ending tile
|
|
924 |
t = tile;
|
|
925 |
do { t += delta; len++; }while (TileBelongsToRailStation(st, t) && (_map5[t]&1) == dir);
|
|
926 |
|
|
927 |
return len - 1;
|
|
928 |
}
|
|
929 |
|
|
930 |
|
|
931 |
static int32 RemoveRailroadStation(Station *st, uint32 flags)
|
|
932 |
{
|
|
933 |
uint tile;
|
|
934 |
int w,h;
|
|
935 |
int32 cost;
|
|
936 |
|
|
937 |
/* Current player owns the station? */
|
|
938 |
if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
|
|
939 |
return CMD_ERROR;
|
|
940 |
|
|
941 |
/* determine width and height of platforms */
|
|
942 |
tile = st->train_tile;
|
|
943 |
w = st->trainst_w;
|
|
944 |
h = st->trainst_h;
|
|
945 |
|
|
946 |
assert(w != 0 && h != 0);
|
|
947 |
|
|
948 |
/* cost is area * constant */
|
|
949 |
cost = w*h*_price.remove_rail_station;
|
|
950 |
|
|
951 |
/* clear all areas of the station */
|
|
952 |
do {
|
|
953 |
int w_bak = w;
|
|
954 |
do {
|
|
955 |
// for nonuniform stations, only remove tiles that are actually train station tiles
|
|
956 |
if (TileBelongsToRailStation(st, tile)) {
|
|
957 |
if (!EnsureNoVehicle(tile))
|
|
958 |
return CMD_ERROR;
|
|
959 |
if (flags & DC_EXEC)
|
|
960 |
DoClearSquare(tile);
|
|
961 |
}
|
|
962 |
tile += TILE_XY(1, 0);
|
|
963 |
} while (--w);
|
|
964 |
w = w_bak;
|
|
965 |
tile = tile + TILE_XY(-w, 1);
|
|
966 |
} while (--h);
|
|
967 |
|
|
968 |
if (flags & DC_EXEC) {
|
|
969 |
st->train_tile = 0;
|
|
970 |
st->facilities &= ~FACIL_TRAIN;
|
|
971 |
|
|
972 |
UpdateStationVirtCoordDirty(st);
|
|
973 |
DeleteStationIfEmpty(st);
|
|
974 |
}
|
|
975 |
|
|
976 |
return cost;
|
|
977 |
}
|
|
978 |
|
|
979 |
int32 DoConvertStationRail(uint tile, uint totype, bool exec)
|
|
980 |
{
|
|
981 |
Station *st;
|
|
982 |
|
|
983 |
st = DEREF_STATION(_map2[tile]);
|
|
984 |
if (!CheckOwnership(st->owner) || !EnsureNoVehicle(tile)) return CMD_ERROR;
|
|
985 |
|
|
986 |
// tile is not a railroad station?
|
|
987 |
if (_map5[tile] >= 8) return CMD_ERROR;
|
|
988 |
|
|
989 |
// tile is already of requested type?
|
|
990 |
if ( (uint)(_map3_lo[tile] & 0xF) == totype) return CMD_ERROR;
|
|
991 |
|
|
992 |
if (exec) {
|
|
993 |
// change type.
|
|
994 |
_map3_lo[tile] = (_map3_lo[tile] & 0xF0) + totype;
|
|
995 |
MarkTileDirtyByTile(tile);
|
|
996 |
}
|
|
997 |
|
|
998 |
return _price.build_rail >> 1;
|
|
999 |
}
|
|
1000 |
|
|
1001 |
/* Build a bus station
|
|
1002 |
* p1 - direction
|
|
1003 |
* p2 - unused
|
|
1004 |
*/
|
|
1005 |
|
|
1006 |
int32 CmdBuildBusStation(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|
1007 |
{
|
|
1008 |
uint tile;
|
|
1009 |
int32 cost;
|
|
1010 |
Station *st;
|
|
1011 |
|
|
1012 |
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
|
1013 |
|
|
1014 |
tile = TILE_FROM_XY(x,y);
|
|
1015 |
|
|
1016 |
if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile))
|
|
1017 |
return CMD_ERROR;
|
|
1018 |
|
|
1019 |
if ((cost=CheckFlatLandBelow(tile, 1, 1, flags, 1 << p1, NULL)) == CMD_ERROR)
|
|
1020 |
return CMD_ERROR;
|
|
1021 |
|
|
1022 |
st = GetStationAround(tile, 1, 1, -1);
|
|
1023 |
if (st == CHECK_STATIONS_ERR)
|
|
1024 |
return CMD_ERROR;
|
|
1025 |
|
|
1026 |
/* Find a station close to us */
|
|
1027 |
if (st == NULL) {
|
|
1028 |
st = GetClosestStationFromTile(tile, 8, _current_player);
|
|
1029 |
if (st!=NULL && st->facilities) st = NULL;
|
|
1030 |
}
|
|
1031 |
|
|
1032 |
if (st != NULL) {
|
|
1033 |
if (st->owner != OWNER_NONE && st->owner != _current_player)
|
|
1034 |
return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
|
|
1035 |
|
|
1036 |
if (!CheckStationSpreadOut(st, tile, 1, 1))
|
|
1037 |
return CMD_ERROR;
|
|
1038 |
|
|
1039 |
if (st->bus_tile != 0)
|
|
1040 |
return_cmd_error(STR_3044_TOO_CLOSE_TO_ANOTHER_BUS);
|
|
1041 |
} else {
|
|
1042 |
Town *t;
|
|
1043 |
|
|
1044 |
st = AllocateStation();
|
|
1045 |
if (st == NULL)
|
|
1046 |
return CMD_ERROR;
|
|
1047 |
|
|
1048 |
st->town = t = ClosestTownFromTile(tile, (uint)-1);
|
|
1049 |
|
|
1050 |
if (_current_player < MAX_PLAYERS && flags&DC_EXEC)
|
|
1051 |
SETBIT(t->have_ratings, _current_player);
|
|
1052 |
|
|
1053 |
st->sign.width_1 = 0;
|
|
1054 |
|
|
1055 |
if (!GenerateStationName(st, tile, 0))
|
|
1056 |
return CMD_ERROR;
|
|
1057 |
|
|
1058 |
if (flags & DC_EXEC)
|
|
1059 |
StationInitialize(st, tile);
|
|
1060 |
}
|
|
1061 |
|
|
1062 |
cost += _price.build_bus_station;
|
|
1063 |
|
|
1064 |
if (flags & DC_EXEC) {
|
|
1065 |
st->bus_tile = tile;
|
|
1066 |
if (!st->facilities) st->xy = tile;
|
|
1067 |
st->facilities |= FACIL_BUS_STOP;
|
|
1068 |
st->bus_stop_status = 3;
|
|
1069 |
st->owner = _current_player;
|
|
1070 |
|
|
1071 |
ModifyTile(tile,
|
|
1072 |
MP_SETTYPE(MP_STATION) | MP_MAPOWNER_CURRENT |
|
|
1073 |
MP_MAP2 | MP_MAP5 | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR,
|
|
1074 |
st->index, /* map2 parameter */
|
|
1075 |
p1 + 0x47 /* map5 parameter */
|
|
1076 |
);
|
|
1077 |
|
|
1078 |
UpdateStationVirtCoordDirty(st);
|
|
1079 |
UpdateStationAcceptance(st, false);
|
|
1080 |
InvalidateWindow(WC_STATION_LIST, st->owner);
|
|
1081 |
}
|
|
1082 |
return cost;
|
|
1083 |
}
|
|
1084 |
|
|
1085 |
// Remove a bus station
|
|
1086 |
static int32 RemoveBusStation(Station *st, uint32 flags)
|
|
1087 |
{
|
|
1088 |
uint tile;
|
|
1089 |
|
|
1090 |
if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
|
|
1091 |
return CMD_ERROR;
|
|
1092 |
|
|
1093 |
tile = st->bus_tile;
|
|
1094 |
|
|
1095 |
if (!EnsureNoVehicle(tile))
|
|
1096 |
return CMD_ERROR;
|
|
1097 |
|
|
1098 |
if (flags & DC_EXEC) {
|
|
1099 |
DoClearSquare(tile);
|
|
1100 |
|
|
1101 |
st->bus_tile = 0;
|
|
1102 |
st->facilities &= ~FACIL_BUS_STOP;
|
|
1103 |
|
|
1104 |
UpdateStationVirtCoordDirty(st);
|
|
1105 |
DeleteStationIfEmpty(st);
|
|
1106 |
}
|
|
1107 |
|
|
1108 |
return _price.remove_bus_station;
|
|
1109 |
}
|
|
1110 |
|
|
1111 |
|
|
1112 |
/* Build a truck station
|
|
1113 |
* p1 - direction
|
|
1114 |
* p2 - unused
|
|
1115 |
*/
|
|
1116 |
int32 CmdBuildTruckStation(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|
1117 |
{
|
|
1118 |
uint tile;
|
|
1119 |
int32 cost = 0;
|
|
1120 |
Station *st;
|
|
1121 |
|
|
1122 |
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
|
1123 |
|
|
1124 |
tile = TILE_FROM_XY(x,y);
|
|
1125 |
|
|
1126 |
if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile))
|
|
1127 |
return CMD_ERROR;
|
|
1128 |
|
|
1129 |
if ((cost=CheckFlatLandBelow(tile, 1, 1, flags, 1 << p1, NULL)) == CMD_ERROR)
|
|
1130 |
return CMD_ERROR;
|
|
1131 |
|
|
1132 |
st = GetStationAround(tile, 1, 1, -1);
|
|
1133 |
if (st == CHECK_STATIONS_ERR)
|
|
1134 |
return CMD_ERROR;
|
|
1135 |
|
|
1136 |
/* Find a station close to us */
|
|
1137 |
if (st == NULL) {
|
|
1138 |
st = GetClosestStationFromTile(tile, 8, _current_player);
|
|
1139 |
if (st!=NULL && st->facilities) st = NULL;
|
|
1140 |
}
|
|
1141 |
|
|
1142 |
if (st != NULL) {
|
|
1143 |
if (st->owner != OWNER_NONE && st->owner != _current_player)
|
|
1144 |
return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
|
|
1145 |
|
|
1146 |
if (!CheckStationSpreadOut(st, tile, 1, 1))
|
|
1147 |
return CMD_ERROR;
|
|
1148 |
|
|
1149 |
if (st->lorry_tile != 0)
|
|
1150 |
return_cmd_error(STR_3045_TOO_CLOSE_TO_ANOTHER_TRUCK);
|
|
1151 |
} else {
|
|
1152 |
Town *t;
|
|
1153 |
|
|
1154 |
st = AllocateStation();
|
|
1155 |
if (st == NULL)
|
|
1156 |
return CMD_ERROR;
|
|
1157 |
|
|
1158 |
st->town = t = ClosestTownFromTile(tile, (uint)-1);
|
|
1159 |
|
|
1160 |
if (_current_player < MAX_PLAYERS && flags&DC_EXEC)
|
|
1161 |
SETBIT(t->have_ratings, _current_player);
|
|
1162 |
|
|
1163 |
st->sign.width_1 = 0;
|
|
1164 |
|
|
1165 |
if (!GenerateStationName(st, tile, 0))
|
|
1166 |
return CMD_ERROR;
|
|
1167 |
|
|
1168 |
if (flags & DC_EXEC)
|
|
1169 |
StationInitialize(st, tile);
|
|
1170 |
}
|
|
1171 |
|
|
1172 |
cost += _price.build_truck_station;
|
|
1173 |
|
|
1174 |
if (flags & DC_EXEC) {
|
|
1175 |
st->lorry_tile = tile;
|
|
1176 |
if (!st->facilities) st->xy = tile;
|
|
1177 |
st->facilities |= FACIL_TRUCK_STOP;
|
|
1178 |
st->truck_stop_status = 3;
|
|
1179 |
st->owner = _current_player;
|
|
1180 |
|
|
1181 |
ModifyTile(tile,
|
|
1182 |
MP_SETTYPE(MP_STATION) | MP_MAPOWNER_CURRENT |
|
|
1183 |
MP_MAP2 | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR | MP_MAP5,
|
|
1184 |
st->index, /* map2 parameter */
|
|
1185 |
p1 + 0x43 /* map5 parameter */
|
|
1186 |
);
|
|
1187 |
|
|
1188 |
UpdateStationVirtCoordDirty(st);
|
|
1189 |
UpdateStationAcceptance(st, false);
|
|
1190 |
InvalidateWindow(WC_STATION_LIST, st->owner);
|
|
1191 |
}
|
|
1192 |
return cost;
|
|
1193 |
}
|
|
1194 |
|
|
1195 |
// Remove a truck station
|
|
1196 |
static int32 RemoveTruckStation(Station *st, uint32 flags)
|
|
1197 |
{
|
|
1198 |
uint tile;
|
|
1199 |
|
|
1200 |
if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
|
|
1201 |
return CMD_ERROR;
|
|
1202 |
|
|
1203 |
tile = st->lorry_tile;
|
|
1204 |
|
|
1205 |
if (!EnsureNoVehicle(tile))
|
|
1206 |
return CMD_ERROR;
|
|
1207 |
|
|
1208 |
if (flags & DC_EXEC) {
|
|
1209 |
DoClearSquare(tile);
|
|
1210 |
|
|
1211 |
st->lorry_tile = 0;
|
|
1212 |
st->facilities &= ~FACIL_TRUCK_STOP;
|
|
1213 |
|
|
1214 |
UpdateStationVirtCoordDirty(st);
|
|
1215 |
DeleteStationIfEmpty(st);
|
|
1216 |
}
|
|
1217 |
|
|
1218 |
return _price.remove_truck_station;
|
|
1219 |
}
|
|
1220 |
|
|
1221 |
// FIXME -- need to move to its corresponding Airport variable
|
|
1222 |
// Country Airfield (small)
|
|
1223 |
static const byte _airport_map5_tiles_country[] = {
|
|
1224 |
54, 53, 52, 65,
|
|
1225 |
58, 57, 56, 55,
|
|
1226 |
64, 63, 63, 62
|
|
1227 |
};
|
|
1228 |
|
|
1229 |
// City Airport (large)
|
|
1230 |
static const byte _airport_map5_tiles_town[] = {
|
|
1231 |
31, 9, 33, 9, 9, 32,
|
|
1232 |
27, 36, 29, 34, 8, 10,
|
|
1233 |
30, 11, 35, 13, 20, 21,
|
|
1234 |
51, 12, 14, 17, 19, 28,
|
|
1235 |
38, 13, 15, 16, 18, 39,
|
|
1236 |
26, 22, 23, 24, 25, 26
|
|
1237 |
};
|
|
1238 |
|
|
1239 |
// Metropolitain Airport (large) - 2 runways
|
|
1240 |
static const byte _airport_map5_tiles_metropolitan[] = {
|
|
1241 |
31, 9, 33, 9, 9, 32,
|
|
1242 |
27, 36, 29, 34, 8, 10,
|
|
1243 |
30, 11, 35, 13, 20, 21,
|
|
1244 |
102, 8, 8, 8, 8, 28,
|
|
1245 |
83, 84, 84, 84, 84, 83,
|
|
1246 |
26, 23, 23, 23, 23, 26
|
|
1247 |
};
|
|
1248 |
|
|
1249 |
// International Airport (large) - 2 runways
|
|
1250 |
static const byte _airport_map5_tiles_international[] = {
|
|
1251 |
88, 89, 89, 89, 89, 89, 88,
|
|
1252 |
51, 8, 8, 8, 8, 8, 32,
|
|
1253 |
30, 8, 11, 27, 11, 8, 10,
|
|
1254 |
32, 8, 11, 27, 11, 8, 114,
|
|
1255 |
87, 8, 11, 85, 11, 8, 114,
|
|
1256 |
87, 8, 8, 8, 8, 8, 90,
|
|
1257 |
26, 23, 23, 23, 23, 23, 26
|
|
1258 |
};
|
|
1259 |
|
|
1260 |
// Heliport
|
|
1261 |
static const byte _airport_map5_tiles_heliport[] = {
|
|
1262 |
66,
|
|
1263 |
};
|
|
1264 |
|
|
1265 |
static const byte * const _airport_map5_tiles[] = {
|
|
1266 |
_airport_map5_tiles_country, // Country Airfield (small)
|
|
1267 |
_airport_map5_tiles_town, // City Airport (large)
|
|
1268 |
_airport_map5_tiles_heliport, // Heliport
|
|
1269 |
_airport_map5_tiles_metropolitan, // Metropolitain Airport (large)
|
|
1270 |
_airport_map5_tiles_international, // International Airport (xlarge)
|
|
1271 |
};
|
|
1272 |
|
|
1273 |
/* Place an Airport
|
|
1274 |
* p1 - airport type
|
|
1275 |
* p2 - unused
|
|
1276 |
*/
|
|
1277 |
int32 CmdBuildAirport(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|
1278 |
{
|
|
1279 |
uint tile;
|
|
1280 |
Town *t;
|
|
1281 |
Station *st;
|
|
1282 |
int32 cost;
|
|
1283 |
int w,h;
|
|
1284 |
bool airport_upgrade = true;
|
|
1285 |
|
|
1286 |
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
|
1287 |
|
|
1288 |
tile = TILE_FROM_XY(x,y);
|
|
1289 |
|
|
1290 |
if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile))
|
|
1291 |
return CMD_ERROR;
|
|
1292 |
|
|
1293 |
t = ClosestTownFromTile(tile, (uint)-1);
|
|
1294 |
|
|
1295 |
/* Check if local auth refuses a new airport */
|
|
1296 |
{
|
|
1297 |
uint num = 0;
|
|
1298 |
FOR_ALL_STATIONS(st) {
|
|
1299 |
if (st->xy != 0 && st->town == t && st->facilities&FACIL_AIRPORT && st->airport_type != AT_OILRIG)
|
|
1300 |
num++;
|
|
1301 |
}
|
|
1302 |
if (num >= 2) {
|
|
1303 |
SET_DPARAM16(0, t->index);
|
|
1304 |
return_cmd_error(STR_2035_LOCAL_AUTHORITY_REFUSES);
|
|
1305 |
}
|
|
1306 |
}
|
|
1307 |
|
|
1308 |
w = _airport_size_x[p1];
|
|
1309 |
h = _airport_size_y[p1];
|
|
1310 |
|
|
1311 |
cost = CheckFlatLandBelow(tile, w, h, flags, 0, NULL);
|
|
1312 |
if (cost == CMD_ERROR)
|
|
1313 |
return CMD_ERROR;
|
|
1314 |
|
|
1315 |
st = GetStationAround(tile, w, h, -1);
|
|
1316 |
if (st == CHECK_STATIONS_ERR)
|
|
1317 |
return CMD_ERROR;
|
|
1318 |
|
|
1319 |
/* Find a station close to us */
|
|
1320 |
if (st == NULL) {
|
|
1321 |
st = GetClosestStationFromTile(tile, 8, _current_player);
|
|
1322 |
if (st!=NULL && st->facilities) st = NULL;
|
|
1323 |
}
|
|
1324 |
|
|
1325 |
if (st != NULL) {
|
|
1326 |
if (st->owner != OWNER_NONE && st->owner != _current_player)
|
|
1327 |
return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
|
|
1328 |
|
|
1329 |
if (!CheckStationSpreadOut(st, tile, 1, 1))
|
|
1330 |
return CMD_ERROR;
|
|
1331 |
|
|
1332 |
if (st->airport_tile != 0)
|
|
1333 |
return_cmd_error(STR_300D_TOO_CLOSE_TO_ANOTHER_AIRPORT);
|
|
1334 |
} else {
|
|
1335 |
Town *t;
|
|
1336 |
|
|
1337 |
airport_upgrade = false;
|
|
1338 |
|
|
1339 |
st = AllocateStation();
|
|
1340 |
if (st == NULL)
|
|
1341 |
return CMD_ERROR;
|
|
1342 |
|
|
1343 |
st->town = t = ClosestTownFromTile(tile, (uint)-1);
|
|
1344 |
|
|
1345 |
if (_current_player < MAX_PLAYERS && flags&DC_EXEC)
|
|
1346 |
SETBIT(t->have_ratings, _current_player);
|
|
1347 |
|
|
1348 |
st->sign.width_1 = 0;
|
|
1349 |
|
|
1350 |
// if airport type equals Heliport then generate
|
|
1351 |
// type 5 name, which is heliport, otherwise airport names (1)
|
|
1352 |
if (!GenerateStationName(st, tile, p1 == AT_HELIPORT ? 5 : 1))
|
|
1353 |
return CMD_ERROR;
|
|
1354 |
|
|
1355 |
if (flags & DC_EXEC)
|
|
1356 |
StationInitialize(st, tile);
|
|
1357 |
}
|
|
1358 |
|
|
1359 |
cost += _price.build_airport * w * h;
|
|
1360 |
|
|
1361 |
if (flags & DC_EXEC) {
|
|
1362 |
st->owner = _current_player;
|
|
1363 |
if (_current_player == _local_player && p1 <= AT_INTERNATIONAL) {
|
|
1364 |
_last_built_aircraft_depot_tile = tile + GetAirport(p1)->airport_depots[0];
|
|
1365 |
}
|
|
1366 |
|
|
1367 |
st->airport_tile = tile;
|
|
1368 |
if (!st->facilities) st->xy = tile;
|
|
1369 |
st->facilities |= FACIL_AIRPORT;
|
|
1370 |
st->airport_type = (byte)p1;
|
|
1371 |
st->airport_flags = 0;
|
|
1372 |
|
|
1373 |
/* if airport was demolished while planes were en-route to it, the positions can no longer
|
|
1374 |
be the same (v->u.air.pos), since different airports have different indexes. So update
|
|
1375 |
all planes en-route to this airport. Only update if
|
|
1376 |
1. airport is upgraded
|
|
1377 |
2. airport is added to existing station (unfortunately unavoideable)
|
|
1378 |
*/
|
|
1379 |
if (airport_upgrade) {UpdateAirplanesOnNewStation(st);}
|
|
1380 |
|
|
1381 |
{
|
|
1382 |
const byte *b = _airport_map5_tiles[p1];
|
|
1383 |
BEGIN_TILE_LOOP(tile_cur,w,h,tile)
|
|
1384 |
ModifyTile(tile_cur,
|
|
1385 |
MP_SETTYPE(MP_STATION) | MP_MAPOWNER_CURRENT |
|
|
1386 |
MP_MAP2 | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR | MP_MAP5,
|
|
1387 |
st->index, *b++);
|
|
1388 |
END_TILE_LOOP(tile_cur,w,h,tile)
|
|
1389 |
}
|
|
1390 |
|
|
1391 |
UpdateStationVirtCoordDirty(st);
|
|
1392 |
UpdateStationAcceptance(st, false);
|
|
1393 |
InvalidateWindow(WC_STATION_LIST, st->owner);
|
|
1394 |
}
|
|
1395 |
|
|
1396 |
return cost;
|
|
1397 |
}
|
|
1398 |
|
|
1399 |
static int32 RemoveAirport(Station *st, uint32 flags)
|
|
1400 |
{
|
|
1401 |
uint tile;
|
|
1402 |
int w,h;
|
|
1403 |
int32 cost;
|
|
1404 |
|
|
1405 |
if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
|
|
1406 |
return CMD_ERROR;
|
|
1407 |
|
|
1408 |
tile = st->airport_tile;
|
|
1409 |
|
|
1410 |
w = _airport_size_x[st->airport_type];
|
|
1411 |
h = _airport_size_y[st->airport_type];
|
|
1412 |
|
|
1413 |
cost = w * h * _price.remove_airport;
|
|
1414 |
|
|
1415 |
{
|
|
1416 |
BEGIN_TILE_LOOP(tile_cur,w,h,tile)
|
|
1417 |
if (!EnsureNoVehicle(tile_cur))
|
|
1418 |
return CMD_ERROR;
|
|
1419 |
|
|
1420 |
if (flags & DC_EXEC) {
|
|
1421 |
DeleteAnimatedTile(tile_cur);
|
|
1422 |
DoClearSquare(tile_cur);
|
|
1423 |
}
|
|
1424 |
END_TILE_LOOP(tile_cur, w,h,tile)
|
|
1425 |
}
|
|
1426 |
|
|
1427 |
if (flags & DC_EXEC) {
|
|
1428 |
if (st->airport_type <= AT_INTERNATIONAL)
|
|
1429 |
DeleteWindowById(WC_VEHICLE_DEPOT, tile + GetAirport(st->airport_type)->airport_depots[0]);
|
|
1430 |
st->airport_tile = 0;
|
|
1431 |
st->facilities &= ~FACIL_AIRPORT;
|
|
1432 |
|
|
1433 |
UpdateStationVirtCoordDirty(st);
|
|
1434 |
DeleteStationIfEmpty(st);
|
|
1435 |
}
|
|
1436 |
|
|
1437 |
return cost;
|
|
1438 |
}
|
|
1439 |
|
|
1440 |
/* Build a buoy
|
|
1441 |
* p1,p2 unused
|
|
1442 |
*/
|
|
1443 |
|
|
1444 |
int32 CmdBuildBuoy(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|
1445 |
{
|
|
1446 |
TileInfo ti;
|
|
1447 |
Station *st;
|
|
1448 |
|
|
1449 |
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
|
1450 |
|
|
1451 |
FindLandscapeHeight(&ti, x, y);
|
|
1452 |
|
|
1453 |
if (ti.type != MP_WATER || ti.tileh != 0 || ti.map5 != 0 || ti.tile == 0)
|
|
1454 |
return_cmd_error(STR_304B_SITE_UNSUITABLE);
|
|
1455 |
|
|
1456 |
st = AllocateStation();
|
|
1457 |
if (st == NULL)
|
|
1458 |
return CMD_ERROR;
|
|
1459 |
|
|
1460 |
st->town = ClosestTownFromTile(ti.tile, (uint)-1);
|
|
1461 |
st->sign.width_1 = 0;
|
|
1462 |
|
|
1463 |
if (!GenerateStationName(st, ti.tile, 4))
|
|
1464 |
return CMD_ERROR;
|
|
1465 |
|
|
1466 |
if (flags & DC_EXEC) {
|
|
1467 |
StationInitialize(st, ti.tile);
|
|
1468 |
st->dock_tile = ti.tile;
|
|
1469 |
st->facilities |= FACIL_DOCK;
|
|
1470 |
st->had_vehicle_of_type |= HVOT_BUOY;
|
|
1471 |
st->owner = OWNER_NONE;
|
|
1472 |
|
|
1473 |
ModifyTile(ti.tile,
|
|
1474 |
MP_SETTYPE(MP_STATION) |
|
|
1475 |
MP_MAP2 | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR | MP_MAPOWNER | MP_MAP5,
|
|
1476 |
st->index, /* map2 */
|
|
1477 |
OWNER_NONE, /* map_owner */
|
|
1478 |
0x52 /* map5 */
|
|
1479 |
);
|
|
1480 |
|
|
1481 |
UpdateStationVirtCoordDirty(st);
|
|
1482 |
|
|
1483 |
UpdateStationAcceptance(st, false);
|
|
1484 |
InvalidateWindow(WC_STATION_LIST, st->owner);
|
|
1485 |
}
|
|
1486 |
|
|
1487 |
return _price.build_dock;
|
|
1488 |
}
|
|
1489 |
|
|
1490 |
static int32 RemoveBuoy(Station *st, uint32 flags)
|
|
1491 |
{
|
|
1492 |
uint tile;
|
|
1493 |
|
|
1494 |
if (_current_player >= MAX_PLAYERS) {
|
|
1495 |
/* XXX: strange stuff */
|
|
1496 |
return_cmd_error(INVALID_STRING_ID);
|
|
1497 |
}
|
|
1498 |
|
|
1499 |
tile = st->dock_tile;
|
|
1500 |
|
|
1501 |
if (!EnsureNoVehicle(tile))
|
|
1502 |
return CMD_ERROR;
|
|
1503 |
|
|
1504 |
if (flags & DC_EXEC) {
|
|
1505 |
st->dock_tile = 0;
|
|
1506 |
st->facilities &= ~FACIL_DOCK;
|
|
1507 |
st->had_vehicle_of_type &= ~HVOT_BUOY;
|
|
1508 |
|
|
1509 |
ModifyTile(tile,
|
|
1510 |
MP_SETTYPE(MP_WATER) |
|
|
1511 |
MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR | MP_MAPOWNER | MP_MAP5 | MP_MAP2_CLEAR,
|
|
1512 |
OWNER_WATER, /* map_owner */
|
|
1513 |
0 /* map5 */
|
|
1514 |
);
|
|
1515 |
|
|
1516 |
UpdateStationVirtCoordDirty(st);
|
|
1517 |
DeleteStationIfEmpty(st);
|
|
1518 |
}
|
|
1519 |
|
|
1520 |
return _price.remove_truck_station;
|
|
1521 |
}
|
|
1522 |
|
|
1523 |
static const TileIndexDiff _dock_tileoffs_chkaround[4] = {
|
|
1524 |
TILE_XY(-1,0),
|
|
1525 |
0,0,
|
|
1526 |
TILE_XY(0,-1),
|
|
1527 |
};
|
|
1528 |
static const byte _dock_w_chk[4] = { 2,1,2,1 };
|
|
1529 |
static const byte _dock_h_chk[4] = { 1,2,1,2 };
|
|
1530 |
|
|
1531 |
int32 CmdBuildDock(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|
1532 |
{
|
|
1533 |
TileInfo ti;
|
|
1534 |
int direction;
|
|
1535 |
int32 cost;
|
|
1536 |
uint tile, tile_cur;
|
|
1537 |
Station *st;
|
|
1538 |
|
|
1539 |
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
|
1540 |
|
|
1541 |
FindLandscapeHeight(&ti, x, y);
|
|
1542 |
|
|
1543 |
if ((direction=0,ti.tileh) != 3 &&
|
|
1544 |
(direction++,ti.tileh) != 9 &&
|
|
1545 |
(direction++,ti.tileh) != 12 &&
|
|
1546 |
(direction++,ti.tileh) != 6)
|
|
1547 |
return_cmd_error(STR_304B_SITE_UNSUITABLE);
|
|
1548 |
|
|
1549 |
if (!EnsureNoVehicle(ti.tile))
|
|
1550 |
return CMD_ERROR;
|
|
1551 |
|
|
1552 |
cost = DoCommandByTile(ti.tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
|
1553 |
if (cost == CMD_ERROR)
|
|
1554 |
return CMD_ERROR;
|
|
1555 |
|
|
1556 |
tile_cur = (tile=ti.tile) + _tileoffs_by_dir[direction];
|
|
1557 |
|
|
1558 |
if (!EnsureNoVehicle(tile_cur))
|
|
1559 |
return CMD_ERROR;
|
|
1560 |
|
|
1561 |
FindLandscapeHeightByTile(&ti, tile_cur);
|
|
1562 |
if (ti.tileh != 0 || ti.type != MP_WATER)
|
|
1563 |
return_cmd_error(STR_304B_SITE_UNSUITABLE);
|
|
1564 |
|
|
1565 |
cost = DoCommandByTile(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
|
1566 |
if (cost == CMD_ERROR)
|
|
1567 |
return CMD_ERROR;
|
|
1568 |
|
|
1569 |
tile_cur = tile_cur + _tileoffs_by_dir[direction];
|
|
1570 |
FindLandscapeHeightByTile(&ti, tile_cur);
|
|
1571 |
if (ti.tileh != 0 || ti.type != MP_WATER)
|
|
1572 |
return_cmd_error(STR_304B_SITE_UNSUITABLE);
|
|
1573 |
|
|
1574 |
/* middle */
|
|
1575 |
st = GetStationAround(tile + _dock_tileoffs_chkaround[direction],
|
|
1576 |
_dock_w_chk[direction], _dock_h_chk[direction], -1);
|
|
1577 |
if (st == CHECK_STATIONS_ERR)
|
|
1578 |
return CMD_ERROR;
|
|
1579 |
|
|
1580 |
/* Find a station close to us */
|
|
1581 |
if (st == NULL) {
|
|
1582 |
st = GetClosestStationFromTile(tile, 8, _current_player);
|
|
1583 |
if (st!=NULL && st->facilities) st = NULL;
|
|
1584 |
}
|
|
1585 |
|
|
1586 |
if (st != NULL) {
|
|
1587 |
if (st->owner != OWNER_NONE && st->owner != _current_player)
|
|
1588 |
return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
|
|
1589 |
|
|
1590 |
if (!CheckStationSpreadOut(st, tile, 1, 1))
|
|
1591 |
return CMD_ERROR;
|
|
1592 |
|
|
1593 |
if (st->dock_tile != 0)
|
|
1594 |
return_cmd_error(STR_304C_TOO_CLOSE_TO_ANOTHER_DOCK);
|
|
1595 |
} else {
|
|
1596 |
Town *t;
|
|
1597 |
|
|
1598 |
st = AllocateStation();
|
|
1599 |
if (st == NULL)
|
|
1600 |
return CMD_ERROR;
|
|
1601 |
|
|
1602 |
st->town = t = ClosestTownFromTile(tile, (uint)-1);
|
|
1603 |
|
|
1604 |
if (_current_player < MAX_PLAYERS && flags&DC_EXEC)
|
|
1605 |
SETBIT(t->have_ratings, _current_player);
|
|
1606 |
|
|
1607 |
st->sign.width_1 = 0;
|
|
1608 |
|
|
1609 |
if (!GenerateStationName(st, tile, 3))
|
|
1610 |
return CMD_ERROR;
|
|
1611 |
|
|
1612 |
if (flags & DC_EXEC)
|
|
1613 |
StationInitialize(st, tile);
|
|
1614 |
}
|
|
1615 |
|
|
1616 |
if (flags & DC_EXEC) {
|
|
1617 |
st->dock_tile = tile;
|
|
1618 |
if (!st->facilities) st->xy = tile;
|
|
1619 |
st->facilities |= FACIL_DOCK;
|
|
1620 |
st->owner = _current_player;
|
|
1621 |
|
|
1622 |
ModifyTile(tile,
|
|
1623 |
MP_SETTYPE(MP_STATION) | MP_MAPOWNER_CURRENT |
|
|
1624 |
MP_MAP2 | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR |
|
|
1625 |
MP_MAP5,
|
|
1626 |
st->index,
|
|
1627 |
direction + 0x4C);
|
|
1628 |
|
|
1629 |
ModifyTile(tile + _tileoffs_by_dir[direction],
|
|
1630 |
MP_SETTYPE(MP_STATION) | MP_MAPOWNER_CURRENT |
|
|
1631 |
MP_MAP2 | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR |
|
|
1632 |
MP_MAP5,
|
|
1633 |
st->index,
|
|
1634 |
(direction&1) + 0x50);
|
|
1635 |
|
|
1636 |
UpdateStationVirtCoordDirty(st);
|
|
1637 |
UpdateStationAcceptance(st, false);
|
|
1638 |
InvalidateWindow(WC_STATION_LIST, st->owner);
|
|
1639 |
}
|
|
1640 |
return _price.build_dock;
|
|
1641 |
}
|
|
1642 |
|
|
1643 |
static int32 RemoveDock(Station *st, uint32 flags)
|
|
1644 |
{
|
|
1645 |
uint tile1, tile2;
|
|
1646 |
|
|
1647 |
if (!CheckOwnership(st->owner))
|
|
1648 |
return CMD_ERROR;
|
|
1649 |
|
|
1650 |
tile1 = st->dock_tile;
|
|
1651 |
tile2 = tile1 + _tileoffs_by_dir[_map5[tile1] - 0x4C];
|
|
1652 |
|
|
1653 |
if (!EnsureNoVehicle(tile1))
|
|
1654 |
return CMD_ERROR;
|
|
1655 |
|
|
1656 |
if (!EnsureNoVehicle(tile2))
|
|
1657 |
return CMD_ERROR;
|
|
1658 |
|
|
1659 |
if (flags & DC_EXEC) {
|
|
1660 |
DoClearSquare(tile1);
|
|
1661 |
|
|
1662 |
// convert the water tile to water.
|
|
1663 |
ModifyTile(tile2, MP_SETTYPE(MP_WATER) | MP_MAPOWNER | MP_MAP5 | MP_MAP2_CLEAR | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR, OWNER_WATER, 0);
|
|
1664 |
|
|
1665 |
st->dock_tile = 0;
|
|
1666 |
st->facilities &= ~FACIL_DOCK;
|
|
1667 |
|
|
1668 |
UpdateStationVirtCoordDirty(st);
|
|
1669 |
DeleteStationIfEmpty(st);
|
|
1670 |
}
|
|
1671 |
|
|
1672 |
return _price.remove_dock;
|
|
1673 |
}
|
|
1674 |
|
|
1675 |
#include "table/station_land.h"
|
|
1676 |
|
|
1677 |
|
|
1678 |
typedef struct DrawTileSeqStruct {
|
|
1679 |
int8 delta_x;
|
|
1680 |
int8 delta_y;
|
|
1681 |
int8 delta_z;
|
|
1682 |
byte width,height;
|
|
1683 |
byte unk;
|
|
1684 |
SpriteID image;
|
|
1685 |
} DrawTileSeqStruct;
|
|
1686 |
|
|
1687 |
|
|
1688 |
static void DrawTile_Station(TileInfo *ti)
|
|
1689 |
{
|
|
1690 |
uint32 image_or_modificator;
|
|
1691 |
uint32 base_img, image;
|
|
1692 |
const DrawTileSeqStruct *dtss;
|
|
1693 |
const byte *t;
|
|
1694 |
|
|
1695 |
// station_land array has been increased from 82 elements to 114
|
|
1696 |
// but this is something else. If AI builds station with 114 it looks all weird
|
|
1697 |
base_img = (_map3_lo[ti->tile] & 0xF) * 82;
|
|
1698 |
|
|
1699 |
{
|
|
1700 |
uint owner = _map_owner[ti->tile];
|
|
1701 |
image_or_modificator = 0x315 << 16; /* NOTE: possible bug in ttd here? */
|
|
1702 |
if (owner < MAX_PLAYERS)
|
|
1703 |
image_or_modificator = PLAYER_SPRITE_COLOR(owner);
|
|
1704 |
}
|
|
1705 |
|
|
1706 |
// don't show foundation for docks (docks are between 76 (0x4C) and 81 (0x51))
|
|
1707 |
if (ti->tileh != 0 && (ti->map5 < 0x4C || ti->map5 > 0x51))
|
|
1708 |
DrawFoundation(ti, ti->tileh);
|
|
1709 |
|
|
1710 |
t = _station_display_datas[ti->map5];
|
|
1711 |
|
|
1712 |
image = *(uint32*)t;
|
|
1713 |
t += sizeof(uint32);
|
|
1714 |
if (image & 0x8000)
|
|
1715 |
image |= image_or_modificator;
|
|
1716 |
DrawGroundSprite(image + base_img);
|
|
1717 |
|
|
1718 |
for(dtss = (DrawTileSeqStruct *)t; (byte)dtss->delta_x != 0x80; dtss++) {
|
|
1719 |
if ((byte)dtss->delta_z != 0x80) {
|
|
1720 |
image = dtss->image + base_img;
|
|
1721 |
if (_display_opt & DO_TRANS_BUILDINGS) {
|
|
1722 |
if (image&0x8000) image |= image_or_modificator;
|
|
1723 |
} else {
|
|
1724 |
image = (image & 0x3FFF) | 0x03224000;
|
|
1725 |
}
|
|
1726 |
|
|
1727 |
AddSortableSpriteToDraw(image, ti->x + dtss->delta_x, ti->y + dtss->delta_y, dtss->width, dtss->height, dtss->unk, ti->z + dtss->delta_z);
|
|
1728 |
} else {
|
|
1729 |
image = *(uint32*)&dtss->height + base_img; /* endian ok */
|
|
1730 |
|
|
1731 |
if (_display_opt & DO_TRANS_BUILDINGS) {
|
|
1732 |
if (image&0x8000) image |= image_or_modificator;
|
|
1733 |
} else {
|
|
1734 |
image = (image & 0x3FFF) | 0x03224000;
|
|
1735 |
}
|
|
1736 |
AddChildSpriteScreen(image, dtss->delta_x, dtss->delta_y);
|
|
1737 |
}
|
|
1738 |
}
|
|
1739 |
}
|
|
1740 |
|
|
1741 |
void StationPickerDrawSprite(int x, int y, int railtype, int image)
|
|
1742 |
{
|
|
1743 |
uint32 ormod, img;
|
|
1744 |
const DrawTileSeqStruct *dtss;
|
|
1745 |
const byte *t;
|
|
1746 |
|
|
1747 |
/* baseimage */
|
|
1748 |
railtype *= TRACKTYPE_SPRITE_PITCH;
|
|
1749 |
|
|
1750 |
ormod = PLAYER_SPRITE_COLOR(_local_player);
|
|
1751 |
|
|
1752 |
t = _station_display_datas[image];
|
|
1753 |
|
|
1754 |
img = *(uint32*)t;
|
|
1755 |
t += sizeof(uint32);
|
|
1756 |
if (img & 0x8000)
|
|
1757 |
img |= ormod;
|
|
1758 |
DrawSprite(img, x, y);
|
|
1759 |
|
|
1760 |
for(dtss = (DrawTileSeqStruct *)t; (byte)dtss->delta_x != 0x80; dtss++) {
|
|
1761 |
Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z);
|
|
1762 |
DrawSprite((dtss->image | ormod) + railtype, x + pt.x, y + pt.y);
|
|
1763 |
}
|
|
1764 |
}
|
|
1765 |
|
|
1766 |
static uint GetSlopeZ_Station(TileInfo *ti)
|
|
1767 |
{
|
|
1768 |
uint z = ti->z;
|
|
1769 |
if (ti->tileh != 0)
|
|
1770 |
z += 8;
|
|
1771 |
return z;
|
|
1772 |
}
|
|
1773 |
|
|
1774 |
static void GetAcceptedCargo_Station(uint tile, AcceptedCargo *ac)
|
|
1775 |
{
|
|
1776 |
/* not used */
|
|
1777 |
}
|
|
1778 |
|
|
1779 |
static void GetTileDesc_Station(uint tile, TileDesc *td)
|
|
1780 |
{
|
|
1781 |
byte m5;
|
|
1782 |
StringID str;
|
|
1783 |
|
|
1784 |
td->owner = _map_owner[tile];
|
|
1785 |
|
|
1786 |
m5 = _map5[tile];
|
|
1787 |
(str=STR_305E_RAILROAD_STATION, m5 < 8) ||
|
|
1788 |
(str=STR_305F_AIRCRAFT_HANGAR, m5==32 || m5==45) || // hangars
|
|
1789 |
(str=STR_3060_AIRPORT, m5 < 0x43 || (m5 >= 83 && m5 <= 114)) ||
|
|
1790 |
(str=STR_3061_TRUCK_LOADING_AREA, m5 < 0x47) ||
|
|
1791 |
(str=STR_3062_BUS_STATION, m5 < 0x4B) ||
|
|
1792 |
(str=STR_4807_OIL_RIG, m5 == 0x4B) ||
|
|
1793 |
(str=STR_3063_SHIP_DOCK, m5 != 0x52) ||
|
|
1794 |
(str=STR_3069_BUOY, true);
|
|
1795 |
td->str = str;
|
|
1796 |
}
|
|
1797 |
|
|
1798 |
|
|
1799 |
static const byte _tile_track_status_rail[8] = { 1,2,1,2,1,2,1,2 };
|
|
1800 |
|
|
1801 |
static uint32 GetTileTrackStatus_Station(uint tile, int mode) {
|
|
1802 |
uint i = _map5[tile];
|
|
1803 |
uint j = 0;
|
|
1804 |
|
|
1805 |
if (mode == 0) {
|
|
1806 |
if (i < 8)
|
|
1807 |
j = _tile_track_status_rail[i];
|
|
1808 |
} else if (mode == 2) {
|
|
1809 |
// not needed
|
|
1810 |
} else if (mode == 4) {
|
|
1811 |
// buoy
|
|
1812 |
if (i == 0x52) j = 0x3F;
|
|
1813 |
}
|
|
1814 |
|
|
1815 |
return j + (j << 8);
|
|
1816 |
}
|
|
1817 |
|
|
1818 |
static void TileLoop_Station(uint tile)
|
|
1819 |
{
|
|
1820 |
//FIXME -- GetTileTrackStatus_Station -> animated stationtiles
|
|
1821 |
// hardcoded.....not good
|
|
1822 |
// 0x27 - large big airport (39)
|
|
1823 |
// 0x66 - radar metropolitan airport (102)
|
|
1824 |
// 0x5A - radar international airport (90)
|
|
1825 |
// 0x3A - flag small airport (58)
|
|
1826 |
if (_map5[tile] == 39 || _map5[tile] == 58 || _map5[tile] == 90 || _map5[tile] == 102)
|
|
1827 |
AddAnimatedTile(tile);
|
|
1828 |
|
|
1829 |
// treat a bouy tile as water.
|
|
1830 |
else if (_map5[tile] == 0x52)
|
|
1831 |
TileLoop_Water(tile);
|
|
1832 |
}
|
|
1833 |
|
|
1834 |
|
|
1835 |
static void AnimateTile_Station(uint tile)
|
|
1836 |
{
|
|
1837 |
byte m5 = _map5[tile];
|
|
1838 |
//FIXME -- AnimateTile_Station -> not nice code, lots of things double
|
|
1839 |
// again hardcoded...was a quick hack
|
|
1840 |
|
|
1841 |
// turning radar / windsack on airport
|
|
1842 |
if (m5 >= 39 && m5 <= 50) { // turning radar (39 - 50)
|
|
1843 |
if (_tick_counter & 3)
|
|
1844 |
return;
|
|
1845 |
|
|
1846 |
if (++m5 == 50+1)
|
|
1847 |
m5 = 39;
|
|
1848 |
|
|
1849 |
_map5[tile] = m5;
|
|
1850 |
MarkTileDirtyByTile(tile);
|
|
1851 |
//added - begin
|
|
1852 |
} else if (m5 >= 90 && m5 <= 113) { // turning radar with ground under it (different fences) (90 - 101 | 102 - 113)
|
|
1853 |
if (_tick_counter & 3)
|
|
1854 |
return;
|
|
1855 |
|
|
1856 |
m5++;
|
|
1857 |
|
|
1858 |
if (m5 == 101+1) {m5 = 90;} // radar with fences in south
|
|
1859 |
else if (m5 == 113+1) {m5 = 102;} // radar with fences in north
|
|
1860 |
|
|
1861 |
_map5[tile] = m5;
|
|
1862 |
MarkTileDirtyByTile(tile);
|
|
1863 |
//added - end
|
|
1864 |
} else if (m5 >= 0x3A && m5 <= 0x3D) { // windsack (58 - 61)
|
|
1865 |
if (_tick_counter & 1)
|
|
1866 |
return;
|
|
1867 |
|
|
1868 |
if (++m5 == 0x3D+1)
|
|
1869 |
m5 = 0x3A;
|
|
1870 |
|
|
1871 |
_map5[tile] = m5;
|
|
1872 |
MarkTileDirtyByTile(tile);
|
|
1873 |
}
|
|
1874 |
}
|
|
1875 |
|
|
1876 |
static void ClickTile_Station(uint tile)
|
|
1877 |
{
|
|
1878 |
// 0x20 - hangar large airport (32)
|
|
1879 |
// 0x41 - hangar small airport (65)
|
|
1880 |
if (_map5[tile] == 32 || _map5[tile] == 65) {
|
|
1881 |
ShowAircraftDepotWindow(tile);
|
|
1882 |
} else {
|
|
1883 |
ShowStationViewWindow(_map2[tile]);
|
|
1884 |
}
|
|
1885 |
}
|
|
1886 |
|
|
1887 |
static INLINE bool IsTrainStationTile(uint tile) {
|
|
1888 |
return IS_TILETYPE(tile, MP_STATION) && IS_BYTE_INSIDE(_map5[tile], 0, 8);
|
|
1889 |
}
|
|
1890 |
|
|
1891 |
static const byte _enter_station_speedtable[12] = {
|
|
1892 |
215, 195, 175, 155, 135, 115, 95, 75, 55, 35, 15, 0
|
|
1893 |
};
|
|
1894 |
|
|
1895 |
static uint32 VehicleEnter_Station(Vehicle *v, uint tile, int x, int y)
|
|
1896 |
{
|
|
1897 |
byte station_id;
|
|
1898 |
byte dir;
|
|
1899 |
uint16 spd;
|
|
1900 |
|
|
1901 |
if (v->type == VEH_Train) {
|
|
1902 |
if (IS_BYTE_INSIDE(_map5[tile], 0, 8) && v->subtype == 0 &&
|
|
1903 |
!IsTrainStationTile(tile + _tileoffs_by_dir[v->direction >> 1])) {
|
|
1904 |
|
|
1905 |
station_id = _map2[tile];
|
|
1906 |
if ((!(v->next_order & OF_NON_STOP) && !_patches.new_nonstop) ||
|
|
1907 |
(((v->next_order & OT_MASK) == OT_GOTO_STATION && v->next_order_param == station_id))) {
|
|
1908 |
|
|
1909 |
if (!(_patches.new_nonstop && (v->next_order & OF_NON_STOP)) && v->next_order != OT_LEAVESTATION && v->last_station_visited != station_id) {
|
|
1910 |
x &= 0xF;
|
|
1911 |
y &= 0xF;
|
|
1912 |
|
|
1913 |
dir = v->direction & 6;
|
|
1914 |
if (dir & 2) intswap(x,y);
|
|
1915 |
if (y == 8) {
|
|
1916 |
if (dir != 2 && dir != 4) {
|
|
1917 |
x = (~x)&0xF;
|
|
1918 |
}
|
|
1919 |
if (x == 12) return 2 | (station_id << 8); /* enter station */
|
|
1920 |
if (x < 12) {
|
|
1921 |
v->vehstatus |= VS_TRAIN_SLOWING;
|
|
1922 |
spd = _enter_station_speedtable[x];
|
|
1923 |
if (spd < v->cur_speed)
|
|
1924 |
v->cur_speed = spd;
|
|
1925 |
}
|
|
1926 |
}
|
|
1927 |
}
|
|
1928 |
}
|
|
1929 |
}
|
|
1930 |
} else if (v->type == VEH_Road) {
|
|
1931 |
if (v->u.road.state < 16 && (v->u.road.state&4)==0 && v->u.road.frame==0) {
|
|
1932 |
Station *st = DEREF_STATION(_map2[tile]);
|
|
1933 |
byte m5 = _map5[tile];
|
|
1934 |
byte *b, bb,state;
|
|
1935 |
|
|
1936 |
if (IS_BYTE_INSIDE(m5, 0x43, 0x4B)) {
|
|
1937 |
b = (m5 >= 0x47) ? &st->bus_stop_status : &st->truck_stop_status;
|
|
1938 |
|
|
1939 |
bb = *b;
|
|
1940 |
|
|
1941 |
/* bb bits 1..0 describe current the two parking spots.
|
|
1942 |
0 means occupied, 1 means free. */
|
|
1943 |
|
|
1944 |
// Station busy?
|
|
1945 |
if (bb & 0x80 || (bb&3) == 0)
|
|
1946 |
return 8;
|
|
1947 |
|
|
1948 |
state = v->u.road.state + 32;
|
|
1949 |
if (bb & 1) {
|
|
1950 |
bb &= ~1;
|
|
1951 |
} else {
|
|
1952 |
bb &= ~2;
|
|
1953 |
state += 2;
|
|
1954 |
}
|
|
1955 |
*b = bb;
|
|
1956 |
v->u.road.state = state;
|
|
1957 |
}
|
|
1958 |
}
|
|
1959 |
}
|
|
1960 |
|
|
1961 |
return 0;
|
|
1962 |
}
|
|
1963 |
|
|
1964 |
static void DeleteStation(Station *st)
|
|
1965 |
{
|
|
1966 |
int index;
|
|
1967 |
st->xy = 0;
|
|
1968 |
|
|
1969 |
DeleteName(st->string_id);
|
|
1970 |
MarkStationDirty(st);
|
|
1971 |
_station_sort_dirty = true;
|
|
1972 |
|
|
1973 |
index = st->index;
|
|
1974 |
DeleteWindowById(WC_STATION_VIEW, index);
|
|
1975 |
DeleteCommandFromVehicleSchedule((index << 8) + OT_GOTO_STATION);
|
|
1976 |
DeleteSubsidyWithStation(index);
|
|
1977 |
}
|
|
1978 |
|
|
1979 |
void DeleteAllPlayerStations()
|
|
1980 |
{
|
|
1981 |
Station *st;
|
|
1982 |
|
|
1983 |
FOR_ALL_STATIONS(st) {
|
|
1984 |
if (st->xy && st->owner < MAX_PLAYERS)
|
|
1985 |
DeleteStation(st);
|
|
1986 |
}
|
|
1987 |
}
|
|
1988 |
|
|
1989 |
/* this function is called for one station each tick */
|
|
1990 |
static void StationHandleBigTick(Station *st)
|
|
1991 |
{
|
|
1992 |
UpdateStationAcceptance(st, true);
|
|
1993 |
|
|
1994 |
if (st->facilities == 0) {
|
|
1995 |
if (++st->delete_ctr >= 8)
|
|
1996 |
DeleteStation(st);
|
|
1997 |
}
|
|
1998 |
}
|
|
1999 |
|
|
2000 |
static INLINE void byte_inc_sat(byte *p) { byte b = *p + 1; if (b != 0) *p = b; }
|
|
2001 |
|
|
2002 |
static byte _rating_boost[3] = { 0, 31, 63};
|
|
2003 |
|
|
2004 |
static void UpdateStationRating(Station *st)
|
|
2005 |
{
|
|
2006 |
GoodsEntry *ge;
|
|
2007 |
int rating, index;
|
|
2008 |
int waiting;
|
|
2009 |
bool waiting_changed = false;
|
|
2010 |
|
|
2011 |
byte_inc_sat(&st->time_since_load);
|
|
2012 |
byte_inc_sat(&st->time_since_unload);
|
|
2013 |
|
|
2014 |
ge = st->goods;
|
|
2015 |
do {
|
|
2016 |
if (ge->enroute_from != 0xFF) {
|
|
2017 |
byte_inc_sat(&ge->enroute_time);
|
|
2018 |
byte_inc_sat(&ge->days_since_pickup);
|
|
2019 |
|
|
2020 |
rating = 0;
|
|
2021 |
|
|
2022 |
{
|
|
2023 |
int b = ge->last_speed;
|
|
2024 |
if ((b-=85) >= 0)
|
|
2025 |
rating += b >> 2;
|
|
2026 |
}
|
|
2027 |
|
|
2028 |
{
|
|
2029 |
byte age = ge->last_age;
|
|
2030 |
(age >= 3) ||
|
|
2031 |
(rating += 10, age >= 2) ||
|
|
2032 |
(rating += 10, age >= 1) ||
|
|
2033 |
(rating += 13, true);
|
|
2034 |
}
|
|
2035 |
|
|
2036 |
{
|
|
2037 |
if (!IS_HUMAN_PLAYER(st->owner) && st->owner != OWNER_NONE)
|
|
2038 |
rating += _rating_boost[_opt.diff.competitor_intelligence];
|
|
2039 |
}
|
|
2040 |
|
|
2041 |
if (st->owner < 8 && HASBIT(st->town->statues, st->owner))
|
|
2042 |
rating += 26;
|
|
2043 |
|
|
2044 |
{
|
|
2045 |
byte days = ge->days_since_pickup;
|
|
2046 |
if (st->last_vehicle != INVALID_VEHICLE &&
|
|
2047 |
(&_vehicles[st->last_vehicle])->type == VEH_Ship)
|
|
2048 |
days >>= 2;
|
|
2049 |
(days > 21) ||
|
|
2050 |
(rating += 25, days > 12) ||
|
|
2051 |
(rating += 25, days > 6) ||
|
|
2052 |
(rating += 45, days > 3) ||
|
|
2053 |
(rating += 35, true);
|
|
2054 |
}
|
|
2055 |
|
|
2056 |
{
|
|
2057 |
waiting = ge->waiting_acceptance & 0xFFF;
|
|
2058 |
(rating -= 90, waiting > 1500) ||
|
|
2059 |
(rating += 55, waiting > 1000) ||
|
|
2060 |
(rating += 35, waiting > 600) ||
|
|
2061 |
(rating += 10, waiting > 300) ||
|
|
2062 |
(rating += 20, waiting > 100) ||
|
|
2063 |
(rating += 10, true);
|
|
2064 |
}
|
|
2065 |
|
|
2066 |
{
|
|
2067 |
int or = ge->rating; // old rating
|
|
2068 |
|
|
2069 |
// only modify rating in steps of -2, -1, 0, 1 or 2
|
|
2070 |
ge->rating = rating = or + clamp(clamp(rating, 0, 255) - or, -2, 2);
|
|
2071 |
|
|
2072 |
// if rating is <= 64 and more than 200 items waiting, remove some random amount of goods from the station
|
|
2073 |
if (rating <= 64 && waiting >= 200) {
|
|
2074 |
int dec = Random() & 0x1F;
|
|
2075 |
if (waiting < 400) dec &= 7;
|
|
2076 |
waiting -= dec + 1;
|
|
2077 |
waiting_changed = true;
|
|
2078 |
}
|
|
2079 |
|
|
2080 |
// if rating is <= 127 and there are any items waiting, maybe remove some goods.
|
|
2081 |
if (rating <= 127 && waiting != 0) {
|
|
2082 |
uint32 r = Random();
|
|
2083 |
if ( (uint)rating <= (r & 0x7F) ) {
|
|
2084 |
waiting = max(waiting - ((r >> 8)&3) - 1, 0);
|
|
2085 |
waiting_changed = true;
|
|
2086 |
}
|
|
2087 |
}
|
|
2088 |
|
|
2089 |
if (waiting_changed)
|
|
2090 |
ge->waiting_acceptance = (ge->waiting_acceptance & ~0xFFF) + waiting;
|
|
2091 |
}
|
|
2092 |
}
|
|
2093 |
} while (++ge != endof(st->goods));
|
|
2094 |
|
|
2095 |
index = st->index;
|
|
2096 |
|
|
2097 |
if (waiting_changed)
|
|
2098 |
InvalidateWindow(WC_STATION_VIEW, index);
|
|
2099 |
else
|
|
2100 |
InvalidateWindowWidget(WC_STATION_VIEW, index, 5);
|
|
2101 |
}
|
|
2102 |
|
|
2103 |
/* called for every station each tick */
|
|
2104 |
static void StationHandleSmallTick(Station *st)
|
|
2105 |
{
|
|
2106 |
byte b;
|
|
2107 |
|
|
2108 |
if (st->facilities == 0)
|
|
2109 |
return;
|
|
2110 |
|
|
2111 |
b = st->delete_ctr + 1;
|
|
2112 |
if (b >= 185) b = 0;
|
|
2113 |
st->delete_ctr = b;
|
|
2114 |
|
|
2115 |
if (b == 0)
|
|
2116 |
UpdateStationRating(st);
|
|
2117 |
}
|
|
2118 |
|
|
2119 |
void OnTick_Station()
|
|
2120 |
{
|
|
2121 |
int i;
|
|
2122 |
Station *st;
|
|
2123 |
|
|
2124 |
if (_game_mode == GM_EDITOR)
|
|
2125 |
return;
|
|
2126 |
|
|
2127 |
i = _station_tick_ctr;
|
|
2128 |
if (++_station_tick_ctr == lengthof(_stations))
|
|
2129 |
_station_tick_ctr = 0;
|
|
2130 |
|
|
2131 |
st = DEREF_STATION(i);
|
|
2132 |
if (st->xy != 0)
|
|
2133 |
StationHandleBigTick(st);
|
|
2134 |
|
|
2135 |
FOR_ALL_STATIONS(st) {
|
|
2136 |
if (st->xy != 0)
|
|
2137 |
StationHandleSmallTick(st);
|
|
2138 |
}
|
|
2139 |
|
|
2140 |
}
|
|
2141 |
|
|
2142 |
void StationMonthlyLoop()
|
|
2143 |
{
|
|
2144 |
Station *st;
|
|
2145 |
|
|
2146 |
FOR_ALL_STATIONS(st) {
|
|
2147 |
if (st->blocked_months != 0)
|
|
2148 |
st->blocked_months--;
|
|
2149 |
}
|
|
2150 |
|
|
2151 |
}
|
|
2152 |
|
|
2153 |
|
|
2154 |
void ModifyStationRatingAround(TileIndex tile, byte owner, int amount, uint radius)
|
|
2155 |
{
|
|
2156 |
Station *st;
|
|
2157 |
GoodsEntry *ge;
|
|
2158 |
int i;
|
|
2159 |
|
|
2160 |
FOR_ALL_STATIONS(st) {
|
|
2161 |
if (st->xy != 0 && st->owner == owner && GetTileDist(tile, st->xy) <= radius) {
|
|
2162 |
ge = st->goods;
|
|
2163 |
for(i=0; i!=NUM_CARGO; i++,ge++) {
|
|
2164 |
if (ge->enroute_from != 0xFF) {
|
|
2165 |
ge->rating = clamp(ge->rating + amount, 0, 255);
|
|
2166 |
}
|
|
2167 |
}
|
|
2168 |
}
|
|
2169 |
}
|
|
2170 |
}
|
|
2171 |
|
|
2172 |
static void UpdateStationWaiting(Station *st, int type, uint amount)
|
|
2173 |
{
|
|
2174 |
st->goods[type].waiting_acceptance =
|
|
2175 |
(st->goods[type].waiting_acceptance & ~0xFFF) +
|
|
2176 |
min(0xFFF, (st->goods[type].waiting_acceptance & 0xFFF) + amount);
|
|
2177 |
|
|
2178 |
st->goods[type].enroute_time = 0;
|
|
2179 |
st->goods[type].enroute_from = st->index;
|
|
2180 |
InvalidateWindow(WC_STATION_VIEW, st->index);
|
|
2181 |
}
|
|
2182 |
|
|
2183 |
int32 CmdRenameStation(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|
2184 |
{
|
|
2185 |
StringID str,old_str;
|
|
2186 |
Station *st;
|
|
2187 |
|
|
2188 |
str = AllocateName((byte*)_decode_parameters, 6);
|
|
2189 |
if (str == 0)
|
|
2190 |
return CMD_ERROR;
|
|
2191 |
|
|
2192 |
if (flags & DC_EXEC) {
|
|
2193 |
st = DEREF_STATION(p1);
|
|
2194 |
old_str = st->string_id;
|
|
2195 |
st->string_id = str;
|
|
2196 |
UpdateStationVirtCoord(st);
|
|
2197 |
DeleteName(old_str);
|
|
2198 |
_station_sort_dirty = true;
|
|
2199 |
MarkWholeScreenDirty();
|
|
2200 |
} else {
|
|
2201 |
DeleteName(str);
|
|
2202 |
}
|
|
2203 |
|
|
2204 |
return 0;
|
|
2205 |
}
|
|
2206 |
|
|
2207 |
|
|
2208 |
uint MoveGoodsToStation(uint tile, int w, int h, int type, uint amount)
|
|
2209 |
{
|
|
2210 |
Station *around_ptr[8];
|
|
2211 |
byte around[8], st_index;
|
|
2212 |
int i;
|
|
2213 |
Station *st;
|
|
2214 |
uint moved;
|
|
2215 |
uint best_rating, best_rating2;
|
|
2216 |
Station *st1, *st2;
|
|
2217 |
int t;
|
|
2218 |
|
|
2219 |
memset(around, 0xff, sizeof(around));
|
|
2220 |
|
|
2221 |
w += 8;
|
|
2222 |
h += 8;
|
|
2223 |
|
|
2224 |
BEGIN_TILE_LOOP(cur_tile, w, h, tile - TILE_XY(4,4))
|
|
2225 |
cur_tile = TILE_MASK(cur_tile);
|
|
2226 |
if (IS_TILETYPE(cur_tile, MP_STATION)) {
|
|
2227 |
st_index = _map2[cur_tile];
|
|
2228 |
for(i=0; i!=8; i++) {
|
|
2229 |
if (around[i] == 0xFF) {
|
|
2230 |
st = DEREF_STATION(st_index);
|
|
2231 |
if ((st->had_vehicle_of_type & HVOT_BUOY) == 0 &&
|
|
2232 |
st->blocked_months == 0 &&
|
|
2233 |
st->goods[type].rating != 0 &&
|
|
2234 |
(!_patches.selectgoods || st->goods[type].last_speed) && // if last_speed is 0, no vehicle has been there.
|
|
2235 |
((st->facilities & (byte)~FACIL_BUS_STOP)!=0 || type==CT_PASSENGERS) &&
|
|
2236 |
((st->facilities & (byte)~FACIL_TRUCK_STOP)!=0 || type!=CT_PASSENGERS)) {
|
|
2237 |
|
|
2238 |
around[i] = st_index;
|
|
2239 |
around_ptr[i] = st;
|
|
2240 |
}
|
|
2241 |
break;
|
|
2242 |
} else if (around[i] == st_index)
|
|
2243 |
break;
|
|
2244 |
}
|
|
2245 |
}
|
|
2246 |
END_TILE_LOOP(cur_tile, w, h, tile - TILE_XY(4,4))
|
|
2247 |
|
|
2248 |
/* no stations around at all? */
|
|
2249 |
if (around[0] == 0xFF)
|
|
2250 |
return 0;
|
|
2251 |
|
|
2252 |
if (around[1] == 0xFF) {
|
|
2253 |
/* only one station around */
|
|
2254 |
moved = (amount * around_ptr[0]->goods[type].rating >> 8) + 1;
|
|
2255 |
UpdateStationWaiting(around_ptr[0], type, moved);
|
|
2256 |
return moved;
|
|
2257 |
}
|
|
2258 |
|
|
2259 |
/* several stations around, find the two with the highest rating */
|
|
2260 |
st2 = st1 = NULL;
|
|
2261 |
best_rating = best_rating2 = 0;
|
|
2262 |
for(i=0; i!=8 && around[i] != 0xFF; i++) {
|
|
2263 |
if (around_ptr[i]->goods[type].rating >= best_rating) {
|
|
2264 |
best_rating2 = best_rating;
|
|
2265 |
st2 = st1;
|
|
2266 |
|
|
2267 |
best_rating = around_ptr[i]->goods[type].rating;
|
|
2268 |
st1 = around_ptr[i];
|
|
2269 |
} else if (around_ptr[i]->goods[type].rating >= best_rating2) {
|
|
2270 |
best_rating2 = around_ptr[i]->goods[type].rating;
|
|
2271 |
st2 = around_ptr[i];
|
|
2272 |
}
|
|
2273 |
}
|
|
2274 |
|
|
2275 |
assert(st1 != NULL);
|
|
2276 |
assert(st2 != NULL);
|
|
2277 |
assert(best_rating != 0 || best_rating2 != 0);
|
|
2278 |
|
|
2279 |
/* the 2nd highest one gets a penalty */
|
|
2280 |
best_rating2 >>= 1;
|
|
2281 |
|
|
2282 |
/* amount given to station 1 */
|
|
2283 |
t = (best_rating * (amount + 1)) / (best_rating + best_rating2);
|
|
2284 |
|
|
2285 |
moved = 0;
|
|
2286 |
if (t != 0) {
|
|
2287 |
moved = (t * best_rating >> 8) + 1;
|
|
2288 |
amount -= t;
|
|
2289 |
UpdateStationWaiting(st1, type, moved);
|
|
2290 |
}
|
|
2291 |
|
|
2292 |
assert(amount >= 0);
|
|
2293 |
|
|
2294 |
if (amount != 0) {
|
|
2295 |
moved += (amount = (amount * best_rating2 >> 8) + 1);
|
|
2296 |
UpdateStationWaiting(st2, type, amount);
|
|
2297 |
}
|
|
2298 |
|
|
2299 |
return moved;
|
|
2300 |
}
|
|
2301 |
|
|
2302 |
void BuildOilRig(uint tile)
|
|
2303 |
{
|
|
2304 |
Station *st;
|
|
2305 |
int j;
|
|
2306 |
|
|
2307 |
FOR_ALL_STATIONS(st) {
|
|
2308 |
if (st->xy == 0) {
|
|
2309 |
st->town = ClosestTownFromTile(tile, (uint)-1);
|
|
2310 |
st->sign.width_1 = 0;
|
|
2311 |
if (!GenerateStationName(st, tile, 2))
|
|
2312 |
return;
|
|
2313 |
|
|
2314 |
_map_type_and_height[tile] &= 0xF;
|
|
2315 |
_map_type_and_height[tile] |= MP_STATION << 4;
|
|
2316 |
_map5[tile] = 0x4B;
|
|
2317 |
_map_owner[tile] = OWNER_NONE;
|
|
2318 |
_map3_lo[tile] = 0;
|
|
2319 |
_map3_hi[tile] = 0;
|
|
2320 |
_map2[tile] = st->index;
|
|
2321 |
|
|
2322 |
st->owner = 0x10;
|
|
2323 |
st->airport_flags = 0;
|
|
2324 |
st->airport_type = AT_OILRIG;
|
|
2325 |
st->xy = tile;
|
|
2326 |
st->bus_tile = 0;
|
|
2327 |
st->lorry_tile = 0;
|
|
2328 |
st->airport_tile = tile;
|
|
2329 |
st->dock_tile = tile;
|
|
2330 |
st->train_tile = 0;
|
|
2331 |
st->had_vehicle_of_type = 0;
|
|
2332 |
st->time_since_load = 255;
|
|
2333 |
st->time_since_unload = 255;
|
|
2334 |
st->delete_ctr = 0;
|
|
2335 |
st->last_vehicle = INVALID_VEHICLE;
|
|
2336 |
st->facilities = FACIL_AIRPORT | FACIL_DOCK;
|
|
2337 |
for(j=0; j!=NUM_CARGO; j++) {
|
|
2338 |
st->goods[j].waiting_acceptance = 0;
|
|
2339 |
st->goods[j].days_since_pickup = 0;
|
|
2340 |
st->goods[j].enroute_from = 0xFF;
|
|
2341 |
st->goods[j].rating = 175;
|
|
2342 |
st->goods[j].last_speed = 0;
|
|
2343 |
st->goods[j].last_age = 255;
|
|
2344 |
}
|
|
2345 |
|
|
2346 |
UpdateStationVirtCoordDirty(st);
|
|
2347 |
UpdateStationAcceptance(st, false);
|
|
2348 |
return;
|
|
2349 |
}
|
|
2350 |
}
|
|
2351 |
}
|
|
2352 |
|
|
2353 |
void DeleteOilRig(uint tile)
|
|
2354 |
{
|
|
2355 |
Station *st = DEREF_STATION(_map2[tile]);
|
|
2356 |
|
|
2357 |
DoClearSquare(tile);
|
|
2358 |
|
|
2359 |
st->dock_tile = 0;
|
|
2360 |
st->airport_tile = 0;
|
|
2361 |
st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
|
|
2362 |
st->airport_flags = 0;
|
|
2363 |
UpdateStationVirtCoordDirty(st);
|
|
2364 |
DeleteStation(st);
|
|
2365 |
}
|
|
2366 |
|
|
2367 |
static void ChangeTileOwner_Station(uint tile, byte old_player, byte new_player)
|
|
2368 |
{
|
|
2369 |
if (_map_owner[tile] != old_player)
|
|
2370 |
return;
|
|
2371 |
|
|
2372 |
if (new_player != 255) {
|
|
2373 |
Station *st = DEREF_STATION(_map2[tile]);
|
|
2374 |
_map_owner[tile] = new_player;
|
|
2375 |
st->owner = new_player;
|
|
2376 |
_station_sort_dirty = true;
|
|
2377 |
} else {
|
|
2378 |
DoCommandByTile(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
|
|
2379 |
}
|
|
2380 |
}
|
|
2381 |
|
|
2382 |
static int32 ClearTile_Station(uint tile, byte flags) {
|
|
2383 |
byte m5 = _map5[tile];
|
|
2384 |
Station *st;
|
|
2385 |
|
|
2386 |
if (flags & DC_AUTO) {
|
|
2387 |
if (m5 < 8) return_cmd_error(STR_300B_MUST_DEMOLISH_RAILROAD);
|
|
2388 |
if (m5 < 0x43 || (m5 >= 83 && m5 <= 114)) return_cmd_error(STR_300E_MUST_DEMOLISH_AIRPORT_FIRST);
|
|
2389 |
if (m5 < 0x47) return_cmd_error(STR_3047_MUST_DEMOLISH_TRUCK_STATION);
|
|
2390 |
if (m5 < 0x4B) return_cmd_error(STR_3046_MUST_DEMOLISH_BUS_STATION);
|
|
2391 |
if (m5 == 0x52) return_cmd_error(STR_306A_BUOY_IN_THE_WAY);
|
|
2392 |
if (m5 != 0x4B && m5 < 0x53) return_cmd_error(STR_304D_MUST_DEMOLISH_DOCK_FIRST);
|
|
2393 |
SET_DPARAM16(0, STR_4807_OIL_RIG);
|
|
2394 |
return_cmd_error(STR_4800_IN_THE_WAY);
|
|
2395 |
}
|
|
2396 |
|
|
2397 |
st = DEREF_STATION(_map2[tile]);
|
|
2398 |
|
|
2399 |
if (m5 < 8)
|
|
2400 |
return RemoveRailroadStation(st, flags);
|
|
2401 |
|
|
2402 |
// original airports < 67, new airports between 83 - 114
|
|
2403 |
if (m5 < 0x43 || ( m5 >= 83 && m5 <= 114) )
|
|
2404 |
return RemoveAirport(st, flags);
|
|
2405 |
|
|
2406 |
if (m5 < 0x47)
|
|
2407 |
return RemoveTruckStation(st, flags);
|
|
2408 |
|
|
2409 |
if (m5 < 0x4B)
|
|
2410 |
return RemoveBusStation(st, flags);
|
|
2411 |
|
|
2412 |
if (m5 == 0x52)
|
|
2413 |
return RemoveBuoy(st, flags);
|
|
2414 |
|
|
2415 |
if (m5 != 0x4B && m5 < 0x53)
|
|
2416 |
return RemoveDock(st, flags);
|
|
2417 |
|
|
2418 |
return CMD_ERROR;
|
|
2419 |
|
|
2420 |
}
|
|
2421 |
|
|
2422 |
void InitializeStations()
|
|
2423 |
{
|
|
2424 |
int i;
|
|
2425 |
memset(_stations, 0, sizeof(_stations));
|
|
2426 |
for(i=0;i!=lengthof(_stations); i++)
|
|
2427 |
_stations[i].index=i;
|
|
2428 |
_station_tick_ctr = 0;
|
|
2429 |
_station_sort_dirty = true; // set stations to be sorted on first load
|
|
2430 |
}
|
|
2431 |
|
|
2432 |
|
|
2433 |
const TileTypeProcs _tile_type_station_procs = {
|
|
2434 |
DrawTile_Station, /* draw_tile_proc */
|
|
2435 |
GetSlopeZ_Station, /* get_slope_z_proc */
|
|
2436 |
ClearTile_Station, /* clear_tile_proc */
|
|
2437 |
GetAcceptedCargo_Station, /* get_accepted_cargo_proc */
|
|
2438 |
GetTileDesc_Station, /* get_tile_desc_proc */
|
|
2439 |
GetTileTrackStatus_Station, /* get_tile_track_status_proc */
|
|
2440 |
ClickTile_Station, /* click_tile_proc */
|
|
2441 |
AnimateTile_Station, /* animate_tile_proc */
|
|
2442 |
TileLoop_Station, /* tile_loop_clear */
|
|
2443 |
ChangeTileOwner_Station, /* change_tile_owner_clear */
|
|
2444 |
NULL, /* get_produced_cargo_proc */
|
|
2445 |
VehicleEnter_Station, /* vehicle_enter_tile_proc */
|
|
2446 |
NULL, /* vehicle_leave_tile_proc */
|
|
2447 |
};
|
|
2448 |
|
|
2449 |
|
|
2450 |
static const byte _station_desc[] = {
|
|
2451 |
SLE_VAR(Station,xy, SLE_UINT16),
|
|
2452 |
SLE_VAR(Station,bus_tile, SLE_UINT16),
|
|
2453 |
SLE_VAR(Station,lorry_tile, SLE_UINT16),
|
|
2454 |
SLE_VAR(Station,train_tile, SLE_UINT16),
|
|
2455 |
SLE_VAR(Station,airport_tile, SLE_UINT16),
|
|
2456 |
SLE_VAR(Station,dock_tile, SLE_UINT16),
|
|
2457 |
SLE_REF(Station,town, REF_TOWN),
|
|
2458 |
SLE_VAR(Station,trainst_w, SLE_UINT8),
|
|
2459 |
SLE_CONDVAR(Station,trainst_h, SLE_UINT8, 2, 255),
|
|
2460 |
|
|
2461 |
// alpha_order was stored here in savegame format 0 - 3
|
|
2462 |
SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 1, 0, 3),
|
|
2463 |
|
|
2464 |
SLE_VAR(Station,string_id, SLE_STRINGID),
|
|
2465 |
SLE_VAR(Station,had_vehicle_of_type,SLE_UINT16),
|
|
2466 |
|
|
2467 |
SLE_VAR(Station,time_since_load, SLE_UINT8),
|
|
2468 |
SLE_VAR(Station,time_since_unload, SLE_UINT8),
|
|
2469 |
SLE_VAR(Station,delete_ctr, SLE_UINT8),
|
|
2470 |
SLE_VAR(Station,owner, SLE_UINT8),
|
|
2471 |
SLE_VAR(Station,facilities, SLE_UINT8),
|
|
2472 |
SLE_VAR(Station,airport_type, SLE_UINT8),
|
|
2473 |
SLE_VAR(Station,truck_stop_status, SLE_UINT8),
|
|
2474 |
SLE_VAR(Station,bus_stop_status, SLE_UINT8),
|
|
2475 |
SLE_VAR(Station,blocked_months, SLE_UINT8),
|
|
2476 |
|
|
2477 |
SLE_CONDVAR(Station,airport_flags, SLE_VAR_U32 | SLE_FILE_U16, 0, 2),
|
|
2478 |
SLE_CONDVAR(Station,airport_flags, SLE_UINT32, 3, 255),
|
|
2479 |
|
|
2480 |
SLE_VAR(Station,last_vehicle, SLE_UINT16),
|
|
2481 |
|
|
2482 |
SLE_CONDVAR(Station,class_id, SLE_UINT8, 3, 255),
|
|
2483 |
SLE_CONDVAR(Station,stat_id, SLE_UINT8, 3, 255),
|
|
2484 |
SLE_CONDVAR(Station,build_date, SLE_UINT16, 3, 255),
|
|
2485 |
|
|
2486 |
// reserve extra space in savegame here. (currently 32 bytes)
|
|
2487 |
SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 32, 2, 255),
|
|
2488 |
|
|
2489 |
SLE_END()
|
|
2490 |
};
|
|
2491 |
|
|
2492 |
static const byte _goods_desc[] = {
|
|
2493 |
SLE_VAR(GoodsEntry,waiting_acceptance,SLE_UINT16),
|
|
2494 |
SLE_VAR(GoodsEntry,days_since_pickup, SLE_UINT8),
|
|
2495 |
SLE_VAR(GoodsEntry,rating, SLE_UINT8),
|
|
2496 |
SLE_VAR(GoodsEntry,enroute_from, SLE_UINT8),
|
|
2497 |
SLE_VAR(GoodsEntry,enroute_time, SLE_UINT8),
|
|
2498 |
SLE_VAR(GoodsEntry,last_speed, SLE_UINT8),
|
|
2499 |
SLE_VAR(GoodsEntry,last_age, SLE_UINT8),
|
|
2500 |
|
|
2501 |
SLE_END()
|
|
2502 |
};
|
|
2503 |
|
|
2504 |
|
|
2505 |
static void SaveLoad_STNS(Station *st)
|
|
2506 |
{
|
|
2507 |
int i;
|
|
2508 |
SlObject(st, _station_desc);
|
|
2509 |
for(i=0; i!=NUM_CARGO; i++)
|
|
2510 |
SlObject(&st->goods[i], _goods_desc);
|
|
2511 |
}
|
|
2512 |
|
|
2513 |
static void Save_STNS()
|
|
2514 |
{
|
|
2515 |
Station *st;
|
|
2516 |
// Write the vehicles
|
|
2517 |
FOR_ALL_STATIONS(st) {
|
|
2518 |
if (st->xy != 0) {
|
|
2519 |
SlSetArrayIndex(st->index);
|
|
2520 |
SlAutolength((AutolengthProc*)SaveLoad_STNS, st);
|
|
2521 |
}
|
|
2522 |
}
|
|
2523 |
}
|
|
2524 |
|
|
2525 |
static void Load_STNS()
|
|
2526 |
{
|
|
2527 |
int index;
|
|
2528 |
while ((index = SlIterateArray()) != -1) {
|
|
2529 |
Station *st = &_stations[index];
|
|
2530 |
SaveLoad_STNS(st);
|
|
2531 |
|
|
2532 |
// this means it's an oldstyle savegame without support for nonuniform stations
|
|
2533 |
if (st->train_tile && st->trainst_h == 0) {
|
|
2534 |
int w = st->trainst_w >> 4;
|
|
2535 |
int h = st->trainst_w & 0xF;
|
|
2536 |
if (_map5[st->train_tile]&1) intswap(w,h);
|
|
2537 |
st->trainst_w = w;
|
|
2538 |
st->trainst_h = h;
|
|
2539 |
}
|
|
2540 |
}
|
|
2541 |
}
|
|
2542 |
|
|
2543 |
const ChunkHandler _station_chunk_handlers[] = {
|
|
2544 |
{ 'STNS', Save_STNS, Load_STNS, CH_ARRAY | CH_LAST},
|
|
2545 |
};
|
|
2546 |
|