|
|
#include "stdafx.h"
|
|
|
#include "ttd.h"
|
|
|
#include "airport.h"
|
|
|
#include "depot.h"
|
|
|
#include "table/strings.h"
|
|
|
#include "vehicle.h"
|
|
|
#include "waypoint.h"
|
|
|
#include "command.h"
|
|
|
#include "station.h"
|
|
|
#include "player.h"
|
|
|
#include "news.h"
|
|
|
#include "saveload.h"
|
|
|
|
|
|
enum {
|
|
|
/* Max orders: 64000 (64 * 1000) */
|
|
|
ORDER_POOL_BLOCK_SIZE_BITS = 6, /* In bits, so (1 << 6) == 64 */
|
|
|
ORDER_POOL_MAX_BLOCKS = 1000,
|
|
|
};
|
|
|
|
|
@@ -138,48 +141,184 @@ void AssignOrder(Order *order, Order dat
|
|
|
|
|
|
/**
|
|
|
*
|
|
|
* Add an order to the orderlist of a vehicle
|
|
|
*
|
|
|
* @param veh_sel First 16 bits are the ID of the vehicle. The next 16 are the selected order (if any)
|
|
|
* If the lastone is given, order will be inserted above thatone
|
|
|
* @param packed_order Packed order to insert
|
|
|
*
|
|
|
*/
|
|
|
int32 CmdInsertOrder(int x, int y, uint32 flags, uint32 veh_sel, uint32 packed_order)
|
|
|
{
|
|
|
Vehicle *v = GetVehicle(veh_sel & 0xFFFF);
|
|
|
Vehicle *v;
|
|
|
int sel = veh_sel >> 16;
|
|
|
Order new_order = UnpackOrder(packed_order);
|
|
|
|
|
|
if (!IsVehicleIndex(veh_sel & 0xFFFF)) return CMD_ERROR;
|
|
|
v = GetVehicle(veh_sel & 0xFFFF);
|
|
|
if (v->type == 0 || !CheckOwnership(v->owner)) return CMD_ERROR;
|
|
|
|
|
|
switch (new_order.type) {
|
|
|
case OT_GOTO_STATION: {
|
|
|
const Station* st;
|
|
|
|
|
|
if (!IsStationIndex(new_order.station)) return CMD_ERROR;
|
|
|
st = GetStation(new_order.station);
|
|
|
|
|
|
if (!IsValidStation(st) ||
|
|
|
(st->airport_type != AT_OILRIG && !CheckOwnership(st->owner))) {
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
|
|
|
switch (v->type) {
|
|
|
case VEH_Train:
|
|
|
if (!(st->facilities & FACIL_TRAIN)) return CMD_ERROR;
|
|
|
break;
|
|
|
|
|
|
case VEH_Road:
|
|
|
if (v->cargo_type == CT_PASSENGERS) {
|
|
|
if (!(st->facilities & FACIL_BUS_STOP)) return CMD_ERROR;
|
|
|
} else {
|
|
|
if (!(st->facilities & FACIL_TRUCK_STOP)) return CMD_ERROR;
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case VEH_Ship:
|
|
|
if (!(st->facilities & FACIL_DOCK)) return CMD_ERROR;
|
|
|
break;
|
|
|
|
|
|
case VEH_Aircraft:
|
|
|
if (!(st->facilities & FACIL_AIRPORT)) return CMD_ERROR;
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
|
|
|
switch (new_order.flags) {
|
|
|
case 0:
|
|
|
case OF_FULL_LOAD:
|
|
|
case OF_UNLOAD:
|
|
|
case OF_NON_STOP:
|
|
|
case OF_NON_STOP | OF_FULL_LOAD:
|
|
|
case OF_NON_STOP | OF_UNLOAD:
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
case OT_GOTO_DEPOT: {
|
|
|
if (v->type == VEH_Aircraft) {
|
|
|
const Station* st;
|
|
|
|
|
|
if (!IsStationIndex(new_order.station)) return CMD_ERROR;
|
|
|
st = GetStation(new_order.station);
|
|
|
|
|
|
if (!IsValidStation(st) ||
|
|
|
(st->airport_type != AT_OILRIG && !CheckOwnership(st->owner)) ||
|
|
|
!(st->facilities & FACIL_AIRPORT) ||
|
|
|
GetAirport(st->airport_type)->nof_depots == 0) {
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
} else {
|
|
|
const Depot* dp;
|
|
|
|
|
|
if (!IsDepotIndex(new_order.station)) return CMD_ERROR;
|
|
|
dp = GetDepot(new_order.station);
|
|
|
|
|
|
if (!IsValidDepot(dp) ||
|
|
|
!CheckOwnership(GetTileOwner(dp->xy))) {
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
|
|
|
switch (v->type) {
|
|
|
case VEH_Train:
|
|
|
if (!IsTileDepotType(dp->xy, TRANSPORT_RAIL)) return CMD_ERROR;
|
|
|
break;
|
|
|
|
|
|
case VEH_Road:
|
|
|
if (!IsTileDepotType(dp->xy, TRANSPORT_ROAD)) return CMD_ERROR;
|
|
|
break;
|
|
|
|
|
|
case VEH_Ship:
|
|
|
if (!IsTileDepotType(dp->xy, TRANSPORT_WATER)) return CMD_ERROR;
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
switch (new_order.flags) {
|
|
|
case OF_PART_OF_ORDERS:
|
|
|
case OF_PART_OF_ORDERS | OF_HALT_IN_DEPOT:
|
|
|
case OF_NON_STOP | OF_PART_OF_ORDERS:
|
|
|
case OF_NON_STOP | OF_PART_OF_ORDERS | OF_HALT_IN_DEPOT:
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
case OT_GOTO_WAYPOINT: {
|
|
|
const Waypoint* wp;
|
|
|
|
|
|
if (v->type != VEH_Train) return CMD_ERROR;
|
|
|
|
|
|
if (!IsWaypointIndex(new_order.station)) return CMD_ERROR;
|
|
|
wp = GetWaypoint(new_order.station);
|
|
|
|
|
|
if (!CheckOwnership(GetTileOwner(wp->xy))) return CMD_ERROR;
|
|
|
|
|
|
switch (new_order.flags) {
|
|
|
case 0:
|
|
|
case OF_NON_STOP:
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
|
|
|
if (sel > v->num_orders)
|
|
|
return_cmd_error(STR_EMPTY);
|
|
|
|
|
|
if (IsOrderPoolFull())
|
|
|
return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS);
|
|
|
|
|
|
/* XXX - This limit is only here because the backuppedorders can't
|
|
|
handle any more then this.. */
|
|
|
if (v->num_orders >= 40)
|
|
|
return_cmd_error(STR_8832_TOO_MANY_ORDERS);
|
|
|
|
|
|
/* For ships, make sure that the station is not too far away from the
|
|
|
* previous destination, for human players with new pathfinding disabled */
|
|
|
if (v->type == VEH_Ship && IS_HUMAN_PLAYER(v->owner) &&
|
|
|
sel != 0 && GetVehicleOrder(v, sel - 1)->type == OT_GOTO_STATION
|
|
|
&& !_patches.new_pathfinding_all) {
|
|
|
|
|
|
int dist = DistanceManhattan(
|
|
|
GetStation(GetVehicleOrder(v, sel - 1)->station)->xy,
|
|
|
GetStation(new_order.station)->xy
|
|
|
GetStation(new_order.station)->xy // XXX type != OT_GOTO_STATION?
|
|
|
);
|
|
|
if (dist >= 130)
|
|
|
return_cmd_error(STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO);
|
|
|
}
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
Order *new;
|
|
|
Vehicle *u;
|
|
|
|
|
|
new = AllocateOrder();
|
|
|
AssignOrder(new, new_order);
|
|
|
|
|
@@ -260,28 +399,33 @@ static int32 DecloneOrder(Vehicle *dst,
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
*
|
|
|
* Delete an order from the orderlist of a vehicle
|
|
|
*
|
|
|
* @param vehicle_id The ID of the vehicle
|
|
|
* @param selected The order to delete
|
|
|
*
|
|
|
*/
|
|
|
int32 CmdDeleteOrder(int x, int y, uint32 flags, uint32 vehicle_id, uint32 selected)
|
|
|
{
|
|
|
Vehicle *v = GetVehicle(vehicle_id), *u;
|
|
|
Vehicle *v;
|
|
|
Vehicle *u;
|
|
|
uint sel = selected;
|
|
|
Order *order;
|
|
|
|
|
|
if (!IsVehicleIndex(vehicle_id)) return CMD_ERROR;
|
|
|
v = GetVehicle(vehicle_id);
|
|
|
if (v->type == 0 || !CheckOwnership(v->owner)) return CMD_ERROR;
|
|
|
|
|
|
/* XXX -- Why is this here? :s */
|
|
|
_error_message = STR_EMPTY;
|
|
|
|
|
|
/* If we did not select an order, we maybe want to de-clone the orders */
|
|
|
if (sel >= v->num_orders)
|
|
|
return DecloneOrder(v, flags);
|
|
|
|
|
|
order = GetVehicleOrder(v, sel);
|
|
|
if (order == NULL)
|
|
|
return CMD_ERROR;
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
@@ -338,25 +482,29 @@ int32 CmdDeleteOrder(int x, int y, uint3
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
*
|
|
|
* Goto next order of order-list
|
|
|
*
|
|
|
* @param vehicle_id The ID of the vehicle
|
|
|
*
|
|
|
*/
|
|
|
int32 CmdSkipOrder(int x, int y, uint32 flags, uint32 vehicle_id, uint32 not_used)
|
|
|
{
|
|
|
Vehicle *v = GetVehicle(vehicle_id);
|
|
|
Vehicle *v;
|
|
|
|
|
|
if (!IsVehicleIndex(vehicle_id)) return CMD_ERROR;
|
|
|
v = GetVehicle(vehicle_id);
|
|
|
if (v->type == 0 || !CheckOwnership(v->owner)) return CMD_ERROR;
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
/* Goto next order */
|
|
|
{
|
|
|
byte b = v->cur_order_index + 1;
|
|
|
if (b >= v->num_orders)
|
|
|
b = 0;
|
|
|
|
|
|
v->cur_order_index = b;
|
|
|
|
|
|
if (v->type == VEH_Train)
|
|
|
v->u.rail.days_since_order_progr = 0;
|
|
@@ -385,52 +533,59 @@ int32 CmdSkipOrder(int x, int y, uint32
|
|
|
|
|
|
/**
|
|
|
*
|
|
|
* Add an order to the orderlist of a vehicle
|
|
|
*
|
|
|
* @param veh_sel First 16 bits are the ID of the vehicle. The next 16 are the selected order (if any)
|
|
|
* If the lastone is given, order will be inserted above thatone
|
|
|
* @param mode Mode to change the order to
|
|
|
*
|
|
|
*/
|
|
|
int32 CmdModifyOrder(int x, int y, uint32 flags, uint32 veh_sel, uint32 mode)
|
|
|
{
|
|
|
Vehicle *v = GetVehicle(veh_sel & 0xFFFF);
|
|
|
Vehicle *v;
|
|
|
byte sel = veh_sel >> 16;
|
|
|
Order *order;
|
|
|
|
|
|
if (!IsVehicleIndex(veh_sel & 0xFFFF)) return CMD_ERROR;
|
|
|
v = GetVehicle(veh_sel & 0xFFFF);
|
|
|
if (v->type == 0 || !CheckOwnership(v->owner)) return CMD_ERROR;
|
|
|
|
|
|
/* Is it a valid order? */
|
|
|
if (sel >= v->num_orders)
|
|
|
return CMD_ERROR;
|
|
|
|
|
|
order = GetVehicleOrder(v, sel);
|
|
|
if (order->type != OT_GOTO_STATION &&
|
|
|
(order->type != OT_GOTO_DEPOT || mode == OFB_UNLOAD) &&
|
|
|
(order->type != OT_GOTO_WAYPOINT || mode != OFB_NON_STOP))
|
|
|
return CMD_ERROR;
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
switch (mode) {
|
|
|
case OFB_FULL_LOAD:
|
|
|
TOGGLEBIT(order->flags, OFB_FULL_LOAD);
|
|
|
if (order->type != OT_GOTO_DEPOT)
|
|
|
CLRBIT(order->flags, OFB_UNLOAD);
|
|
|
break;
|
|
|
case OFB_UNLOAD:
|
|
|
TOGGLEBIT(order->flags, OFB_UNLOAD);
|
|
|
CLRBIT(order->flags, OFB_FULL_LOAD);
|
|
|
break;
|
|
|
case OFB_NON_STOP:
|
|
|
TOGGLEBIT(order->flags, OFB_NON_STOP);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
|
|
|
/* Update the windows, also for vehicles that share the same order list */
|
|
|
{
|
|
|
Vehicle *u = GetFirstVehicleFromSharedList(v);
|
|
|
while (u != NULL) {
|
|
|
InvalidateVehicleOrder(u);
|
|
|
u = u->next_shared;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -644,26 +799,31 @@ void RestoreVehicleOrders(Vehicle *v, Ba
|
|
|
|
|
|
/**
|
|
|
*
|
|
|
* Restore the current-order-index of a vehicle and sets service-interval
|
|
|
*
|
|
|
* @param vehicle_id The ID of the vehicle
|
|
|
* @param data First 16 bits are the current-order-index
|
|
|
* The last 16 bits are the service-interval
|
|
|
*
|
|
|
*/
|
|
|
int32 CmdRestoreOrderIndex(int x, int y, uint32 flags, uint32 vehicle_id, uint32 data)
|
|
|
{
|
|
|
Vehicle* v;
|
|
|
|
|
|
if (!IsVehicleIndex(vehicle_id)) return CMD_ERROR;
|
|
|
v = GetVehicle(vehicle_id);
|
|
|
if (v->type == 0 || !CheckOwnership(v->owner)) return CMD_ERROR;
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
Vehicle *v = GetVehicle(vehicle_id);
|
|
|
v->service_interval = data >> 16;
|
|
|
v->cur_order_index = data & 0xFFFF;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
*
|
|
|
* Check the orders of a vehicle, to see if there are invalid orders and stuff
|
|
|
*
|
|
|
*/
|