author | dominik |
Sat, 21 Aug 2004 09:57:02 +0000 | |
changeset 98 | 91d63b83cece |
parent 76 | 30511cbc5188 |
child 103 | 3af99b9373a1 |
permissions | -rw-r--r-- |
0 | 1 |
#include "stdafx.h" |
2 |
#include "ttd.h" |
|
3 |
#include "vehicle.h" |
|
4 |
#include "command.h" |
|
5 |
#include "pathfind.h" |
|
6 |
#include "station.h" |
|
7 |
#include "table/train_cmd.h" |
|
8 |
#include "gfx.h" |
|
9 |
#include "news.h" |
|
10 |
#include "engine.h" |
|
11 |
#include "player.h" |
|
12 |
||
13 |
||
14 |
#define is_firsthead_sprite(spritenum) \ |
|
15 |
(is_custom_sprite(spritenum) \ |
|
16 |
? is_custom_firsthead_sprite(spritenum) \ |
|
17 |
: _engine_sprite_add[spritenum] == 0) |
|
18 |
||
19 |
||
20 |
static const byte _vehicle_initial_x_fract[4] = {10,8,4,8}; |
|
21 |
static const byte _vehicle_initial_y_fract[4] = {8,4,8,10}; |
|
22 |
static const byte _state_dir_table[4] = { 0x20, 8, 0x10, 4 }; |
|
23 |
||
24 |
static const byte _signal_onedir[14] = { |
|
25 |
0x80, 0x80, 0x80, 0x20, 0x40, 0x10, 0, 0, |
|
26 |
0x40, 0x40, 0x40, 0x10, 0x80, 0x20 |
|
27 |
}; |
|
28 |
||
29 |
static const byte _signal_otherdir[14] = { |
|
30 |
0x40, 0x40, 0x40, 0x10, 0x80, 0x20, 0, 0, |
|
31 |
0x80, 0x80, 0x80, 0x20, 0x40, 0x10 |
|
32 |
}; |
|
33 |
||
34 |
void UpdateTrainAcceleration(Vehicle *v) |
|
35 |
{ |
|
36 |
uint acc, power=0, max_speed=5000, weight=0; |
|
37 |
Vehicle *u = v; |
|
38 |
||
39 |
assert(v->subtype == 0); |
|
40 |
||
41 |
// compute stuff like max speed, power, and weight. |
|
42 |
do { |
|
43 |
const RailVehicleInfo *rvi = &_rail_vehicle_info[u->engine_type]; |
|
44 |
||
45 |
// power is sum of the power for all engines |
|
46 |
power += rvi->power; |
|
47 |
||
48 |
// limit the max speed to the speed of the slowest vehicle. |
|
49 |
if (rvi->max_speed && rvi->max_speed <= max_speed) max_speed = rvi->max_speed; |
|
50 |
||
51 |
// weight is the sum of the weight of the wagon and the weight of the cargo. |
|
52 |
weight += rvi->weight; |
|
53 |
weight += (_cargoc.weights[u->cargo_type] * u->cargo_count) >> 4; |
|
54 |
||
55 |
} while ( (u=u->next) != NULL); |
|
56 |
||
57 |
// these are shown in the UI |
|
58 |
v->u.rail.cached_weight = weight; |
|
59 |
v->u.rail.cached_power = power; |
|
60 |
v->max_speed = max_speed; |
|
61 |
||
62 |
assert(weight != 0); |
|
63 |
||
64 |
// compute acceleration |
|
65 |
acc = power / weight * 4; |
|
66 |
||
67 |
if (acc >= 255) acc=255; |
|
68 |
if (acc == 0) acc++; |
|
69 |
||
70 |
v->acceleration = (byte)acc; |
|
71 |
} |
|
72 |
||
73 |
#define F_GRAV 9.82f |
|
74 |
#define F_THETA 0.05f |
|
75 |
||
76 |
#define F_HP_KW 0.74569f |
|
77 |
#define F_KPH_MS 0.27778f |
|
78 |
#define F_MU 0.3f |
|
79 |
||
80 |
#define F_COEF_FRIC 0.04f |
|
81 |
#define F_COEF_ROLL 0.18f |
|
82 |
||
83 |
#define F_CURVE_FACTOR (1/96.f) |
|
84 |
||
85 |
bool IsTunnelTile(TileIndex tile); |
|
86 |
||
87 |
static int GetRealisticAcceleration(Vehicle *v) |
|
88 |
{ |
|
89 |
uint emass = 0; |
|
90 |
Vehicle *u = v; |
|
91 |
float f = 0.0f, spd; |
|
92 |
int curves = 0; |
|
93 |
||
94 |
assert(v->subtype == 0); |
|
95 |
||
96 |
// compute inclination force and number of curves. |
|
97 |
do { |
|
98 |
const RailVehicleInfo *rvi = &_rail_vehicle_info[u->engine_type]; |
|
99 |
uint mass = rvi->weight + ((_cargoc.weights[u->cargo_type] * u->cargo_count) >> 4); |
|
100 |
if (rvi->power) emass += mass; |
|
101 |
||
102 |
if (u->u.rail.flags & VRF_GOINGUP) { |
|
103 |
f += (float)mass * ( -F_GRAV * F_THETA); |
|
104 |
} else if (u->u.rail.flags & VRF_GOINGDOWN) { |
|
105 |
f += (float)mass * ( F_GRAV * F_THETA); |
|
106 |
} |
|
107 |
||
108 |
// compute curve penalty.. |
|
109 |
if (u->next != NULL) { |
|
110 |
uint diff = (u->direction - u->next->direction) & 7; |
|
111 |
if (diff) { |
|
112 |
curves += (diff == 1 || diff == 7) ? 1 : 3; |
|
113 |
} |
|
114 |
} |
|
115 |
} while ((u = u->next) != NULL); |
|
116 |
||
117 |
spd = (float)(v->cur_speed ? v->cur_speed : 1); |
|
118 |
||
119 |
// compute tractive effort |
|
120 |
{ |
|
121 |
float te = (float)v->u.rail.cached_power * (F_HP_KW/F_KPH_MS) / spd; |
|
122 |
float te2 = (float)emass * (F_MU * F_GRAV); |
|
123 |
if (te > te2) te = te2; |
|
124 |
f += te; |
|
125 |
} |
|
126 |
||
127 |
// add air resistance |
|
128 |
{ |
|
129 |
float cx = 1.0f; // NOT DONE |
|
130 |
||
131 |
// air resistance is doubled in tunnels. |
|
132 |
if (v->vehstatus == 0x40) cx *= 2; |
|
133 |
||
134 |
f -= cx * spd * spd * (F_KPH_MS * F_KPH_MS * 0.001f); |
|
135 |
} |
|
136 |
||
137 |
// after this f contains the acceleration. |
|
138 |
f /= (float)v->u.rail.cached_weight; |
|
139 |
||
140 |
// add friction to sum of forces (avoid mul by weight). (0.001 because we want kN) |
|
141 |
f -= (F_COEF_FRIC * F_GRAV * 0.001f + (F_COEF_ROLL * F_KPH_MS * F_GRAV * 0.001f) * spd); |
|
142 |
||
143 |
// penalty for curves? |
|
144 |
if (curves) |
|
145 |
f -= (float)min(curves, 8) * F_CURVE_FACTOR; |
|
146 |
||
147 |
return (int)(f * (1.0/(F_KPH_MS * 0.015f)) + 0.5f); |
|
148 |
} |
|
149 |
||
150 |
||
151 |
int GetTrainImage(Vehicle *v, byte direction) |
|
152 |
{ |
|
153 |
int img = v->spritenum; |
|
154 |
int base; |
|
155 |
||
156 |
if (is_custom_sprite(img)) { |
|
157 |
base = GetCustomVehicleSprite(v, direction + 4 * is_custom_secondhead_sprite(img)); |
|
158 |
if (base) return base; |
|
159 |
img = _engine_original_sprites[v->engine_type]; |
|
160 |
} |
|
161 |
||
162 |
base = _engine_sprite_base[img] + ((direction + _engine_sprite_add[img]) & _engine_sprite_and[img]); |
|
163 |
||
164 |
if (v->cargo_count >= (v->cargo_cap >> 1)) |
|
165 |
base += _wagon_full_adder[img]; |
|
166 |
return base; |
|
167 |
} |
|
168 |
||
169 |
void DrawTrainEngine(int x, int y, int engine, uint32 image_ormod) |
|
170 |
{ |
|
171 |
const RailVehicleInfo *rvi = &_rail_vehicle_info[engine]; |
|
172 |
||
173 |
int img = rvi->image_index; |
|
174 |
uint32 image = 0; |
|
175 |
||
176 |
if (is_custom_sprite(img)) { |
|
177 |
image = GetCustomEngineSprite(engine, -1, CID_PURCHASE, 0, 0, 6); |
|
178 |
if (!image) img = _engine_original_sprites[engine]; |
|
179 |
} |
|
180 |
if (!image) { |
|
181 |
image = (6 & _engine_sprite_and[img]) + _engine_sprite_base[img]; |
|
182 |
} |
|
183 |
||
184 |
if (rvi->flags & RVI_MULTIHEAD) { |
|
185 |
DrawSprite(image | image_ormod, x-14, y); |
|
186 |
x += 15; |
|
187 |
image = 0; |
|
188 |
if (is_custom_sprite(img)) { |
|
189 |
image = GetCustomEngineSprite(engine, -1, CID_PURCHASE, 0, 0, 2); |
|
190 |
if (!image) img = _engine_original_sprites[engine]; |
|
191 |
} |
|
192 |
if (!image) { |
|
193 |
image = ((6 + _engine_sprite_add[img+1]) & _engine_sprite_and[img+1]) + _engine_sprite_base[img+1]; |
|
194 |
} |
|
195 |
} |
|
196 |
DrawSprite(image | image_ormod, x, y); |
|
197 |
} |
|
198 |
||
199 |
void DrawTrainEngineInfo(int engine, int x, int y, int maxw) |
|
200 |
{ |
|
201 |
const RailVehicleInfo *rvi = &_rail_vehicle_info[engine]; |
|
202 |
int cap; |
|
203 |
uint multihead = ((rvi->flags & RVI_MULTIHEAD) ? 1 : 0); |
|
204 |
||
205 |
SET_DPARAM32(0, ((_price.build_railvehicle >> 3) * rvi->base_cost) >> 5); |
|
206 |
SET_DPARAM16(2, rvi->max_speed * 10 >> 4); |
|
207 |
SET_DPARAM16(3, rvi->power << multihead); |
|
208 |
SET_DPARAM16(1, rvi->weight << multihead); |
|
209 |
||
210 |
SET_DPARAM32(4, (rvi->running_cost_base * _price.running_rail[rvi->engclass] >> 8) << multihead); |
|
211 |
||
212 |
cap = rvi->capacity; |
|
213 |
SET_DPARAM16(5, STR_8838_N_A); |
|
214 |
if (cap != 0) { |
|
215 |
SET_DPARAM16(6, cap << multihead); |
|
216 |
SET_DPARAM16(5, _cargoc.names_long_p[rvi->cargo_type]); |
|
217 |
} |
|
218 |
DrawStringMultiCenter(x, y, STR_885B_COST_WEIGHT_T_SPEED_POWER, maxw); |
|
219 |
} |
|
220 |
||
221 |
||
222 |
int32 CmdBuildRailWagon(uint engine, uint tile, uint32 flags) |
|
223 |
{ |
|
224 |
int32 value; |
|
225 |
Vehicle *v; |
|
226 |
const RailVehicleInfo *rvi; |
|
227 |
int dir; |
|
228 |
const Engine *e; |
|
229 |
int x,y; |
|
230 |
||
231 |
rvi = &_rail_vehicle_info[engine]; |
|
232 |
value = (rvi->base_cost * _price.build_railwagon) >> 8; |
|
233 |
||
234 |
if (!(flags & DC_QUERY_COST)) { |
|
235 |
_error_message = STR_00E1_TOO_MANY_VEHICLES_IN_GAME; |
|
236 |
||
237 |
v = AllocateVehicle(); |
|
238 |
if (v == NULL) |
|
239 |
return CMD_ERROR; |
|
240 |
||
241 |
if (flags & DC_EXEC) { |
|
242 |
byte img = rvi->image_index; |
|
243 |
Vehicle *u; |
|
244 |
||
245 |
v->spritenum = img; |
|
246 |
||
247 |
u = _vehicles; |
|
248 |
for(;;) { |
|
249 |
if (u->type == VEH_Train && u->tile == (TileIndex)tile && |
|
250 |
u->subtype == 4 && u->engine_type == engine) { |
|
251 |
u = GetLastVehicleInChain(u); |
|
252 |
break; |
|
253 |
} |
|
254 |
||
255 |
if (++u == endof(_vehicles)) { |
|
256 |
u = NULL; |
|
257 |
break; |
|
258 |
} |
|
259 |
} |
|
260 |
||
261 |
v->engine_type = engine; |
|
262 |
||
263 |
dir = _map5[tile] & 3; |
|
264 |
||
265 |
v->direction = (byte)(dir*2+1); |
|
266 |
v->tile = (TileIndex)tile; |
|
267 |
||
268 |
x = GET_TILE_X(tile)*16 | _vehicle_initial_x_fract[dir]; |
|
269 |
y = GET_TILE_Y(tile)*16 | _vehicle_initial_y_fract[dir]; |
|
270 |
||
271 |
v->x_pos = x; |
|
272 |
v->y_pos = y; |
|
273 |
v->z_pos = GetSlopeZ(x,y); |
|
274 |
v->owner = _current_player; |
|
275 |
v->z_height = 6; |
|
276 |
v->u.rail.track = 0x80; |
|
277 |
v->vehstatus = VS_HIDDEN | VS_DEFPAL; |
|
278 |
||
279 |
v->subtype = 4; |
|
280 |
if (u != NULL) { |
|
281 |
u->next = v; |
|
282 |
v->subtype = 2; |
|
283 |
v->u.rail.first_engine = u->u.rail.first_engine; |
|
284 |
if (v->u.rail.first_engine == 0xffff && u->subtype == 0) |
|
285 |
v->u.rail.first_engine = u->engine_type; |
|
286 |
} else { |
|
287 |
v->u.rail.first_engine = 0xffff; |
|
288 |
} |
|
289 |
||
290 |
v->cargo_type = rvi->cargo_type; |
|
291 |
v->cargo_cap = rvi->capacity; |
|
292 |
v->value = value; |
|
293 |
// v->day_counter = 0; |
|
294 |
||
295 |
e = &_engines[engine]; |
|
296 |
v->u.rail.railtype = e->railtype; |
|
297 |
||
298 |
v->build_year = _cur_year; |
|
299 |
v->type = VEH_Train; |
|
300 |
v->cur_image = 0xAC2; |
|
301 |
||
302 |
_new_wagon_id = v->index; |
|
303 |
||
304 |
VehiclePositionChanged(v); |
|
305 |
||
306 |
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
|
307 |
} |
|
308 |
} |
|
309 |
||
310 |
return value; |
|
311 |
} |
|
312 |
||
313 |
// Move all free vehicles in the depot to the train |
|
314 |
static void NormalizeTrainVehInDepot(Vehicle *u) |
|
315 |
{ |
|
316 |
Vehicle *v; |
|
317 |
FOR_ALL_VEHICLES(v) { |
|
318 |
if (v->type == VEH_Train && v->subtype==4 && |
|
319 |
v->tile == u->tile && |
|
320 |
v->u.rail.track == 0x80) { |
|
321 |
if (DoCommandByTile(0,v->index | (u->index<<16), 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE) == CMD_ERROR) |
|
322 |
break; |
|
323 |
} |
|
324 |
} |
|
325 |
} |
|
326 |
||
327 |
static const byte _railveh_unk1[] = { |
|
328 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
329 |
0, 0, 0, 1, 1, 0, 0, 0, |
|
330 |
0, 0, 0, 0, 1, 0, 1, 0, |
|
331 |
0, 1, 1, 0, 0, 0, 0, 0, |
|
332 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
333 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
334 |
0, 0, 0, 0, 0, 0, 0, 1, |
|
335 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
336 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
337 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
338 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
339 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
340 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
341 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
342 |
0, 0, 0, 0, |
|
343 |
}; |
|
344 |
||
345 |
static const byte _railveh_score[] = { |
|
346 |
1, 4, 7, 19, 20, 30, 31, 19, |
|
347 |
20, 21, 22, 10, 11, 30, 31, 32, |
|
348 |
33, 34, 35, 29, 45, 32, 50, 40, |
|
349 |
41, 51, 52, 0, 0, 0, 0, 0, |
|
350 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
351 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
352 |
0, 0, 0, 0, 0, 0, 60, 62, |
|
353 |
63, 0, 0, 0, 0, 0, 0, 0, |
|
354 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
355 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
356 |
0, 0, 0, 0, 70, 71, 72, 73, |
|
357 |
74, 0, 0, 0, 0, 0, 0, 0, |
|
358 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
359 |
0, 0, 0, 0, 0, 0, 0, 0, |
|
360 |
0, 0, 0, 0, |
|
361 |
}; |
|
362 |
||
363 |
||
364 |
static int32 EstimateTrainCost(const RailVehicleInfo *rvi) |
|
365 |
{ |
|
366 |
return (rvi->base_cost * (_price.build_railvehicle >> 3)) >> 5; |
|
367 |
} |
|
368 |
||
369 |
/* Build a railroad vehicle |
|
370 |
* p1 = vehicle type id |
|
371 |
*/ |
|
372 |
||
373 |
int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
|
374 |
{ |
|
375 |
const RailVehicleInfo *rvi; |
|
376 |
int value,dir; |
|
377 |
Vehicle *v, *u; |
|
378 |
byte unit_num; |
|
379 |
Engine *e; |
|
380 |
uint tile; |
|
381 |
||
382 |
_cmd_build_rail_veh_var1 = 0; |
|
383 |
||
384 |
SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
385 |
||
386 |
tile = TILE_FROM_XY(x,y); |
|
387 |
rvi = &_rail_vehicle_info[p1]; |
|
388 |
||
389 |
if (rvi->flags & RVI_WAGON) { |
|
390 |
return CmdBuildRailWagon(p1, tile, flags); |
|
391 |
} |
|
392 |
||
393 |
value = EstimateTrainCost(rvi); |
|
394 |
||
395 |
if (!(flags & DC_QUERY_COST)) { |
|
396 |
v = AllocateVehicle(); |
|
397 |
if (v == NULL || _ptr_to_next_order >= endof(_order_array)) |
|
398 |
return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); |
|
399 |
||
400 |
unit_num = GetFreeUnitNumber(VEH_Train); |
|
401 |
if (unit_num > _patches.max_trains) |
|
402 |
return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); |
|
403 |
||
404 |
if (flags & DC_EXEC) { |
|
405 |
v->unitnumber = unit_num; |
|
406 |
||
407 |
dir = _map5[tile] & 3; |
|
408 |
||
409 |
v->direction = (byte)(dir*2+1); |
|
410 |
v->tile = (TileIndex)tile; |
|
411 |
v->owner = _current_player; |
|
412 |
v->x_pos = (x |= _vehicle_initial_x_fract[dir]); |
|
413 |
v->y_pos = (y |= _vehicle_initial_y_fract[dir]); |
|
414 |
v->z_pos = GetSlopeZ(x,y); |
|
415 |
v->z_height = 6; |
|
416 |
v->u.rail.track = 0x80; |
|
417 |
v->u.rail.first_engine = 0xffff; |
|
418 |
v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; |
|
419 |
// v->subtype = 0; |
|
420 |
v->spritenum = rvi->image_index; |
|
421 |
v->cargo_type = rvi->cargo_type; |
|
422 |
v->cargo_cap = rvi->capacity; |
|
423 |
v->max_speed = rvi->max_speed; |
|
424 |
// v->cargo_count = 0; |
|
425 |
v->value = value; |
|
426 |
// v->day_counter = 0; |
|
427 |
// v->next_order = 0; |
|
428 |
// v->next_station = 0; |
|
429 |
// v->load_unload_time_rem = 0; |
|
430 |
// v->progress = 0; |
|
431 |
// v->targetairport = 0; |
|
432 |
// v->crash_anim_pos = 0; |
|
433 |
v->last_station_visited = 0xff; |
|
434 |
v->dest_tile = 0; |
|
435 |
// v->profit_last_year = 0; |
|
436 |
// v->profit_this_year = 0; |
|
437 |
||
438 |
v->engine_type = (byte)p1; |
|
439 |
e = &_engines[p1]; |
|
440 |
||
441 |
v->reliability = e->reliability; |
|
442 |
v->reliability_spd_dec = e->reliability_spd_dec; |
|
443 |
v->max_age = e->lifelength * 366; |
|
444 |
||
445 |
v->string_id = STR_SV_TRAIN_NAME; |
|
446 |
// v->cur_speed = 0; |
|
447 |
// v->subspeed = 0; |
|
448 |
v->u.rail.railtype = e->railtype; |
|
449 |
_new_train_id = v->index; |
|
450 |
// v->cur_order_index = 0; |
|
451 |
// v->num_orders = 0; |
|
452 |
||
453 |
*(v->schedule_ptr = _ptr_to_next_order++) = 0; |
|
454 |
// v->next_in_chain = 0xffff; |
|
455 |
// v->next = NULL; |
|
456 |
||
457 |
v->service_interval = _patches.servint_trains; |
|
458 |
// v->breakdown_ctr = 0; |
|
459 |
// v->breakdowns_since_last_service = 0; |
|
460 |
// v->unk4D = 0; |
|
461 |
v->date_of_last_service = _date; |
|
462 |
v->build_year = _cur_year; |
|
463 |
v->type = VEH_Train; |
|
464 |
v->cur_image = 0xAC2; |
|
465 |
||
466 |
VehiclePositionChanged(v); |
|
467 |
||
468 |
if (rvi->flags&RVI_MULTIHEAD && (u=AllocateVehicle()) != NULL) { |
|
469 |
u->direction = v->direction; |
|
470 |
u->owner = v->owner; |
|
471 |
u->tile = v->tile; |
|
472 |
u->x_pos = v->x_pos; |
|
473 |
u->y_pos = v->y_pos; |
|
474 |
u->z_pos = v->z_pos; |
|
475 |
u->z_height = 6; |
|
476 |
u->u.rail.track = 0x80; |
|
477 |
v->u.rail.first_engine = 0xffff; |
|
478 |
u->vehstatus = VS_HIDDEN | VS_DEFPAL; |
|
479 |
u->subtype = 2; |
|
480 |
u->spritenum = v->spritenum + 1; |
|
481 |
u->cargo_type = v->cargo_type; |
|
482 |
u->cargo_cap = v->cargo_cap; |
|
483 |
u->u.rail.railtype = v->u.rail.railtype; |
|
484 |
// u->next_in_chain = 0xffff; |
|
485 |
v->next = u; |
|
486 |
u->engine_type = v->engine_type; |
|
487 |
u->build_year = v->build_year; |
|
488 |
v->value = u->value = v->value >> 1; |
|
489 |
// u->day_counter = 0; |
|
490 |
u->type = VEH_Train; |
|
491 |
u->cur_image = 0xAC2; |
|
492 |
VehiclePositionChanged(u); |
|
493 |
} |
|
494 |
||
495 |
UpdateTrainAcceleration(v); |
|
496 |
NormalizeTrainVehInDepot(v); |
|
497 |
||
498 |
InvalidateWindow(WC_VEHICLE_DEPOT, tile); |
|
499 |
InvalidateWindow(WC_TRAINS_LIST, v->owner); |
|
500 |
InvalidateWindow(WC_COMPANY, v->owner); |
|
501 |
} |
|
502 |
} |
|
503 |
_cmd_build_rail_veh_var1 = _railveh_unk1[p1]; |
|
504 |
_cmd_build_rail_veh_score = _railveh_score[p1]; |
|
505 |
return value; |
|
506 |
} |
|
507 |
||
508 |
||
509 |
bool IsTrainDepotTile(TileIndex tile) |
|
510 |
{ |
|
511 |
return IS_TILETYPE(tile, MP_RAILWAY) && |
|
512 |
(_map5[tile] & 0xFC) == 0xC0; |
|
513 |
} |
|
514 |
||
515 |
bool IsTunnelTile(TileIndex tile) |
|
516 |
{ |
|
517 |
return IS_TILETYPE(tile, MP_TUNNELBRIDGE) && |
|
518 |
(_map5[tile]&0x80) == 0; |
|
519 |
} |
|
520 |
||
521 |
||
522 |
int CheckStoppedInDepot(Vehicle *v) |
|
523 |
{ |
|
524 |
int count; |
|
525 |
TileIndex tile = v->tile; |
|
526 |
||
527 |
/* check if stopped in a depot */ |
|
528 |
if (!IsTrainDepotTile(tile) || v->cur_speed != 0) { |
|
529 |
errmsg: |
|
530 |
_error_message = STR_881A_TRAINS_CAN_ONLY_BE_ALTERED; |
|
531 |
return -1; |
|
532 |
} |
|
533 |
||
534 |
count = 0; |
|
535 |
do { |
|
536 |
count++; |
|
537 |
if (v->u.rail.track != 0x80 || v->tile != (TileIndex)tile || |
|
538 |
(v->subtype==0 && !(v->vehstatus&VS_STOPPED))) |
|
539 |
goto errmsg; |
|
540 |
} while ( (v=v->next) != NULL); |
|
541 |
||
542 |
return count; |
|
543 |
} |
|
544 |
||
545 |
// unlink a rail wagon from the linked list. |
|
546 |
// returns the new value of first |
|
547 |
static Vehicle *UnlinkWagon(Vehicle *v, Vehicle *first) |
|
548 |
{ |
|
549 |
// unlinking the first vehicle of the chain? |
|
550 |
v->u.rail.first_engine = 0xffff; |
|
551 |
if (v == first) { |
|
552 |
Vehicle *u; |
|
553 |
if ((v=v->next) == NULL) return NULL; |
|
554 |
for (u=v; u; u=u->next) u->u.rail.first_engine = v->engine_type; |
|
555 |
v->subtype = 4; |
|
556 |
return v; |
|
557 |
} else { |
|
558 |
Vehicle *u; |
|
559 |
for(u=first; u->next!=v; u=u->next) {} |
|
560 |
u->next = v->next; |
|
561 |
return first; |
|
562 |
} |
|
563 |
} |
|
564 |
||
565 |
static Vehicle *FindGoodVehiclePos(Vehicle *src) |
|
566 |
{ |
|
567 |
Vehicle *dst; |
|
568 |
uint16 eng = src->engine_type; |
|
569 |
TileIndex tile = src->tile; |
|
570 |
||
571 |
FOR_ALL_VEHICLES(dst) { |
|
572 |
if (dst->type==VEH_Train && dst->subtype==4 && dst->tile==tile) { |
|
573 |
// check so all vehicles in the line have the same engine. |
|
574 |
Vehicle *v = dst; |
|
575 |
while (v->engine_type == eng) { |
|
576 |
if ((v = v->next) == NULL) return dst; |
|
577 |
} |
|
578 |
} |
|
579 |
} |
|
580 |
||
581 |
return NULL; |
|
582 |
} |
|
583 |
||
584 |
/* p1 & 0xffff= source vehicle index |
|
585 |
p1 & 0xffff0000 = what wagon to put the wagon AFTER, 0xffff0000 to make a new line |
|
586 |
p2 & 1 = move all vehicles following the vehicle.. |
|
587 |
*/ |
|
588 |
||
589 |
int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
|
590 |
{ |
|
591 |
Vehicle *src, *dst, *src_head, *dst_head; |
|
592 |
bool is_loco; |
|
593 |
||
594 |
src = &_vehicles[p1 & 0xffff]; |
|
595 |
if (src->type != VEH_Train) return CMD_ERROR; |
|
596 |
||
597 |
is_loco = !(_rail_vehicle_info[src->engine_type].flags & RVI_WAGON) |
|
598 |
&& is_firsthead_sprite(src->spritenum); |
|
599 |
||
600 |
// if nothing is selected as destination, try and find a matching vehicle to drag to. |
|
601 |
if (((int32)p1 >> 16) == -1) { |
|
602 |
dst = NULL; |
|
603 |
if (!is_loco) dst = FindGoodVehiclePos(src); |
|
604 |
} else { |
|
605 |
dst = &_vehicles[((int32)p1 >> 16)]; |
|
606 |
} |
|
607 |
||
608 |
// don't move the same vehicle.. |
|
609 |
if (src == dst) |
|
610 |
return 0; |
|
611 |
||
612 |
/* the player must be the owner */ |
|
613 |
if (!CheckOwnership(src->owner) || (dst!=NULL && !CheckOwnership(dst->owner))) |
|
614 |
return CMD_ERROR; |
|
615 |
||
616 |
/* locate the head of the two chains */ |
|
617 |
src_head = GetFirstVehicleInChain(src); |
|
618 |
dst_head = NULL; |
|
619 |
if (dst != NULL) dst_head = GetFirstVehicleInChain(dst); |
|
620 |
||
621 |
/* check if all vehicles in the source train are stopped */ |
|
622 |
if (CheckStoppedInDepot(src_head) < 0) |
|
623 |
return CMD_ERROR; |
|
624 |
||
625 |
/* check if all the vehicles in the dest train are stopped, |
|
626 |
* and that the length of the dest train is no longer than XXX vehicles */ |
|
627 |
if (dst_head != NULL) { |
|
628 |
int num = CheckStoppedInDepot(dst_head); |
|
629 |
if (num < 0) |
|
630 |
return CMD_ERROR; |
|
631 |
||
632 |
if (num > (_patches.mammoth_trains ? 100 : 9) && dst_head->subtype==0) |
|
633 |
return_cmd_error(STR_8819_TRAIN_TOO_LONG); |
|
634 |
||
635 |
// if it's a multiheaded vehicle we're dragging to, drag to the vehicle before.. |
|
636 |
while (is_custom_secondhead_sprite(dst->spritenum) |
|
637 |
|| (!is_custom_sprite(dst->spritenum) && _engine_sprite_add[dst->spritenum] != 0)) { |
|
638 |
Vehicle *v = GetPrevVehicleInChain(dst); |
|
639 |
if (!v || src == v) break; |
|
640 |
dst = v; |
|
641 |
} |
|
642 |
||
643 |
assert(dst_head->tile == src_head->tile); |
|
644 |
} |
|
645 |
||
646 |
// when moving all wagons, we can't have the same src_head and dst_head |
|
647 |
if (p2 & 1 && src_head == dst_head) |
|
648 |
return 0; |
|
649 |
||
650 |
// moving a loco to a new line?, then we need to assign a unitnumber. |
|
651 |
if (dst == NULL && src->subtype != 0 && is_loco) { |
|
652 |
uint unit_num = GetFreeUnitNumber(VEH_Train); |
|
653 |
if (unit_num > _patches.max_trains) |
|
654 |
return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); |
|
655 |
||
656 |
if (flags & DC_EXEC) |
|
657 |
src->unitnumber = unit_num; |
|
658 |
} |
|
659 |
||
660 |
||
661 |
/* do it? */ |
|
662 |
if (flags & DC_EXEC) { |
|
663 |
if (p2 & 1) { |
|
664 |
// unlink ALL wagons |
|
665 |
if (src != src_head) { |
|
666 |
Vehicle *v = src_head; |
|
667 |
while (v->next != src) v=v->next; |
|
668 |
v->next = NULL; |
|
669 |
} |
|
670 |
} else { |
|
671 |
// unlink single wagon from linked list |
|
672 |
UnlinkWagon(src, src_head); |
|
673 |
src->next = NULL; |
|
674 |
} |
|
675 |
||
676 |
if (dst == NULL) { |
|
677 |
// move the train to an empty line. for locomotives, we set the type to 0. for wagons, 4. |
|
678 |
if (is_loco) { |
|
679 |
if (src->subtype != 0) { |
|
680 |
// setting the type to 0 also involves setting up the schedule_ptr field. |
|
681 |
src->subtype = 0; |
|
682 |
assert(src->schedule_ptr == NULL); |
|
683 |
*(src->schedule_ptr = _ptr_to_next_order++) = 0; |
|
684 |
src->num_orders = 0; |
|
685 |
} |
|
686 |
dst_head = src; |
|
687 |
} else { |
|
688 |
src->subtype = 4; |
|
689 |
} |
|
690 |
src->u.rail.first_engine = 0xffff; |
|
691 |
} else { |
|
692 |
if (src->subtype == 0) { |
|
693 |
// the vehicle was previously a loco. need to free the schedule list and delete vehicle windows etc. |
|
694 |
DeleteWindowById(WC_VEHICLE_VIEW, src->index); |
|
695 |
DeleteVehicleSchedule(src); |
|
696 |
} |
|
697 |
||
698 |
src->subtype = 2; |
|
699 |
src->unitnumber = 0; // doesn't occupy a unitnumber anymore. |
|
700 |
||
701 |
// setup first_engine |
|
702 |
src->u.rail.first_engine = dst->u.rail.first_engine; |
|
703 |
if (src->u.rail.first_engine == 0xffff && dst->subtype == 0) |
|
704 |
src->u.rail.first_engine = dst->engine_type; |
|
705 |
||
706 |
// link in the wagon(s) in the chain. |
|
707 |
{ |
|
708 |
Vehicle *v = src; |
|
709 |
while (v->next != NULL) { |
|
710 |
v->next->u.rail.first_engine = v->u.rail.first_engine; |
|
711 |
v = v->next; |
|
712 |
} |
|
713 |
v->next = dst->next; |
|
714 |
} |
|
715 |
dst->next = src; |
|
716 |
} |
|
717 |
||
718 |
if (src_head->subtype == 0) |
|
719 |
UpdateTrainAcceleration(src_head); |
|
720 |
InvalidateWindow(WC_VEHICLE_DETAILS, src_head->index); |
|
721 |
||
722 |
if (dst_head) { |
|
723 |
if (dst_head->subtype == 0) |
|
724 |
UpdateTrainAcceleration(dst_head); |
|
725 |
InvalidateWindow(WC_VEHICLE_DETAILS, dst_head->index); |
|
726 |
} |
|
727 |
||
728 |
InvalidateWindow(WC_VEHICLE_DEPOT, src_head->tile); |
|
729 |
InvalidateWindow(WC_TRAINS_LIST, _current_player); |
|
730 |
} |
|
731 |
||
732 |
return 0; |
|
733 |
} |
|
734 |
||
735 |
/* p1 = train to start / stop */ |
|
736 |
int32 CmdStartStopTrain(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
|
737 |
{ |
|
738 |
Vehicle *v; |
|
739 |
||
740 |
v = &_vehicles[p1]; |
|
741 |
||
742 |
if (!CheckOwnership(v->owner)) |
|
743 |
return CMD_ERROR; |
|
744 |
||
745 |
if (flags & DC_EXEC) { |
|
746 |
v->u.rail.days_since_order_progr = 0; |
|
747 |
v->vehstatus ^= VS_STOPPED; |
|
748 |
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4); |
|
749 |
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
|
750 |
} |
|
751 |
return 0; |
|
752 |
} |
|
753 |
||
754 |
// p1 = wagon/loco index |
|
755 |
// p2 = mode |
|
756 |
// 0: sell just the vehicle |
|
757 |
// 1: sell the vehicle and all vehicles that follow it in the chain |
|
758 |
// 2: when selling attached locos, rearrange all vehicles after it to separate lines. |
|
759 |
int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
|
760 |
{ |
|
761 |
Vehicle *v, *first,*last; |
|
762 |
int32 cost; |
|
763 |
||
764 |
SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
765 |
||
766 |
v = &_vehicles[p1]; |
|
767 |
||
768 |
if (!CheckOwnership(v->owner)) |
|
769 |
return CMD_ERROR; |
|
770 |
||
771 |
// get first vehicle in chain |
|
772 |
first = v; |
|
773 |
if (first->subtype != 0) { |
|
774 |
first = GetFirstVehicleInChain(first); |
|
775 |
last = NULL; |
|
776 |
} else { |
|
777 |
if (p2 != 1) { |
|
778 |
// sell last part of multiheaded? |
|
779 |
last = GetLastVehicleInChain(v); |
|
73
34d5f66cfad7
(svn r74) -Fix: [1009631] Wrong multihead selling (Bodewes)
truelight
parents:
22
diff
changeset
|
780 |
// Check if the end-part is the same engine and check if it is the rear-end |
34d5f66cfad7
(svn r74) -Fix: [1009631] Wrong multihead selling (Bodewes)
truelight
parents:
22
diff
changeset
|
781 |
if (last->engine_type != first->engine_type || is_firsthead_sprite(last->spritenum)) |
0 | 782 |
last = NULL; |
783 |
} else { |
|
784 |
last = NULL; |
|
785 |
} |
|
786 |
} |
|
787 |
||
788 |
// make sure the vehicle is stopped in the depot |
|
789 |
if (CheckStoppedInDepot(first) < 0) |
|
790 |
return CMD_ERROR; |
|
791 |
||
792 |
||
793 |
if (flags & DC_EXEC) { |
|
794 |
// always redraw the depot. maybe redraw train list |
|
795 |
InvalidateWindow(WC_VEHICLE_DEPOT, first->tile); |
|
796 |
if (first->subtype == 0) InvalidateWindow(WC_TRAINS_LIST, first->owner); |
|
797 |
// when selling an attached locomotive. we need to delete its window. |
|
798 |
if (v->subtype == 0) { |
|
799 |
DeleteWindowById(WC_VEHICLE_VIEW, v->index); |
|
800 |
||
801 |
// rearrange all vehicles that follow to separate lines. |
|
802 |
if (p2 == 2) { |
|
803 |
Vehicle *u,*tmp; |
|
804 |
u = v->next; |
|
805 |
while (u != last) { |
|
806 |
tmp = u; |
|
807 |
u = u->next; |
|
808 |
DoCommandByTile(tmp->tile, tmp->index | ((-1)<<16), 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
809 |
} |
|
810 |
} |
|
811 |
} |
|
812 |
||
813 |
// delete the vehicles |
|
814 |
cost = 0; |
|
815 |
for(;;) { |
|
816 |
Vehicle *tmp; |
|
817 |
||
818 |
assert(first); |
|
819 |
first = UnlinkWagon(v, first); |
|
820 |
cost -= v->value; |
|
821 |
tmp = v; |
|
822 |
v = v->next; |
|
823 |
DeleteVehicle(tmp); |
|
824 |
if (v == NULL || p2 != 1) break; |
|
825 |
} |
|
826 |
||
827 |
// delete last vehicle of multiheaded train? |
|
828 |
if (last) { |
|
829 |
first = UnlinkWagon(last, first); |
|
830 |
cost -= last->value; |
|
831 |
DeleteVehicle(last); |
|
832 |
} |
|
833 |
||
834 |
// an attached train changed? |
|
835 |
if (first && first->subtype == 0) { |
|
836 |
UpdateTrainAcceleration(first); |
|
837 |
InvalidateWindow(WC_VEHICLE_DETAILS, first->index); |
|
838 |
} |
|
839 |
} else { |
|
840 |
cost = 0; |
|
841 |
for(;;) { |
|
842 |
cost -= v->value; |
|
843 |
if ((v=v->next) == NULL || p2 != 1) break; |
|
844 |
} |
|
845 |
if (last) cost -= last->value; |
|
846 |
} |
|
847 |
||
848 |
return cost; |
|
849 |
} |
|
850 |
||
851 |
static void UpdateTrainDeltaXY(Vehicle *v, int direction) |
|
852 |
{ |
|
853 |
#define MKIT(a,b,c,d) ((a&0xFF)<<24) | ((b&0xFF)<<16) | ((c&0xFF)<<8) | ((d&0xFF)<<0) |
|
854 |
static const uint32 _delta_xy_table[8] = { |
|
855 |
MKIT(3, 3, -1, -1), |
|
856 |
MKIT(3, 7, -1, -3), |
|
857 |
MKIT(3, 3, -1, -1), |
|
858 |
MKIT(7, 3, -3, -1), |
|
859 |
MKIT(3, 3, -1, -1), |
|
860 |
MKIT(3, 7, -1, -3), |
|
861 |
MKIT(3, 3, -1, -1), |
|
862 |
MKIT(7, 3, -3, -1), |
|
863 |
}; |
|
864 |
#undef MKIT |
|
865 |
||
866 |
uint32 x = _delta_xy_table[direction]; |
|
867 |
||
868 |
v->x_offs = (byte)x; |
|
869 |
v->y_offs = (byte)(x>>=8); |
|
870 |
v->sprite_width = (byte)(x>>=8); |
|
871 |
v->sprite_height = (byte)(x>>=8); |
|
872 |
} |
|
873 |
||
874 |
static void UpdateVarsAfterSwap(Vehicle *v) |
|
875 |
{ |
|
876 |
UpdateTrainDeltaXY(v, v->direction); |
|
877 |
v->cur_image = GetTrainImage(v, v->direction); |
|
878 |
BeginVehicleMove(v); |
|
879 |
VehiclePositionChanged(v); |
|
880 |
EndVehicleMove(v); |
|
881 |
} |
|
882 |
||
883 |
static void SetLastSpeed(Vehicle *v, int spd) { |
|
884 |
int old = v->u.rail.last_speed; |
|
885 |
if (spd != old) { |
|
886 |
v->u.rail.last_speed = spd; |
|
887 |
if (_patches.vehicle_speed || !old != !spd) |
|
888 |
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4); |
|
889 |
} |
|
890 |
} |
|
891 |
||
892 |
static void ReverseTrainSwapVeh(Vehicle *v, int l, int r) |
|
893 |
{ |
|
894 |
Vehicle *a, *b; |
|
895 |
||
896 |
/* locate vehicles to swap */ |
|
897 |
for(a=v; l!=0; l--) { a = a->next; } |
|
898 |
for(b=v; r!=0; r--) { b = b->next; } |
|
899 |
||
900 |
if (a != b) { |
|
901 |
/* swap the hidden bits */ |
|
902 |
{ |
|
903 |
uint16 tmp = (a->vehstatus & ~VS_HIDDEN) | (b->vehstatus&VS_HIDDEN); |
|
904 |
b->vehstatus = (b->vehstatus & ~VS_HIDDEN) | (a->vehstatus&VS_HIDDEN); |
|
905 |
a->vehstatus = tmp; |
|
906 |
} |
|
907 |
||
908 |
/* swap variables */ |
|
909 |
swap_byte(&a->u.rail.track, &b->u.rail.track); |
|
910 |
swap_byte(&a->direction, &b->direction); |
|
911 |
||
912 |
/* toggle direction */ |
|
913 |
if (!(a->u.rail.track & 0x80)) a->direction ^= 4; |
|
914 |
if (!(b->u.rail.track & 0x80)) b->direction ^= 4; |
|
915 |
||
916 |
/* swap more variables */ |
|
917 |
swap_int16(&a->x_pos, &b->x_pos); |
|
918 |
swap_int16(&a->y_pos, &b->y_pos); |
|
919 |
swap_tile(&a->tile, &b->tile); |
|
920 |
swap_byte(&a->z_pos, &b->z_pos); |
|
921 |
||
922 |
/* update other vars */ |
|
923 |
UpdateVarsAfterSwap(a); |
|
924 |
UpdateVarsAfterSwap(b); |
|
925 |
} else { |
|
926 |
if (!(a->u.rail.track & 0x80)) a->direction ^= 4; |
|
927 |
UpdateVarsAfterSwap(a); |
|
928 |
} |
|
929 |
} |
|
930 |
||
931 |
static void ReverseTrainDirection(Vehicle *v) |
|
932 |
{ |
|
933 |
int l = 0, r = -1; |
|
934 |
Vehicle *u; |
|
935 |
||
936 |
if (IsTrainDepotTile(v->tile)) |
|
937 |
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
|
938 |
||
939 |
// count number of vehicles |
|
940 |
u = v; |
|
941 |
do r++; while ( (u = u->next) != NULL ); |
|
942 |
||
943 |
/* swap start<>end, start+1<>end-1, ... */ |
|
944 |
do { |
|
945 |
ReverseTrainSwapVeh(v, l++, r--); |
|
946 |
} while (l <= r); |
|
947 |
||
948 |
if (IsTrainDepotTile(v->tile)) |
|
949 |
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
|
950 |
||
951 |
v->u.rail.flags &= ~VRF_REVERSING; |
|
952 |
} |
|
953 |
||
954 |
/* p1 = vehicle */ |
|
955 |
int32 CmdReverseTrainDirection(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
|
956 |
{ |
|
957 |
Vehicle *v; |
|
958 |
||
959 |
v = &_vehicles[p1]; |
|
960 |
||
961 |
if (!CheckOwnership(v->owner)) |
|
962 |
return CMD_ERROR; |
|
963 |
||
964 |
_error_message = STR_EMPTY; |
|
965 |
||
966 |
// if (v->u.rail.track & 0x80 || IsTrainDepotTile(v->tile)) |
|
967 |
// return CMD_ERROR; |
|
968 |
||
969 |
if (v->u.rail.crash_anim_pos != 0 || v->breakdown_ctr != 0) |
|
970 |
return CMD_ERROR; |
|
971 |
||
972 |
if (flags & DC_EXEC) { |
|
973 |
if (_patches.realistic_acceleration && v->cur_speed != 0) { |
|
974 |
v->u.rail.flags ^= VRF_REVERSING; |
|
975 |
} else { |
|
976 |
v->cur_speed = 0; |
|
977 |
SetLastSpeed(v, 0); |
|
978 |
ReverseTrainDirection(v); |
|
979 |
} |
|
980 |
} |
|
981 |
return 0; |
|
982 |
} |
|
983 |
||
984 |
int32 CmdForceTrainProceed(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
|
985 |
{ |
|
986 |
Vehicle *v; |
|
987 |
||
988 |
v = &_vehicles[p1]; |
|
989 |
||
990 |
if (!CheckOwnership(v->owner)) |
|
991 |
return CMD_ERROR; |
|
992 |
||
993 |
if (flags & DC_EXEC) |
|
994 |
v->u.rail.force_proceed = 0x50; |
|
995 |
||
996 |
return 0; |
|
997 |
} |
|
998 |
||
999 |
// p1 = vehicle to refit |
|
1000 |
// p2 = new cargo |
|
1001 |
||
1002 |
int32 CmdRefitRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
|
1003 |
{ |
|
1004 |
Vehicle *v; |
|
1005 |
int32 cost; |
|
1006 |
uint num; |
|
1007 |
||
1008 |
SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
1009 |
||
1010 |
v = &_vehicles[p1]; |
|
1011 |
if (!CheckOwnership(v->owner) || CheckStoppedInDepot(v) < 0) |
|
1012 |
return CMD_ERROR; |
|
1013 |
||
1014 |
cost = 0; |
|
1015 |
num = 0; |
|
1016 |
||
1017 |
do { |
|
1018 |
if (!(_rail_vehicle_info[v->engine_type].flags & RVI_WAGON) && (byte)p2 != v->cargo_type && v->cargo_cap != 0) { |
|
15
255d0f685325
(svn r16) Fix: Train refit cost based on _price.ship_base
dominik
parents:
11
diff
changeset
|
1019 |
cost += (_price.build_railvehicle >> 8); |
0 | 1020 |
num += v->cargo_cap; |
1021 |
if (flags & DC_EXEC) { |
|
1022 |
v->cargo_count = 0; |
|
1023 |
v->cargo_type = (byte)p2; |
|
1024 |
InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
1025 |
} |
|
1026 |
} |
|
1027 |
} while ( (v=v->next) != NULL); |
|
1028 |
||
1029 |
_returned_refit_amount = num; |
|
1030 |
||
1031 |
return cost; |
|
1032 |
} |
|
1033 |
||
1034 |
int GetDepotByTile(uint tile) |
|
1035 |
{ |
|
1036 |
Depot *d; |
|
1037 |
int i=0; |
|
1038 |
for(d=_depots; d->xy != (TileIndex)tile; d++) { i++; } |
|
1039 |
return i; |
|
1040 |
} |
|
1041 |
||
1042 |
typedef struct TrainFindDepotData { |
|
1043 |
uint best_length; |
|
1044 |
uint tile; |
|
1045 |
byte owner; |
|
1046 |
} TrainFindDepotData; |
|
1047 |
||
1048 |
static bool TrainFindDepotEnumProc(uint tile, TrainFindDepotData *tfdd, int track, uint length, byte *state) |
|
1049 |
{ |
|
1050 |
if (IS_TILETYPE(tile, MP_RAILWAY) && _map_owner[tile] == tfdd->owner) { |
|
1051 |
if ((_map5[tile] & ~0x3) == 0xC0) { |
|
1052 |
if (length < tfdd->best_length) { |
|
1053 |
tfdd->best_length = length; |
|
1054 |
tfdd->tile = tile; |
|
1055 |
} |
|
1056 |
return true; |
|
1057 |
} |
|
1058 |
||
1059 |
// make sure the train doesn't run against a oneway signal |
|
1060 |
if ((_map5[tile] & 0xC0) == 0x40) { |
|
1061 |
if (!(_map3_lo[tile] & _signal_onedir[track]) && _map3_lo[tile] & _signal_otherdir[track]) |
|
1062 |
return true; |
|
1063 |
} |
|
1064 |
} |
|
1065 |
||
1066 |
// stop searching if we've found a destination that is closer already. |
|
1067 |
return length >= tfdd->best_length; |
|
1068 |
} |
|
1069 |
||
1070 |
// returns the tile of a depot to goto to |
|
1071 |
static uint FindClosestTrainDepot(Vehicle *v) |
|
1072 |
{ |
|
1073 |
int i; |
|
1074 |
TrainFindDepotData tfdd; |
|
1075 |
uint tile = v->tile; |
|
1076 |
||
1077 |
if (IsTrainDepotTile(tile)) |
|
1078 |
return tile; |
|
1079 |
||
1080 |
if (v->u.rail.track == 0x40) { tile = GetVehicleOutOfTunnelTile(v); } |
|
1081 |
||
1082 |
tfdd.owner = v->owner; |
|
1083 |
tfdd.best_length = (uint)-1; |
|
1084 |
||
1085 |
if (!_patches.new_depot_finding) { |
|
1086 |
// search in all directions |
|
1087 |
for(i=0; i!=4; i++) |
|
1088 |
NewTrainPathfind(tile, i, (TPFEnumProc*)TrainFindDepotEnumProc, &tfdd, NULL); |
|
1089 |
if (tfdd.best_length != (uint)-1) |
|
1090 |
return tfdd.tile; |
|
1091 |
} else { |
|
1092 |
// search in the forward direction first. |
|
1093 |
i = v->direction >> 1; |
|
1094 |
if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[i]) { i = (i - 1) & 3; } |
|
1095 |
NewTrainPathfind(tile, i, (TPFEnumProc*)TrainFindDepotEnumProc, &tfdd, NULL); |
|
1096 |
if (tfdd.best_length != (uint)-1) |
|
1097 |
return tfdd.tile; |
|
1098 |
||
1099 |
// search in backwards direction |
|
1100 |
i = (v->direction^4) >> 1; |
|
1101 |
if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[i]) { i = (i - 1) & 3; } |
|
1102 |
NewTrainPathfind(tile, i, (TPFEnumProc*)TrainFindDepotEnumProc, &tfdd, NULL); |
|
1103 |
if (tfdd.best_length != (uint)-1) |
|
1104 |
return tfdd.tile; |
|
1105 |
} |
|
1106 |
||
1107 |
return (uint)-1; |
|
1108 |
} |
|
1109 |
||
1110 |
int32 CmdTrainGotoDepot(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
|
1111 |
{ |
|
1112 |
Vehicle *v = &_vehicles[p1]; |
|
1113 |
uint depot_tile; |
|
1114 |
||
1115 |
if ((v->next_order & OT_MASK) == OT_GOTO_DEPOT) { |
|
1116 |
if (flags & DC_EXEC) { |
|
1117 |
if (v->next_order & OF_UNLOAD) { |
|
1118 |
v->u.rail.days_since_order_progr = 0; |
|
1119 |
v->cur_order_index++; |
|
1120 |
} |
|
1121 |
||
1122 |
v->next_order = OT_DUMMY; |
|
1123 |
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4); |
|
1124 |
} |
|
1125 |
return 0; |
|
1126 |
} |
|
1127 |
||
1128 |
depot_tile = FindClosestTrainDepot(v); |
|
1129 |
if (depot_tile == (uint)-1) |
|
1130 |
return_cmd_error(STR_883A_UNABLE_TO_FIND_ROUTE_TO); |
|
1131 |
||
1132 |
if (flags & DC_EXEC) { |
|
1133 |
v->dest_tile = depot_tile; |
|
1134 |
v->next_order = OF_NON_STOP | OF_FULL_LOAD | OT_GOTO_DEPOT; |
|
1135 |
v->next_order_param = GetDepotByTile(depot_tile); |
|
1136 |
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4); |
|
1137 |
} |
|
1138 |
||
1139 |
return 0; |
|
1140 |
} |
|
1141 |
||
1142 |
/* p1 = vehicle |
|
1143 |
* p2 = new service int |
|
1144 |
*/ |
|
1145 |
int32 CmdChangeTrainServiceInt(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
|
1146 |
{ |
|
1147 |
Vehicle *v = &_vehicles[p1]; |
|
1148 |
||
1149 |
if (!CheckOwnership(v->owner)) |
|
1150 |
return CMD_ERROR; |
|
1151 |
||
1152 |
if (flags & DC_EXEC) { |
|
1153 |
v->service_interval = (uint16)p2; |
|
1154 |
InvalidateWindowWidget(WC_VEHICLE_DETAILS, v->index, 8); |
|
1155 |
} |
|
1156 |
||
1157 |
return 0; |
|
1158 |
} |
|
1159 |
||
1160 |
void OnTick_Train() |
|
1161 |
{ |
|
1162 |
_age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1); |
|
1163 |
} |
|
1164 |
||
1165 |
static const int8 _vehicle_smoke_pos[16] = { |
|
1166 |
-4, -4, -4, 0, 4, 4, 4, 0, |
|
1167 |
-4, 0, 4, 4, 4, 0,-4,-4, |
|
1168 |
}; |
|
1169 |
||
1170 |
static void HandleLocomotiveSmokeCloud(Vehicle *v) |
|
1171 |
{ |
|
1172 |
Vehicle *u; |
|
1173 |
||
1174 |
if (v->vehstatus & VS_TRAIN_SLOWING || v->load_unload_time_rem != 0 || v->cur_speed < 2) |
|
1175 |
return; |
|
1176 |
||
1177 |
u = v; |
|
1178 |
||
1179 |
do { |
|
1180 |
int engtype = v->engine_type; |
|
1181 |
||
1182 |
// no smoke? |
|
1183 |
if (_rail_vehicle_info[engtype].flags & 2 |
|
1184 |
|| _engines[engtype].railtype > 0 |
|
1185 |
|| (v->vehstatus&VS_HIDDEN) || (v->u.rail.track & 0xC0) ) |
|
1186 |
continue; |
|
1187 |
||
1188 |
switch (_rail_vehicle_info[engtype].engclass) { |
|
1189 |
case 0: |
|
1190 |
// steam smoke. |
|
1191 |
if ( (v->tick_counter&0xF) == 0 && !IsTrainDepotTile(v->tile) && !IsTunnelTile(v->tile)) { |
|
1192 |
CreateEffectVehicleRel(v, |
|
1193 |
(_vehicle_smoke_pos[v->direction]), |
|
1194 |
(_vehicle_smoke_pos[v->direction+8]), |
|
1195 |
10, |
|
1196 |
EV_STEAM_SMOKE); |
|
1197 |
} |
|
1198 |
break; |
|
1199 |
||
1200 |
case 1: |
|
1201 |
// diesel smoke |
|
1202 |
if (u->cur_speed <= 40 && !IsTrainDepotTile(v->tile) && !IsTunnelTile(v->tile) && (uint16)Random() <= 0x1E00) { |
|
1203 |
CreateEffectVehicleRel(v, 0,0,10, EV_SMOKE_3); |
|
1204 |
} |
|
1205 |
break; |
|
1206 |
||
1207 |
case 2: |
|
1208 |
// blue spark |
|
1209 |
if ( (v->tick_counter&0x3) == 0 && !IsTrainDepotTile(v->tile) && !IsTunnelTile(v->tile) && (uint16)Random() <= 0x5B0) { |
|
1210 |
CreateEffectVehicleRel(v, 0,0,10, EV_SMOKE_2); |
|
1211 |
} |
|
1212 |
break; |
|
1213 |
} |
|
1214 |
} while ( (v = v->next) != NULL ); |
|
1215 |
||
1216 |
} |
|
1217 |
||
1218 |
static void TrainPlayLeaveStationSound(Vehicle *v) |
|
1219 |
{ |
|
1220 |
static const byte sfx[3] = { 0x2, 0x8, 0x8 }; |
|
1221 |
int engtype = v->engine_type; |
|
1222 |
||
1223 |
switch (_engines[engtype].railtype) { |
|
1224 |
case 0: SndPlayVehicleFx(sfx[_rail_vehicle_info[engtype].engclass], v); break; |
|
1225 |
case 1: SndPlayVehicleFx(0x41, v); break; |
|
1226 |
case 2: SndPlayVehicleFx(0x47, v); break; |
|
1227 |
} |
|
1228 |
} |
|
1229 |
||
1230 |
static bool CheckTrainStayInDepot(Vehicle *v) |
|
1231 |
{ |
|
1232 |
Vehicle *u; |
|
1233 |
if (v->u.rail.track != 0x80) // first wagon (eg engine) in depot |
|
1234 |
return false; |
|
1235 |
||
1236 |
// make sure that all vehicles are in the depot |
|
1237 |
u = GetLastVehicleInChain(v); |
|
1238 |
if (u->u.rail.track != 0x80) |
|
1239 |
return false; |
|
1240 |
||
1241 |
// fix hung train if both ends are in depots (when here first wagon and last wagon is in depot) |
|
1242 |
// both first and last should be in the same depot, eg on the same tile |
|
1243 |
if (v->tile != u->tile) |
|
1244 |
return false; |
|
1245 |
||
1246 |
if (v->u.rail.force_proceed == 0) { |
|
1247 |
if (++v->load_unload_time_rem < 37) |
|
1248 |
return true; |
|
1249 |
v->load_unload_time_rem = 0; |
|
1250 |
||
1251 |
if (UpdateSignalsOnSegment(v->tile, v->direction)) |
|
1252 |
return true; |
|
1253 |
} |
|
1254 |
||
1255 |
TrainPlayLeaveStationSound(v); |
|
1256 |
||
1257 |
v->u.rail.track = 1; |
|
1258 |
if (v->direction & 2) |
|
1259 |
v->u.rail.track = 2; |
|
1260 |
||
1261 |
v->vehstatus &= ~VS_HIDDEN; |
|
1262 |
v->cur_speed = 0; |
|
1263 |
||
1264 |
UpdateTrainDeltaXY(v, v->direction); |
|
1265 |
v->cur_image = GetTrainImage(v, v->direction); |
|
1266 |
VehiclePositionChanged(v); |
|
1267 |
UpdateSignalsOnSegment(v->tile, v->direction); |
|
1268 |
UpdateTrainAcceleration(v); |
|
1269 |
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
|
1270 |
||
1271 |
return false; |
|
1272 |
} |
|
1273 |
||
1274 |
typedef struct TrainTrackFollowerData { |
|
1275 |
TileIndex dest_coords; |
|
1276 |
int station_index; // station index we're heading for |
|
1277 |
uint best_bird_dist; |
|
1278 |
uint best_track_dist; |
|
1279 |
byte best_track; |
|
1280 |
} TrainTrackFollowerData; |
|
1281 |
||
1282 |
static bool TrainTrackFollower(uint tile, TrainTrackFollowerData *ttfd, int track, uint length, byte *state){ |
|
1283 |
if (IS_TILETYPE(tile, MP_RAILWAY) && (_map5[tile]&0xC0) == 0x40) { |
|
1284 |
// the tile has a signal |
|
1285 |
byte m3 = _map3_lo[tile]; |
|
1286 |
if (!(m3 & _signal_onedir[track])) { |
|
1287 |
// if one way signal not pointing towards us, stop going in this direction. |
|
1288 |
if (m3 & _signal_otherdir[track]) |
|
1289 |
return true; |
|
1290 |
} else if (_map2[tile] & _signal_onedir[track]) { |
|
1291 |
// green signal in our direction. either one way or two way. |
|
1292 |
*state = true; |
|
1293 |
} else if (m3 & _signal_otherdir[track]) { |
|
1294 |
// two way signal. unless we passed another green signal on the way, |
|
1295 |
// stop going in this direction. |
|
1296 |
if (!*state) return true; |
|
1297 |
} |
|
1298 |
} |
|
1299 |
||
1300 |
// heading for nowhere? |
|
1301 |
if (ttfd->dest_coords == 0) |
|
1302 |
return false; |
|
1303 |
||
1304 |
// did we reach the final station? |
|
1305 |
if (tile == ttfd->dest_coords || |
|
1306 |
(IS_TILETYPE(tile, MP_STATION) && IS_BYTE_INSIDE(_map5[tile], 0, 8) && _map2[tile] == ttfd->station_index)) { |
|
1307 |
// found station |
|
1308 |
ttfd->best_bird_dist = 0; |
|
1309 |
if (length < ttfd->best_track_dist) { |
|
1310 |
ttfd->best_track_dist = length; |
|
1311 |
ttfd->best_track = state[1]; |
|
1312 |
} |
|
1313 |
return true; |
|
1314 |
} else { |
|
1315 |
uint dist; |
|
1316 |
||
1317 |
// we've actually found the destination already. no point searching in directions longer than this. |
|
1318 |
if (ttfd->best_track_dist != (uint)-1) |
|
1319 |
return length >= ttfd->best_track_dist; |
|
1320 |
||
1321 |
// didn't find station |
|
1322 |
dist = GetTileDist(tile, ttfd->dest_coords); |
|
1323 |
if (dist < ttfd->best_bird_dist) { |
|
1324 |
ttfd->best_bird_dist = dist; |
|
1325 |
ttfd->best_track = state[1]; |
|
1326 |
} |
|
1327 |
return false; |
|
1328 |
} |
|
1329 |
} |
|
1330 |
||
1331 |
static void FillWithStationData(TrainTrackFollowerData *fd, Vehicle *v) |
|
1332 |
{ |
|
1333 |
uint tile; |
|
1334 |
||
1335 |
fd->dest_coords = tile = v->dest_tile; |
|
1336 |
fd->station_index = -1; |
|
1337 |
||
1338 |
if (IS_TILETYPE(tile, MP_STATION) && IS_BYTE_INSIDE(_map5[tile], 0, 8) ) |
|
1339 |
fd->station_index = _map2[tile]; |
|
1340 |
} |
|
1341 |
||
1342 |
static const byte _initial_tile_subcoord[6][4][3] = { |
|
1343 |
{{ 15, 8, 1 },{ 0, 0, 0 },{ 0, 8, 5 },{ 0, 0, 0 }}, |
|
1344 |
{{ 0, 0, 0 },{ 8, 0, 3 },{ 0, 0, 0 },{ 8,15, 7 }}, |
|
1345 |
{{ 0, 0, 0 },{ 7, 0, 2 },{ 0, 7, 6 },{ 0, 0, 0 }}, |
|
1346 |
{{ 15, 8, 2 },{ 0, 0, 0 },{ 0, 0, 0 },{ 8,15, 6 }}, |
|
1347 |
{{ 15, 7, 0 },{ 8, 0, 4 },{ 0, 0, 0 },{ 0, 0, 0 }}, |
|
1348 |
{{ 0, 0, 0 },{ 0, 0, 0 },{ 0, 8, 4 },{ 7,15, 0 }}, |
|
1349 |
}; |
|
1350 |
||
1351 |
static const uint32 _reachable_tracks[4] = { |
|
1352 |
0x10091009, |
|
1353 |
0x00160016, |
|
1354 |
0x05200520, |
|
1355 |
0x2A002A00, |
|
1356 |
}; |
|
1357 |
||
1358 |
static const byte _search_directions[6][4] = { |
|
1359 |
{ 0, 9, 2, 9 }, // track 1 |
|
1360 |
{ 9, 1, 9, 3 }, // track 2 |
|
1361 |
{ 9, 0, 3, 9 }, // track upper |
|
1362 |
{ 1, 9, 9, 2 }, // track lower |
|
1363 |
{ 3, 2, 9, 9 }, // track left |
|
1364 |
{ 9, 9, 1, 0 }, // track right |
|
1365 |
}; |
|
1366 |
||
1367 |
static const byte _pick_track_table[6] = {1, 3, 2, 2, 0, 0}; |
|
1368 |
||
1369 |
/* choose a track */ |
|
1370 |
static byte ChooseTrainTrack(Vehicle *v, uint tile, int direction, byte trackbits) |
|
1371 |
{ |
|
1372 |
TrainTrackFollowerData fd; |
|
1373 |
int bits = trackbits; |
|
1374 |
uint best_track; |
|
1375 |
#if 0 |
|
1376 |
int time = rdtsc(); |
|
1377 |
static float f; |
|
1378 |
#endif |
|
1379 |
||
1380 |
assert( (bits & ~0x3F) == 0); |
|
1381 |
||
1382 |
/* quick return in case only one possible direction is available */ |
|
1383 |
if (KILL_FIRST_BIT(bits) == 0) |
|
1384 |
return FIND_FIRST_BIT(bits); |
|
1385 |
||
1386 |
FillWithStationData(&fd, v); |
|
1387 |
||
1388 |
if (_patches.new_pathfinding) { |
|
1389 |
fd.best_bird_dist = (uint)-1; |
|
1390 |
fd.best_track_dist = (uint)-1; |
|
1391 |
fd.best_track = 0xFF; |
|
1392 |
NewTrainPathfind(tile - _tileoffs_by_dir[direction], direction, (TPFEnumProc*)TrainTrackFollower, &fd, NULL); |
|
1393 |
||
1394 |
// printf("Train %d %s\n", v->unitnumber, fd.best_track_dist == -1 ? "NOTFOUND" : "FOUND"); |
|
1395 |
||
1396 |
if (fd.best_track == 0xff) { |
|
1397 |
// blaha |
|
1398 |
best_track = FIND_FIRST_BIT(bits); |
|
1399 |
} else { |
|
1400 |
best_track = fd.best_track & 7; |
|
1401 |
} |
|
1402 |
} else { |
|
1403 |
int i, r; |
|
1404 |
uint best_bird_dist = 0; |
|
1405 |
uint best_track_dist = 0; |
|
1406 |
byte train_dir = v->direction & 3; |
|
1407 |
||
1408 |
||
1409 |
best_track = -1; |
|
1410 |
||
1411 |
do { |
|
1412 |
i = FIND_FIRST_BIT(bits); |
|
1413 |
bits = KILL_FIRST_BIT(bits); |
|
1414 |
||
1415 |
fd.best_bird_dist = (uint)-1; |
|
1416 |
fd.best_track_dist = (uint)-1; |
|
1417 |
||
1418 |
NewTrainPathfind(tile, _search_directions[i][direction], (TPFEnumProc*)TrainTrackFollower, &fd, NULL); |
|
1419 |
if (best_track != -1) { |
|
1420 |
if (best_track_dist == -1) { |
|
1421 |
if (fd.best_track_dist == -1) { |
|
1422 |
/* neither reached the destination, pick the one with the smallest bird dist */ |
|
1423 |
if (fd.best_bird_dist > best_bird_dist) goto bad; |
|
1424 |
if (fd.best_bird_dist < best_bird_dist) goto good; |
|
1425 |
} else { |
|
1426 |
/* we found the destination for the first time */ |
|
1427 |
goto good; |
|
1428 |
} |
|
1429 |
} else { |
|
1430 |
if (fd.best_track_dist == -1) { |
|
1431 |
/* didn't find destination, but we've found the destination previously */ |
|
1432 |
goto bad; |
|
1433 |
} else { |
|
1434 |
/* both old & new reached the destination, compare track length */ |
|
1435 |
if (fd.best_track_dist > best_track_dist) goto bad; |
|
1436 |
if (fd.best_track_dist < best_track_dist) goto good; |
|
1437 |
} |
|
1438 |
} |
|
1439 |
||
1440 |
/* if we reach this position, there's two paths of equal value so far. |
|
1441 |
* pick one randomly. */ |
|
1442 |
r = (byte)Random(); |
|
1443 |
if (_pick_track_table[i] == train_dir) r += 80; |
|
1444 |
if (_pick_track_table[best_track] == train_dir) r -= 80; |
|
1445 |
||
1446 |
if (r <= 127) goto bad; |
|
1447 |
} |
|
1448 |
good:; |
|
1449 |
best_track = i; |
|
1450 |
best_bird_dist = fd.best_bird_dist; |
|
1451 |
best_track_dist = fd.best_track_dist; |
|
1452 |
bad:; |
|
1453 |
} while (bits != 0); |
|
1454 |
// printf("Train %d %s\n", v->unitnumber, best_track_dist == -1 ? "NOTFOUND" : "FOUND"); |
|
1455 |
assert(best_track != -1); |
|
1456 |
} |
|
1457 |
||
1458 |
#if 0 |
|
1459 |
time = rdtsc() - time; |
|
1460 |
f = f * 0.99 + 0.01 * time; |
|
1461 |
printf("PF time = %d %f\n", time, f); |
|
1462 |
#endif |
|
1463 |
||
1464 |
return best_track; |
|
1465 |
} |
|
1466 |
||
1467 |
||
1468 |
static bool CheckReverseTrain(Vehicle *v) |
|
1469 |
{ |
|
1470 |
TrainTrackFollowerData fd; |
|
1471 |
int i, r; |
|
1472 |
int best_track; |
|
1473 |
uint best_bird_dist = 0; |
|
1474 |
uint best_track_dist = 0; |
|
1475 |
uint reverse, reverse_best; |
|
1476 |
||
1477 |
if (_opt.diff.line_reverse_mode != 0 || |
|
1478 |
v->u.rail.track & 0xC0 || |
|
1479 |
!(v->direction & 1)) |
|
1480 |
return false; |
|
1481 |
||
1482 |
FillWithStationData(&fd, v); |
|
1483 |
||
1484 |
best_track = -1; |
|
1485 |
reverse_best = reverse = 0; |
|
1486 |
||
1487 |
assert(v->u.rail.track); |
|
1488 |
||
1489 |
i = _search_directions[FIND_FIRST_BIT(v->u.rail.track)][v->direction>>1]; |
|
1490 |
||
1491 |
while(true) { |
|
1492 |
fd.best_bird_dist = (uint)-1; |
|
1493 |
fd.best_track_dist = (uint)-1; |
|
1494 |
||
1495 |
NewTrainPathfind(v->tile, reverse ^ i, (TPFEnumProc*)TrainTrackFollower, &fd, NULL); |
|
1496 |
||
1497 |
if (best_track != -1) { |
|
1498 |
if (best_bird_dist != 0) { |
|
1499 |
if (fd.best_bird_dist != 0) { |
|
1500 |
/* neither reached the destination, pick the one with the smallest bird dist */ |
|
1501 |
if (fd.best_bird_dist > best_bird_dist) goto bad; |
|
1502 |
if (fd.best_bird_dist < best_bird_dist) goto good; |
|
1503 |
} else { |
|
1504 |
/* we found the destination for the first time */ |
|
1505 |
goto good; |
|
1506 |
} |
|
1507 |
} else { |
|
1508 |
if (fd.best_bird_dist != 0) { |
|
1509 |
/* didn't find destination, but we've found the destination previously */ |
|
1510 |
goto bad; |
|
1511 |
} else { |
|
1512 |
/* both old & new reached the destination, compare track length */ |
|
1513 |
if (fd.best_track_dist > best_track_dist) goto bad; |
|
1514 |
if (fd.best_track_dist < best_track_dist) goto good; |
|
1515 |
} |
|
1516 |
} |
|
1517 |
||
1518 |
/* if we reach this position, there's two paths of equal value so far. |
|
1519 |
* pick one randomly. */ |
|
1520 |
r = (byte)Random(); |
|
1521 |
if (_pick_track_table[i] == (v->direction & 3)) r += 80; |
|
1522 |
if (_pick_track_table[best_track] == (v->direction & 3)) r -= 80; |
|
1523 |
if (r <= 127) goto bad; |
|
1524 |
} |
|
1525 |
good:; |
|
1526 |
best_track = i; |
|
1527 |
best_bird_dist = fd.best_bird_dist; |
|
1528 |
best_track_dist = fd.best_track_dist; |
|
1529 |
reverse_best = reverse; |
|
1530 |
bad:; |
|
1531 |
if (reverse != 0) |
|
1532 |
break; |
|
1533 |
reverse = 2; |
|
1534 |
} |
|
1535 |
||
1536 |
return reverse_best != 0; |
|
1537 |
} |
|
1538 |
||
1539 |
static bool ProcessTrainOrder(Vehicle *v) |
|
1540 |
{ |
|
1541 |
uint order; |
|
1542 |
bool result; |
|
1543 |
||
1544 |
// These are un-interruptible |
|
1545 |
if ((v->next_order & OT_MASK) >= OT_GOTO_DEPOT && (v->next_order & OT_MASK) <= OT_LEAVESTATION) { |
|
1546 |
||
1547 |
// Let a depot order in the schedule interrupt. |
|
1548 |
if ((v->next_order & (OT_MASK|OF_UNLOAD)) != (OT_GOTO_DEPOT|OF_UNLOAD)) |
|
1549 |
return false; |
|
1550 |
} |
|
1551 |
||
1552 |
if ((v->next_order & (OT_MASK|OF_UNLOAD|OF_FULL_LOAD)) == (OT_GOTO_DEPOT|OF_UNLOAD|OF_FULL_LOAD) && |
|
1553 |
v->date_of_last_service+v->service_interval > _date) { |
|
1554 |
v->cur_order_index++; |
|
1555 |
} |
|
1556 |
||
1557 |
// check if we've reached the checkpoint? |
|
1558 |
if ((v->next_order & OT_MASK) == OT_GOTO_CHECKPOINT && v->tile == v->dest_tile) { |
|
1559 |
v->cur_order_index++; |
|
1560 |
} |
|
1561 |
||
1562 |
// check if we've reached a non-stop station while TTDPatch nonstop is enabled.. |
|
1563 |
if (_patches.new_nonstop && (v->next_order & OF_NON_STOP) && v->next_order_param == _map2[v->tile]) { |
|
1564 |
v->cur_order_index++; |
|
1565 |
} |
|
1566 |
||
1567 |
// Get the current order |
|
1568 |
if (v->cur_order_index >= v->num_orders) |
|
1569 |
v->cur_order_index = 0; |
|
1570 |
order = v->schedule_ptr[v->cur_order_index]; |
|
1571 |
||
1572 |
// If no order, do nothing. |
|
1573 |
if (order == 0) { |
|
1574 |
v->next_order = OT_NOTHING; |
|
1575 |
v->dest_tile = 0; |
|
1576 |
return false; |
|
1577 |
} |
|
1578 |
||
1579 |
// If it is unchanged, keep it. |
|
1580 |
if (order == (uint)((v->next_order | (v->next_order_param<<8)))) |
|
1581 |
return false; |
|
1582 |
||
1583 |
// Otherwise set it, and determine the destination tile. |
|
1584 |
v->next_order = (byte)order; |
|
1585 |
v->next_order_param = (byte)(order >> 8); |
|
1586 |
||
1587 |
v->dest_tile = 0; |
|
1588 |
||
1589 |
result = false; |
|
1590 |
if ((order & OT_MASK) == OT_GOTO_STATION) { |
|
1591 |
if ( (byte)(order >> 8) == v->last_station_visited) |
|
1592 |
v->last_station_visited = 0xFF; |
|
1593 |
v->dest_tile = DEREF_STATION(order >> 8)->xy; |
|
1594 |
result = CheckReverseTrain(v); |
|
1595 |
} else if ((order & OT_MASK) == OT_GOTO_DEPOT) { |
|
1596 |
v->dest_tile = _depots[order >> 8].xy; |
|
1597 |
result = CheckReverseTrain(v); |
|
1598 |
} else if ((order & OT_MASK) == OT_GOTO_CHECKPOINT) { |
|
1599 |
v->dest_tile = _checkpoints[order >> 8].xy; |
|
1600 |
result = CheckReverseTrain(v); |
|
1601 |
} |
|
1602 |
||
1603 |
InvalidateVehicleOrderWidget(v); |
|
1604 |
||
1605 |
return result; |
|
1606 |
} |
|
1607 |
||
1608 |
static void MarkTrainDirty(Vehicle *v) |
|
1609 |
{ |
|
1610 |
do { |
|
1611 |
v->cur_image = GetTrainImage(v, v->direction); |
|
1612 |
MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1); |
|
1613 |
} while ( (v=v->next) != NULL); |
|
1614 |
} |
|
1615 |
||
1616 |
static void HandleTrainLoading(Vehicle *v, bool mode) |
|
1617 |
{ |
|
1618 |
if (v->next_order == OT_NOTHING) |
|
1619 |
return; |
|
1620 |
||
1621 |
if (v->next_order != OT_DUMMY) { |
|
1622 |
if ((v->next_order&OT_MASK) != OT_LOADING) |
|
1623 |
return; |
|
1624 |
||
1625 |
if (mode) |
|
1626 |
return; |
|
1627 |
||
1628 |
// don't mark the train as lost if we're loading on the final station. |
|
1629 |
if (v->next_order & OF_NON_STOP) |
|
1630 |
v->u.rail.days_since_order_progr = 0; |
|
1631 |
||
1632 |
if (--v->load_unload_time_rem) |
|
1633 |
return; |
|
1634 |
||
1635 |
if (v->next_order&OF_FULL_LOAD && CanFillVehicle(v)) { |
|
1636 |
SET_EXPENSES_TYPE(EXPENSES_TRAIN_INC); |
|
1637 |
if (LoadUnloadVehicle(v)) { |
|
1638 |
InvalidateWindow(WC_TRAINS_LIST, v->owner); |
|
1639 |
MarkTrainDirty(v); |
|
1640 |
||
1641 |
// need to update acceleration since the goods on the train changed. |
|
1642 |
UpdateTrainAcceleration(v); |
|
1643 |
} |
|
1644 |
return; |
|
1645 |
} |
|
1646 |
||
1647 |
TrainPlayLeaveStationSound(v); |
|
1648 |
||
1649 |
{ |
|
1650 |
byte b = v->next_order; |
|
1651 |
v->next_order = OT_LEAVESTATION; |
|
1652 |
||
1653 |
// If this was not the final order, don't remove it from the list. |
|
1654 |
if (!(b & OF_NON_STOP)) |
|
1655 |
return; |
|
1656 |
} |
|
1657 |
} |
|
1658 |
||
1659 |
v->u.rail.days_since_order_progr = 0; |
|
1660 |
v->cur_order_index++; |
|
1661 |
InvalidateVehicleOrderWidget(v); |
|
1662 |
} |
|
1663 |
||
1664 |
static int UpdateTrainSpeed(Vehicle *v) |
|
1665 |
{ |
|
1666 |
uint spd; |
|
1667 |
uint accel; |
|
1668 |
||
1669 |
if (v->vehstatus & VS_STOPPED || v->u.rail.flags & VRF_REVERSING) { |
|
1670 |
accel = -v->acceleration * 2; |
|
1671 |
} else { |
|
1672 |
accel = v->acceleration; |
|
1673 |
if (_patches.realistic_acceleration) { |
|
1674 |
accel = GetRealisticAcceleration(v); |
|
1675 |
} |
|
1676 |
} |
|
1677 |
||
1678 |
spd = v->subspeed + accel * 2; |
|
1679 |
v->subspeed = (byte)spd; |
|
1680 |
v->cur_speed = spd = clamp(v->cur_speed + ((int)spd >> 8), 0, v->max_speed); |
|
1681 |
||
1682 |
if (!(v->direction & 1)) spd = spd * 3 >> 2; |
|
1683 |
||
1684 |
spd += v->progress; |
|
1685 |
v->progress = (byte)spd; |
|
1686 |
return (spd >> 8); |
|
1687 |
} |
|
1688 |
||
1689 |
static void TrainEnterStation(Vehicle *v, int station) |
|
1690 |
{ |
|
1691 |
Station *st; |
|
1692 |
uint32 flags; |
|
1693 |
||
1694 |
v->last_station_visited = station; |
|
1695 |
||
1696 |
/* check if a train ever visited this station before */ |
|
1697 |
st = DEREF_STATION(station); |
|
1698 |
if (!(st->had_vehicle_of_type & HVOT_TRAIN)) { |
|
1699 |
st->had_vehicle_of_type |= HVOT_TRAIN; |
|
1700 |
SET_DPARAM16(0, st->index); |
|
1701 |
flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0); |
|
1702 |
AddNewsItem( |
|
1703 |
STR_8801_CITIZENS_CELEBRATE_FIRST, |
|
1704 |
flags, |
|
1705 |
v->index, |
|
1706 |
0); |
|
1707 |
} |
|
1708 |
||
1709 |
// Did we reach the final destination? |
|
1710 |
if ((v->next_order&OT_MASK) == OT_GOTO_STATION && v->next_order_param == (byte)station) { |
|
1711 |
// Yeah, keep the load/unload flags |
|
1712 |
// Non Stop now means if the order should be increased. |
|
1713 |
v->next_order = (v->next_order & (OF_FULL_LOAD|OF_UNLOAD)) | OF_NON_STOP | OT_LOADING; |
|
1714 |
} else { |
|
1715 |
// No, just do a simple load |
|
1716 |
v->next_order = OT_LOADING; |
|
1717 |
} |
|
1718 |
v->next_order_param = 0; |
|
1719 |
||
1720 |
SET_EXPENSES_TYPE(EXPENSES_TRAIN_INC); |
|
1721 |
if (LoadUnloadVehicle(v) != 0) { |
|
1722 |
InvalidateWindow(WC_TRAINS_LIST, v->owner); |
|
1723 |
MarkTrainDirty(v); |
|
1724 |
UpdateTrainAcceleration(v); |
|
1725 |
} |
|
1726 |
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4); |
|
1727 |
} |
|
1728 |
||
1729 |
static byte AfterSetTrainPos(Vehicle *v) |
|
1730 |
{ |
|
1731 |
byte new_z, old_z; |
|
1732 |
||
1733 |
// need this hint so it returns the right z coordinate on bridges. |
|
1734 |
_get_z_hint = v->z_pos; |
|
1735 |
new_z = GetSlopeZ(v->x_pos, v->y_pos); |
|
1736 |
_get_z_hint = 0; |
|
1737 |
||
1738 |
old_z = v->z_pos; |
|
1739 |
v->z_pos = new_z; |
|
1740 |
||
1741 |
v->u.rail.flags &= ~(VRF_GOINGUP | VRF_GOINGDOWN); |
|
1742 |
||
1743 |
if (new_z != old_z) { |
|
1744 |
v->u.rail.flags |= (new_z > old_z) ? VRF_GOINGUP : VRF_GOINGDOWN; |
|
1745 |
} |
|
1746 |
||
1747 |
VehiclePositionChanged(v); |
|
1748 |
EndVehicleMove(v); |
|
1749 |
return old_z; |
|
1750 |
} |
|
1751 |
||
1752 |
static const byte _new_vehicle_direction_table[11] = { |
|
1753 |
0, 7, 6, 0, |
|
1754 |
1, 0, 5, 0, |
|
1755 |
2, 3, 4, |
|
1756 |
}; |
|
1757 |
||
1758 |
static int GetNewVehicleDirectionByTile(uint new_tile, uint old_tile) |
|
1759 |
{ |
|
1760 |
uint offs = (GET_TILE_Y(new_tile) - GET_TILE_Y(old_tile) + 1) * 4 + |
|
1761 |
GET_TILE_X(new_tile) - GET_TILE_X(old_tile) + 1; |
|
1762 |
assert(offs < 11); |
|
1763 |
return _new_vehicle_direction_table[offs]; |
|
1764 |
} |
|
1765 |
||
1766 |
static int GetNewVehicleDirection(Vehicle *v, int x, int y) |
|
1767 |
{ |
|
1768 |
uint offs = (y - v->y_pos + 1) * 4 + (x - v->x_pos + 1); |
|
1769 |
assert(offs < 11); |
|
1770 |
return _new_vehicle_direction_table[offs]; |
|
1771 |
} |
|
1772 |
||
1773 |
static int GetDirectionToVehicle(Vehicle *v, int x, int y) |
|
1774 |
{ |
|
1775 |
byte offs; |
|
1776 |
||
1777 |
x -= v->x_pos; |
|
1778 |
if (x >= 0) { |
|
1779 |
offs = (x > 2) ? 0 : 1; |
|
1780 |
} else { |
|
1781 |
offs = (x < -2) ? 2 : 1; |
|
1782 |
} |
|
1783 |
||
1784 |
y -= v->y_pos; |
|
1785 |
if (y >= 0) { |
|
1786 |
offs += ((y > 2) ? 0 : 1) * 4; |
|
1787 |
} else { |
|
1788 |
offs += ((y < -2) ? 2 : 1) * 4; |
|
1789 |
} |
|
1790 |
||
1791 |
assert(offs < 11); |
|
1792 |
return _new_vehicle_direction_table[offs]; |
|
1793 |
} |
|
1794 |
||
1795 |
/* Check if the vehicle is compatible with the specified tile */ |
|
1796 |
static bool CheckCompatibleRail(Vehicle *v, uint tile) |
|
1797 |
{ |
|
1798 |
if (IS_TILETYPE(tile, MP_RAILWAY) || |
|
1799 |
IS_TILETYPE(tile, MP_STATION)) { |
|
1800 |
||
1801 |
} else if (IS_TILETYPE(tile, MP_TUNNELBRIDGE)) { |
|
1802 |
if ((_map5[tile] & 0xC0) == 0xC0) {// is bridge middle part? |
|
1803 |
TileInfo ti; |
|
1804 |
FindLandscapeHeightByTile(&ti, tile); |
|
1805 |
||
1806 |
// correct Z position of a train going under a bridge on slopes |
|
1807 |
if (CORRECT_Z(ti.tileh)) |
|
1808 |
ti.z += 8; |
|
1809 |
||
1810 |
if(v->z_pos != ti.z) // train is going over bridge |
|
1811 |
return true; |
|
1812 |
} |
|
1813 |
} else |
|
1814 |
return true; |
|
1815 |
||
1816 |
if (_map_owner[tile] != v->owner || |
|
1817 |
(v->subtype == 0 && (_map3_lo[tile] & 0xF) != v->u.rail.railtype)) |
|
1818 |
return false; |
|
1819 |
||
1820 |
return true; |
|
1821 |
} |
|
1822 |
||
1823 |
typedef struct { |
|
1824 |
byte small_turn, large_turn; |
|
1825 |
byte z_up; // fraction to remove when moving up |
|
1826 |
byte z_down; // fraction to remove when moving down |
|
1827 |
} RailtypeSlowdownParams; |
|
1828 |
||
1829 |
static const RailtypeSlowdownParams _railtype_slowdown[3] = { |
|
1830 |
// normal accel |
|
1831 |
{256/4, 256/2, 256/4, 2}, // normal |
|
1832 |
{256/4, 256/2, 256/4, 2}, // monorail |
|
1833 |
{0, 256/2, 256/4, 2}, // maglev |
|
1834 |
}; |
|
1835 |
||
1836 |
/* Modify the speed of the vehicle due to a turn */ |
|
1837 |
static void AffectSpeedByDirChange(Vehicle *v, byte new_dir) |
|
1838 |
{ |
|
1839 |
byte diff; |
|
1840 |
const RailtypeSlowdownParams *rsp; |
|
1841 |
||
1842 |
if (_patches.realistic_acceleration || (diff = (v->direction - new_dir) & 7) == 0) |
|
1843 |
return; |
|
1844 |
||
1845 |
rsp = &_railtype_slowdown[v->u.rail.railtype]; |
|
1846 |
v->cur_speed -= ((diff == 1 || diff == 7) ? rsp->small_turn : rsp->large_turn) * v->cur_speed >> 8; |
|
1847 |
} |
|
1848 |
||
1849 |
/* Modify the speed of the vehicle due to a change in altitude */ |
|
1850 |
static void AffectSpeedByZChange(Vehicle *v, byte old_z) |
|
1851 |
{ |
|
1852 |
const RailtypeSlowdownParams *rsp; |
|
1853 |
if (old_z == v->z_pos || _patches.realistic_acceleration) |
|
1854 |
return; |
|
1855 |
||
1856 |
rsp = &_railtype_slowdown[v->u.rail.railtype]; |
|
1857 |
||
1858 |
if (old_z < v->z_pos) { |
|
1859 |
v->cur_speed -= (v->cur_speed * rsp->z_up >> 8); |
|
1860 |
} else { |
|
1861 |
uint16 spd = v->cur_speed + rsp->z_down; |
|
1862 |
if (spd <= v->max_speed) |
|
1863 |
v->cur_speed = spd; |
|
1864 |
} |
|
1865 |
} |
|
1866 |
||
1867 |
static const byte _otherside_signal_directions[14] = { |
|
1868 |
1, 3, 1, 3, 5, 3, 0, 0, |
|
1869 |
5, 7, 7, 5, 7, 1, |
|
1870 |
}; |
|
1871 |
||
1872 |
static void TrainMovedChangeSignals(uint tile, int dir) |
|
1873 |
{ |
|
1874 |
int i; |
|
1875 |
if (IS_TILETYPE(tile, MP_RAILWAY) && (_map5[tile]&0xC0)==0x40) { |
|
1876 |
i = FindFirstBit2x64((_map5[tile]+(_map5[tile]<<8)) & _reachable_tracks[dir]); |
|
1877 |
UpdateSignalsOnSegment(tile, _otherside_signal_directions[i]); |
|
1878 |
} |
|
1879 |
} |
|
1880 |
||
1881 |
||
1882 |
typedef struct TrainCollideChecker { |
|
1883 |
Vehicle *v, *v_skip; |
|
1884 |
||
1885 |
} TrainCollideChecker; |
|
1886 |
||
1887 |
void *FindTrainCollideEnum(Vehicle *v, TrainCollideChecker *tcc) |
|
1888 |
{ |
|
1889 |
if (v == tcc->v || v == tcc->v_skip || v->type != VEH_Train || v->u.rail.track==0x80) |
|
1890 |
return 0; |
|
1891 |
||
1892 |
if ( myabs(v->z_pos - tcc->v->z_pos) > 6 || |
|
1893 |
myabs(v->x_pos - tcc->v->x_pos) >= 6 || |
|
1894 |
myabs(v->y_pos - tcc->v->y_pos) >= 6) |
|
1895 |
return NULL; |
|
1896 |
return v; |
|
1897 |
} |
|
1898 |
||
1899 |
static void SetVehicleCrashed(Vehicle *v) |
|
1900 |
{ |
|
1901 |
Vehicle *u; |
|
1902 |
||
1903 |
if (v->u.rail.crash_anim_pos != 0) |
|
1904 |
return; |
|
1905 |
||
1906 |
v->u.rail.crash_anim_pos++; |
|
1907 |
||
1908 |
u = v; |
|
1909 |
BEGIN_ENUM_WAGONS(v) |
|
1910 |
v->vehstatus |= VS_CRASHED; |
|
1911 |
END_ENUM_WAGONS(v) |
|
1912 |
||
1913 |
InvalidateWindowWidget(WC_VEHICLE_VIEW, u->index, 4); |
|
1914 |
} |
|
1915 |
||
1916 |
static int CountPassengersInTrain(Vehicle *v) |
|
1917 |
{ |
|
1918 |
int num = 0; |
|
1919 |
BEGIN_ENUM_WAGONS(v) |
|
1920 |
if (v->cargo_type == 0) num += v->cargo_count; |
|
1921 |
END_ENUM_WAGONS(v) |
|
1922 |
return num; |
|
1923 |
} |
|
1924 |
||
22 | 1925 |
/* |
1926 |
* Checks whether the specified tried has a collision with another vehicle. If |
|
1927 |
* so, destroys this vehicle, and the other vehicle if its subtype is 0 (?). |
|
1928 |
* Reports the incident in a flashy news item, modifies station ratings and |
|
1929 |
* plays a sound. |
|
1930 |
*/ |
|
0 | 1931 |
static void CheckTrainCollision(Vehicle *v) |
1932 |
{ |
|
1933 |
TrainCollideChecker tcc; |
|
98 | 1934 |
Vehicle *coll,*realcoll; |
0 | 1935 |
int num; |
1936 |
||
1937 |
/* can't collide in depot */ |
|
1938 |
if (v->u.rail.track == 0x80) |
|
1939 |
return; |
|
1940 |
||
98 | 1941 |
if ( !(v->u.rail.track == 0x40) ) |
1942 |
assert((uint)TILE_FROM_XY(v->x_pos, v->y_pos) == v->tile); |
|
0 | 1943 |
|
1944 |
tcc.v = v; |
|
1945 |
tcc.v_skip = v->next; |
|
1946 |
||
1947 |
/* find colliding vehicle */ |
|
98 | 1948 |
realcoll = coll = VehicleFromPos(TILE_FROM_XY(v->x_pos, v->y_pos), &tcc, (VehicleFromPosProc*)FindTrainCollideEnum); |
0 | 1949 |
if (coll == NULL) |
1950 |
return; |
|
98 | 1951 |
|
0 | 1952 |
|
1953 |
coll = GetFirstVehicleInChain(coll); |
|
1954 |
||
1955 |
/* it can't collide with its own wagons */ |
|
98 | 1956 |
if ( (v == coll) || ( (v->u.rail.track & 0x40) && ( (v->direction & 2) != (realcoll->direction & 2) ) ) ) |
0 | 1957 |
return; |
1958 |
||
1959 |
//two drivers + passangers killed in train v |
|
1960 |
num = 2 + CountPassengersInTrain(v); |
|
1961 |
if(!(coll->vehstatus&VS_CRASHED)) |
|
1962 |
//two drivers + passangers killed in train coll (if it was not crashed already) |
|
1963 |
num += 2 + CountPassengersInTrain(coll); |
|
1964 |
||
1965 |
SetVehicleCrashed(v); |
|
1966 |
if (coll->subtype == 0) |
|
1967 |
SetVehicleCrashed(coll); |
|
98 | 1968 |
|
0 | 1969 |
|
1970 |
SET_DPARAM16(0, num); |
|
1971 |
||
1972 |
AddNewsItem(STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL, |
|
1973 |
NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0), |
|
1974 |
v->index, |
|
1975 |
0); |
|
1976 |
||
1977 |
ModifyStationRatingAround(v->tile, v->owner, -160, 30); |
|
1978 |
SndPlayVehicleFx(17, v); |
|
1979 |
} |
|
1980 |
||
1981 |
static void *CheckVehicleAtSignal(Vehicle *v, void *data) |
|
1982 |
{ |
|
1983 |
uint32 d = (uint32)data; |
|
1984 |
||
1985 |
if (v->type == VEH_Train && v->subtype == 0 && v->tile == (TileIndex)(d >> 8)) { |
|
1986 |
byte diff = (v->direction - (byte)d + 2) & 7; |
|
1987 |
if (diff == 2 || (v->cur_speed <= 5 && diff <= 4)) |
|
1988 |
return (void*)1; |
|
1989 |
} |
|
1990 |
return 0; |
|
1991 |
} |
|
1992 |
||
1993 |
static void TrainController(Vehicle *v) |
|
1994 |
{ |
|
1995 |
Vehicle *prev = NULL; |
|
1996 |
GetNewVehiclePosResult gp; |
|
1997 |
uint32 r, tracks,ts; |
|
1998 |
int dir, i; |
|
1999 |
byte chosen_dir; |
|
2000 |
byte chosen_track; |
|
2001 |
byte old_z; |
|
2002 |
||
22 | 2003 |
/* For every vehicle after and including the given vehicle */ |
0 | 2004 |
for(;;) { |
2005 |
BeginVehicleMove(v); |
|
2006 |
||
2007 |
if (v->u.rail.track != 0x40) { |
|
22 | 2008 |
/* Not inside tunnel */ |
0 | 2009 |
if (GetNewVehiclePos(v, &gp)) { |
22 | 2010 |
/* Staying in the old tile */ |
0 | 2011 |
if (v->u.rail.track == 0x80) { |
2012 |
/* inside depot */ |
|
2013 |
gp.x = v->x_pos; |
|
2014 |
gp.y = v->y_pos; |
|
2015 |
} else { |
|
22 | 2016 |
/* is not inside depot */ |
0 | 2017 |
r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); |
2018 |
if (r & 0x8) |
|
2019 |
goto invalid_rail; |
|
2020 |
if (r & 0x2) { |
|
2021 |
TrainEnterStation(v, r >> 8); |
|
2022 |
return; |
|
2023 |
} |
|
2024 |
||
2025 |
if (v->next_order == OT_LEAVESTATION) { |
|
2026 |
v->next_order = OT_NOTHING; |
|
2027 |
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4); |
|
2028 |
} |
|
2029 |
} |
|
2030 |
} else { |
|
2031 |
/* A new tile is about to be entered. */ |
|
2032 |
||
2033 |
/* Determine what direction we're entering the new tile from */ |
|
2034 |
dir = GetNewVehicleDirectionByTile(gp.new_tile, gp.old_tile); |
|
2035 |
assert(dir==1 || dir==3 || dir==5 || dir==7); |
|
2036 |
||
2037 |
/* Get the status of the tracks in the new tile and mask |
|
2038 |
* away the bits that aren't reachable. */ |
|
2039 |
ts = GetTileTrackStatus(gp.new_tile, 0) & _reachable_tracks[dir >> 1]; |
|
2040 |
||
2041 |
/* Combine the from & to directions. |
|
2042 |
* Now, the lower byte contains the track status, and the byte at bit 16 contains |
|
2043 |
* the signal status. */ |
|
2044 |
tracks = ts|(ts >> 8); |
|
2045 |
if ( (byte) tracks == 0) |
|
2046 |
goto invalid_rail; |
|
2047 |
||
2048 |
/* Check if the new tile contrains tracks that are compatible |
|
2049 |
* with the current train, if not, bail out. */ |
|
2050 |
if (!CheckCompatibleRail(v, gp.new_tile)) |
|
2051 |
goto invalid_rail; |
|
2052 |
||
2053 |
if (prev == NULL) { |
|
2054 |
/* Currently the locomotive is active. Determine which one of the |
|
2055 |
* available tracks to choose */ |
|
2056 |
chosen_track = 1 << ChooseTrainTrack(v, gp.new_tile, dir>>1, (byte)tracks); |
|
2057 |
||
2058 |
/* Check if it's a red signal and that force proceed is not clicked. */ |
|
2059 |
if ( (tracks>>16)&chosen_track && v->u.rail.force_proceed == 0) goto red_light; |
|
2060 |
} else { |
|
2061 |
static byte _matching_tracks[8] = {0x30, 1, 0xC, 2, 0x30, 1, 0xC, 2}; |
|
2062 |
||
2063 |
/* The wagon is active, simply follow the prev vehicle. */ |
|
2064 |
chosen_track = (byte)(_matching_tracks[GetDirectionToVehicle(prev, gp.x, gp.y)] & tracks); |
|
2065 |
} |
|
2066 |
||
2067 |
/* make sure chosen track is a valid track */ |
|
2068 |
assert(chosen_track==1 || chosen_track==2 || chosen_track==4 || chosen_track==8 || chosen_track==16 || chosen_track==32); |
|
2069 |
||
2070 |
/* Update XY to reflect the entrance to the new tile, and select the direction to use */ |
|
2071 |
{ |
|
2072 |
const byte *b = _initial_tile_subcoord[FIND_FIRST_BIT(chosen_track)][dir>>1]; |
|
2073 |
gp.x = (gp.x & ~0xF) | b[0]; |
|
2074 |
gp.y = (gp.y & ~0xF) | b[1]; |
|
2075 |
chosen_dir = b[2]; |
|
2076 |
} |
|
2077 |
||
2078 |
/* Call the landscape function and tell it that the vehicle entered the tile */ |
|
2079 |
r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); |
|
2080 |
if (r&0x8) |
|
2081 |
goto invalid_rail; |
|
2082 |
||
2083 |
if (v->subtype == 0) v->load_unload_time_rem = 0; |
|
2084 |
||
2085 |
if (!(r&0x4)) { |
|
2086 |
v->tile = gp.new_tile; |
|
2087 |
v->u.rail.track = chosen_track; |
|
2088 |
} |
|
2089 |
||
2090 |
if (v->subtype == 0) |
|
2091 |
TrainMovedChangeSignals(gp.new_tile, dir>>1); |
|
2092 |
||
22 | 2093 |
/* Signals can only change when the first |
2094 |
* (above) or the last vehicle moves. */ |
|
0 | 2095 |
if (v->next == NULL) |
2096 |
TrainMovedChangeSignals(gp.old_tile, (dir>>1) ^ 2); |
|
2097 |
||
2098 |
if (prev == NULL) { |
|
2099 |
AffectSpeedByDirChange(v, chosen_dir); |
|
2100 |
} |
|
2101 |
||
2102 |
v->direction = chosen_dir; |
|
2103 |
} |
|
2104 |
} else { |
|
2105 |
/* in tunnel */ |
|
2106 |
GetNewVehiclePos(v, &gp); |
|
2107 |
||
2108 |
if (IS_TILETYPE(gp.new_tile, MP_TUNNELBRIDGE) && |
|
2109 |
!(_map5[gp.new_tile] & 0xF0)) { |
|
2110 |
r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); |
|
2111 |
if (r & 0x4) goto common; |
|
2112 |
} |
|
2113 |
||
2114 |
v->x_pos = gp.x; |
|
2115 |
v->y_pos = gp.y; |
|
2116 |
VehiclePositionChanged(v); |
|
98 | 2117 |
if (prev == NULL) |
2118 |
CheckTrainCollision(v); |
|
0 | 2119 |
goto next_vehicle; |
2120 |
} |
|
2121 |
common:; |
|
2122 |
||
2123 |
/* update image of train, as well as delta XY */ |
|
2124 |
dir = GetNewVehicleDirection(v, gp.x, gp.y); |
|
2125 |
UpdateTrainDeltaXY(v, dir); |
|
2126 |
v->cur_image = GetTrainImage(v, dir); |
|
2127 |
||
2128 |
v->x_pos = gp.x; |
|
2129 |
v->y_pos = gp.y; |
|
2130 |
||
2131 |
/* update the Z position of the vehicle */ |
|
2132 |
old_z = AfterSetTrainPos(v); |
|
2133 |
||
2134 |
if (prev == NULL) { |
|
22 | 2135 |
/* This is the first vehicle in the train */ |
0 | 2136 |
AffectSpeedByZChange(v, old_z); |
2137 |
CheckTrainCollision(v); |
|
2138 |
} |
|
2139 |
||
22 | 2140 |
next_vehicle:; |
0 | 2141 |
/* continue with next vehicle */ |
2142 |
prev = v; |
|
2143 |
if ((v=v->next) == NULL) |
|
2144 |
return; |
|
2145 |
} |
|
2146 |
||
2147 |
invalid_rail: |
|
22 | 2148 |
/* We've reached end of line?? */ |
0 | 2149 |
if (prev != NULL) { |
2150 |
error("!Disconnecting train"); |
|
2151 |
} |
|
2152 |
goto reverse_train_direction; |
|
2153 |
||
2154 |
red_light: { |
|
22 | 2155 |
/* We're in front of a red signal ?? */ |
0 | 2156 |
/* find the first set bit in ts. need to do it in 2 steps, since |
2157 |
* FIND_FIRST_BIT only handles 6 bits at a time. */ |
|
2158 |
i = FindFirstBit2x64(ts); |
|
2159 |
||
2160 |
if (!(_map3_lo[gp.new_tile] & _signal_otherdir[i])) { |
|
2161 |
v->cur_speed = 0; |
|
2162 |
v->subspeed = 0; |
|
2163 |
v->progress = 255-100; |
|
2164 |
if (++v->load_unload_time_rem < _patches.wait_oneway_signal * 20) |
|
2165 |
return; |
|
2166 |
} else if (_map3_lo[gp.new_tile] & _signal_onedir[i]){ |
|
2167 |
v->cur_speed = 0; |
|
2168 |
v->subspeed = 0; |
|
2169 |
v->progress = 255-10; |
|
2170 |
if (++v->load_unload_time_rem < _patches.wait_twoway_signal * 73) { |
|
2171 |
uint o_tile = gp.new_tile + _tileoffs_by_dir[dir>>1]; |
|
2172 |
/* check if a train is waiting on the other side */ |
|
2173 |
if (VehicleFromPos(o_tile, (void*)( (o_tile<<8) | (dir^4)), (VehicleFromPosProc*)CheckVehicleAtSignal) == NULL) |
|
2174 |
return; |
|
2175 |
} |
|
2176 |
} |
|
2177 |
} |
|
2178 |
||
2179 |
reverse_train_direction: |
|
2180 |
v->load_unload_time_rem = 0; |
|
2181 |
v->cur_speed = 0; |
|
2182 |
v->subspeed = 0; |
|
2183 |
ReverseTrainDirection(v); |
|
2184 |
||
2185 |
} |
|
2186 |
||
98 | 2187 |
extern uint CheckTunnelBusy(uint tile, int *length); |
2188 |
||
0 | 2189 |
static void DeleteLastWagon(Vehicle *v) |
2190 |
{ |
|
2191 |
Vehicle *u = v; |
|
2192 |
int t; |
|
2193 |
||
2194 |
while (v->next != NULL) { |
|
2195 |
u = v; |
|
2196 |
v = v->next; |
|
2197 |
} |
|
2198 |
u->next = NULL; |
|
2199 |
||
2200 |
InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
2201 |
DeleteWindowById(WC_VEHICLE_VIEW, v->index); |
|
2202 |
InvalidateWindow(WC_TRAINS_LIST, v->owner); |
|
2203 |
InvalidateWindow(WC_COMPANY, v->owner); |
|
2204 |
||
2205 |
BeginVehicleMove(v); |
|
2206 |
EndVehicleMove(v); |
|
2207 |
DeleteVehicle(v); |
|
2208 |
||
2209 |
if (!((t=v->u.rail.track) & 0xC0)) { |
|
2210 |
SetSignalsOnBothDir(v->tile, FIND_FIRST_BIT(t)); |
|
2211 |
} |
|
98 | 2212 |
|
2213 |
if (v->u.rail.track == 0x40) { |
|
2214 |
int length; |
|
2215 |
TileIndex endtile = CheckTunnelBusy(v->tile, &length); |
|
2216 |
if ((v->direction == 1) || (v->direction == 5) ) |
|
2217 |
SetSignalsOnBothDir(v->tile,0); |
|
2218 |
SetSignalsOnBothDir(endtile,0); |
|
2219 |
if ((v->direction == 3) || (v->direction == 7) ) |
|
2220 |
SetSignalsOnBothDir(v->tile,1); |
|
2221 |
SetSignalsOnBothDir(endtile,1); |
|
2222 |
} |
|
0 | 2223 |
} |
2224 |
||
2225 |
static void ChangeTrainDirRandomly(Vehicle *v) |
|
2226 |
{ |
|
2227 |
static int8 _random_dir_change[4] = { -1, 0, 0, 1}; |
|
2228 |
||
2229 |
do { |
|
98 | 2230 |
//I need to buffer the train direction |
2231 |
if (!v->u.rail.track & 0x40) |
|
2232 |
v->direction = (v->direction + _random_dir_change[Random()&3]) & 7; |
|
0 | 2233 |
if (!(v->vehstatus & VS_HIDDEN)) { |
2234 |
BeginVehicleMove(v); |
|
2235 |
UpdateTrainDeltaXY(v, v->direction); |
|
2236 |
v->cur_image = GetTrainImage(v, v->direction); |
|
2237 |
AfterSetTrainPos(v); |
|
2238 |
} |
|
2239 |
} while ( (v=v->next) != NULL); |
|
2240 |
} |
|
2241 |
||
2242 |
static void HandleCrashedTrain(Vehicle *v) |
|
2243 |
{ |
|
2244 |
int state = ++v->u.rail.crash_anim_pos, index; |
|
2245 |
uint32 r; |
|
2246 |
Vehicle *u; |
|
2247 |
||
98 | 2248 |
if ( (state == 4) && (v->u.rail.track != 0x40) ) { |
0 | 2249 |
CreateEffectVehicleRel(v, 4, 4, 8, EV_CRASHED_SMOKE); |
2250 |
} |
|
2251 |
||
2252 |
if (state <= 200 && (uint16)(r=Random()) <= 0x2492) { |
|
2253 |
index = (r * 10 >> 16); |
|
2254 |
||
2255 |
u = v; |
|
2256 |
do { |
|
2257 |
if (--index < 0) { |
|
2258 |
r = Random(); |
|
2259 |
||
2260 |
CreateEffectVehicleRel(u, |
|
2261 |
2 + ((r>>8)&7), |
|
2262 |
2 + ((r>>16)&7), |
|
2263 |
5 + (r&7), |
|
2264 |
EV_DEMOLISH); |
|
2265 |
break; |
|
2266 |
} |
|
2267 |
} while ( (u=u->next) != NULL); |
|
2268 |
} |
|
2269 |
||
2270 |
if (state <= 240 && !(v->tick_counter&3)) { |
|
2271 |
ChangeTrainDirRandomly(v); |
|
2272 |
} |
|
2273 |
||
2274 |
if (state >= 4440 && !(v->tick_counter&0x1F)) |
|
2275 |
DeleteLastWagon(v); |
|
2276 |
} |
|
2277 |
||
2278 |
static void HandleBrokenTrain(Vehicle *v) |
|
2279 |
{ |
|
2280 |
if (v->breakdown_ctr != 1) { |
|
2281 |
v->breakdown_ctr = 1; |
|
2282 |
v->cur_speed = 0; |
|
2283 |
||
2284 |
if (v->breakdowns_since_last_service != 255) |
|
2285 |
v->breakdowns_since_last_service++; |
|
2286 |
||
2287 |
InvalidateWindow(WC_VEHICLE_VIEW, v->index); |
|
2288 |
InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
2289 |
||
2290 |
SndPlayVehicleFx((_opt.landscape != LT_CANDY) ? 0xE : 0x3A, v); |
|
2291 |
||
2292 |
if (!(v->vehstatus & VS_HIDDEN)) { |
|
2293 |
Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE); |
|
2294 |
if (u) |
|
2295 |
u->u.special.unk0 = v->breakdown_delay * 2; |
|
2296 |
} |
|
2297 |
} |
|
2298 |
||
2299 |
if (!(v->tick_counter & 3)) { |
|
2300 |
if (!--v->breakdown_delay) { |
|
2301 |
v->breakdown_ctr = 0; |
|
2302 |
InvalidateWindow(WC_VEHICLE_VIEW, v->index); |
|
2303 |
} |
|
2304 |
} |
|
2305 |
} |
|
2306 |
||
2307 |
static const byte _breakdown_speeds[16] = { |
|
2308 |
225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15 |
|
2309 |
}; |
|
2310 |
||
2311 |
static void TrainCheckIfLineEnds(Vehicle *v) |
|
2312 |
{ |
|
2313 |
uint tile; |
|
2314 |
uint x,y; |
|
2315 |
int t; |
|
2316 |
uint32 ts; |
|
2317 |
||
2318 |
if ((uint)(t=v->breakdown_ctr) > 1) { |
|
2319 |
v->vehstatus |= VS_TRAIN_SLOWING; |
|
2320 |
||
2321 |
t = _breakdown_speeds[ ((~t) >> 4) & 0xF]; |
|
2322 |
if ((uint16)t <= v->cur_speed) |
|
2323 |
v->cur_speed = t; |
|
2324 |
} else { |
|
2325 |
v->vehstatus &= ~VS_TRAIN_SLOWING; |
|
2326 |
} |
|
2327 |
||
2328 |
// exit if inside a tunnel |
|
2329 |
if (v->u.rail.track & 0x40) |
|
2330 |
return; |
|
2331 |
||
2332 |
tile = v->tile; |
|
2333 |
||
2334 |
// tunnel entrance? |
|
2335 |
if (IS_TILETYPE(tile, MP_TUNNELBRIDGE) && |
|
2336 |
(_map5[tile] & 0xF0) == 0 && (byte)((_map5[tile] & 3)*2+1) == v->direction) |
|
2337 |
return; |
|
2338 |
||
2339 |
// depot? |
|
2340 |
if (IS_TILETYPE(tile, MP_RAILWAY) && (_map5[tile] & 0xFC) == 0xC0) |
|
2341 |
return; |
|
2342 |
||
22 | 2343 |
/* Determine the non-diagonal direction in which we will exit this tile */ |
0 | 2344 |
t = v->direction >> 1; |
2345 |
if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[t]) { |
|
2346 |
t = (t - 1) & 3; |
|
2347 |
} |
|
22 | 2348 |
/* Calculate next tile */ |
0 | 2349 |
tile += _tileoffs_by_dir[t]; |
22 | 2350 |
// determine the track status on the next tile. |
0 | 2351 |
ts = GetTileTrackStatus(tile, 0) & _reachable_tracks[t]; |
2352 |
||
22 | 2353 |
/* Calc position within the current tile ?? */ |
0 | 2354 |
x = v->x_pos & 0xF; |
2355 |
y = v->y_pos & 0xF; |
|
2356 |
||
2357 |
switch(v->direction) { |
|
2358 |
case 0: |
|
2359 |
x = (~x) + (~y) + 24; |
|
2360 |
break; |
|
2361 |
case 7: |
|
2362 |
x = y; |
|
2363 |
/* fall through */ |
|
2364 |
case 1: |
|
2365 |
x = (~x) + 16; |
|
2366 |
break; |
|
2367 |
case 2: |
|
2368 |
x = (~x) + y + 8; |
|
2369 |
break; |
|
2370 |
case 3: |
|
2371 |
x = y; |
|
2372 |
break; |
|
2373 |
case 4: |
|
2374 |
x = x + y - 8; |
|
2375 |
break; |
|
2376 |
case 6: |
|
2377 |
x = (~y) + x + 8; |
|
2378 |
break; |
|
2379 |
} |
|
2380 |
||
2381 |
if ( (uint16)ts != 0) { |
|
2382 |
if ((ts &= (ts >> 16)) == 0) { |
|
2383 |
// make a rail/road crossing red |
|
2384 |
if (IS_TILETYPE(tile, MP_STREET) && (_map5[tile] & 0xF0)==0x10) { |
|
2385 |
if (!(_map5[tile] & 4)) { |
|
2386 |
_map5[tile] |= 4; |
|
2387 |
SndPlayVehicleFx(12, v); |
|
2388 |
MarkTileDirtyByTile(tile); |
|
2389 |
} |
|
2390 |
} |
|
2391 |
return; |
|
2392 |
} |
|
2393 |
} else if (x + 4 > 15) { |
|
2394 |
v->cur_speed = 0; |
|
2395 |
ReverseTrainDirection(v); |
|
2396 |
return; |
|
2397 |
} |
|
2398 |
||
2399 |
// slow down |
|
2400 |
v->vehstatus |= VS_TRAIN_SLOWING; |
|
2401 |
t = _breakdown_speeds[x & 0xF]; |
|
2402 |
if (!(v->direction&1)) t>>=1; |
|
2403 |
if ((uint16)t < v->cur_speed) |
|
2404 |
v->cur_speed = t; |
|
2405 |
} |
|
2406 |
||
2407 |
static void TrainLocoHandler(Vehicle *v, bool mode) |
|
2408 |
{ |
|
2409 |
int j; |
|
2410 |
||
2411 |
/* train has crashed? */ |
|
2412 |
if (v->u.rail.crash_anim_pos != 0) { |
|
2413 |
if (!mode) HandleCrashedTrain(v); |
|
2414 |
return; |
|
2415 |
} |
|
2416 |
||
2417 |
if (v->u.rail.force_proceed != 0) |
|
2418 |
v->u.rail.force_proceed--; |
|
2419 |
||
2420 |
/* train is broken down? */ |
|
2421 |
if (v->breakdown_ctr != 0) { |
|
2422 |
if (v->breakdown_ctr <= 2) { |
|
2423 |
HandleBrokenTrain(v); |
|
2424 |
return; |
|
2425 |
} |
|
2426 |
v->breakdown_ctr--; |
|
2427 |
} |
|
2428 |
||
2429 |
if (v->u.rail.flags & VRF_REVERSING && v->cur_speed == 0) { |
|
2430 |
ReverseTrainDirection(v); |
|
2431 |
} |
|
2432 |
||
2433 |
/* exit if train is stopped */ |
|
2434 |
if (v->vehstatus & VS_STOPPED && v->cur_speed == 0) |
|
2435 |
return; |
|
2436 |
||
2437 |
||
2438 |
if (ProcessTrainOrder(v)) { |
|
2439 |
v->load_unload_time_rem = 0; |
|
2440 |
v->cur_speed = 0; |
|
2441 |
v->subspeed = 0; |
|
2442 |
ReverseTrainDirection(v); |
|
2443 |
return; |
|
2444 |
} |
|
2445 |
||
2446 |
HandleTrainLoading(v, mode); |
|
2447 |
||
2448 |
if ((v->next_order & OT_MASK) == OT_LOADING) |
|
2449 |
return; |
|
2450 |
||
2451 |
if (CheckTrainStayInDepot(v)) |
|
2452 |
return; |
|
2453 |
||
2454 |
if (!mode) HandleLocomotiveSmokeCloud(v); |
|
2455 |
||
2456 |
j = UpdateTrainSpeed(v); |
|
2457 |
if (j == 0) { |
|
2458 |
// if the vehicle has speed 0, update the last_speed field. |
|
2459 |
if (v->cur_speed != 0) |
|
2460 |
return; |
|
2461 |
} else { |
|
2462 |
TrainCheckIfLineEnds(v); |
|
2463 |
||
2464 |
do { |
|
2465 |
TrainController(v); |
|
2466 |
if (v->cur_speed <= 0x100) |
|
2467 |
break; |
|
2468 |
} while (--j != 0); |
|
2469 |
} |
|
2470 |
||
2471 |
SetLastSpeed(v, v->cur_speed); |
|
2472 |
} |
|
2473 |
||
2474 |
||
2475 |
void Train_Tick(Vehicle *v) |
|
2476 |
{ |
|
2477 |
if (_age_cargo_skip_counter == 0 && v->cargo_days != 0xff) |
|
2478 |
v->cargo_days++; |
|
2479 |
||
2480 |
v->tick_counter++; |
|
2481 |
||
2482 |
if (v->subtype == 0) { |
|
2483 |
TrainLocoHandler(v, false); |
|
2484 |
||
2485 |
// make sure vehicle wasn't deleted. |
|
2486 |
if (v->type == VEH_Train && v->subtype == 0) |
|
2487 |
TrainLocoHandler(v, true); |
|
2488 |
} |
|
2489 |
} |
|
2490 |
||
2491 |
||
2492 |
static const byte _depot_track_ind[4] = {0,1,0,1}; |
|
2493 |
||
2494 |
void TrainEnterDepot(Vehicle *v, uint tile) |
|
2495 |
{ |
|
2496 |
byte t; |
|
2497 |
||
2498 |
SetSignalsOnBothDir(tile, _depot_track_ind[_map5[tile]&3]); |
|
2499 |
||
2500 |
if (v->subtype != 0) |
|
2501 |
v = GetFirstVehicleInChain(v); |
|
2502 |
||
2503 |
v->date_of_last_service = _date; |
|
2504 |
v->breakdowns_since_last_service = 0; |
|
2505 |
v->reliability = _engines[v->engine_type].reliability; |
|
2506 |
InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
2507 |
||
2508 |
v->load_unload_time_rem = 0; |
|
2509 |
v->cur_speed = 0; |
|
2510 |
||
2511 |
MaybeRenewVehicle(v, EstimateTrainCost(&_rail_vehicle_info[v->engine_type])); |
|
2512 |
||
2513 |
if ((v->next_order&OT_MASK) == OT_GOTO_DEPOT) { |
|
2514 |
InvalidateWindow(WC_VEHICLE_VIEW, v->index); |
|
2515 |
||
2516 |
t = v->next_order; |
|
2517 |
v->next_order = OT_DUMMY; |
|
2518 |
||
2519 |
// Part of the schedule? |
|
2520 |
if (t & OF_UNLOAD) { v->u.rail.days_since_order_progr = 0; v->cur_order_index++; } |
|
2521 |
||
2522 |
// User initiated? |
|
2523 |
else if (t & OF_FULL_LOAD) { |
|
2524 |
v->vehstatus |= VS_STOPPED; |
|
2525 |
if (v->owner == _local_player) { |
|
2526 |
SET_DPARAM16(0, v->unitnumber); |
|
2527 |
AddNewsItem( |
|
2528 |
STR_8814_TRAIN_IS_WAITING_IN_DEPOT, |
|
2529 |
NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), |
|
2530 |
v->index, |
|
2531 |
0); |
|
2532 |
} |
|
2533 |
} |
|
2534 |
} |
|
2535 |
} |
|
2536 |
||
2537 |
static void CheckIfTrainNeedsService(Vehicle *v) |
|
2538 |
{ |
|
2539 |
uint tile; |
|
2540 |
byte depot; |
|
2541 |
||
76
30511cbc5188
(svn r77) -Fix: [1010788] AI service interval bug (tnx truesatan)
truelight
parents:
73
diff
changeset
|
2542 |
if (_patches.servint_trains == 0) |
0 | 2543 |
return; |
2544 |
||
2545 |
if (v->date_of_last_service + v->service_interval > _date) |
|
2546 |
return; |
|
2547 |
||
2548 |
if (v->vehstatus & VS_STOPPED) |
|
2549 |
return; |
|
2550 |
||
76
30511cbc5188
(svn r77) -Fix: [1010788] AI service interval bug (tnx truesatan)
truelight
parents:
73
diff
changeset
|
2551 |
if (_patches.gotodepot && ScheduleHasDepotOrders(v->schedule_ptr)) |
0 | 2552 |
return; |
2553 |
||
2554 |
// Don't interfere with a depot visit scheduled by the user, or a |
|
2555 |
// depot visit by the order list. |
|
2556 |
if ((v->next_order & OT_MASK) == OT_GOTO_DEPOT && |
|
2557 |
(v->next_order & (OF_FULL_LOAD|OF_UNLOAD)) != 0) |
|
2558 |
return; |
|
2559 |
||
2560 |
tile = FindClosestTrainDepot(v); |
|
2561 |
if (tile == (uint)-1 || GetTileDist(v->tile, tile) > 12) { |
|
2562 |
if ((v->next_order & OT_MASK) == OT_GOTO_DEPOT) { |
|
2563 |
v->next_order = OT_DUMMY; |
|
2564 |
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4); |
|
2565 |
} |
|
2566 |
return; |
|
2567 |
} |
|
2568 |
||
2569 |
depot = GetDepotByTile(tile); |
|
2570 |
||
2571 |
if ((v->next_order & OT_MASK) == OT_GOTO_DEPOT && v->next_order_param != depot && !CHANCE16(3,16)) |
|
2572 |
return; |
|
2573 |
||
2574 |
v->next_order = OT_GOTO_DEPOT | OF_NON_STOP; |
|
2575 |
v->next_order_param = depot; |
|
2576 |
v->dest_tile = tile; |
|
2577 |
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4); |
|
2578 |
} |
|
2579 |
||
2580 |
int32 GetTrainRunningCost(Vehicle *v) |
|
2581 |
{ |
|
2582 |
int32 cost = 0; |
|
2583 |
||
2584 |
do { |
|
2585 |
const RailVehicleInfo *rvi = &_rail_vehicle_info[v->engine_type]; |
|
2586 |
if (rvi->running_cost_base) |
|
2587 |
cost += rvi->running_cost_base * _price.running_rail[rvi->engclass]; |
|
2588 |
} while ( (v=v->next) != NULL ); |
|
2589 |
||
2590 |
return cost; |
|
2591 |
} |
|
2592 |
||
2593 |
void OnNewDay_Train(Vehicle *v) |
|
2594 |
{ |
|
2595 |
TileIndex tile; |
|
2596 |
||
2597 |
if ((++v->day_counter & 7) == 0) |
|
2598 |
DecreaseVehicleValue(v); |
|
2599 |
||
2600 |
if (v->subtype == 0) { |
|
2601 |
CheckVehicleBreakdown(v); |
|
2602 |
AgeVehicle(v); |
|
2603 |
||
2604 |
CheckIfTrainNeedsService(v); |
|
2605 |
||
2606 |
// check if train hasn't advanced in its order list for a set number of days |
|
2607 |
if (_patches.lost_train_days && v->num_orders && !(v->vehstatus & VS_STOPPED) && ++v->u.rail.days_since_order_progr >= _patches.lost_train_days && v->owner == _local_player) { |
|
2608 |
v->u.rail.days_since_order_progr = 0; |
|
2609 |
SET_DPARAM16(0, v->unitnumber); |
|
2610 |
AddNewsItem( |
|
2611 |
STR_TRAIN_IS_LOST, |
|
2612 |
NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), |
|
2613 |
v->index, |
|
2614 |
0); |
|
2615 |
} |
|
2616 |
||
19
6080d2b6a959
(svn r20) Feature: warning when a vehicle has invalid orders (celestar)
dominik
parents:
15
diff
changeset
|
2617 |
CheckOrders(v); |
6080d2b6a959
(svn r20) Feature: warning when a vehicle has invalid orders (celestar)
dominik
parents:
15
diff
changeset
|
2618 |
|
0 | 2619 |
/* update destination */ |
2620 |
if ((v->next_order & OT_MASK) == OT_GOTO_STATION && |
|
2621 |
(tile=DEREF_STATION(v->next_order_param)->train_tile) != 0) |
|
2622 |
v->dest_tile = tile; |
|
2623 |
||
2624 |
if ((v->vehstatus & VS_STOPPED) == 0) { |
|
2625 |
/* running costs */ |
|
2626 |
int32 cost = GetTrainRunningCost(v) / 364; |
|
2627 |
||
2628 |
v->profit_this_year -= cost >> 8; |
|
2629 |
||
2630 |
SET_EXPENSES_TYPE(EXPENSES_TRAIN_RUN); |
|
2631 |
SubtractMoneyFromPlayerFract(v->owner, cost); |
|
2632 |
||
2633 |
InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
2634 |
InvalidateWindow(WC_TRAINS_LIST, v->owner); |
|
2635 |
} |
|
2636 |
} |
|
2637 |
} |
|
2638 |
||
2639 |
void TrainsYearlyLoop() |
|
2640 |
{ |
|
2641 |
Vehicle *v; |
|
2642 |
||
2643 |
FOR_ALL_VEHICLES(v) { |
|
2644 |
if (v->type == VEH_Train && v->subtype == 0) { |
|
2645 |
||
2646 |
// show warning if train is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) |
|
2647 |
if (_patches.train_income_warn && v->owner == _local_player && v->age >= 730 && v->profit_this_year < 0) { |
|
2648 |
SET_DPARAM32(1, v->profit_this_year); |
|
2649 |
SET_DPARAM16(0, v->unitnumber); |
|
2650 |
AddNewsItem( |
|
2651 |
STR_TRAIN_IS_UNPROFITABLE, |
|
2652 |
NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), |
|
2653 |
v->index, |
|
2654 |
0); |
|
2655 |
} |
|
2656 |
||
2657 |
v->profit_last_year = v->profit_this_year; |
|
2658 |
v->profit_this_year = 0; |
|
2659 |
InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
2660 |
} |
|
2661 |
} |
|
2662 |
} |
|
2663 |
||
2664 |
extern void ShowTrainViewWindow(Vehicle *v); |
|
2665 |
||
2666 |
void HandleClickOnTrain(Vehicle *v) |
|
2667 |
{ |
|
2668 |
if (v->subtype != 0) v = GetFirstVehicleInChain(v); |
|
2669 |
ShowTrainViewWindow(v); |
|
2670 |
} |
|
2671 |
||
2672 |
void InitializeTrains() |
|
2673 |
{ |
|
2674 |
_age_cargo_skip_counter = 1; |
|
2675 |
} |
|
2676 |
||
2677 |
int ScheduleHasDepotOrders(uint16 *schedule) |
|
2678 |
{ |
|
2679 |
for (;*schedule!=0;schedule++) |
|
2680 |
if ((*schedule&OT_MASK) == OT_GOTO_DEPOT) |
|
2681 |
return true; |
|
2682 |
return false; |
|
2683 |
} |