Skip to content

Instantly share code, notes, and snippets.

@andythenorth
Created February 4, 2023 19:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andythenorth/8a0e7e2c5877b748342c668eda80f749 to your computer and use it in GitHub Desktop.
Save andythenorth/8a0e7e2c5877b748342c668eda80f749 to your computer and use it in GitHub Desktop.
/*
* This file is part of SuperLib, which is an AI Library for OpenTTD
* Copyright (C) 2010 Leif Linse
*
* SuperLib is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License
*
* SuperLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SuperLib; If not, see <http://www.gnu.org/licenses/> or
* write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*
* This file contains two classes:
* - Order - utility functions for orders
* - OrderList - for applying a set of orders to a vehicle
*/
//////////////////////////////////////////////////////////////////////
// //
// Order class - utility functions for orders //
// //
//////////////////////////////////////////////////////////////////////
class _SuperLib_Order {
/* Gets the current destination that a vehicle heads to
* Returns -1 if the current order is a conditional order
*/
static function GetCurrentOrderDestination(vehicle_id);
/* A function to clear all orders of a vehicle, that works so that if shared orders
* is used, each of the shared order is removed, but the sharing of the orders is
* not affected - only clearing of them.
*/
static function ClearOrdersOfSharedGroup(vehicle_id);
/* Get an GSList of the stations that the vehicle has
* orders to visit (or go via)
*/
static function GetStationListFromOrders(vehicle_id);
/* Checks if a given station exists in the orders of a vehicle */
static function HasStationInOrders(vehicle_id, station_id);
}
function _SuperLib_Order::GetCurrentOrderDestination(vehicle_id)
{
if(GSOrder.IsConditionalOrder(vehicle_id, GSOrder.ORDER_CURRENT))
return -1;
return GSOrder.GetOrderDestination(vehicle_id, GSOrder.ORDER_CURRENT);
}
function _SuperLib_Order::ClearOrdersOfSharedGroup(vehicle_id)
{
local num_orders = GSOrder.GetOrderCount(vehicle_id);
for(local i = 0; i < num_orders; i++)
{
GSOrder.RemoveOrder(vehicle_id, 0);
}
if(GSOrder.GetOrderCount(vehicle_id) != 0)
_SuperLib_Log.Error("ClearOrdersOfSharedGroup: Not all orders cleared", _SuperLib_Log.LVL_INFO);
}
function _SuperLib_Order::GetStationListFromOrders(vehicle_id)
{
local station_list = GSList();
local num_orders = GSOrder.GetOrderCount(vehicle_id);
for(local i = 0; i < num_orders; i++)
{
local tile_id = GSOrder.GetOrderDestination(vehicle_id, i);
local station_id = GSStation.GetStationID(tile_id);
if(GSStation.IsValidStation(station_id))
{
station_list.AddItem(station_id, 0);
}
}
return station_list;
}
function _SuperLib_Order::HasStationInOrders(vehicle_id, station_id)
{
local station_list = _SuperLib_Order.GetStationListFromOrders(vehicle_id);
return station_list.HasItem(station_id);
}
//////////////////////////////////////////////////////////////////////
// //
// OrderList class - applying a list of orders to a vehicle //
// //
//////////////////////////////////////////////////////////////////////
class _SuperLib_OrderList {
list = null;
skip_to_last_when_full = false;
constructor()
{
this.list = [];
skip_to_last_when_full = false;
}
/* Purpose: To have a way of applying orders to vehicles that will not do any
* harm if the orders are already present. If a single order is missing, then
* the other station orders will remain intact so that when you add a new
* station to the order list, not all vehicles will lose track of where they
* are heading.
*/
/* Usage:
*
* 1a) Call AddStop for all station you want the vehicle(s) to stop at/go via.
* You must call AddStop in the order you want the stations to be visited.
*
* 1b) Call SkipToLastWhenFull(true) if you want to enable it. (when this is
* done related to 1a) is not important, as long as you do it before 2). )
*
* 2) Call ApplyToVehicle(vehicle_id) for any number of vehicles that you
* want to apply the order list to.
*/
/*
* Add a stop at a station.
*
* station_id_value = station id
* flags_value = GSOrder flags to use for the station
*/
function AddStop(station_id_value, flags_value);
/*
* Call this function to enable/disable if vehicles
* should skip to the last station order if they are full
*
* If you enable this, conditional orders for this will be
* inserted to the vehicle orders to acomplish that.
*/
function SkipToLastWhenFull(enable);
/*
* Applies the order list to a given vehicle.
*/
function ApplyToVehicle(vehicle_id);
/*
* Returns the order index that has a given destination.
* start search from order index 'begin'
*/
static function FindOrderDestination(vehicle_id, begin, match_destination);
}
function _SuperLib_OrderList::AddStop(station_id_value, flags_value)
{
list.append(
{station_id=station_id_value,
flags=flags_value}
);
}
function _SuperLib_OrderList::SkipToLastWhenFull(enable)
{
skip_to_last_when_full = enable;
}
function _SuperLib_OrderList::ApplyToVehicle(vehicle_id)
{
if(!GSVehicle.IsValidVehicle(vehicle_id))
{
_SuperLib_Log.Warning("OrderList::ApplyToVehicle: invalid vehicle id (" + vehicle_id + ") supplied", _SuperLib_Log.LVL_INFO);
return;
}
_SuperLib_Log.Info("order list len: " + this.list.len(), _SuperLib_Log.LVL_DEBUG);
//Helper.BreakPoint(GSVehicle.GetLocation(vehicle_id));
local i = 0;
for(; i < list.len(); i++)
{
local station_id = list[i].station_id;
local flags = list[i].flags;
//Helper.SetSign( GSStation.GetLocation(station_id), "flags: " + flags);
_SuperLib_Log.Info("station " + station_id + " with flags " + flags, _SuperLib_Log.LVL_DEBUG);
}
i = 0;
//local i = 0; // this.list position
local k = 0; // order list position
for(; i < list.len(); i++)
{
//// Add a normal order to the target in this.list ////
// if current position is a conditional order, then remove it until a non-conditional order is found
// or end of order list is reached
while(k < GSOrder.GetOrderCount(vehicle_id) && GSOrder.IsConditionalOrder(vehicle_id, k))
{
// remove conditional order
GSOrder.RemoveOrder(vehicle_id, k);
}
if(k < GSOrder.GetOrderCount(vehicle_id))
{
// k is a normal order -> change it or insert a new
// check if the destination is correct
local curr_order_dest_station = GSStation.GetStationID(GSOrder.GetOrderDestination(vehicle_id, k));
if(curr_order_dest_station == this.list[i].station_id)
{
// current order has correct destination
_SuperLib_Log.Info("current order has correct destination", _SuperLib_Log.LVL_DEBUG);
GSOrder.SetOrderFlags(vehicle_id, k, this.list[i].flags); // Make sure the flags are correct
}
else
{
// current order has wrong destination
_SuperLib_Log.Info("current order has Wrong destination", _SuperLib_Log.LVL_DEBUG);
// First try to see if there is an order with this destination
local later_order = _SuperLib_OrderList.FindOrderDestination(vehicle_id, k+1, GSStation.GetLocation(this.list[i].station_id));
if(later_order != -1)
{
// Found the destination later in list, move it to i
_SuperLib_Log.Info("Found correct destination later in list", _SuperLib_Log.LVL_DEBUG);
GSOrder.MoveOrder(vehicle_id, later_order, k);
GSOrder.SetOrderFlags(vehicle_id, k, this.list[i].flags); // Make sure the flags are correct
}
else
{
// There is no order with this destination, make a new order.
_SuperLib_Log.Info("Did not found correct destination later in list -> insert a new one", _SuperLib_Log.LVL_DEBUG);
//Helper.SetSign(GSStation.GetLocation(this.list[i].station_id), "insert " + this.list[i].flags);
GSOrder.InsertOrder(vehicle_id, k, GSStation.GetLocation(this.list[i].station_id), this.list[i].flags);
_SuperLib_Log.Info("Insert order error msg: " + GSError.GetLastErrorString(), _SuperLib_Log.LVL_DEBUG);
}
}
}
else
{
// no existing order -> append a new one
// append a new normal order
_SuperLib_Log.Info("Existing order list is shorter than current k -> Append new order", _SuperLib_Log.LVL_DEBUG);
//Helper.SetSign(GSStation.GetLocation(this.list[i].station_id), "append");
GSOrder.AppendOrder(vehicle_id, GSStation.GetLocation(this.list[i].station_id), this.list[i].flags);
_SuperLib_Log.Info("Append order error msg: " + GSError.GetLastErrorString(), _SuperLib_Log.LVL_DEBUG);
//Helper.BreakPoint(GSVehicle.GetLocation(vehicle_id));
}
k++;
//// Add a conditional order if wanted ////
if(skip_to_last_when_full && i < list.len() - 2)
{
if(k < GSOrder.GetOrderCount(vehicle_id) && GSOrder.IsConditionalOrder(vehicle_id, k))
{
// there is a conditional order, so don't do anything
}
else
{
// next order don't exist or is not a conditional order
GSOrder.InsertConditionalOrder(vehicle_id, k, 0); // Add a dummy conditional order pointing on first order
_SuperLib_Log.Info("Insert conditional order error msg: " + GSError.GetLastErrorString(), _SuperLib_Log.LVL_DEBUG);
}
k++;
_SuperLib_Log.Info("Breaking after conditional order have been added", _SuperLib_Log.LVL_DEBUG);
//Helper.BreakPoint(GSVehicle.GetLocation(vehicle_id));
}
//_SuperLib_Log.Info("Breaking before next loop", _SuperLib_Log.LVL_DEBUG);
//Helper.BreakPoint(GSVehicle.GetLocation(vehicle_id));
}
_SuperLib_Log.Info("Breaking before removal", _SuperLib_Log.LVL_DEBUG);
//Helper.BreakPoint(GSVehicle.GetLocation(vehicle_id));
// if the vehicle order list contained orders that we don't want to have anymore, they will be located at the end.
// so remove all orders that are after the last item number in this.list
local num_to_remove = GSOrder.GetOrderCount(vehicle_id) - k;
if(num_to_remove > 0)
{
for(local j = 0; j < num_to_remove; j++)
{
// Remove the one with id last wanted + 1, since the remaining ones move up after each removal.
GSOrder.RemoveOrder(vehicle_id, k);
}
}
_SuperLib_Log.Info("Breaking after removal", _SuperLib_Log.LVL_DEBUG);
//Helper.BreakPoint(GSVehicle.GetLocation(vehicle_id));
// Update the conditional orders
if(skip_to_last_when_full)
{
local num_orders = GSOrder.GetOrderCount(vehicle_id);
local jump_to = num_orders - 1;
for(local order = 0; order < num_orders; order++)
{
if(GSOrder.IsConditionalOrder(vehicle_id, order))
{
if(!GSOrder.SetOrderJumpTo(vehicle_id, order, jump_to))
{
_SuperLib_Log.Warning("You are using a OpenTTD version < r16063 where the SetOrderJumpTo function is broken. The old (less good) ApplyToVehicle function will be used instead.", _SuperLib_Log.LVL_INFO);
return this.Old_070_Compatible_ApplyToVehicle(vehicle_id);
}
_SuperLib_Log.Info("Set order jump to - error message: " + GSError.GetLastErrorString(), _SuperLib_Log.LVL_DEBUG);
GSOrder.SetOrderCondition(vehicle_id, order, GSOrder.OC_LOAD_PERCENTAGE);
GSOrder.SetOrderCompareFunction(vehicle_id, order, GSOrder.CF_EQUALS);
GSOrder.SetOrderCompareValue(vehicle_id, order, 100);
}
}
}
_SuperLib_Log.Info("Breaking at end of ApplyOrder", _SuperLib_Log.LVL_DEBUG);
//Helper.BreakPoint(GSVehicle.GetLocation(vehicle_id));
}
function _SuperLib_OrderList::Old_070_Compatible_ApplyToVehicle(vehicle_id)
{
_SuperLib_Log.Info("order list len: " + this.list.len(), _SuperLib_Log.LVL_DEBUG);
//Helper.BreakPoint(GSVehicle.GetLocation(vehicle_id));
local i = 0;
for(; i < list.len(); i++)
{
if(i < GSOrder.GetOrderCount(vehicle_id))
{
_SuperLib_Log.Info(" i < num orders of vehicle", _SuperLib_Log.LVL_DEBUG);
// If current order is a conditional order
if(GSOrder.IsConditionalOrder(vehicle_id, i))
{
// remove conditional order
GSOrder.RemoveOrder(vehicle_id, i);
// skip to next order. Since one order was removed next order now has same id as the removed order (if it exists)
i--;
continue;
// then at the end we add back conditional orders
}
local curr_order_dest_station = GSStation.GetStationID(GSOrder.GetOrderDestination(vehicle_id, i));
if(curr_order_dest_station == this.list[i].station_id)
{
// current order has correct destination
_SuperLib_Log.Info("current order has correct destination", _SuperLib_Log.LVL_DEBUG);
GSOrder.SetOrderFlags(vehicle_id, i, this.list[i].flags); // Make sure the flags are correct
}
else
{
// current order has wrong destination
_SuperLib_Log.Info("current order has Wrong destination", _SuperLib_Log.LVL_DEBUG);
// First try to see if there is an order with this destination
local later_order = _SuperLib_OrderList.FindOrderDestination(vehicle_id, i+1, GSStation.GetLocation(this.list[i].station_id));
if(later_order != -1)
{
// Found the destination later in list, move it to i
_SuperLib_Log.Info("Found correct destination later in list", _SuperLib_Log.LVL_DEBUG);
GSOrder.MoveOrder(vehicle_id, later_order, i);
GSOrder.SetOrderFlags(vehicle_id, i, this.list[i].flags); // Make sure the flags are correct
}
else
{
// There is no order with this destination, make a new order.
_SuperLib_Log.Info("Did not found correct destination later in list -> insert a new one", _SuperLib_Log.LVL_DEBUG);
//Helper.SetSign(GSStation.GetLocation(this.list[i].station_id), "insert");
GSOrder.InsertOrder(vehicle_id, i, GSStation.GetLocation(this.list[i].station_id), this.list[i].flags);
_SuperLib_Log.Info("Insert order error msg: " + GSError.GetLastErrorString(), _SuperLib_Log.LVL_DEBUG);
}
}
}
else
{
// the existing order list is shorter than current wanted order.
_SuperLib_Log.Info("Existing order list is shorter than current i -> Append new order", _SuperLib_Log.LVL_DEBUG);
// Append wanted order at the end
//Helper.SetSign(GSStation.GetLocation(this.list[i].station_id), "append");
GSOrder.AppendOrder(vehicle_id, GSStation.GetLocation(this.list[i].station_id), this.list[i].flags);
_SuperLib_Log.Info("Append order error msg: " + GSError.GetLastErrorString(), _SuperLib_Log.LVL_DEBUG);
}
_SuperLib_Log.Info("Breaking before next loop", _SuperLib_Log.LVL_DEBUG);
//Helper.BreakPoint(GSVehicle.GetLocation(vehicle_id));
}
_SuperLib_Log.Info("Breaking before removal", _SuperLib_Log.LVL_DEBUG);
//Helper.BreakPoint(GSVehicle.GetLocation(vehicle_id));
// if the vehicle order list contained orders that we don't want to have anymore, they will be located at the end.
// so remove all orders that are after the last item number in this.list
local num_to_remove = GSOrder.GetOrderCount(vehicle_id) - this.list.len();
if(num_to_remove > 0)
{
for(local j = 0; j < num_to_remove; j++)
{
// Remove the one with id last wanted + 1, since the remaining ones move up after each removal.
GSOrder.RemoveOrder(vehicle_id, this.list.len());
}
}
_SuperLib_Log.Info("Breaking after removal", _SuperLib_Log.LVL_DEBUG);
//Helper.BreakPoint(GSVehicle.GetLocation(vehicle_id));
// Add conditional orders if wanted
if(skip_to_last_when_full && GSOrder.GetOrderCount(vehicle_id) >= 2)
{
// for all but last
local order = 0;
local num_regular_orders = GSOrder.GetOrderCount(vehicle_id);
_SuperLib_Log.Info("Num regular orders: " + num_regular_orders, _SuperLib_Log.LVL_DEBUG);
for(local i = 0; i < num_regular_orders - 1; i++)
{
_SuperLib_Log.Info("Inserting a conditional order at location " + (order + 1), _SuperLib_Log.LVL_DEBUG);
GSOrder.InsertConditionalOrder(vehicle_id, order + 1, GSOrder.GetOrderCount(vehicle_id) - 1);
GSOrder.SetOrderCondition(vehicle_id, order + 1, GSOrder.OC_LOAD_PERCENTAGE);
GSOrder.SetOrderCompareFunction(vehicle_id, order + 1, GSOrder.CF_EQUALS);
GSOrder.SetOrderCompareValue(vehicle_id, order + 1, 100);
_SuperLib_Log.Info("Order count: " + GSOrder.GetOrderCount(vehicle_id), _SuperLib_Log.LVL_DEBUG);
order += 2; // next regular order
}
}
}
/* static */ function _SuperLib_OrderList::FindOrderDestination(vehicle_id, begin, match_destination)
{
local num = GSOrder.GetOrderCount(vehicle_id);
for(local i = begin; i < num; i++)
{
if(GSOrder.GetOrderDestination(vehicle_id, i) == match_destination)
return i;
}
return -1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment