Editing (game port, OpenTTDClient): a core of JGRPP's scheduled dispatch DoCommands — set_scheduled_dispatch (enable/disable), add/remove schedule, add/remove/clear slots, and set duration/start date. Adds the command IDs to protocol.py. Viewing (admin, OpenTTDAdminClient.get_dispatch): the GameScript API has no dispatch support, so a new server patch (docker/patches/0002-*) adds read-only GSOrder.GetScheduledDispatch* / IsScheduledDispatchEnabled getters, an AdminBridge GameScript get_dispatch handler exposes them, and get_dispatch() returns the live schedules and slots (mirrors get_timetable). Note: set_dispatch_start_date values are normalised by the engine relative to current game time, so they read back offset from the requested value. Includes unit + e2e tests, a demo in main.py, and protocol/timetable docs. The AdminBridge GameScript and the patched OpenTTD-patches clone live outside this repo; the 0002 patch file is the durable source for the latter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
196 lines
8.3 KiB
Diff
196 lines
8.3 KiB
Diff
From 1e4bdcca84e956ece32d2d77dc8001bfd1d8e2f8 Mon Sep 17 00:00:00 2001
|
|
From: kovagoadi <kovagoadi@gmail.com>
|
|
Date: Fri, 24 Jul 2026 22:32:29 +0200
|
|
Subject: [PATCH] Add GameScript API scheduled dispatch getters to ScriptOrder
|
|
|
|
Expose read-only scheduled dispatch data to AI/GS scripts: per-vehicle
|
|
schedule count and enabled flag, per-schedule duration, start tick, max
|
|
delay and slot re-use, and per-slot offset and flags. Enables the
|
|
AdminBridge GameScript's get_dispatch command and the Python client's
|
|
OpenTTDAdminClient.get_dispatch().
|
|
|
|
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
---
|
|
src/script/api/script_order.cpp | 81 +++++++++++++++++++++++++++++++++
|
|
src/script/api/script_order.hpp | 75 ++++++++++++++++++++++++++++++
|
|
2 files changed, 156 insertions(+)
|
|
|
|
diff --git a/src/script/api/script_order.cpp b/src/script/api/script_order.cpp
|
|
index ee18f7d588..3db4639f14 100644
|
|
--- a/src/script/api/script_order.cpp
|
|
+++ b/src/script/api/script_order.cpp
|
|
@@ -822,3 +822,84 @@ static void _DoCommandReturnSetOrderFlags(class ScriptInstance &instance)
|
|
if (duration == INVALID_TICKS) return -1;
|
|
return duration;
|
|
}
|
|
+
|
|
+/**
|
|
+ * Resolve a scheduled dispatch schedule for a vehicle, or nullptr if the vehicle/schedule is invalid.
|
|
+ */
|
|
+static const DispatchSchedule *ResolveDispatchSchedule(VehicleID vehicle_id, SQInteger schedule_index)
|
|
+{
|
|
+ if (!ScriptVehicle::IsPrimaryVehicle(vehicle_id)) return nullptr;
|
|
+ const Vehicle *v = ::Vehicle::Get(vehicle_id);
|
|
+ if (v->orders == nullptr) return nullptr;
|
|
+ if (schedule_index < 0 || static_cast<uint>(schedule_index) >= v->orders->GetScheduledDispatchScheduleCount()) return nullptr;
|
|
+ return &v->orders->GetDispatchScheduleByIndex(static_cast<uint>(schedule_index));
|
|
+}
|
|
+
|
|
+/* static */ SQInteger ScriptOrder::GetScheduledDispatchScheduleCount(VehicleID vehicle_id)
|
|
+{
|
|
+ if (!ScriptVehicle::IsPrimaryVehicle(vehicle_id)) return -1;
|
|
+
|
|
+ const Vehicle *v = ::Vehicle::Get(vehicle_id);
|
|
+ if (v->orders == nullptr) return 0;
|
|
+ return v->orders->GetScheduledDispatchScheduleCount();
|
|
+}
|
|
+
|
|
+/* static */ SQInteger ScriptOrder::IsScheduledDispatchEnabled(VehicleID vehicle_id)
|
|
+{
|
|
+ if (!ScriptVehicle::IsPrimaryVehicle(vehicle_id)) return -1;
|
|
+
|
|
+ return ::Vehicle::Get(vehicle_id)->vehicle_flags.Test(VehicleFlag::ScheduledDispatch) ? 1 : 0;
|
|
+}
|
|
+
|
|
+/* static */ SQInteger ScriptOrder::GetScheduledDispatchDuration(VehicleID vehicle_id, SQInteger schedule_index)
|
|
+{
|
|
+ const DispatchSchedule *ds = ::ResolveDispatchSchedule(vehicle_id, schedule_index);
|
|
+ if (ds == nullptr) return -1;
|
|
+ return ds->GetScheduledDispatchDuration();
|
|
+}
|
|
+
|
|
+/* static */ SQInteger ScriptOrder::GetScheduledDispatchStartTick(VehicleID vehicle_id, SQInteger schedule_index)
|
|
+{
|
|
+ const DispatchSchedule *ds = ::ResolveDispatchSchedule(vehicle_id, schedule_index);
|
|
+ if (ds == nullptr) return -1;
|
|
+ return ds->GetScheduledDispatchStartTick().base();
|
|
+}
|
|
+
|
|
+/* static */ SQInteger ScriptOrder::GetScheduledDispatchDelay(VehicleID vehicle_id, SQInteger schedule_index)
|
|
+{
|
|
+ const DispatchSchedule *ds = ::ResolveDispatchSchedule(vehicle_id, schedule_index);
|
|
+ if (ds == nullptr) return -1;
|
|
+ return ds->GetScheduledDispatchDelay();
|
|
+}
|
|
+
|
|
+/* static */ SQInteger ScriptOrder::GetScheduledDispatchReuseSlots(VehicleID vehicle_id, SQInteger schedule_index)
|
|
+{
|
|
+ const DispatchSchedule *ds = ::ResolveDispatchSchedule(vehicle_id, schedule_index);
|
|
+ if (ds == nullptr) return -1;
|
|
+ return ds->GetScheduledDispatchReuseSlots() ? 1 : 0;
|
|
+}
|
|
+
|
|
+/* static */ SQInteger ScriptOrder::GetScheduledDispatchSlotCount(VehicleID vehicle_id, SQInteger schedule_index)
|
|
+{
|
|
+ const DispatchSchedule *ds = ::ResolveDispatchSchedule(vehicle_id, schedule_index);
|
|
+ if (ds == nullptr) return -1;
|
|
+ return (SQInteger)ds->GetScheduledDispatch().size();
|
|
+}
|
|
+
|
|
+/* static */ SQInteger ScriptOrder::GetScheduledDispatchSlotOffset(VehicleID vehicle_id, SQInteger schedule_index, SQInteger slot_index)
|
|
+{
|
|
+ const DispatchSchedule *ds = ::ResolveDispatchSchedule(vehicle_id, schedule_index);
|
|
+ if (ds == nullptr) return -1;
|
|
+ const std::vector<DispatchSlot> &slots = ds->GetScheduledDispatch();
|
|
+ if (slot_index < 0 || static_cast<size_t>(slot_index) >= slots.size()) return -1;
|
|
+ return slots[static_cast<size_t>(slot_index)].offset;
|
|
+}
|
|
+
|
|
+/* static */ SQInteger ScriptOrder::GetScheduledDispatchSlotFlags(VehicleID vehicle_id, SQInteger schedule_index, SQInteger slot_index)
|
|
+{
|
|
+ const DispatchSchedule *ds = ::ResolveDispatchSchedule(vehicle_id, schedule_index);
|
|
+ if (ds == nullptr) return -1;
|
|
+ const std::vector<DispatchSlot> &slots = ds->GetScheduledDispatch();
|
|
+ if (slot_index < 0 || static_cast<size_t>(slot_index) >= slots.size()) return -1;
|
|
+ return slots[static_cast<size_t>(slot_index)].flags;
|
|
+}
|
|
diff --git a/src/script/api/script_order.hpp b/src/script/api/script_order.hpp
|
|
index 6c96b91b3a..d0a7e41fa7 100644
|
|
--- a/src/script/api/script_order.hpp
|
|
+++ b/src/script/api/script_order.hpp
|
|
@@ -720,6 +720,81 @@ public:
|
|
* invalid, has no orders, or the timetable is not complete.
|
|
*/
|
|
static SQInteger GetTimetableTotalDuration(VehicleID vehicle_id);
|
|
+
|
|
+ /**
|
|
+ * Gets the number of scheduled dispatch schedules of the given vehicle.
|
|
+ * @param vehicle_id The vehicle to query.
|
|
+ * @pre ScriptVehicle::IsPrimaryVehicle(vehicle_id).
|
|
+ * @return The number of dispatch schedules (0 when the vehicle has no order list),
|
|
+ * or -1 when the vehicle is invalid.
|
|
+ */
|
|
+ static SQInteger GetScheduledDispatchScheduleCount(VehicleID vehicle_id);
|
|
+
|
|
+ /**
|
|
+ * Gets whether scheduled dispatch is enabled for the given vehicle.
|
|
+ * @param vehicle_id The vehicle to query.
|
|
+ * @pre ScriptVehicle::IsPrimaryVehicle(vehicle_id).
|
|
+ * @return 1 if enabled, 0 if disabled, or -1 when the vehicle is invalid.
|
|
+ */
|
|
+ static SQInteger IsScheduledDispatchEnabled(VehicleID vehicle_id);
|
|
+
|
|
+ /**
|
|
+ * Gets the duration in ticks of a dispatch schedule.
|
|
+ * @param vehicle_id The vehicle to query.
|
|
+ * @param schedule_index The dispatch schedule index.
|
|
+ * @return The schedule duration in ticks, or -1 when the vehicle or schedule is invalid.
|
|
+ */
|
|
+ static SQInteger GetScheduledDispatchDuration(VehicleID vehicle_id, SQInteger schedule_index);
|
|
+
|
|
+ /**
|
|
+ * Gets the start tick of a dispatch schedule.
|
|
+ * @param vehicle_id The vehicle to query.
|
|
+ * @param schedule_index The dispatch schedule index.
|
|
+ * @return The absolute start state tick, or -1 when the vehicle or schedule is invalid.
|
|
+ */
|
|
+ static SQInteger GetScheduledDispatchStartTick(VehicleID vehicle_id, SQInteger schedule_index);
|
|
+
|
|
+ /**
|
|
+ * Gets the maximum allowed delay of a dispatch schedule.
|
|
+ * @param vehicle_id The vehicle to query.
|
|
+ * @param schedule_index The dispatch schedule index.
|
|
+ * @return The maximum delay in ticks, or -1 when the vehicle or schedule is invalid.
|
|
+ */
|
|
+ static SQInteger GetScheduledDispatchDelay(VehicleID vehicle_id, SQInteger schedule_index);
|
|
+
|
|
+ /**
|
|
+ * Gets whether a dispatch schedule re-uses its dispatch slots.
|
|
+ * @param vehicle_id The vehicle to query.
|
|
+ * @param schedule_index The dispatch schedule index.
|
|
+ * @return 1 if slots are re-used, 0 if not, or -1 when the vehicle or schedule is invalid.
|
|
+ */
|
|
+ static SQInteger GetScheduledDispatchReuseSlots(VehicleID vehicle_id, SQInteger schedule_index);
|
|
+
|
|
+ /**
|
|
+ * Gets the number of departure slots in a dispatch schedule.
|
|
+ * @param vehicle_id The vehicle to query.
|
|
+ * @param schedule_index The dispatch schedule index.
|
|
+ * @return The number of slots, or -1 when the vehicle or schedule is invalid.
|
|
+ */
|
|
+ static SQInteger GetScheduledDispatchSlotCount(VehicleID vehicle_id, SQInteger schedule_index);
|
|
+
|
|
+ /**
|
|
+ * Gets the departure offset (in ticks, within the schedule duration) of a dispatch slot.
|
|
+ * @param vehicle_id The vehicle to query.
|
|
+ * @param schedule_index The dispatch schedule index.
|
|
+ * @param slot_index The slot index within the schedule.
|
|
+ * @return The slot offset, or -1 when the vehicle, schedule or slot is invalid.
|
|
+ */
|
|
+ static SQInteger GetScheduledDispatchSlotOffset(VehicleID vehicle_id, SQInteger schedule_index, SQInteger slot_index);
|
|
+
|
|
+ /**
|
|
+ * Gets the flag word of a dispatch slot.
|
|
+ * @param vehicle_id The vehicle to query.
|
|
+ * @param schedule_index The dispatch schedule index.
|
|
+ * @param slot_index The slot index within the schedule.
|
|
+ * @return The slot flags, or -1 when the vehicle, schedule or slot is invalid.
|
|
+ */
|
|
+ static SQInteger GetScheduledDispatchSlotFlags(VehicleID vehicle_id, SQInteger schedule_index, SQInteger slot_index);
|
|
};
|
|
DECLARE_ENUM_AS_BIT_SET(ScriptOrder::ScriptOrderFlags)
|
|
|
|
--
|
|
2.54.0
|
|
|