truelight@0: #include "stdafx.h" truelight@0: #include "ttd.h" tron@507: #include "table/strings.h" truelight@0: #include "vehicle.h" truelight@0: #include "command.h" truelight@0: #include "station.h" truelight@0: #include "player.h" dominik@19: #include "news.h" truelight@0: truelight@0: /* p1 & 0xFFFF = vehicle truelight@0: * p1 >> 16 = index in order list truelight@0: * p2 = order command to insert truelight@0: */ truelight@0: int32 CmdInsertOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@0: Vehicle *v = &_vehicles[p1 & 0xFFFF]; truelight@0: int sel = p1 >> 16; tron@555: Order new_order = UnpackOrder(p2); truelight@0: truelight@0: if (sel > v->num_orders) return_cmd_error(STR_EMPTY); truelight@0: if (_ptr_to_next_order == endof(_order_array)) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); truelight@0: if (v->num_orders >= 40) return_cmd_error(STR_8832_TOO_MANY_ORDERS); truelight@0: truelight@0: // for ships, make sure that the station is not too far away from the previous destination. truelight@0: if (v->type == VEH_Ship && IS_HUMAN_PLAYER(v->owner) && tron@555: sel != 0 && v->schedule_ptr[sel - 1].type == OT_GOTO_STATION) { truelight@193: tron@555: int dist = GetTileDist( tron@555: DEREF_STATION(v->schedule_ptr[sel - 1].station)->xy, tron@555: DEREF_STATION(new_order.station)->xy tron@555: ); truelight@0: if (dist >= 130) truelight@0: return_cmd_error(STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO); truelight@0: } truelight@0: truelight@0: if (flags & DC_EXEC) { tron@555: Order *s1; tron@555: Order *s2; truelight@0: Vehicle *u; truelight@0: truelight@0: s1 = &v->schedule_ptr[sel]; truelight@0: s2 = _ptr_to_next_order++; truelight@0: do s2[1] = s2[0]; while (--s2 >= s1); tron@555: *s1 = new_order; truelight@0: truelight@0: s1 = v->schedule_ptr; truelight@193: truelight@0: FOR_ALL_VEHICLES(u) { truelight@0: if (u->type != 0 && u->schedule_ptr != NULL) { truelight@0: if (s1 < u->schedule_ptr) { truelight@0: u->schedule_ptr++; truelight@0: } else if (s1 == u->schedule_ptr) { // handle shared orders truelight@0: u->num_orders++; truelight@0: truelight@0: if ((byte)sel <= u->cur_order_index) { truelight@0: sel++; truelight@0: if ((byte)sel < u->num_orders) truelight@0: u->cur_order_index = sel; truelight@0: } truelight@0: InvalidateWindow(WC_VEHICLE_VIEW, u->index); truelight@0: InvalidateWindow(WC_VEHICLE_ORDERS, u->index); truelight@0: } truelight@0: } truelight@0: } tron@588: tron@588: RebuildVehicleLists(); truelight@0: } truelight@0: truelight@0: return 0; truelight@0: } truelight@0: truelight@0: static int32 DecloneOrder(Vehicle *dst, uint32 flags) truelight@0: { truelight@0: if (_ptr_to_next_order == endof(_order_array)) truelight@0: return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); truelight@0: truelight@0: if (flags & DC_EXEC) { truelight@0: DeleteVehicleSchedule(dst); truelight@193: truelight@0: dst->num_orders = 0; tron@555: _ptr_to_next_order->type = OT_NOTHING; tron@555: _ptr_to_next_order->flags = 0; tron@555: dst->schedule_ptr = _ptr_to_next_order++; truelight@193: truelight@0: InvalidateWindow(WC_VEHICLE_ORDERS, dst->index); tron@588: tron@588: RebuildVehicleLists(); truelight@0: } truelight@0: return 0; truelight@0: } truelight@0: truelight@0: /* p1 = vehicle truelight@0: * p2 = sel truelight@0: */ truelight@0: int32 CmdDeleteOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@0: Vehicle *v = &_vehicles[p1], *u; truelight@0: uint sel = (uint)p2; truelight@0: truelight@0: _error_message = STR_EMPTY; truelight@0: if (sel >= v->num_orders) truelight@0: return DecloneOrder(v, flags); truelight@0: truelight@0: if (flags & DC_EXEC) { tron@555: Order *s1 = &v->schedule_ptr[sel]; truelight@0: truelight@0: // copy all orders to get rid of the hole truelight@0: do s1[0] = s1[1]; while (++s1 != _ptr_to_next_order); truelight@0: _ptr_to_next_order--; truelight@0: truelight@0: s1 = v->schedule_ptr; truelight@0: truelight@0: FOR_ALL_VEHICLES(u) { truelight@0: if (u->type != 0 && u->schedule_ptr != NULL) { truelight@0: if (s1 < u->schedule_ptr) { truelight@0: u->schedule_ptr--; truelight@0: } else if (s1 == u->schedule_ptr) {// handle shared orders truelight@0: u->num_orders--; truelight@0: if ((byte)sel < u->cur_order_index) truelight@0: u->cur_order_index--; truelight@0: tron@555: if ((byte)sel == u->cur_order_index && tron@555: u->current_order.type == OT_LOADING && tron@555: u->current_order.flags & OF_NON_STOP) { tron@555: u->current_order.flags = 0; tron@555: } truelight@0: truelight@0: InvalidateWindow(WC_VEHICLE_VIEW, u->index); truelight@0: InvalidateWindow(WC_VEHICLE_ORDERS, u->index); truelight@0: } truelight@0: } truelight@0: } tron@588: tron@588: RebuildVehicleLists(); truelight@0: } truelight@193: truelight@0: return 0; truelight@0: } truelight@0: truelight@0: /* p1 = vehicle */ truelight@0: int32 CmdSkipOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@0: if (flags & DC_EXEC) { truelight@0: Vehicle *v = &_vehicles[p1]; truelight@0: truelight@0: { truelight@0: byte b = v->cur_order_index + 1; truelight@0: if (b >= v->num_orders) b = 0; truelight@0: v->cur_order_index = b; truelight@193: truelight@0: if (v->type == VEH_Train) truelight@0: v->u.rail.days_since_order_progr = 0; truelight@0: } truelight@0: tron@555: if (v->current_order.type == OT_LOADING && tron@555: v->current_order.flags & OF_NON_STOP) { tron@555: v->current_order.flags = 0; tron@555: } truelight@0: truelight@0: InvalidateWindow(WC_VEHICLE_ORDERS, v->index); truelight@0: } truelight@0: return 0; truelight@0: } truelight@0: truelight@0: /* p1 = vehicle truelight@0: * p2&0xFF = sel truelight@0: * p2>>8 = mode truelight@0: */ truelight@0: int32 CmdModifyOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@0: Vehicle *v = &_vehicles[p1]; truelight@0: byte sel = (byte)p2; tron@555: Order *sched; truelight@0: truelight@0: if (sel >= v->num_orders) truelight@0: return CMD_ERROR; truelight@0: truelight@0: sched = &v->schedule_ptr[sel]; tron@555: if (sched->type != OT_GOTO_STATION && tron@555: (sched->type != OT_GOTO_DEPOT || (p2 >> 8) == 1)) truelight@0: return CMD_ERROR; truelight@0: truelight@0: if (flags & DC_EXEC) { tron@555: switch (p2 >> 8) { truelight@0: case 0: // full load tron@555: sched->flags ^= OF_FULL_LOAD; tron@555: if (sched->type != OT_GOTO_DEPOT) sched->flags &= ~OF_UNLOAD; truelight@0: break; truelight@0: case 1: // unload tron@555: sched->flags ^= OF_UNLOAD; tron@555: sched->flags &= ~OF_FULL_LOAD; truelight@0: break; truelight@0: case 2: // non stop tron@555: sched->flags ^= OF_NON_STOP; truelight@0: break; truelight@0: } truelight@0: sched = v->schedule_ptr; truelight@0: truelight@0: FOR_ALL_VEHICLES(v) { truelight@0: if (v->schedule_ptr == sched) truelight@0: InvalidateWindow(WC_VEHICLE_ORDERS, v->index); truelight@0: } truelight@193: truelight@0: } truelight@193: truelight@0: return 0; truelight@0: } truelight@0: truelight@0: // Clone an order truelight@0: // p1 & 0xFFFF is destination vehicle truelight@0: // p1 >> 16 is source vehicle truelight@0: truelight@0: // p2 is truelight@0: // 0 - clone truelight@0: // 1 - copy truelight@0: // 2 - unclone truelight@0: truelight@0: truelight@0: int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@0: Vehicle *dst = &_vehicles[p1 & 0xFFFF]; truelight@193: truelight@0: if (!(dst->type && dst->owner == _current_player)) truelight@0: return CMD_ERROR; truelight@0: truelight@0: switch(p2) { truelight@193: truelight@0: // share vehicle orders? truelight@0: case 0: { truelight@0: Vehicle *src = &_vehicles[p1 >> 16]; truelight@0: truelight@0: // sanity checks truelight@0: if (!(src->owner == _current_player && dst->type == src->type && dst != src)) truelight@0: return CMD_ERROR; truelight@193: darkvater@29: // let's see what happens with road vehicles darkvater@29: if (src->type == VEH_Road) { darkvater@29: if (src->cargo_type != dst->cargo_type && (src->cargo_type == CT_PASSENGERS || dst->cargo_type == CT_PASSENGERS)) darkvater@29: return CMD_ERROR; darkvater@29: } truelight@0: truelight@0: if (flags & DC_EXEC) { truelight@0: DeleteVehicleSchedule(dst); truelight@0: dst->schedule_ptr = src->schedule_ptr; truelight@0: dst->num_orders = src->num_orders; truelight@0: truelight@0: InvalidateWindow(WC_VEHICLE_ORDERS, src->index); truelight@0: InvalidateWindow(WC_VEHICLE_ORDERS, dst->index); tron@588: tron@588: RebuildVehicleLists(); truelight@0: } truelight@0: break; truelight@0: } truelight@0: truelight@0: // copy vehicle orders? truelight@0: case 1: { truelight@0: Vehicle *src = &_vehicles[p1 >> 16]; truelight@0: int delta; truelight@0: truelight@0: // sanity checks truelight@0: if (!(src->owner == _current_player && dst->type == src->type && dst != src)) truelight@0: return CMD_ERROR; truelight@193: darkvater@29: // let's see what happens with road vehicles darkvater@29: if (src->type == VEH_Road) { tron@555: const Order *i; darkvater@29: TileIndex required_dst; truelight@193: tron@555: for (i = src->schedule_ptr; i->type != OT_NOTHING; ++i) { tron@555: if (i->type == OT_GOTO_STATION) { tron@555: const Station *st = DEREF_STATION(i->station); darkvater@29: required_dst = (dst->cargo_type == CT_PASSENGERS) ? st->bus_tile : st->lorry_tile; darkvater@29: if ( !required_dst ) darkvater@29: return CMD_ERROR; darkvater@29: } darkvater@29: } darkvater@29: } truelight@0: truelight@0: // make sure there's orders available truelight@0: delta = IsScheduleShared(dst) ? src->num_orders + 1 : src->num_orders - dst->num_orders; truelight@0: if (delta > endof(_order_array) - _ptr_to_next_order) truelight@0: return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); truelight@0: truelight@0: if (flags & DC_EXEC) { truelight@0: DeleteVehicleSchedule(dst); truelight@0: dst->schedule_ptr = _ptr_to_next_order; truelight@0: dst->num_orders = src->num_orders; truelight@0: _ptr_to_next_order += src->num_orders + 1; truelight@0: memcpy(dst->schedule_ptr, src->schedule_ptr, (src->num_orders + 1) * sizeof(uint16)); truelight@0: truelight@0: InvalidateWindow(WC_VEHICLE_ORDERS, dst->index); tron@588: tron@588: RebuildVehicleLists(); truelight@0: } truelight@0: break; truelight@0: } truelight@0: truelight@0: // declone vehicle orders? truelight@0: case 2: return DecloneOrder(dst, flags); truelight@0: } truelight@0: truelight@0: return 0; truelight@0: } truelight@0: truelight@0: void BackupVehicleOrders(Vehicle *v, BackuppedOrders *bak) truelight@0: { truelight@0: Vehicle *u = IsScheduleShared(v); truelight@0: truelight@0: bak->orderindex = v->cur_order_index; truelight@0: bak->service_interval = v->service_interval; truelight@193: truelight@0: if ((v->string_id & 0xF800) != 0x7800) { truelight@0: bak->name[0] = 0; truelight@0: } else { truelight@0: GetName(v->string_id & 0x7FF, bak->name); truelight@0: } truelight@0: truelight@0: // stored shared orders in this special way? tron@555: if (u != NULL) { tron@555: bak->clone = u->index; tron@555: } else { tron@555: Order *sched = v->schedule_ptr; tron@555: Order *os = bak->order; tron@555: tron@555: bak->clone = INVALID_VEHICLE; tron@555: tron@555: do { tron@555: *os++ = *sched++; tron@555: } while (sched->type != OT_NOTHING); truelight@0: } truelight@0: } truelight@0: truelight@0: void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak) truelight@0: { tron@555: int i; truelight@0: truelight@0: if (bak->name[0]) { truelight@0: strcpy((char*)_decode_parameters, bak->name); truelight@0: DoCommandP(0, v->index, 0, NULL, CMD_NAME_VEHICLE); truelight@0: } truelight@0: truelight@543: DoCommandP(0, v->index, bak->orderindex|(bak->service_interval<<16) , NULL, CMD_RESTORE_ORDER_INDEX); truelight@193: tron@555: if (bak->clone != INVALID_VEHICLE) { tron@555: DoCommandP(0, v->index | bak->clone << 16, 0, NULL, CMD_CLONE_ORDER); truelight@0: return; truelight@0: } truelight@0: truelight@543: // CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the truelight@543: // order number is one more then the current amount of orders, and because truelight@543: // in network the commands are queued before send, the second insert always truelight@543: // fails in test mode. By bypassing the test-mode, that no longer is a problem. tron@555: for (i = 0; bak->order[i].type != OT_NOTHING; ++i) tron@555: if (!DoCommandP(0, v->index + (i << 16), PackOrder(&bak->order[i]), NULL, CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) truelight@0: break; truelight@0: } truelight@0: truelight@0: /* p1 = vehicle truelight@0: * upper 16 bits p2 = service_interval truelight@0: * lower 16 bits p2 = cur_order_index truelight@0: */ truelight@0: int32 CmdRestoreOrderIndex(int x, int y, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@0: // nonsense to update the windows, since, train rebought will have its window deleted truelight@0: if (flags & DC_EXEC) { truelight@0: Vehicle *v = &_vehicles[p1]; truelight@0: v->service_interval = (uint16)(p2>>16); truelight@0: v->cur_order_index = (byte)(p2&0xFFFF); truelight@0: } truelight@0: return 0; truelight@0: } dominik@19: dominik@19: int CheckOrders(Vehicle *v) dominik@19: { dominik@55: if (!_patches.order_review_system) //User doesn't want things to be checked dominik@55: return 0; truelight@193: dominik@711: if ( (_patches.order_review_system == 1) && ((v->vehstatus & VS_STOPPED) || (v->vehstatus & VS_CRASHED)) ) dominik@55: return 0; dominik@19: dominik@55: /* only check every 20 days, so that we don't flood the message log */ dominik@19: if ( ( ( v->day_counter % 20) == 0 ) && (v->owner == _local_player) ) { tron@555: Order order; tron@555: Order old_order; dominik@55: int i, n_st, problem_type = -1; dominik@55: Station *st; dominik@55: int message=0; dominik@55: TileIndex required_tile=-1; dominik@55: dominik@55: /* check the order list */ dominik@55: order = v->schedule_ptr[0]; dominik@55: n_st = 0; dominik@55: tron@555: old_order.type = OT_NOTHING; tron@555: old_order.flags = 0; tron@555: for (i = 0; order.type != OT_NOTHING; i++) { dominik@19: order = v->schedule_ptr[i]; tron@555: if (order.type == old_order.type && tron@555: order.flags == old_order.flags && tron@555: order.station == old_order.station) { dominik@55: problem_type = 2; dominik@55: break; dominik@55: } tron@555: if (order.type == OT_DUMMY) { dominik@55: problem_type = 1; dominik@55: break; dominik@55: } tron@555: if (order.type == OT_GOTO_STATION /*&& (order != old_order) */) { dominik@19: //I uncommented this in order not to get two error messages dominik@19: //when two identical entries are in the list dominik@19: n_st++; tron@555: st = DEREF_STATION(order.station); dominik@55: required_tile = GetStationTileForVehicle(v,st); dominik@294: if (!required_tile) problem_type = 3; dominik@19: } dominik@55: old_order = order; //store the old order dominik@19: } dominik@19: dominik@19: //Now, check the last and the first order dominik@19: //as the last order is the end of order marker, jump back 2 tron@555: if (i > 2 && tron@555: v->schedule_ptr[0].type == v->schedule_ptr[i - 2].type && tron@555: v->schedule_ptr[0].flags == v->schedule_ptr[i - 2].flags && tron@555: v->schedule_ptr[0].station == v->schedule_ptr[i - 2].station) dominik@55: problem_type = 2; truelight@193: celestar@340: if ( (n_st < 2) && (problem_type == -1) ) problem_type = 0; truelight@193: tron@534: SetDParam(0, v->unitnumber); truelight@193: dominik@55: message = (STR_TRAIN_HAS_TOO_FEW_ORDERS) + (((v->type) - VEH_Train) << 2) + problem_type; truelight@193: dominik@55: if (problem_type < 0) return 0; truelight@193: dominik@55: AddNewsItem( dominik@55: message, dominik@55: NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), dominik@55: v->index, dominik@55: 0); dominik@19: } dominik@19: // End of order check dominik@19: dominik@55: return 1; dominik@19: }