Add support for multiple scheduled dispatch schedules per order list

This commit is contained in:
Jonathan G Rennison
2022-01-13 19:46:43 +00:00
parent 29521ef883
commit ff3473fe6a
23 changed files with 755 additions and 367 deletions

View File

@@ -82,6 +82,7 @@ CommandCost CmdScheduledDispatch(TileIndex tile, DoCommandFlag flags, uint32 p1,
CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, uint32 binary_length)
{
VehicleID veh = GB(p1, 0, 20);
uint schedule_index = GB(p1, 20, 12);
uint32 offset = GB(p3, 0, 32);
uint32 extra_slots = GB(p3, 32, 16);
@@ -93,14 +94,17 @@ CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32
if (v->orders.list == nullptr) return CMD_ERROR;
if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
if (extra_slots > 512) return_cmd_error(STR_ERROR_SCHDISPATCH_TRIED_TO_ADD_TOO_MANY_SLOTS);
if (extra_slots > 0 && offset == 0) return CMD_ERROR;
if (flags & DC_EXEC) {
v->orders.list->AddScheduledDispatch(p2);
DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index);
ds.AddScheduledDispatch(p2);
for (uint i = 0; i < extra_slots; i++) {
p2 += offset;
v->orders.list->AddScheduledDispatch(p2);
ds.AddScheduledDispatch(p2);
}
SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
}
@@ -120,6 +124,7 @@ CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32
CommandCost CmdScheduledDispatchRemove(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
VehicleID veh = GB(p1, 0, 20);
uint schedule_index = GB(p1, 20, 12);
Vehicle *v = Vehicle::GetIfValid(veh);
if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
@@ -129,8 +134,10 @@ CommandCost CmdScheduledDispatchRemove(TileIndex tile, DoCommandFlag flags, uint
if (v->orders.list == nullptr) return CMD_ERROR;
if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
if (flags & DC_EXEC) {
v->orders.list->RemoveScheduledDispatch(p2);
v->orders.list->GetDispatchScheduleByIndex(schedule_index).RemoveScheduledDispatch(p2);
SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
}
@@ -150,6 +157,7 @@ CommandCost CmdScheduledDispatchRemove(TileIndex tile, DoCommandFlag flags, uint
CommandCost CmdScheduledDispatchSetDuration(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
VehicleID veh = GB(p1, 0, 20);
uint schedule_index = GB(p1, 20, 12);
Vehicle *v = Vehicle::GetIfValid(veh);
if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
@@ -159,9 +167,12 @@ CommandCost CmdScheduledDispatchSetDuration(TileIndex tile, DoCommandFlag flags,
if (v->orders.list == nullptr) return CMD_ERROR;
if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
if (flags & DC_EXEC) {
v->orders.list->SetScheduledDispatchDuration(p2);
v->orders.list->UpdateScheduledDispatch();
DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index);
ds.SetScheduledDispatchDuration(p2);
ds.UpdateScheduledDispatch();
SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
}
@@ -173,23 +184,22 @@ CommandCost CmdScheduledDispatchSetDuration(TileIndex tile, DoCommandFlag flags,
*
* The parameter is quite tricky. The default maximum of daylength factor is 125,
* and with DAY_TICKS of 74 the result (maximum scaled tick per day) fits in 14 bit.
* Vehicle index in p1 takes 20 bit, so we have 12 bit here. The MSB of the fraction is stored here.
* The 2-bit LSB is stored in MSB of p2, which is start date. The default date is stored in int32,
* which only have topmost bit available. However, if the date reached 31 bits, that means it is over 1,000,000 years,
* so I think it is safe to steal another bit here.
*
* See also the static_assert at the top of the file.
*
* @param tile Not used.
* @param flags Operation to perform.
* @param p1 MSB of Start Full Date Fraction || Vehicle index
* @param p2 LSB of Start Full Date Fraction || Date to add.
* @param p1 Vehicle index
* @param p2 Date to add.
* @param p3 various bitstuffed elements
* - p3 = (bit 0 - 15) - Full date fraction
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, uint32 binary_length)
{
VehicleID veh = GB(p1, 0, 20);
uint schedule_index = GB(p1, 20, 12);
Vehicle *v = Vehicle::GetIfValid(veh);
if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
@@ -199,12 +209,15 @@ CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags
if (v->orders.list == nullptr) return CMD_ERROR;
int32 date = (int32) GB(p2, 0, 30);
uint16 full_date_fract = (GB(p1, 20, 12) << 2) + GB(p2, 30, 2);
if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
int32 date = (int32)p2;
uint16 full_date_fract = GB(p3, 0, 16);
if (flags & DC_EXEC) {
v->orders.list->SetScheduledDispatchStartDate(date, full_date_fract);
v->orders.list->UpdateScheduledDispatch();
DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index);
ds.SetScheduledDispatchStartDate(date, full_date_fract);
ds.UpdateScheduledDispatch();
SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
}
@@ -224,6 +237,7 @@ CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags
CommandCost CmdScheduledDispatchSetDelay(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
VehicleID veh = GB(p1, 0, 20);
uint schedule_index = GB(p1, 20, 12);
Vehicle *v = Vehicle::GetIfValid(veh);
if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
@@ -233,8 +247,10 @@ CommandCost CmdScheduledDispatchSetDelay(TileIndex tile, DoCommandFlag flags, ui
if (v->orders.list == nullptr) return CMD_ERROR;
if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
if (flags & DC_EXEC) {
v->orders.list->SetScheduledDispatchDelay(p2);
v->orders.list->GetDispatchScheduleByIndex(schedule_index).SetScheduledDispatchDelay(p2);
SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
}
@@ -259,6 +275,7 @@ CommandCost CmdScheduledDispatchSetDelay(TileIndex tile, DoCommandFlag flags, ui
CommandCost CmdScheduledDispatchResetLastDispatch(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
VehicleID veh = GB(p1, 0, 20);
uint schedule_index = GB(p1, 20, 12);
Vehicle *v = Vehicle::GetIfValid(veh);
if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
@@ -268,8 +285,10 @@ CommandCost CmdScheduledDispatchResetLastDispatch(TileIndex tile, DoCommandFlag
if (v->orders.list == nullptr) return CMD_ERROR;
if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
if (flags & DC_EXEC) {
v->orders.list->SetScheduledDispatchLastDispatch(0);
v->orders.list->GetDispatchScheduleByIndex(schedule_index).SetScheduledDispatchLastDispatch(0);
SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
}
@@ -289,6 +308,7 @@ CommandCost CmdScheduledDispatchResetLastDispatch(TileIndex tile, DoCommandFlag
CommandCost CmdScheduledDispatchClear(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
VehicleID veh = GB(p1, 0, 20);
uint schedule_index = GB(p1, 20, 12);
Vehicle *v = Vehicle::GetIfValid(veh);
if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
@@ -298,19 +318,106 @@ CommandCost CmdScheduledDispatchClear(TileIndex tile, DoCommandFlag flags, uint3
if (v->orders.list == nullptr) return CMD_ERROR;
if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
if (flags & DC_EXEC) {
v->orders.list->ClearScheduledDispatch();
v->orders.list->GetDispatchScheduleByIndex(schedule_index).ClearScheduledDispatch();
SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
}
return CommandCost();
}
/**
* Add a new scheduled dispatch schedule
*
* @param tile Not used.
* @param flags Operation to perform.
* @param p1 Vehicle index
* @param p2 Duration, in scaled tick
* @param p3 various bitstuffed elements
* - p3 = (bit 0 - 31) - Start date
* - p3 = (bit 32 - 47) - Full date fraction
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdScheduledDispatchAddNewSchedule(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, uint32 binary_length)
{
VehicleID veh = GB(p1, 0, 20);
Vehicle *v = Vehicle::GetIfValid(veh);
if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
CommandCost ret = CheckOwnership(v->owner);
if (ret.Failed()) return ret;
if (v->orders.list == nullptr) return CMD_ERROR;
if (v->orders.list->GetScheduledDispatchScheduleCount() >= 4096) return CMD_ERROR;
int32 date = GB(p3, 0, 32);
uint16 full_date_fract = GB(p3, 32, 16);
if (flags & DC_EXEC) {
v->orders.list->GetScheduledDispatchScheduleSet().emplace_back();
DispatchSchedule &ds = v->orders.list->GetScheduledDispatchScheduleSet().back();
ds.SetScheduledDispatchDuration(p2);
ds.SetScheduledDispatchStartDate(date, full_date_fract);
ds.UpdateScheduledDispatch();
SetWindowClassesDirty(WC_VEHICLE_TIMETABLE);
SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
}
return CommandCost();
}
/**
* Remove scheduled dispatch schedule
*
* @param tile Not used.
* @param flags Operation to perform.
* @param p1 Vehicle index
* @param p2 Not used
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdScheduledDispatchRemoveSchedule(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
VehicleID veh = GB(p1, 0, 20);
uint schedule_index = GB(p1, 20, 12);
Vehicle *v = Vehicle::GetIfValid(veh);
if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
CommandCost ret = CheckOwnership(v->owner);
if (ret.Failed()) return ret;
if (v->orders.list == nullptr) return CMD_ERROR;
if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
if (flags & DC_EXEC) {
std::vector<DispatchSchedule> &scheds = v->orders.list->GetScheduledDispatchScheduleSet();
scheds.erase(scheds.begin() + schedule_index);
for (Order *o = v->GetFirstOrder(); o != nullptr; o = o->next) {
int idx = o->GetDispatchScheduleIndex();
if (idx == (int)schedule_index) {
o->SetDispatchScheduleIndex(-1);
} else if (idx > (int)schedule_index) {
o->SetDispatchScheduleIndex(idx - 1);
}
}
SetWindowClassesDirty(WC_VEHICLE_TIMETABLE);
InvalidateWindowClassesData(WC_SCHDISPATCH_SLOTS, VIWD_MODIFY_ORDERS);
}
return CommandCost();
}
/**
* Set scheduled dispatch slot list.
* @param dispatch_list The offset time list, must be correctly sorted.
*/
void OrderList::SetScheduledDispatch(std::vector<uint32> dispatch_list)
void DispatchSchedule::SetScheduledDispatch(std::vector<uint32> dispatch_list)
{
this->scheduled_dispatch = std::move(dispatch_list);
assert(std::is_sorted(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end()));
@@ -321,7 +428,7 @@ void OrderList::SetScheduledDispatch(std::vector<uint32> dispatch_list)
* Add new scheduled dispatch slot at offsets time.
* @param offset The offset time to add.
*/
void OrderList::AddScheduledDispatch(uint32 offset)
void DispatchSchedule::AddScheduledDispatch(uint32 offset)
{
/* Maintain sorted list status */
auto insert_position = std::lower_bound(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end(), offset);
@@ -336,7 +443,7 @@ void OrderList::AddScheduledDispatch(uint32 offset)
* Remove scheduled dispatch slot at offsets time.
* @param offset The offset time to remove.
*/
void OrderList::RemoveScheduledDispatch(uint32 offset)
void DispatchSchedule::RemoveScheduledDispatch(uint32 offset)
{
/* Maintain sorted list status */
auto erase_position = std::lower_bound(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end(), offset);
@@ -349,7 +456,7 @@ void OrderList::RemoveScheduledDispatch(uint32 offset)
/**
* Update the scheduled dispatch start time to be the most recent possible.
*/
void OrderList::UpdateScheduledDispatch()
void DispatchSchedule::UpdateScheduledDispatch()
{
bool update_windows = false;
if (this->GetScheduledDispatchStartTick() == 0) {
@@ -388,45 +495,3 @@ void OrderList::UpdateScheduledDispatch()
}
if (update_windows) InvalidateWindowClassesData(WC_SCHDISPATCH_SLOTS, VIWD_MODIFY_ORDERS);
}
/**
* Reset the scheduled dispatch schedule.
*
* This only occurs during initialization of the scheduled dispatch for each shared order. Basically we set
* proper default value for start time and duration
*/
void OrderList::ResetScheduledDispatch()
{
uint32 windex = this->first_shared->index;
Date start_date;
uint16 start_full_date_fract;
uint32 duration;
if (_settings_time.time_in_minutes) {
/* Set to 00:00 of today, and 1 day */
DateTicksScaled val;
val = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, 0);
val -= _settings_time.clock_offset;
val *= _settings_time.ticks_per_minute;
SchdispatchConvertToFullDateFract(val, &start_date, &start_full_date_fract);
duration = 24 * 60 * _settings_time.ticks_per_minute;
} else {
/* Set Jan 1st and 365 day */
start_date = DAYS_TILL(_cur_year);
start_full_date_fract = 0;
duration = 365*DAY_TICKS;
}
DoCommandP(0, windex, duration, CMD_SCHEDULED_DISPATCH_SET_DURATION | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
uint32 p1 = 0, p2 = 0;
SB(p1, 0, 20, windex);
SB(p1, 20, 12, GB(start_full_date_fract, 2, 12));
SB(p2, 0, 30, start_date);
SB(p2, 30, 2, GB(start_full_date_fract, 0, 2));
DoCommandP(0, p1, p2, CMD_SCHEDULED_DISPATCH_SET_START_DATE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
}