(svn r15027) -Merge: tomatos and bananas left to be, here is NoAI for all to see.
NoAI is an API (a framework) to build your own AIs in. See: http://wiki.openttd.org/wiki/index.php/AI:Main_Page With many thanks to: - glx and Rubidium for their syncing, feedback and hard work - Yexo for his feedback, patches, and AIs which tested the system very deep - Morloth for his feedback and patches - TJIP for hosting a challenge which kept NoAI on track - All AI authors for testing our AI API, and all other people who helped in one way or another -Remove: all old AIs and their cheats/hackspull/155/head
parent
ec97e1fd0d
commit
c2406cd42d
@ -0,0 +1,12 @@
|
||||
/* $Id$ */
|
||||
|
||||
class AyStar extends AILibrary {
|
||||
function GetAuthor() { return "OpenTTD NoAI Developers Team"; }
|
||||
function GetName() { return "AyStar"; }
|
||||
function GetDescription() { return "An implementation of AyStar"; }
|
||||
function GetVersion() { return 4; }
|
||||
function GetDate() { return "2008-06-11"; }
|
||||
function CreateInstance() { return "AyStar"; }
|
||||
}
|
||||
|
||||
RegisterLibrary(AyStar());
|
@ -0,0 +1,238 @@
|
||||
/* $Id$ */
|
||||
|
||||
/**
|
||||
* An AyStar implementation.
|
||||
* It solves graphs by finding the fastest route from one point to the other.
|
||||
*/
|
||||
class AyStar
|
||||
{
|
||||
_queue_class = import("queue.binary_heap", "", 1);
|
||||
_cost_callback = null;
|
||||
_estimate_callback = null;
|
||||
_neighbours_callback = null;
|
||||
_check_direction_callback = null;
|
||||
_cost_callback_param = null;
|
||||
_estimate_callback_param = null;
|
||||
_neighbours_callback_param = null;
|
||||
_check_direction_callback_param = null;
|
||||
_open = null;
|
||||
_closed = null;
|
||||
_goals = null;
|
||||
|
||||
/**
|
||||
* @param cost_callback A function that returns the cost of a path. It
|
||||
* should accept four parameters, old_path, new_tile, new_direction and
|
||||
* cost_callback_param. old_path is an instance of AyStar.Path, and
|
||||
* new_node is the new node that is added to that path. It should return
|
||||
* the cost of the path including new_node.
|
||||
* @param estimate_callback A function that returns an estimate from a node
|
||||
* to the goal node. It should accept four parameters, tile, direction,
|
||||
* goal_nodes and estimate_callback_param. It should return an estimate to
|
||||
* the cost from the lowest cost between node and any node out of goal_nodes.
|
||||
* Note that this estimate is not allowed to be higher than the real cost
|
||||
* between node and any of goal_nodes. A lower value is fine, however the
|
||||
* closer it is to the real value, the better the performance.
|
||||
* @param neighbours_callback A function that returns all neighbouring nodes
|
||||
* from a given node. It should accept three parameters, current_path, node
|
||||
* and neighbours_callback_param. It should return an array containing all
|
||||
* neighbouring nodes, which are an array in the form [tile, direction].
|
||||
* @param check_direction_callback A function that returns either false or
|
||||
* true. It should accept four parameters, tile, existing_direction,
|
||||
* new_direction and check_direction_callback_param. It should check
|
||||
* if both directions can go together on a single tile.
|
||||
* @param cost_callback_param This parameters will be passed to cost_callback
|
||||
* as fourth parameter. Useful to send is an instance of an object.
|
||||
* @param estimate_callback_param This parameters will be passed to
|
||||
* estimate_callback as fourth parameter. Useful to send is an instance of an
|
||||
* object.
|
||||
* @param neighbours_callback_param This parameters will be passed to
|
||||
* neighbours_callback as third parameter. Useful to send is an instance of
|
||||
* an object.
|
||||
* @param check_direction_callback_param This parameters will be passed to
|
||||
* check_direction_callback as fourth parameter. Useful to send is an
|
||||
* instance of an object.
|
||||
*/
|
||||
constructor(cost_callback, estimate_callback, neighbours_callback, check_direction_callback, cost_callback_param = null,
|
||||
estimate_callback_param = null, neighbours_callback_param = null, check_direction_callback_param = null)
|
||||
{
|
||||
if (typeof(cost_callback) != "function") throw("'cost_callback' has to be a function-pointer.");
|
||||
if (typeof(estimate_callback) != "function") throw("'estimate_callback' has to be a function-pointer.");
|
||||
if (typeof(neighbours_callback) != "function") throw("'neighbours_callback' has to be a function-pointer.");
|
||||
if (typeof(check_direction_callback) != "function") throw("'check_direction_callback' has to be a function-pointer.");
|
||||
|
||||
this._cost_callback = cost_callback;
|
||||
this._estimate_callback = estimate_callback;
|
||||
this._neighbours_callback = neighbours_callback;
|
||||
this._check_direction_callback = check_direction_callback;
|
||||
this._cost_callback_param = cost_callback_param;
|
||||
this._estimate_callback_param = estimate_callback_param;
|
||||
this._neighbours_callback_param = neighbours_callback_param;
|
||||
this._check_direction_callback_param = check_direction_callback_param;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a path search between sources and goals.
|
||||
* @param sources The source nodes. This can an array of either [tile, direction]-pairs or AyStar.Path-instances.
|
||||
* @param goals The target tiles. This can be an array of either tiles or [tile, next_tile]-pairs.
|
||||
* @param ignored_tiles An array of tiles that cannot occur in the final path.
|
||||
*/
|
||||
function InitializePath(sources, goals, ignored_tiles = []);
|
||||
|
||||
/**
|
||||
* Try to find the path as indicated with InitializePath with the lowest cost.
|
||||
* @param iterations After how many iterations it should abort for a moment.
|
||||
* This value should either be -1 for infinite, or > 0. Any other value
|
||||
* aborts immediatly and will never find a path.
|
||||
* @return A route if one was found, or false if the amount of iterations was
|
||||
* reached, or null if no path was found.
|
||||
* You can call this function over and over as long as it returns false,
|
||||
* which is an indication it is not yet done looking for a route.
|
||||
*/
|
||||
function FindPath(iterations);
|
||||
};
|
||||
|
||||
function AyStar::InitializePath(sources, goals, ignored_tiles = [])
|
||||
{
|
||||
if (typeof(sources) != "array" || sources.len() == 0) throw("sources has be a non-empty array.");
|
||||
if (typeof(goals) != "array" || goals.len() == 0) throw("goals has be a non-empty array.");
|
||||
|
||||
this._open = this._queue_class();
|
||||
this._closed = AIList();
|
||||
|
||||
foreach (node in sources) {
|
||||
if (typeof(node) == "array") {
|
||||
if (node[1] <= 0) throw("directional value should never be zero or negative.");
|
||||
|
||||
local new_path = this.Path(null, node[0], node[1], this._cost_callback, this._cost_callback_param);
|
||||
this._open.Insert(new_path, new_path.GetCost() + this._estimate_callback(node[0], node[1], goals, this._estimate_callback_param));
|
||||
} else {
|
||||
this._open.Insert(node, node.GetCost());
|
||||
}
|
||||
}
|
||||
|
||||
this._goals = goals;
|
||||
|
||||
foreach (tile in ignored_tiles) {
|
||||
this._closed.AddItem(tile, ~0);
|
||||
}
|
||||
}
|
||||
|
||||
function AyStar::FindPath(iterations)
|
||||
{
|
||||
if (this._open == null) throw("can't execute over an uninitialized path");
|
||||
|
||||
while (this._open.Count() > 0 && (iterations == -1 || iterations-- > 0)) {
|
||||
/* Get the path with the best score so far */
|
||||
local path = this._open.Pop();
|
||||
local cur_tile = path.GetTile();
|
||||
/* Make sure we didn't already passed it */
|
||||
if (this._closed.HasItem(cur_tile)) {
|
||||
/* If the direction is already on the list, skip this entry */
|
||||
if ((this._closed.GetValue(cur_tile) & path.GetDirection()) != 0) continue;
|
||||
|
||||
/* Scan the path for a possible collision */
|
||||
local scan_path = path.GetParent();
|
||||
|
||||
local mismatch = false;
|
||||
while (scan_path != null) {
|
||||
if (scan_path.GetTile() == cur_tile) {
|
||||
if (!this._check_direction_callback(cur_tile, scan_path.GetDirection(), path.GetDirection(), this._check_direction_callback_param)) {
|
||||
mismatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
scan_path = scan_path.GetParent();
|
||||
}
|
||||
if (mismatch) continue;
|
||||
|
||||
/* Add the new direction */
|
||||
this._closed.SetValue(cur_tile, this._closed.GetValue(cur_tile) | path.GetDirection());
|
||||
} else {
|
||||
/* New entry, make sure we don't check it again */
|
||||
this._closed.AddItem(cur_tile, path.GetDirection());
|
||||
}
|
||||
/* Check if we found the end */
|
||||
foreach (goal in this._goals) {
|
||||
if (typeof(goal) == "array") {
|
||||
if (cur_tile == goal[0]) {
|
||||
local neighbours = this._neighbours_callback(path, cur_tile, this._neighbours_callback_param);
|
||||
foreach (node in neighbours) {
|
||||
if (node[0] == goal[1]) {
|
||||
this._CleanPath();
|
||||
return path;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (cur_tile == goal) {
|
||||
this._CleanPath();
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Scan all neighbours */
|
||||
local neighbours = this._neighbours_callback(path, cur_tile, this._neighbours_callback_param);
|
||||
foreach (node in neighbours) {
|
||||
if (node[1] <= 0) throw("directional value should never be zero or negative.");
|
||||
|
||||
if ((this._closed.GetValue(node[0]) & node[1]) != 0) continue;
|
||||
/* Calculate the new paths and add them to the open list */
|
||||
local new_path = this.Path(path, node[0], node[1], this._cost_callback, this._cost_callback_param);
|
||||
this._open.Insert(new_path, new_path.GetCost() + this._estimate_callback(node[0], node[1], this._goals, this._estimate_callback_param));
|
||||
}
|
||||
}
|
||||
|
||||
if (this._open.Count() > 0) return false;
|
||||
this._CleanPath();
|
||||
return null;
|
||||
}
|
||||
|
||||
function AyStar::_CleanPath()
|
||||
{
|
||||
this._closed = null;
|
||||
this._open = null;
|
||||
this._goals = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path of the AyStar algorithm.
|
||||
* It is reversed, that is, the first entry is more close to the goal-nodes
|
||||
* than his GetParent(). You can walk this list to find the whole path.
|
||||
* The last entry has a GetParent() of null.
|
||||
*/
|
||||
class AyStar.Path
|
||||
{
|
||||
_prev = null;
|
||||
_tile = null;
|
||||
_direction = null;
|
||||
_cost = null;
|
||||
|
||||
constructor(old_path, new_tile, new_direction, cost_callback, cost_callback_param)
|
||||
{
|
||||
this._prev = old_path;
|
||||
this._tile = new_tile;
|
||||
this._direction = new_direction;
|
||||
this._cost = cost_callback(old_path, new_tile, new_direction, cost_callback_param);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the tile where this (partial-)path ends.
|
||||
*/
|
||||
function GetTile() { return this._tile; }
|
||||
|
||||
/**
|
||||
* Return the direction from which we entered the tile in this (partial-)path.
|
||||
*/
|
||||
function GetDirection() { return this._direction; }
|
||||
|
||||
/**
|
||||
* Return an instance of this class leading to the previous node.
|
||||
*/
|
||||
function GetParent() { return this._prev; }
|
||||
|
||||
/**
|
||||
* Return the cost of this (partial-)path from the beginning up to this node.
|
||||
*/
|
||||
function GetCost() { return this._cost; }
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
/* $Id$ */
|
||||
|
||||
class Rail extends AILibrary {
|
||||
function GetAuthor() { return "OpenTTD NoAI Developers Team"; }
|
||||
function GetName() { return "Rail"; }
|
||||
function GetDescription() { return "An implementation of a rail pathfinder"; }
|
||||
function GetVersion() { return 1; }
|
||||
function GetDate() { return "2008-09-22"; }
|
||||
function CreateInstance() { return "Rail"; }
|
||||
}
|
||||
|
||||
RegisterLibrary(Rail());
|
@ -0,0 +1,389 @@
|
||||
/* $Id$ */
|
||||
|
||||
/**
|
||||
* A Rail Pathfinder.
|
||||
*/
|
||||
class Rail
|
||||
{
|
||||
_aystar_class = import("graph.aystar", "", 4);
|
||||
_max_cost = null; ///< The maximum cost for a route.
|
||||
_cost_tile = null; ///< The cost for a single tile.
|
||||
_cost_diagonal_tile = null; ///< The cost for a diagonal tile.
|
||||
_cost_turn = null; ///< The cost that is added to _cost_tile if the direction changes.
|
||||
_cost_slope = null; ///< The extra cost if a rail tile is sloped.
|
||||
_cost_bridge_per_tile = null; ///< The cost per tile of a new bridge, this is added to _cost_tile.
|
||||
_cost_tunnel_per_tile = null; ///< The cost per tile of a new tunnel, this is added to _cost_tile.
|
||||
_cost_coast = null; ///< The extra cost for a coast tile.
|
||||
_pathfinder = null; ///< A reference to the used AyStar object.
|
||||
_max_bridge_length = null; ///< The maximum length of a bridge that will be build.
|
||||
_max_tunnel_length = null; ///< The maximum length of a tunnel that will be build.
|
||||
|
||||
cost = null; ///< Used to change the costs.
|
||||
_running = null;
|
||||
_goals = null;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._max_cost = 10000000;
|
||||
this._cost_tile = 100;
|
||||
this._cost_diagonal_tile = 70;
|
||||
this._cost_turn = 50;
|
||||
this._cost_slope = 100;
|
||||
this._cost_bridge_per_tile = 150;
|
||||
this._cost_tunnel_per_tile = 120;
|
||||
this._cost_coast = 20;
|
||||
this._max_bridge_length = 6;
|
||||
this._max_tunnel_length = 6;
|
||||
this._pathfinder = this._aystar_class(this._Cost, this._Estimate, this._Neighbours, this._CheckDirection, this, this, this, this);
|
||||
|
||||
this.cost = this.Cost(this);
|
||||
this._running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a path search between sources and goals.
|
||||
* @param sources The source tiles.
|
||||
* @param goals The target tiles.
|
||||
* @param ignored_tiles An array of tiles that cannot occur in the final path.
|
||||
* @see AyStar::InitializePath()
|
||||
*/
|
||||
function InitializePath(sources, goals, ignored_tiles = []) {
|
||||
local nsources = [];
|
||||
|
||||
foreach (node in sources) {
|
||||
local path = this._pathfinder.Path(null, node[1], 0xFF, this._Cost, this);
|
||||
path = this._pathfinder.Path(path, node[0], 0xFF, this._Cost, this);
|
||||
nsources.push(path);
|
||||
}
|
||||
this._goals = goals;
|
||||
this._pathfinder.InitializePath(nsources, goals, ignored_tiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find the path as indicated with InitializePath with the lowest cost.
|
||||
* @param iterations After how many iterations it should abort for a moment.
|
||||
* This value should either be -1 for infinite, or > 0. Any other value
|
||||
* aborts immediatly and will never find a path.
|
||||
* @return A route if one was found, or false if the amount of iterations was
|
||||
* reached, or null if no path was found.
|
||||
* You can call this function over and over as long as it returns false,
|
||||
* which is an indication it is not yet done looking for a route.
|
||||
* @see AyStar::FindPath()
|
||||
*/
|
||||
function FindPath(iterations);
|
||||
};
|
||||
|
||||
class Rail.Cost
|
||||
{
|
||||
_main = null;
|
||||
|
||||
function _set(idx, val)
|
||||
{
|
||||
if (this._main._running) throw("You are not allowed to change parameters of a running pathfinder.");
|
||||
|
||||
switch (idx) {
|
||||
case "max_cost": this._main._max_cost = val; break;
|
||||
case "tile": this._main._cost_tile = val; break;
|
||||
case "diagonal_tile": this._cost_diagonal_tile = val; break;
|
||||
case "turn": this._main._cost_turn = val; break;
|
||||
case "slope": this._main._cost_slope = val; break;
|
||||
case "bridge_per_tile": this._main._cost_bridge_per_tile = val; break;
|
||||
case "tunnel_per_tile": this._main._cost_tunnel_per_tile = val; break;
|
||||
case "coast": this._main._cost_coast = val; break;
|
||||
case "max_bridge_length": this._main._max_bridge_length = val; break;
|
||||
case "max_tunnel_length": this._main._max_tunnel_length = val; break;
|
||||
default: throw("the index '" + idx + "' does not exist");
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
function _get(idx)
|
||||
{
|
||||
switch (idx) {
|
||||
case "max_cost": return this._main._max_cost;
|
||||
case "tile": return this._main._cost_tile;
|
||||
case "diagonal_tile": return this._cost_diagonal_tile;
|
||||
case "turn": return this._main._cost_turn;
|
||||
case "slope": return this._main._cost_slope;
|
||||
case "bridge_per_tile": return this._main._cost_bridge_per_tile;
|
||||
case "tunnel_per_tile": return this._main._cost_tunnel_per_tile;
|
||||
case "coast": return this._main._cost_coast;
|
||||
case "max_bridge_length": return this._main._max_bridge_length;
|
||||
case "max_tunnel_length": return this._main._max_tunnel_length;
|
||||
default: throw("the index '" + idx + "' does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
constructor(main)
|
||||
{
|
||||
this._main = main;
|
||||
}
|
||||
};
|
||||
|
||||
function Rail::FindPath(iterations)
|
||||
{
|
||||
local test_mode = AITestMode();
|
||||
local ret = this._pathfinder.FindPath(iterations);
|
||||
this._running = (ret == false) ? true : false;
|
||||
if (!this._running && ret != null) {
|
||||
foreach (goal in this._goals) {
|
||||
if (goal[0] == ret.GetTile()) {
|
||||
return this._pathfinder.Path(ret, goal[1], 0, this._Cost, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function Rail::_GetBridgeNumSlopes(end_a, end_b)
|
||||
{
|
||||
local slopes = 0;
|
||||
local direction = (end_b - end_a) / AIMap.DistanceManhattan(end_a, end_b);
|
||||
local slope = AITile.GetSlope(end_a);
|
||||
if (!((slope == AITile.SLOPE_NE && direction == 1) || (slope == AITile.SLOPE_SE && direction == -AIMap.GetMapSizeX()) ||
|
||||
(slope == AITile.SLOPE_SW && direction == -1) || (slope == AITile.SLOPE_NW && direction == AIMap.GetMapSizeX()) ||
|
||||
slope == AITile.SLOPE_N || slope == AITile.SLOPE_E || slope == AITile.SLOPE_S || slope == AITile.SLOPE_W)) {
|
||||
slopes++;
|
||||
}
|
||||
|
||||
local slope = AITile.GetSlope(end_b);
|
||||
direction = -direction;
|
||||
if (!((slope == AITile.SLOPE_NE && direction == 1) || (slope == AITile.SLOPE_SE && direction == -AIMap.GetMapSizeX()) ||
|
||||
(slope == AITile.SLOPE_SW && direction == -1) || (slope == AITile.SLOPE_NW && direction == AIMap.GetMapSizeX()) ||
|
||||
slope == AITile.SLOPE_N || slope == AITile.SLOPE_E || slope == AITile.SLOPE_S || slope == AITile.SLOPE_W)) {
|
||||
slopes++;
|
||||
}
|
||||
return slopes;
|
||||
}
|
||||
|
||||
function Rail::_nonzero(a, b)
|
||||
{
|
||||
return a != 0 ? a : b;
|
||||
}
|
||||
|
||||
function Rail::_Cost(path, new_tile, new_direction, self)
|
||||
{
|
||||
/* path == null means this is the first node of a path, so the cost is 0. */
|
||||
if (path == null) return 0;
|
||||
|
||||
local prev_tile = path.GetTile();
|
||||
|
||||
/* If the new tile is a bridge / tunnel tile, check whether we came from the other
|
||||
* end of the bridge / tunnel or if we just entered the bridge / tunnel. */
|
||||
if (AIBridge.IsBridgeTile(new_tile)) {
|
||||
if (AIBridge.GetOtherBridgeEnd(new_tile) != prev_tile) {
|
||||
local cost = path.GetCost() + self._cost_tile;
|
||||
if (path.GetParent() != null && path.GetParent().GetTile() - prev_tile != prev_tile - new_tile) cost += self._cost_turn;
|
||||
return cost;
|
||||
}
|
||||
return path.GetCost() + AIMap.DistanceManhattan(new_tile, prev_tile) * self._cost_tile + self._GetBridgeNumSlopes(new_tile, prev_tile) * self._cost_slope;
|
||||
}
|
||||
if (AITunnel.IsTunnelTile(new_tile)) {
|
||||
if (AITunnel.GetOtherTunnelEnd(new_tile) != prev_tile) {
|
||||
local cost = path.GetCost() + self._cost_tile;
|
||||
if (path.GetParent() != null && path.GetParent().GetTile() - prev_tile != prev_tile - new_tile) cost += self._cost_turn;
|
||||
return cost;
|
||||
}
|
||||
return path.GetCost() + AIMap.DistanceManhattan(new_tile, prev_tile) * self._cost_tile;
|
||||
}
|
||||
|
||||
/* If the two tiles are more then 1 tile apart, the pathfinder wants a bridge or tunnel
|
||||
* to be build. It isn't an existing bridge / tunnel, as that case is already handled. */
|
||||
if (AIMap.DistanceManhattan(new_tile, prev_tile) > 1) {
|
||||
/* Check if we should build a bridge or a tunnel. */
|
||||
local cost = path.GetCost();
|
||||
if (AITunnel.GetOtherTunnelEnd(new_tile) == prev_tile) {
|
||||
cost += AIMap.DistanceManhattan(new_tile, prev_tile) * (self._cost_tile + self._cost_tunnel_per_tile);
|
||||
} else {
|
||||
cost += AIMap.DistanceManhattan(new_tile, prev_tile) * (self._cost_tile + self._cost_bridge_per_tile) + self._GetBridgeNumSlopes(new_tile, prev_tile) * self._cost_slope;
|
||||
}
|
||||
if (path.GetParent() != null && path.GetParent().GetParent() != null &&
|
||||
path.GetParent().GetParent().GetTile() - path.GetParent().GetTile() != max(AIMap.GetTileX(prev_tile) - AIMap.GetTileX(new_tile), AIMap.GetTileY(prev_tile) - AIMap.GetTileY(new_tile)) / AIMap.DistanceManhattan(new_tile, prev_tile)) {
|
||||
cost += self._cost_turn;
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
|
||||
/* Check for a turn. We do this by substracting the TileID of the current
|
||||
* node from the TileID of the previous node and comparing that to the
|
||||
* difference between the tile before the previous node and the node before
|
||||
* that. */
|
||||
local cost = self._cost_tile;
|
||||
if (path.GetParent() != null && AIMap.DistanceManhattan(path.GetParent().GetTile(), prev_tile) == 1 && path.GetParent().GetTile() - prev_tile != prev_tile - new_tile) cost = self._cost_diagonal_tile;
|
||||
if (path.GetParent() != null && path.GetParent().GetParent() != null &&
|
||||
AIMap.DistanceManhattan(new_tile, path.GetParent().GetParent().GetTile()) == 3 &&
|
||||
path.GetParent().GetParent().GetTile() - path.GetParent().GetTile() != prev_tile - new_tile) {
|
||||
cost += self._cost_turn;
|
||||
}
|
||||
|
||||
/* Check if the new tile is a coast tile. */
|
||||
if (AITile.IsCoastTile(new_tile)) {
|
||||
cost += self._cost_coast;
|
||||
}
|
||||
|
||||
/* Check if the last tile was sloped. */
|
||||
if (path.GetParent() != null && !AIBridge.IsBridgeTile(prev_tile) && !AITunnel.IsTunnelTile(prev_tile) &&
|
||||
self._IsSlopedRail(path.GetParent().GetTile(), prev_tile, new_tile)) {
|
||||
cost += self._cost_slope;
|
||||
}
|
||||
|
||||
/* We don't use already existing rail, so the following code is unused. It
|
||||
* assigns if no rail exists along the route. */
|
||||
/*
|
||||
if (path.GetParent() != null && !AIRail.AreTilesConnected(path.GetParent().GetTile(), prev_tile, new_tile)) {
|
||||
cost += self._cost_no_existing_rail;
|
||||
}
|
||||
*/
|
||||
|
||||
return path.GetCost() + cost;
|
||||
}
|
||||
|
||||
function Rail::_Estimate(cur_tile, cur_direction, goal_tiles, self)
|
||||
{
|
||||
local min_cost = self._max_cost;
|
||||
/* As estimate we multiply the lowest possible cost for a single tile with
|
||||
* with the minimum number of tiles we need to traverse. */
|
||||
foreach (tile in goal_tiles) {
|
||||
local dx = abs(AIMap.GetTileX(cur_tile) - AIMap.GetTileX(tile[0]));
|
||||
local dy = abs(AIMap.GetTileY(cur_tile) - AIMap.GetTileY(tile[0]));
|
||||
min_cost = min(min_cost, min(dx, dy) * self._cost_diagonal_tile * 2 + (max(dx, dy) - min(dx, dy)) * self._cost_tile);
|
||||
}
|
||||
return min_cost;
|
||||
}
|
||||
|
||||
function Rail::_Neighbours(path, cur_node, self)
|
||||
{
|
||||
if (AITile.HasTransportType(cur_node, AITile.TRANSPORT_RAIL)) return [];
|
||||
/* self._max_cost is the maximum path cost, if we go over it, the path isn't valid. */
|
||||
if (path.GetCost() >= self._max_cost) return [];
|
||||
local tiles = [];
|
||||
local offsets = [AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(0, -1),
|
||||
AIMap.GetTileIndex(1, 0), AIMap.GetTileIndex(-1, 0)];
|
||||
|
||||
/* Check if the current tile is part of a bridge or tunnel. */
|
||||
if (AIBridge.IsBridgeTile(cur_node) || AITunnel.IsTunnelTile(cur_node)) {
|
||||
/* We don't use existing rails, so neither existing bridges / tunnels. */
|
||||
} else if (path.GetParent() != null && AIMap.DistanceManhattan(cur_node, path.GetParent().GetTile()) > 1) {
|
||||
local other_end = path.GetParent().GetTile();
|
||||
local next_tile = cur_node + (cur_node - other_end) / AIMap.DistanceManhattan(cur_node, other_end);
|
||||
foreach (offset in offsets) {
|
||||
if (AIRail.BuildRail(cur_node, next_tile, next_tile + offset)) {
|
||||
tiles.push([next_tile, self._GetDirection(other_end, cur_node, next_tile, true)]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Check all tiles adjacent to the current tile. */
|
||||
foreach (offset in offsets) {
|
||||
local next_tile = cur_node + offset;
|
||||
/* Don't turn back */
|
||||
if (path.GetParent() != null && next_tile == path.GetParent().GetTile()) continue;
|
||||
/* Disallow 90 degree turns */
|
||||
if (path.GetParent() != null && path.GetParent().GetParent() != null &&
|
||||
next_tile - cur_node == path.GetParent().GetParent().GetTile() - path.GetParent().GetTile()) continue;
|
||||
/* We add them to the to the neighbours-list if we can build a rail to
|
||||
* them and no rail exists there. */
|
||||
if ((path.GetParent() == null || AIRail.BuildRail(path.GetParent().GetTile(), cur_node, next_tile))) {
|
||||
if (path.GetParent() != null) {
|
||||
tiles.push([next_tile, self._GetDirection(path.GetParent().GetTile(), cur_node, next_tile, false)]);
|
||||
} else {
|
||||
tiles.push([next_tile, self._GetDirection(null, cur_node, next_tile, false)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (path.GetParent() != null && path.GetParent().GetParent() != null) {
|
||||
local bridges = self._GetTunnelsBridges(path.GetParent().GetTile(), cur_node, self._GetDirection(path.GetParent().GetParent().GetTile(), path.GetParent().GetTile(), cur_node, true));
|
||||
foreach (tile in bridges) {
|
||||
tiles.push(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tiles;
|
||||
}
|
||||
|
||||
function Rail::_CheckDirection(tile, existing_direction, new_direction, self)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
function Rail::_dir(from, to)
|
||||
{
|
||||
if (from - to == 1) return 0;
|
||||
if (from - to == -1) return 1;
|
||||
if (from - to == AIMap.GetMapSizeX()) return 2;
|
||||
if (from - to == -AIMap.GetMapSizeX()) return 3;
|
||||
throw("Shouldn't come here in _dir");
|
||||
}
|
||||
|
||||
function Rail::_GetDirection(pre_from, from, to, is_bridge)
|
||||
{
|
||||
if (is_bridge) {
|
||||
if (from - to == 1) return 1;
|
||||
if (from - to == -1) return 2;
|
||||
if (from - to == AIMap.GetMapSizeX()) return 4;
|
||||
if (from - to == -AIMap.GetMapSizeX()) return 8;
|
||||
}
|
||||
return 1 << (4 + (pre_from == null ? 0 : 4 * this._dir(pre_from, from)) + this._dir(from, to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all bridges and tunnels that can be build from the
|
||||
* current tile. Bridges will only be build starting on non-flat tiles
|
||||
* for performance reasons. Tunnels will only be build if no terraforming
|
||||
* is needed on both ends.
|
||||
*/
|
||||
function Rail::_GetTunnelsBridges(last_node, cur_node, bridge_dir)
|
||||
{
|
||||
local slope = AITile.GetSlope(cur_node);
|
||||
if (slope == AITile.SLOPE_FLAT && AITile.IsBuildable(cur_node + (cur_node - last_node))) return [];
|
||||
local tiles = [];
|
||||
|
||||
for (local i = 2; i < this._max_bridge_length; i++) {
|
||||
local bridge_list = AIBridgeList_Length(i + 1);
|
||||
local target = cur_node + i * (cur_node - last_node);
|
||||
if (!bridge_list.IsEmpty() && AIBridge.BuildBridge(AIVehicle.VEHICLE_RAIL, bridge_list.Begin(), cur_node, target)) {
|
||||
tiles.push([target, bridge_dir]);
|
||||
}
|
||||
}
|
||||
|
||||
if (slope != AITile.SLOPE_SW && slope != AITile.SLOPE_NW && slope != AITile.SLOPE_SE && slope != AITile.SLOPE_NE) return tiles;
|
||||
local other_tunnel_end = AITunnel.GetOtherTunnelEnd(cur_node);
|
||||
if (!AIMap.IsValidTile(other_tunnel_end)) return tiles;
|
||||
|
||||
local tunnel_length = AIMap.DistanceManhattan(cur_node, other_tunnel_end);
|
||||
local prev_tile = cur_node + (cur_node - other_tunnel_end) / tunnel_length;
|
||||
if (AITunnel.GetOtherTunnelEnd(other_tunnel_end) == cur_node && tunnel_length >= 2 &&
|
||||
prev_tile == last_node && tunnel_length < _max_tunnel_length && AITunnel.BuildTunnel(AIVehicle.VEHICLE_RAIL, cur_node)) {
|
||||
tiles.push([other_tunnel_end, bridge_dir]);
|
||||
}
|
||||
return tiles;
|
||||
}
|
||||
|
||||
function Rail::_IsSlopedRail(start, middle, end)
|
||||
{
|
||||
local NW = 0; // Set to true if we want to build a rail to / from the north-west
|
||||
local NE = 0; // Set to true if we want to build a rail to / from the north-east
|
||||
local SW = 0; // Set to true if we want to build a rail to / from the south-west
|
||||
local SE = 0; // Set to true if we want to build a rail to / from the south-east
|
||||
|
||||
if (middle - AIMap.GetMapSizeX() == start || middle - AIMap.GetMapSizeX() == end) NW = 1;
|
||||
if (middle - 1 == start || middle - 1 == end) NE = 1;
|
||||
if (middle + AIMap.GetMapSizeX() == start || middle + AIMap.GetMapSizeX() == end) SE = 1;
|
||||
if (middle + 1 == start || middle + 1 == end) SW = 1;
|
||||
|
||||
/* If there is a turn in the current tile, it can't be sloped. */
|
||||
if ((NW || SE) && (NE || SW)) return false;
|
||||
|
||||
local slope = AITile.GetSlope(middle);
|
||||
/* A rail on a steep slope is always sloped. */
|
||||
if (AITile.IsSteepSlope(slope)) return true;
|
||||
|
||||
/* If only one corner is raised, the rail is sloped. */
|
||||
if (slope == AITile.SLOPE_N || slope == AITile.SLOPE_W) return true;
|
||||
if (slope == AITile.SLOPE_S || slope == AITile.SLOPE_E) return true;
|
||||
|
||||
if (NW && (slope == AITile.SLOPE_NW || slope == AITile.SLOPE_SE)) return true;
|
||||
if (NE && (slope == AITile.SLOPE_NE || slope == AITile.SLOPE_SW)) return true;
|
||||
|
||||
return false;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
/* $Id$ */
|
||||
|
||||
class Road extends AILibrary {
|
||||
function GetAuthor() { return "OpenTTD NoAI Developers Team"; }
|
||||
function GetName() { return "Road"; }
|
||||
function GetDescription() { return "An implementation of a road pathfinder"; }
|
||||
function GetVersion() { return 3; }
|
||||
function GetDate() { return "2008-06-18"; }
|
||||
function CreateInstance() { return "Road"; }
|
||||
}
|
||||
|
||||
RegisterLibrary(Road());
|
@ -0,0 +1,363 @@
|
||||
/* $Id$ */
|
||||
|
||||
/**
|
||||
* A Road Pathfinder.
|
||||
* This road pathfinder tries to find a buildable / existing route for
|
||||
* road vehicles. You can changes the costs below using for example
|
||||
* roadpf.cost.turn = 30. Note that it's not allowed to change the cost
|
||||
* between consecutive calls to FindPath. You can change the cost before
|
||||
* the first call to FindPath and after FindPath has returned an actual
|
||||
* route. To use only existing roads, set cost.no_existing_road to
|
||||
* cost.max_cost.
|
||||
*/
|
||||
class Road
|
||||
{
|
||||
_aystar_class = import("graph.aystar", "", 4);
|
||||
_max_cost = null; ///< The maximum cost for a route.
|
||||
_cost_tile = null; ///< The cost for a single tile.
|
||||
_cost_no_existing_road = null; ///< The cost that is added to _cost_tile if no road exists yet.
|
||||
_cost_turn = null; ///< The cost that is added to _cost_tile if the direction changes.
|
||||
_cost_slope = null; ///< The extra cost if a road tile is sloped.
|
||||
_cost_bridge_per_tile = null; ///< The cost per tile of a new bridge, this is added to _cost_tile.
|
||||
_cost_tunnel_per_tile = null; ///< The cost per tile of a new tunnel, this is added to _cost_tile.
|
||||
_cost_coast = null; ///< The extra cost for a coast tile.
|
||||
_pathfinder = null; ///< A reference to the used AyStar object.
|
||||
_max_bridge_length = null; ///< The maximum length of a bridge that will be build.
|
||||
_max_tunnel_length = null; ///< The maximum length of a tunnel that will be build.
|
||||
|
||||
cost = null; ///< Used to change the costs.
|
||||
_running = null;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._max_cost = 10000000;
|
||||
this._cost_tile = 100;
|
||||
this._cost_no_existing_road = 40;
|
||||
this._cost_turn = 100;
|
||||
this._cost_slope = 200;
|
||||
this._cost_bridge_per_tile = 150;
|
||||
this._cost_tunnel_per_tile = 120;
|
||||
this._cost_coast = 20;
|
||||
this._max_bridge_length = 10;
|
||||
this._max_tunnel_length = 20;
|
||||
this._pathfinder = this._aystar_class(this._Cost, this._Estimate, this._Neighbours, this._CheckDirection, this, this, this, this);
|
||||
|
||||
this.cost = this.Cost(this);
|
||||
this._running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a path search between sources and goals.
|
||||
* @param sources The source tiles.
|
||||
* @param goals The target tiles.
|
||||
* @see AyStar::InitializePath()
|
||||
*/
|
||||
function InitializePath(sources, goals) {
|
||||
local nsources = [];
|
||||
|
||||
foreach (node in sources) {
|
||||
nsources.push([node, 0xFF]);
|
||||
}
|
||||
this._pathfinder.InitializePath(nsources, goals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find the path as indicated with InitializePath with the lowest cost.
|
||||
* @param iterations After how many iterations it should abort for a moment.
|
||||
* This value should either be -1 for infinite, or > 0. Any other value
|
||||
* aborts immediatly and will never find a path.
|
||||
* @return A route if one was found, or false if the amount of iterations was
|
||||
* reached, or null if no path was found.
|
||||
* You can call this function over and over as long as it returns false,
|
||||
* which is an indication it is not yet done looking for a route.
|
||||
* @see AyStar::FindPath()
|
||||
*/
|
||||
function FindPath(iterations);
|
||||
};
|
||||
|
||||
class Road.Cost
|
||||
{
|
||||
_main = null;
|
||||
|
||||
function _set(idx, val)
|
||||
{
|
||||
if (this._main._running) throw("You are not allowed to change parameters of a running pathfinder.");
|
||||
|
||||
switch (idx) {
|
||||
case "max_cost": this._main._max_cost = val; break;
|
||||
case "tile": this._main._cost_tile = val; break;
|
||||
case "no_existing_road": this._main._cost_no_existing_road = val; break;
|
||||
case "turn": this._main._cost_turn = val; break;
|
||||
case "slope": this._main._cost_slope = val; break;
|
||||
case "bridge_per_tile": this._main._cost_bridge_per_tile = val; break;
|
||||
case "tunnel_per_tile": this._main._cost_tunnel_per_tile = val; break;
|
||||
case "coast": this._main._cost_coast = val; break;
|
||||
case "max_bridge_length": this._main._max_bridge_length = val; break;
|
||||
case "max_tunnel_length": this._main._max_tunnel_length = val; break;
|
||||
default: throw("the index '" + idx + "' does not exist");
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
function _get(idx)
|
||||
{
|
||||
switch (idx) {
|
||||
case "max_cost": return this._main._max_cost;
|
||||
case "tile": return this._main._cost_tile;
|
||||
case "no_existing_road": return this._main._cost_no_existing_road;
|
||||
case "turn": return this._main._cost_turn;
|
||||
case "slope": return this._main._cost_slope;
|
||||
case "bridge_per_tile": return this._main._cost_bridge_per_tile;
|
||||
case "tunnel_per_tile": return this._main._cost_tunnel_per_tile;
|
||||
case "coast": return this._main._cost_coast;
|
||||
case "max_bridge_length": return this._main._max_bridge_length;
|
||||
case "max_tunnel_length": return this._main._max_tunnel_length;
|
||||
default: throw("the index '" + idx + "' does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
constructor(main)
|
||||
{
|
||||
this._main = main;
|
||||
}
|
||||
};
|
||||
|
||||
function Road::FindPath(iterations)
|
||||
{
|
||||
local test_mode = AITestMode();
|
||||
local ret = this._pathfinder.FindPath(iterations);
|
||||
this._running = (ret == false) ? true : false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
function Road::_GetBridgeNumSlopes(end_a, end_b)
|
||||
{
|
||||
local slopes = 0;
|
||||
local direction = (end_b - end_a) / AIMap.DistanceManhattan(end_a, end_b);
|
||||
local slope = AITile.GetSlope(end_a);
|
||||
if (!((slope == AITile.SLOPE_NE && direction == 1) || (slope == AITile.SLOPE_SE && direction == -AIMap.GetMapSizeX()) ||
|
||||
(slope == AITile.SLOPE_SW && direction == -1) || (slope == AITile.SLOPE_NW && direction == AIMap.GetMapSizeX()) ||
|
||||
slope == AITile.SLOPE_N || slope == AITile.SLOPE_E || slope == AITile.SLOPE_S || slope == AITile.SLOPE_W)) {
|
||||
slopes++;
|
||||
}
|
||||
|
||||
local slope = AITile.GetSlope(end_b);
|
||||
direction = -direction;
|
||||
if (!((slope == AITile.SLOPE_NE && direction == 1) || (slope == AITile.SLOPE_SE && direction == -AIMap.GetMapSizeX()) ||
|
||||
(slope == AITile.SLOPE_SW && direction == -1) || (slope == AITile.SLOPE_NW && direction == AIMap.GetMapSizeX()) ||
|
||||
slope == AITile.SLOPE_N || slope == AITile.SLOPE_E || slope == AITile.SLOPE_S || slope == AITile.SLOPE_W)) {
|
||||
slopes++;
|
||||
}
|
||||
return slopes;
|
||||
}
|
||||
|
||||
function Road::_Cost(path, new_tile, new_direction, self)
|
||||
{
|
||||
/* path == null means this is the first node of a path, so the cost is 0. */
|
||||
if (path == null) return 0;
|
||||
|
||||
local prev_tile = path.GetTile();
|
||||
|
||||
/* If the new tile is a bridge / tunnel tile, check whether we came from the other
|
||||
* end of the bridge / tunnel or if we just entered the bridge / tunnel. */
|
||||
if (AIBridge.IsBridgeTile(new_tile)) {
|
||||
if (AIBridge.GetOtherBridgeEnd(new_tile) != prev_tile) return path.GetCost() + self._cost_tile;
|
||||
return path.GetCost() + AIMap.DistanceManhattan(new_tile, prev_tile) * self._cost_tile + self._GetBridgeNumSlopes(new_tile, prev_tile) * self._cost_slope;
|
||||
}
|
||||
if (AITunnel.IsTunnelTile(new_tile)) {
|
||||
if (AITunnel.GetOtherTunnelEnd(new_tile) != prev_tile) return path.GetCost() + self._cost_tile;
|
||||
return path.GetCost() + AIMap.DistanceManhattan(new_tile, prev_tile) * self._cost_tile;
|
||||
}
|
||||
|
||||
/* If the two tiles are more then 1 tile apart, the pathfinder wants a bridge or tunnel
|
||||
* to be build. It isn't an existing bridge / tunnel, as that case is already handled. */
|
||||
if (AIMap.DistanceManhattan(new_tile, prev_tile) > 1) {
|
||||
/* Check if we should build a bridge or a tunnel. */
|
||||
if (AITunnel.GetOtherTunnelEnd(new_tile) == prev_tile) {
|
||||
return path.GetCost() + AIMap.DistanceManhattan(new_tile, prev_tile) * (self._cost_tile + self._cost_tunnel_per_tile);
|
||||
} else {
|
||||
return path.GetCost() + AIMap.DistanceManhattan(new_tile, prev_tile) * (self._cost_tile + self._cost_bridge_per_tile) + self._GetBridgeNumSlopes(new_tile, prev_tile) * self._cost_slope;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for a turn. We do this by substracting the TileID of the current node from
|
||||
* the TileID of the previous node and comparing that to the difference between the
|
||||
* previous node and the node before that. */
|
||||
local cost = self._cost_tile;
|
||||
if (path.GetParent() != null && (prev_tile - path.GetParent().GetTile()) != (new_tile - prev_tile) &&
|
||||
AIMap.DistanceManhattan(path.GetParent().GetTile(), prev_tile) == 1) {
|
||||
cost += self._cost_turn;
|
||||
}
|
||||
|
||||
/* Check if the new tile is a coast tile. */
|
||||
if (AITile.IsCoastTile(new_tile)) {
|
||||
cost += self._cost_coast;
|
||||
}
|
||||
|
||||
/* Check if the last tile was sloped. */
|
||||
if (path.GetParent() != null && !AIBridge.IsBridgeTile(prev_tile) && !AITunnel.IsTunnelTile(prev_tile) &&
|
||||
self._IsSlopedRoad(path.GetParent().GetTile(), prev_tile, new_tile)) {
|
||||
cost += self._cost_slope;
|
||||
}
|
||||
|
||||
if (!AIRoad.AreRoadTilesConnected(prev_tile, new_tile)) {
|
||||
cost += self._cost_no_existing_road;
|
||||
}
|
||||
|
||||
return path.GetCost() + cost;
|
||||
}
|
||||
|
||||
function Road::_Estimate(cur_tile, cur_direction, goal_tiles, self)
|
||||
{
|
||||
local min_cost = self._max_cost;
|
||||
/* As estimate we multiply the lowest possible cost for a single tile with
|
||||
* with the minimum number of tiles we need to traverse. */
|
||||
foreach (tile in goal_tiles) {
|
||||
min_cost = min(AIMap.DistanceManhattan(cur_tile, tile) * self._cost_tile, min_cost);
|
||||
}
|
||||
return min_cost;
|
||||
}
|
||||
|
||||
function Road::_Neighbours(path, cur_node, self)
|
||||
{
|
||||
/* self._max_cost is the maximum path cost, if we go over it, the path isn't valid. */
|
||||
if (path.GetCost() >= self._max_cost) return [];
|
||||
local tiles = [];
|
||||
|
||||
/* Check if the current tile is part of a bridge or tunnel. */
|
||||
if ((AIBridge.IsBridgeTile(cur_node) || AITunnel.IsTunnelTile(cur_node)) &&
|
||||
AITile.HasTransportType(cur_node, AITile.TRANSPORT_ROAD)) {
|
||||
local other_end = AIBridge.IsBridgeTile(cur_node) ? AIBridge.GetOtherBridgeEnd(cur_node) : AITunnel.GetOtherTunnelEnd(cur_node);
|
||||
local next_tile = cur_node + (cur_node - other_end) / AIMap.DistanceManhattan(cur_node, other_end);
|
||||
if (AIRoad.AreRoadTilesConnected(cur_node, next_tile) || AITile.IsBuildable(next_tile) || AIRoad.IsRoadTile(next_tile)) {
|
||||
tiles.push([next_tile, self._GetDirection(cur_node, next_tile, false)]);
|
||||
}
|
||||
/* The other end of the bridge / tunnel is a neighbour. */
|
||||
tiles.push([other_end, self._GetDirection(next_tile, cur_node, true) << 4]);
|
||||
} else if (path.GetParent() != null && AIMap.DistanceManhattan(cur_node, path.GetParent().GetTile()) > 1) {
|
||||
local other_end = path.GetParent().GetTile();
|
||||
local next_tile = cur_node + (cur_node - other_end) / AIMap.DistanceManhattan(cur_node, other_end);
|
||||
if (AIRoad.AreRoadTilesConnected(cur_node, next_tile) || AIRoad.BuildRoad(cur_node, next_tile)) {
|
||||
tiles.push([next_tile, self._GetDirection(cur_node, next_tile, false)]);
|
||||
}
|
||||
} else {
|
||||
local offsets = [AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(0, -1),
|
||||
AIMap.GetTileIndex(1, 0), AIMap.GetTileIndex(-1, 0)];
|
||||
/* Check all tiles adjacent to the current tile. */
|
||||
foreach (offset in offsets) {
|
||||
local next_tile = cur_node + offset;
|
||||
/* We add them to the to the neighbours-list if one of the following applies:
|
||||
* 1) There already is a connections between the current tile and the next tile.
|
||||
* 2) We can build a road to the next tile.
|
||||
* 3) The next tile is the entrance of a tunnel / bridge in the correct direction. */
|
||||
if (AIRoad.AreRoadTilesConnected(cur_node, next_tile)) {
|
||||
tiles.push([next_tile, self._GetDirection(cur_node, next_tile, false)]);
|
||||
} else if ((AITile.IsBuildable(next_tile) || AIRoad.IsRoadTile(next_tile)) &&
|
||||
(path.GetParent() == null || AIRoad.CanBuildConnectedRoadPartsHere(cur_node, path.GetParent().GetTile(), next_tile)) &&
|
||||
AIRoad.BuildRoad(cur_node, next_tile)) {
|
||||
tiles.push([next_tile, self._GetDirection(cur_node, next_tile, false)]);
|
||||
} else if (self._CheckTunnelBridge(cur_node, next_tile)) {
|
||||
tiles.push([next_tile, self._GetDirection(cur_node, next_tile, false)]);
|
||||
}
|
||||
}
|
||||
if (path.GetParent() != null) {
|
||||
local bridges = self._GetTunnelsBridges(path.GetParent().GetTile(), cur_node, self._GetDirection(path.GetParent().GetTile(), cur_node, true) << 4);
|
||||
foreach (tile in bridges) {
|
||||
tiles.push(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tiles;
|
||||
}
|
||||
|
||||
function Road::_CheckDirection(tile, existing_direction, new_direction, self)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
function Road::_GetDirection(from, to, is_bridge)
|
||||
{
|
||||
if (!is_bridge && AITile.GetSlope(to) == AITile.SLOPE_FLAT) return 0xFF;
|
||||
if (from - to == 1) return 1;
|
||||
if (from - to == -1) return 2;
|
||||
if (from - to == AIMap.GetMapSizeX()) return 4;
|
||||
if (from - to == -AIMap.GetMapSizeX()) return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all bridges and tunnels that can be build from the
|
||||
* current tile. Bridges will only be build starting on non-flat tiles
|
||||
* for performance reasons. Tunnels will only be build if no terraforming
|
||||
* is needed on both ends.
|
||||
*/
|
||||
function Road::_GetTunnelsBridges(last_node, cur_node, bridge_dir)
|
||||
{
|
||||
local slope = AITile.GetSlope(cur_node);
|
||||
if (slope == AITile.SLOPE_FLAT) return [];
|
||||
local tiles = [];
|
||||
|
||||
for (local i = 2; i < this._max_bridge_length; i++) {
|
||||
local bridge_list = AIBridgeList_Length(i + 1);
|
||||
local target = cur_node + i * (cur_node - last_node);
|
||||
if (!bridge_list.IsEmpty() && AIBridge.BuildBridge(AIVehicle.VEHICLE_ROAD, bridge_list.Begin(), cur_node, target)) {
|
||||
tiles.push([target, bridge_dir]);
|
||||
}
|
||||
}
|
||||
|
||||
if (slope != AITile.SLOPE_SW && slope != AITile.SLOPE_NW && slope != AITile.SLOPE_SE && slope != AITile.SLOPE_NE) return tiles;
|
||||
local other_tunnel_end = AITunnel.GetOtherTunnelEnd(cur_node);
|
||||
if (!AIMap.IsValidTile(other_tunnel_end)) return tiles;
|
||||
|
||||
local tunnel_length = AIMap.DistanceManhattan(cur_node, other_tunnel_end);
|
||||
local prev_tile = cur_node + (cur_node - other_tunnel_end) / tunnel_length;
|
||||
if (AITunnel.GetOtherTunnelEnd(other_tunnel_end) == cur_node && tunnel_length >= 2 &&
|
||||
prev_tile == last_node && tunnel_length < _max_tunnel_length && AITunnel.BuildTunnel(AIVehicle.VEHICLE_ROAD, cur_node)) {
|
||||
tiles.push([other_tunnel_end, bridge_dir]);
|
||||
}
|
||||
return tiles;
|
||||
}
|
||||
|
||||
function Road::_IsSlopedRoad(start, middle, end)
|
||||
{
|
||||
local NW = 0; //Set to true if we want to build a road to / from the north-west
|
||||
local NE = 0; //Set to true if we want to build a road to / from the north-east
|
||||
local SW = 0; //Set to true if we want to build a road to / from the south-west
|
||||
local SE = 0; //Set to true if we want to build a road to / from the south-east
|
||||
|
||||
if (middle - AIMap.GetMapSizeX() == start || middle - AIMap.GetMapSizeX() == end) NW = 1;
|
||||
if (middle - 1 == start || middle - 1 == end) NE = 1;
|
||||
if (middle + AIMap.GetMapSizeX() == start || middle + AIMap.GetMapSizeX() == end) SE = 1;
|
||||
if (middle + 1 == start || middle + 1 == end) SW = 1;
|
||||
|
||||
/* If there is a turn in the current tile, it can't be sloped. */
|
||||
if ((NW || SE) && (NE || SW)) return false;
|
||||
|
||||
local slope = AITile.GetSlope(middle);
|
||||
/* A road on a steep slope is always sloped. */
|
||||
if (AITile.IsSteepSlope(slope)) return true;
|
||||
|
||||
/* If only one corner is raised, the road is sloped. */
|
||||
if (slope == AITile.SLOPE_N || slope == AITile.SLOPE_W) return true;
|
||||
if (slope == AITile.SLOPE_S || slope == AITile.SLOPE_E) return true;
|
||||
|
||||
if (NW && (slope == AITile.SLOPE_NW || slope == AITile.SLOPE_SE)) return true;
|
||||
if (NE && (slope == AITile.SLOPE_NE || slope == AITile.SLOPE_SW)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function Road::_CheckTunnelBridge(current_tile, new_tile)
|
||||
{
|
||||
if (!AIBridge.IsBridgeTile(new_tile) && !AITunnel.IsTunnelTile(new_tile)) return false;
|
||||
local dir = new_tile - current_tile;
|
||||
local other_end = AIBridge.IsBridgeTile(new_tile) ? AIBridge.GetOtherBridgeEnd(new_tile) : AITunnel.GetOtherTunnelEnd(new_tile);
|
||||
local dir2 = other_end - new_tile;
|
||||
if ((dir < 0 && dir2 > 0) || (dir > 0 && dir2 < 0)) return false;
|
||||
dir = abs(dir);
|
||||
dir2 = abs(dir2);
|
||||
if ((dir >= AIMap.GetMapSizeX() && dir2 < AIMap.GetMapSizeX()) ||
|
||||
(dir < AIMap.GetMapSizeX() && dir2 >= AIMap.GetMapSizeX())) return false;
|
||||
|
||||
return true;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
/* $Id$ */
|
||||
|
||||
class BinaryHeap extends AILibrary {
|
||||
function GetAuthor() { return "OpenTTD NoAI Developers Team"; }
|
||||
function GetName() { return "Binary Heap"; }
|
||||
function GetDescription() { return "An implementation of a Binary Heap"; }
|
||||
function GetVersion() { return 1; }
|
||||
function GetDate() { return "2008-06-10"; }
|
||||
function CreateInstance() { return "BinaryHeap"; }
|
||||
}
|
||||
|
||||
RegisterLibrary(BinaryHeap());
|
@ -0,0 +1,131 @@
|
||||
/* $Id$ */
|
||||
|
||||
/**
|
||||
* Binary Heap.
|
||||
* Peek and Pop always return the current lowest value in the list.
|
||||
* Sort is done on insertion and on deletion.
|
||||
*/
|
||||
class BinaryHeap
|
||||
{
|
||||
_queue = null;
|
||||
_count = 0;
|
||||
|
||||
constructor()
|
||||
{
|
||||
_queue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new entry in the list.
|
||||
* The complexity of this operation is O(ln n).
|
||||
* @param item The item to add to the list.
|
||||
* @param priority The priority this item has.
|
||||
*/
|
||||
function Insert(item, priority);
|
||||
|
||||
/**
|
||||
* Pop the first entry of the list.
|
||||
* This is always the item with the lowest priority.
|
||||
* The complexity of this operation is O(ln n).
|
||||
* @return The item of the entry with the lowest priority.
|
||||
*/
|
||||
function Pop();
|
||||
|
||||
/**
|
||||
* Peek the first entry of the list.
|
||||
* This is always the item with the lowest priority.
|
||||
* The complexity of this operation is O(1).
|
||||
* @return The item of the entry with the lowest priority.
|
||||
*/
|
||||
function Peek();
|
||||
|
||||
/**
|
||||
* Get the amount of current items in the list.
|
||||
* The complexity of this operation is O(1).
|
||||
* @return The amount of items currently in the list.
|
||||
*/
|
||||
function Count();
|
||||
|
||||
/**
|
||||
* Check if an item exists in the list.
|
||||
* The complexity of this operation is O(n).
|
||||
* @param item The item to check for.
|
||||
* @return True if the item is already in the list.
|
||||
*/
|
||||
function Exists(item);
|
||||
};
|
||||
|
||||
function BinaryHeap::Insert(item, priority)
|
||||
{
|
||||
/* Append dummy entry */
|
||||
_queue.append(0);
|
||||
_count++;
|
||||
|
||||
local hole;
|
||||
/* Find the point of insertion */
|
||||
for (hole = _count - 1; hole > 0 && priority <= _queue[hole / 2][1]; hole /= 2)
|
||||
_queue[hole] = _queue[hole / 2];
|
||||
/* Insert new pair */
|
||||
_queue[hole] = [item, priority];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function BinaryHeap::Pop()
|
||||
{
|
||||
if (_count == 0) return null;
|
||||
|
||||
local node = _queue[0];
|
||||
/* Remove the item from the list by putting the last value on top */
|
||||
_queue[0] = _queue[_count - 1];
|
||||
_queue.pop();
|
||||
_count--;
|
||||
/* Bubble down the last value to correct the tree again */
|
||||
_BubbleDown();
|
||||
|
||||
return node[0];
|
||||
}
|
||||
|
||||
function BinaryHeap::Peek()
|
||||
{
|
||||
if (_count == 0) return null;
|
||||
|
||||
return _queue[0][0];
|
||||
}
|
||||
|
||||
function BinaryHeap::Count()
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
|
||||
function BinaryHeap::Exists(item)
|
||||
{
|
||||
/* Brute-force find the item (there is no faster way, as we don't have the priority number) */
|
||||
foreach (node in _queue) {
|
||||
if (node[0] == item) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function BinaryHeap::_BubbleDown()
|
||||
{
|
||||
if (_count == 0) return;
|
||||
|
||||
local hole = 1;
|
||||
local tmp = _queue[0];
|
||||
|
||||
/* Start switching parent and child until the tree is restored */
|
||||
while (hole * 2 < _count + 1) {
|
||||
local child = hole * 2;
|
||||
if (child != _count && _queue[child][1] <= _queue[child - 1][1]) child++;
|
||||
if (_queue[child - 1][1] > tmp[1]) break;
|
||||
|
||||
_queue[hole - 1] = _queue[child - 1];
|
||||
hole = child;
|
||||
}
|
||||
/* The top value is now at his new place */
|
||||
_queue[hole - 1] = tmp;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
/* $Id$ */
|
||||
|
||||
class FibonacciHeap extends AILibrary {
|
||||
function GetAuthor() { return "OpenTTD NoAI Developers Team"; }
|
||||
function GetName() { return "Fibonacci Heap"; }
|
||||
function GetDescription() { return "An implementation of a Fibonacci Heap"; }
|
||||
function GetVersion() { return 1; }
|
||||
function GetDate() { return "2008-08-22"; }
|
||||
function CreateInstance() { return "FibonacciHeap"; }
|
||||
}
|
||||
|
||||
RegisterLibrary(FibonacciHeap());
|
@ -0,0 +1,204 @@
|
||||
/* $Id$ */
|
||||
|
||||
/**
|
||||
* Fibonacci heap.
|
||||
* This heap is heavily optimized for the Insert and Pop functions.
|
||||
* Peek and Pop always return the current lowest value in the list.
|
||||
* Insert is implemented as a lazy insert, as it will simply add the new
|
||||
* node to the root list. Sort is done on every Pop operation.
|
||||
*/
|
||||
class FibonacciHeap {
|
||||
_min = null;
|
||||
_min_index = 0;
|
||||
_min_priority = 0;
|
||||
_count = 0;
|
||||
_root_list = null;
|
||||
|
||||
/**
|
||||
* Create a new fibonacci heap.
|
||||
* http://en.wikipedia.org/wiki/Fibonacci_heap
|
||||
*/
|
||||
constructor() {
|
||||
_count = 0;
|
||||
_min = Node();
|
||||
_min.priority = 0x7FFFFFFF;
|
||||
_min_index = 0;
|
||||
_min_priority = 0x7FFFFFFF;
|
||||
_root_list = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new entry in the heap.
|
||||
* The complexity of this operation is O(1).
|
||||
* @param item The item to add to the list.
|
||||
* @param priority The priority this item has.
|
||||
*/
|
||||
function Insert(item, priority);
|
||||
|
||||
/**
|
||||
* Pop the first entry of the list.
|
||||
* This is always the item with the lowest priority.
|
||||
* The complexity of this operation is O(ln n).
|
||||
* @return The item of the entry with the lowest priority.
|
||||
*/
|
||||
function Pop();
|
||||
|
||||
/**
|
||||
* Peek the first entry of the list.
|
||||
* This is always the item with the lowest priority.
|
||||
* The complexity of this operation is O(1).
|
||||
* @return The item of the entry with the lowest priority.
|
||||
*/
|
||||
function Peek();
|
||||
|
||||
/**
|
||||
* Get the amount of current items in the list.
|
||||
* The complexity of this operation is O(1).
|
||||
* @return The amount of items currently in the list.
|
||||
*/
|
||||
function Count();
|
||||
|
||||
/**
|
||||
* Check if an item exists in the list.
|
||||
* The complexity of this operation is O(n).
|
||||
* @param item The item to check for.
|
||||
* @return True if the item is already in the list.
|
||||
*/
|
||||
function Exists(item);
|
||||
};
|
||||
|
||||
function FibonacciHeap::Insert(item, priority) {
|
||||
/* Create a new node instance to add to the heap. */
|
||||
local node = Node();
|
||||
/* Changing params is faster than using constructor values */
|
||||
node.item = item;
|
||||
node.priority = priority;
|
||||
|
||||
/* Update the reference to the minimum node if this node has a
|
||||
* smaller priority. */
|
||||
if (_min_priority > priority) {
|
||||
_min = node;
|
||||
_min_index = _root_list.len();
|
||||
_min_priority = priority;
|
||||
}
|
||||
|
||||
_root_list.append(node);
|
||||
_count++;
|
||||
}
|
||||
|
||||
function FibonacciHeap::Pop() {
|
||||
|
||||
if (_count == 0) return null;
|
||||
|
||||
/* Bring variables from the class scope to this scope explicitly to
|
||||
* optimize variable lookups by Squirrel. */
|
||||
local z = _min;
|
||||
local tmp_root_list = _root_list;
|
||||
|
||||
/* If there are any children, bring them all to the root level. */
|
||||
tmp_root_list.extend(z.child);
|
||||
|
||||
/* Remove the minimum node from the rootList. */
|
||||
tmp_root_list.remove(_min_index);
|
||||
local root_cache = {};
|
||||
|
||||
/* Now we decrease the number of nodes on the root level by
|
||||
* merging nodes which have the same degree. The node with
|
||||
* the lowest priority value will become the parent. */
|
||||
foreach(x in tmp_root_list) {
|
||||
local y;
|
||||
|
||||
/* See if we encountered a node with the same degree already. */
|
||||
while (y = root_cache.rawdelete(x.degree)) {
|
||||
/* Check the priorities. */
|
||||
if (x.priority > y.priority) {
|
||||
local tmp = x;
|
||||
x = y;
|
||||
y = tmp;
|
||||
}
|
||||
|
||||
/* Make y a child of x. */
|
||||
x.child.append(y);
|
||||
x.degree++;
|
||||
}
|
||||
|
||||
root_cache[x.degree] <- x;
|
||||
}
|
||||
|
||||
/* The root_cache contains all the nodes which will form the
|
||||
* new rootList. We reset the priority to the maximum number
|
||||
* for a 32 signed integer to find a new minumum. */
|
||||
tmp_root_list.resize(root_cache.len());
|
||||
local i = 0;
|
||||
local tmp_min_priority = 0x7FFFFFFF;
|
||||
|
||||
/* Now we need to find the new minimum among the root nodes. */
|
||||
foreach (val in root_cache) {
|
||||
if (val.priority < tmp_min_priority) {
|
||||
_min = val;
|
||||
_min_index = i;
|
||||
tmp_min_priority = val.priority;
|
||||
}
|
||||
|
||||
tmp_root_list[i++] = val;
|
||||
}
|
||||
|
||||
/* Update global variables. */
|
||||
_min_priority = tmp_min_priority;
|
||||
|
||||
_count--;
|
||||
return z.item;
|
||||
}
|
||||
|
||||
function FibonacciHeap::Peek() {
|
||||
if (_count == 0) return null;
|
||||
return _min.item;
|
||||
}
|
||||
|
||||
function FibonacciHeap::Count() {
|
||||
return _count;
|
||||
}
|
||||
|
||||
function FibonacciHeap::Exists(item) {
|
||||
return ExistsIn(_root_list, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auxilary function to search through the whole heap.
|
||||
* @param list The list of nodes to look through.
|
||||
* @param item The item to search for.
|
||||
* @return True if the item is found, false otherwise.
|
||||
*/
|
||||
function FibonacciHeap::ExistsIn(list, item) {
|
||||
|
||||
foreach (val in list) {
|
||||
if (val.item == item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (c in val.child) {
|
||||
if (ExistsIn(c, item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No luck, item doesn't exists in the tree rooted under list. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic class the fibonacci heap is composed of.
|
||||
*/
|
||||
class FibonacciHeap.Node {
|
||||
degree = null;
|
||||
child = null;
|
||||
|
||||
item = null;
|
||||
priority = null;
|
||||
|
||||
constructor() {
|
||||
child = [];
|
||||
degree = 0;
|
||||
}
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
/* $Id$ */
|
||||
|
||||
class PriorityQueue extends AILibrary {
|
||||
function GetAuthor() { return "OpenTTD NoAI Developers Team"; }
|
||||
function GetName() { return "Priority Queue"; }
|
||||
function GetDescription() { return "An implementation of a Priority Queue"; }
|
||||
function GetVersion() { return 2; }
|
||||
function GetDate() { return "2008-06-10"; }
|
||||
function CreateInstance() { return "PriorityQueue"; }
|
||||
}
|
||||
|
||||
RegisterLibrary(PriorityQueue());
|
@ -0,0 +1,115 @@
|
||||
/* $Id$ */
|
||||
|
||||
/**
|
||||
* Priority Queue.
|
||||
* Peek and Pop always return the current lowest value in the list.
|
||||
* Sort is done on insertion only.
|
||||
*/
|
||||
class PriorityQueue
|
||||
{
|
||||
_queue = null;
|
||||
_count = 0;
|
||||
|
||||
constructor()
|
||||
{
|
||||
_count = 0;
|
||||
_queue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new entry in the list.
|
||||
* The complexity of this operation is O(n).
|
||||
* @param item The item to add to the list.
|
||||
* @param priority The priority this item has.
|
||||
*/
|
||||
function Insert(item, priority);
|
||||
|
||||
/**
|
||||
* Pop the first entry of the list.
|
||||
* This is always the item with the lowest priority.
|
||||
* The complexity of this operation is O(1).
|
||||
* @return The item of the entry with the lowest priority.
|
||||
*/
|
||||
function Pop();
|
||||
|
||||
/**
|
||||
* Peek the first entry of the list.
|
||||
* This is always the item with the lowest priority.
|
||||
* The complexity of this operation is O(1).
|
||||
* @return The item of the entry with the lowest priority.
|
||||
*/
|
||||
function Peek();
|
||||
|
||||
/**
|
||||
* Get the amount of current items in the list.
|
||||
* The complexity of this operation is O(1).
|
||||
* @return The amount of items currently in the list.
|
||||
*/
|
||||
function Count();
|
||||
|
||||
/**
|
||||
* Check if an item exists in the list.
|
||||
* The complexity of this operation is O(n).
|
||||
* @param item The item to check for.
|
||||
* @return True if the item is already in the list.
|
||||
*/
|
||||
function Exists(item);
|
||||
};
|
||||
|
||||
function PriorityQueue::Insert(item, priority)
|
||||
{
|
||||
/* Append dummy entry */
|
||||
_queue.append(0);
|
||||
_count++;
|
||||
|
||||
local i;
|
||||
/* Find the point of insertion */
|
||||
for (i = _count - 2; i >= 0; i--) {
|
||||
if (priority > _queue[i][1]) {
|
||||
/* All items bigger move one place to the right */
|
||||
_queue[i + 1] = _queue[i];
|
||||
} else if (item == _queue[i][0]) {
|
||||
/* Same item, ignore insertion */
|
||||
return false;
|
||||
} else {
|
||||
/* Found place to insert at */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Insert new pair */
|
||||
_queue[i + 1] = [item, priority];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function PriorityQueue::Pop()
|
||||
{
|
||||
if (_count == 0) return null;
|
||||
|
||||
local node = _queue.pop();
|
||||
_count--;
|
||||
|
||||
return node[0];
|
||||
}
|
||||
|
||||
function PriorityQueue::Peek()
|
||||
{
|
||||
if (_count == 0) return null;
|
||||
|
||||
return _queue[_count - 1][0];
|
||||
}
|
||||
|
||||
function PriorityQueue::Count()
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
|
||||
function PriorityQueue::Exists(item)
|
||||
{
|
||||
/* Brute-force find the item (there is no faster way, as we don't have the priority number) */
|
||||
foreach (node in _queue) {
|
||||
if (node[0] == item) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
#!/bin/sh
|
||||
|
||||
if ! [ -f ai/regression/regression.nut ]; then
|
||||
echo "Make sure you are in the root of OpenTTD before starting this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat ai/regression/regression.nut | tr ';' '\n' | awk '
|
||||
/^function/ {
|
||||
for (local in locals) {
|
||||
delete locals[local]
|
||||
}
|
||||
if (match($0, "function Regression::Start") || match($0, "function Regression::Stop")) next
|
||||
locals["this"] = "AIControllerSquirrel"
|
||||
}
|
||||
|
||||
/local/ {
|
||||
gsub(".*local", "local")
|
||||
if (match($4, "^AI")) {
|
||||
sub("\\(.*", "", $4)
|
||||
locals[$2] = $4
|
||||
}
|
||||
}
|
||||
|
||||
/Valuate/ {
|
||||
gsub(".*Valuate\\(", "")
|
||||
gsub("\\).*", "")
|
||||
gsub(",.*", "")
|
||||
gsub("\\.", "::")
|
||||
print $0
|
||||
}
|
||||
|
||||
/\./ {
|
||||
for (local in locals) {
|
||||
if (match($0, local ".")) {
|
||||
fname = substr($0, index($0, local "."))
|
||||
sub("\\(.*", "", fname)
|
||||
sub("\\.", "::", fname)
|
||||
sub(local, locals[local], fname)
|
||||
print fname
|
||||
if (match(locals[local], "List")) {
|
||||
sub(locals[local], "AIAbstractList", fname)
|
||||
print fname
|
||||
}
|
||||
}
|
||||
}
|
||||
# We want to remove everything before the FIRST occurence of AI.
|
||||
# If we do not remove any other occurences of AI from the string
|
||||
# we will remove everything before the LAST occurence of AI, so
|
||||
# do some little magic to make it work the way we want.
|
||||
sub("AI", "AXXXXY")
|
||||
gsub("AI", "AXXXXX")
|
||||
sub(".*AXXXXY", "AI")
|
||||
if (match($0, "^AI") && match($0, ".")) {
|
||||
sub("\\(.*", "", $0)
|
||||
sub("\\.", "::", $0)
|
||||
print $0
|
||||
}
|
||||
}
|
||||
' | sed 's/ //g' | sort | uniq > tmp.in_regression
|
||||
|
||||
grep 'DefSQ.*Method' ../src/ai/api/*.hpp.sq | grep -v 'AIError::' | grep -v 'AIAbstractList::Valuate' | grep -v '::GetClassName' | sed 's/^[^,]*, &//g;s/,[^,]*//g' | sort > tmp.in_api
|
||||
|
||||
diff -u tmp.in_regression tmp.in_api | grep -v '^+++' | grep '^+' | sed 's/^+//'
|
||||
|
||||
rm -f tmp.in_regression tmp.in_api
|
||||
|
@ -0,0 +1,17 @@
|
||||
[misc]
|
||||
display_opt = SHOW_TOWN_NAMES|SHOW_STATION_NAMES|SHOW_SIGNS|WAYPOINTS
|
||||
language = english.lng
|
||||
|
||||
[gui]
|
||||
autosave = off
|
||||
|
||||
[game_creation]
|
||||
town_name = english
|
||||
|
||||
[ai_players]
|
||||
none =
|
||||
regression =
|
||||
|
||||
[vehicle]
|
||||
road_side = right
|
||||
plane_speed = 2
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
class Regression extends AIInfo {
|
||||
function GetAuthor() { return "OpenTTD NoAI Developers Team"; }
|
||||
function GetName() { return "Regression"; }
|
||||
function GetDescription() { return "This runs regression-tests on all commands. On the same map the result should always be the same."; }
|
||||
function GetVersion() { return 1; }
|
||||
function GetDate() { return "2007-03-18"; }
|
||||
function CreateInstance() { return "Regression"; }
|
||||
}
|
||||
|
||||
RegisterAI(Regression());
|
||||
|
@ -0,0 +1,3 @@
|
||||
|
||||
print(" Required this file");
|
||||
|
@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
|
||||
if ! [ -f ai/regression/regression.nut ]; then
|
||||
echo "Make sure you are in the root of OpenTTD before starting this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp ai/regression/regression.nut ai/regression/main.nut
|
||||
cp ai/regression/regression_info.nut ai/regression/info.nut
|
||||
|
||||
if [ -f scripts/game_start.scr ]; then
|
||||
mv scripts/game_start.scr scripts/game_start.src.regression
|
||||
fi
|
||||
|
||||
params=""
|
||||
gdb=""
|
||||
if [ "$1" != "-r" ]; then
|
||||
params="-snull -mnull -vnull:ticks=30000"
|
||||
fi
|
||||
if [ "$1" = "-g" ]; then
|
||||
gdb="gdb --ex run --args "
|
||||
fi
|
||||
if [ -n "$gdb" ]; then
|
||||
$gdb ./openttd -x -c ai/regression/regression.cfg $params -g ai/regression/regression.sav
|
||||
else
|
||||
./openttd -x -c ai/regression/regression.cfg $params -g ai/regression/regression.sav -d ai=2 2>&1 | awk '{ gsub("0x\\(nil\\)", "0x00000000", $0); gsub("^dbg: \\[ai\\]", "", $0); gsub("^ ", "ERROR: ", $0); gsub("ERROR: \\[1\\] ", "", $0); gsub("\\[P\\] ", "", $0); print $0; }' > tmp.regression
|
||||
fi
|
||||
|
||||
if [ -z "$gdb" ]; then
|
||||
res="`diff -ub ai/regression/regression.txt tmp.regression`"
|
||||
if [ -z "$res" ]; then
|
||||
echo "Regression test passed!"
|
||||
else
|
||||
echo "Regression test failed! Difference:"
|
||||
echo "$res"
|
||||
fi
|
||||
echo ""
|
||||
echo "Regression test done"
|
||||
fi
|
||||
|
||||
rm -f ai/regression/main.nut ai/regression/info.nut
|
||||
|
||||
if [ -f scripts/game_start.scr.regression ]; then
|
||||
mv scripts/game_start.scr.regression scripts/game_start.src
|
||||
fi
|
||||
|
||||
if [ "$1" != "-k" ]; then
|
||||
rm -f tmp.regression
|
||||
fi
|
@ -0,0 +1,15 @@
|
||||
/* $Id$ */
|
||||
|
||||
class WrightAI extends AIInfo {
|
||||
function GetAuthor() { return "OpenTTD NoAI Developers Team"; }
|
||||
function GetName() { return "WrightAI"; }
|
||||
function GetDescription() { return "A simple AI that tries to beat you with only aircrafts"; }
|
||||
function GetVersion() { return 2; }
|
||||
function GetDate() { return "2008-02-24"; }
|
||||
function CreateInstance() { return "WrightAI"; }
|
||||
function GetSettings() {
|
||||
AddSetting({name = "min_town_size", description = "The minimal size of towns to work on", min_value = 100, max_value = 1000, easy_value = 500, medium_value = 400, hard_value = 300, custom_value = 500, flags = 0});
|
||||
}
|
||||
}
|
||||
|
||||
RegisterAI(WrightAI());
|
@ -0,0 +1,387 @@
|
||||
/* $Id$ */
|
||||
|
||||
class WrightAI extends AIController {
|
||||
name = null;
|
||||
towns_used = null;
|
||||
route_1 = null;
|
||||
route_2 = null;
|
||||
distance_of_route = {};
|
||||
vehicle_to_depot = {};
|
||||
delay_build_airport_route = 1000;
|
||||
passenger_cargo_id = -1;
|
||||
|
||||
function Start();
|
||||
|
||||
constructor() {
|
||||
this.towns_used = AIList();
|
||||
this.route_1 = AIList();
|
||||
this.route_2 = AIList();
|
||||
|
||||
local list = AICargoList();
|
||||
for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
|
||||
if (AICargo.HasCargoClass(i, AICargo.CC_PASSENGERS)) {
|
||||
this.passenger_cargo_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if we have enough money (via loan and on bank).
|
||||
*/
|
||||
function WrightAI::HasMoney(money)
|
||||
{
|
||||
if (AICompany.GetBankBalance(AICompany.MY_COMPANY) + (AICompany.GetMaxLoanAmount() - AICompany.GetLoanAmount()) > money) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of money requested, loan if needed.
|
||||
*/
|
||||
function WrightAI::GetMoney(money)
|
||||
{
|
||||
if (!this.HasMoney(money)) return;
|
||||
if (AICompany.GetBankBalance(AICompany.MY_COMPANY) > money) return;
|
||||
|
||||
local loan = money - AICompany.GetBankBalance(AICompany.MY_COMPANY) + AICompany.GetLoanInterval() + AICompany.GetLoanAmount();
|
||||
loan = loan - loan % AICompany.GetLoanInterval();
|
||||
AILog.Info("Need a loan to get " + money + ": " + loan);
|
||||
AICompany.SetLoanAmount(loan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an airport route. Find 2 cities that are big enough and try to build airport in both cities.
|
||||
* Then we can build an aircraft and make some money.
|
||||
*/
|
||||
function WrightAI::BuildAirportRoute()
|
||||
{
|
||||
local airport_type = (AIAirport.AirportAvailable(AIAirport.AT_SMALL) ? AIAirport.AT_SMALL : AIAirport.AT_LARGE);
|
||||
|
||||
/* Get enough money to work with */
|
||||
this.GetMoney(150000);
|
||||
|
||||
AILog.Info("Trying to build an airport route");
|
||||
|
||||
local tile_1 = this.FindSuitableAirportSpot(airport_type, 0);
|
||||
if (tile_1 < 0) return -1;
|
||||
local tile_2 = this.FindSuitableAirportSpot(airport_type, tile_1);
|
||||
if (tile_2 < 0) {
|
||||
this.towns_used.RemoveValue(tile_1);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Build the airports for real */
|
||||
if (!AIAirport.BuildAirport(tile_1, airport_type, true)) {
|
||||
AILog.Error("Although the testing told us we could build 2 airports, it still failed on the first airport at tile " + tile_1 + ".");
|
||||
this.towns_used.RemoveValue(tile_1);
|
||||
this.towns_used.RemoveValue(tile_2);
|
||||
return -3;
|
||||
}
|
||||
if (!AIAirport.BuildAirport(tile_2, airport_type, true)) {
|
||||
AILog.Error("Although the testing told us we could build 2 airports, it still failed on the second airport at tile " + tile_2 + ".");
|
||||
AIAirport.RemoveAirport(tile_1);
|
||||
this.towns_used.RemoveValue(tile_1);
|
||||
this.towns_used.RemoveValue(tile_2);
|
||||
return -4;
|
||||
}
|
||||
|
||||
local ret = this.BuildAircraft(tile_1, tile_2);
|
||||
if (ret < 0) {
|
||||
AIAirport.RemoveAirport(tile_1);
|
||||
AIAirport.RemoveAirport(tile_2);
|
||||
this.towns_used.RemoveValue(tile_1);
|
||||
this.towns_used.RemoveValue(tile_2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
AILog.Info("Done building a route");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an aircraft with orders from tile_1 to tile_2.
|
||||
* The best available aircraft of that time will be bought.
|
||||
*/
|
||||
function WrightAI::BuildAircraft(tile_1, tile_2)
|
||||
{
|
||||
/* Build an aircraft */
|
||||
local hangar = AIAirport.GetHangarOfAirport(tile_1);
|
||||
local engine = null;
|
||||
|
||||
local engine_list = AIEngineList(AIVehicle.VEHICLE_AIR);
|
||||
|
||||
/* When bank balance < 300000, buy cheaper planes */
|
||||
local balance = AICompany.GetBankBalance(AICompany.MY_COMPANY);
|
||||
engine_list.Valuate(AIEngine.GetPrice);
|
||||
engine_list.KeepBelowValue(balance < 300000 ? 50000 : (balance < 1000000 ? 300000 : 1000000));
|
||||
|
||||
engine_list.Valuate(AIEngine.GetCargoType);
|
||||
engine_list.KeepValue(this.passenger_cargo_id);
|
||||
|
||||
engine_list.Valuate(AIEngine.GetCapacity);
|
||||
engine_list.KeepTop(1);
|
||||
|
||||
engine = engine_list.Begin();
|
||||
|
||||
if (!AIEngine.IsValidEngine(engine)) {
|
||||
AILog.Error("Couldn't find a suitable engine");
|
||||
return -5;
|
||||
}
|
||||
local vehicle = AIVehicle.BuildVehicle(hangar, engine);
|
||||
if (!AIVehicle.IsValidVehicle(vehicle)) {
|
||||
AILog.Error("Couldn't build the aircraft");
|
||||
return -6;
|
||||
}
|
||||
/* Send him on his way */
|
||||
AIOrder.AppendOrder(vehicle, tile_1, AIOrder.AIOF_NONE);
|
||||
AIOrder.AppendOrder(vehicle, tile_2, AIOrder.AIOF_NONE);
|
||||
AIVehicle.StartStopVehicle(vehicle);
|
||||
this.distance_of_route.rawset(vehicle, AIMap.DistanceManhattan(tile_1, tile_2));
|
||||
this.route_1.AddItem(vehicle, tile_1);
|
||||
this.route_2.AddItem(vehicle, tile_2);
|
||||
|
||||
AILog.Info("Done building an aircraft");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a suitable spot for an airport, walking all towns hoping to find one.
|
||||
* When a town is used, it is marked as such and not re-used.
|
||||
*/
|
||||
function WrightAI::FindSuitableAirportSpot(airport_type, center_tile)
|
||||
{
|
||||
local airport_x, airport_y, airport_rad;
|
||||
|
||||
airport_x = AIAirport.GetAirportWidth(airport_type);
|
||||
airport_y = AIAirport.GetAirportHeight(airport_type);
|
||||
airport_rad = AIAirport.GetAirportCoverageRadius(airport_type);
|
||||
|
||||
local town_list = AITownList();
|
||||
/* Remove all the towns we already used */
|
||||
town_list.RemoveList(this.towns_used);
|
||||
|
||||
town_list.Valuate(AITown.GetPopulation);
|
||||
town_list.KeepAboveValue(GetSetting("min_town_size"));
|
||||
/* Keep the best 10, if we can't find 2 stations in there, just leave it anyway */
|
||||
town_list.KeepTop(10);
|
||||
town_list.Valuate(AIBase.RandItem);
|
||||
|
||||
/* Now find 2 suitable towns */
|
||||
for (local town = town_list.Begin(); town_list.HasNext(); town = town_list.Next()) {
|
||||
/* Don't make this a CPU hog */
|
||||
Sleep(1);
|
||||
|
||||
local tile = AITown.GetLocation(town);
|
||||
|
||||
/* Create a 30x30 grid around the core of the town and see if we can find a spot for a small airport */
|
||||
local list = AITileList();
|
||||
/* XXX -- We assume we are more than 15 tiles away from the border! */
|
||||
list.AddRectangle(tile - AIMap.GetTileIndex(15, 15), tile + AIMap.GetTileIndex(15, 15));
|
||||
list.Valuate(AITile.IsBuildableRectangle, airport_x, airport_y);
|
||||
list.KeepValue(1);
|
||||
if (center_tile != 0) {
|
||||
/* If we have a tile defined, we don't want to be within 25 tiles of this tile */
|
||||
list.Valuate(AITile.GetDistanceSquareToTile, center_tile);
|
||||
list.KeepAboveValue(625);
|
||||
}
|
||||
/* Sort on acceptance, remove places that don't have acceptance */
|
||||
list.Valuate(AITile.GetCargoAcceptance, this.passenger_cargo_id, airport_x, airport_y, airport_rad);
|
||||
list.RemoveBelowValue(10);
|
||||
|
||||
/* Couldn't find a suitable place for this town, skip to the next */
|
||||
if (list.Count() == 0) continue;
|
||||
/* Walk all the tiles and see if we can build the airport at all */
|
||||
{
|
||||
local test = AITestMode();
|
||||
local good_tile = 0;
|
||||
|
||||
for (tile = list.Begin(); list.HasNext(); tile = list.Next()) {
|
||||
Sleep(1);
|
||||
if (!AIAirport.BuildAirport(tile, airport_type, true)) continue;
|
||||
good_tile = tile;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Did we found a place to build the airport on? */
|
||||
if (good_tile == 0) continue;
|
||||
}
|
||||
|
||||
AILog.Info("Found a good spot for an airport in town " + town + " at tile " + tile);
|
||||
|
||||
/* Make the town as used, so we don't use it again */
|
||||
this.towns_used.AddItem(town, tile);
|
||||
|
||||
return tile;
|
||||
}
|
||||
|
||||
AILog.Info("Couldn't find a suitable town to build an airport in");
|
||||
return -1;
|
||||
}
|
||||
|
||||
function WrightAI::ManageAirRoutes()
|
||||
{
|
||||
local list = AIVehicleList();
|
||||
list.Valuate(AIVehicle.GetAge);
|
||||
/* Give the plane at least 2 years to make a difference */
|
||||
list.KeepAboveValue(365 * 2);
|
||||
list.Valuate(AIVehicle.GetProfitLastYear);
|
||||
|
||||
for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
|
||||
local profit = list.GetValue(i);
|
||||
/* Profit last year and this year bad? Let's sell the vehicle */
|
||||
if (profit < 10000 && AIVehicle.GetProfitThisYear(i) < 10000) {
|
||||
/* Send the vehicle to depot if we didn't do so yet */
|
||||
if (!vehicle_to_depot.rawin(i) || vehicle_to_depot.rawget(i) != true) {
|
||||
AILog.Info("Sending " + i + " to depot as profit is: " + profit + " / " + AIVehicle.GetProfitThisYear(i));
|
||||
AIVehicle.SendVehicleToDepot(i);
|
||||
vehicle_to_depot.rawset(i, true);
|
||||
}
|
||||
}
|
||||
/* Try to sell it over and over till it really is in the depot */
|
||||
if (vehicle_to_depot.rawin(i) && vehicle_to_depot.rawget(i) == true) {
|
||||
if (AIVehicle.SellVehicle(i)) {
|
||||
AILog.Info("Selling " + i + " as it finally is in a depot.");
|
||||
/* Check if we are the last one serving those airports; else sell the airports */
|
||||
local list2 = AIVehicleList_Station(AIStation.GetStationID(this.route_1.GetValue(i)));
|
||||
if (list2.Count() == 0) this.SellAirports(i);
|
||||
vehicle_to_depot.rawdelete(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't try to add planes when we are short on cash */
|
||||
if (!this.HasMoney(50000)) return;
|
||||
|
||||
list = AIStationList(AIStation.STATION_AIRPORT);
|
||||
list.Valuate(AIStation.GetCargoWaiting, this.passenger_cargo_id);
|
||||
list.KeepAboveValue(250);
|
||||
|
||||
for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
|
||||
local list2 = AIVehicleList_Station(i);
|
||||
/* No vehicles going to this station, abort and sell */
|
||||
if (list2.Count() == 0) {
|
||||
this.SellAirports(i);
|
||||
continue;
|
||||
};
|
||||
|
||||
/* Find the first vehicle that is going to this station */
|
||||
local v = list2.Begin();
|
||||
local dist = this.distance_of_route.rawget(v);
|
||||
|
||||
list2.Valuate(AIVehicle.GetAge);
|
||||
list2.KeepBelowValue(dist);
|
||||
/* Do not build a new vehicle if we bought a new one in the last DISTANCE days */
|
||||
if (list2.Count() != 0) continue;
|
||||
|
||||
AILog.Info("Station " + i + " (" + AIStation.GetLocation(i) + ") has too many cargo, adding a new vehicle for the route.");
|
||||
|
||||
/* Make sure we have enough money */
|
||||
this.GetMoney(50000);
|
||||
|
||||
return this.BuildAircraft(this.route_1.GetValue(v), this.route_2.GetValue(v));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sells the airports from route index i
|
||||
* Removes towns from towns_used list too
|
||||
*/
|
||||
function WrightAI::SellAirports(i) {
|
||||
/* Remove the airports */
|
||||
AILog.Info("Removing airports as nobody serves them anymore.");
|
||||
AIAirport.RemoveAirport(this.route_1.GetValue(i));
|
||||
AIAirport.RemoveAirport(this.route_2.GetValue(i));
|
||||
/* Free the towns_used entries */
|
||||
this.towns_used.RemoveValue(this.route_1.GetValue(i));
|
||||
this.towns_used.RemoveValue(this.route_2.GetValue(i));
|
||||
/* Remove the route */
|
||||
this.route_1.RemoveItem(i);
|
||||
this.route_2.RemoveItem(i);
|
||||
}
|
||||
|
||||
function WrightAI::HandleEvents()
|
||||
{
|
||||
while (AIEventController.IsEventWaiting()) {
|
||||
local e = AIEventController.GetNextEvent();
|
||||
switch (e.GetEventType()) {
|
||||
case AIEvent.AI_ET_VEHICLE_CRASHED: {
|
||||
local ec = AIEventVehicleCrashed.Convert(e);
|
||||
local v = ec.GetVehicleID();
|
||||
AILog.Info("We have a crashed vehicle (" + v + "), buying a new one as replacement");
|
||||
this.BuildAircraft(this.route_1.GetValue(v), this.route_2.GetValue(v));
|
||||
this.route_1.RemoveItem(v);
|
||||
this.route_2.RemoveItem(v);
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function WrightAI::Start()
|
||||
{
|
||||
if (this.passenger_cargo_id == -1) {
|
||||
AILog.Error("WrightAI could not find the passenger cargo");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Give the boy a name */
|
||||
if (!AICompany.SetName("WrightAI")) {
|
||||
local i = 2;
|
||||
while (!AICompany.SetName("WrightAI #" + i)) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
this.name = AICompany.GetName(AICompany.MY_COMPANY);
|
||||
/* Say hello to the user */
|
||||
AILog.Info("Welcome to WrightAI. I will be building airports all day long.");
|
||||
AILog.Info(" - Minimum Town Size: " + GetSetting("min_town_size"));
|
||||
|
||||
/* We start with almost no loan, and we take a loan when we want to build something */
|
||||
AICompany.SetLoanAmount(AICompany.GetLoanInterval());
|
||||
|
||||
/* We need our local ticker, as GetTick() will skip ticks */
|
||||
local ticker = 0;
|
||||
/* Determine time we may sleep */
|
||||
local sleepingtime = 100;
|
||||
if (this.delay_build_airport_route < sleepingtime)
|
||||
sleepingtime = this.delay_build_airport_route;
|
||||
|
||||
/* Let's go on for ever */
|
||||
while (true) {
|
||||
/* Once in a while, with enough money, try to build something */
|
||||
if ((ticker % this.delay_build_airport_route == 0 || ticker == 0) && this.HasMoney(100000)) {
|
||||
local ret = this.BuildAirportRoute();
|
||||
if (ret == -1 && ticker != 0) {
|
||||
/* No more route found, delay even more before trying to find an other */
|
||||
this.delay_build_airport_route = 10000;
|
||||
}
|
||||
else if (ret < 0 && ticker == 0) {
|
||||
/* The AI failed to build a first airport and is deemed */
|
||||
AICompany.SetName("Failed " + this.name);
|
||||
AILog.Error("Failed to build first airport route, now giving up building. Repaying loan. Have a nice day!");
|
||||
AICompany.SetLoanAmount(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Manage the routes once in a while */
|
||||
if (ticker % 2000 == 0) {
|
||||
this.ManageAirRoutes();
|
||||
}
|
||||
/* Try to get ride of our loan once in a while */
|
||||
if (ticker % 5000 == 0) {
|
||||
AICompany.SetLoanAmount(0);
|
||||
}
|
||||
/* Check for events once in a while */
|
||||
if (ticker % 100 == 0) {
|
||||
this.HandleEvents();
|
||||
}
|
||||
/* Make sure we do not create infinite loops */
|
||||
Sleep(sleepingtime);
|
||||
ticker += sleepingtime;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,2 @@
|
||||
start_ai MyAI
|
||||
|
@ -1,244 +0,0 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai.cpp Base for all AIs. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../openttd.h"
|
||||
#include "../variables.h"
|
||||
#include "../command_func.h"
|
||||
#include "../network/network.h"
|
||||
#include "../core/alloc_func.hpp"
|
||||
#include "../company_func.h"
|
||||
#include "../company_base.h"
|
||||
#include "ai.h"
|
||||
#include "default/default.h"
|
||||
#include "trolly/trolly.h"
|
||||
#include "../signal_func.h"
|
||||
|
||||
AIStruct _ai;
|
||||
AICompany _ai_company[MAX_COMPANIES];
|
||||
|
||||
/**
|
||||
* Dequeues commands put in the queue via AI_PutCommandInQueue.
|
||||
*/
|
||||
static void AI_DequeueCommands(CompanyID company)
|
||||
{
|
||||
AICommand *com, *entry_com;
|
||||
|
||||
entry_com = _ai_company[company].queue;
|
||||
|
||||
/* It happens that DoCommandP issues a new DoCommandAI which adds a new command
|
||||
* to this very same queue (don't argue about this, if it currently doesn't
|
||||
* happen I can tell you it will happen with AIScript -- TrueLight). If we
|
||||
* do not make the queue NULL, that commands will be dequeued immediatly.
|
||||
* Therefor we safe the entry-point to entry_com, and make the queue NULL, so
|
||||
* the new queue can be safely built up. */
|
||||
_ai_company[company].queue = NULL;
|
||||
_ai_company[company].queue_tail = NULL;
|
||||
|
||||
/* Dequeue all commands */
|
||||
while ((com = entry_com) != NULL) {
|
||||
_current_company = company;
|
||||
|
||||
DoCommandP(com->tile, com->p1, com->p2, com->cmd, com->callback, com->text);
|
||||
|
||||
/* Free item */
|
||||
entry_com = com->next;
|
||||
free(com->text);
|
||||
free(com);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed for SP; we need to delay DoCommand with 1 tick, because else events
|
||||
* will make infinite loops (AIScript).
|
||||
*/
|
||||
static void AI_PutCommandInQueue(CompanyID company, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text = NULL)
|
||||
{
|
||||
AICommand *com;
|
||||
|
||||
if (_ai_company[company].queue_tail == NULL) {
|
||||
/* There is no item in the queue yet, create the queue */
|
||||
_ai_company[company].queue = MallocT<AICommand>(1);
|
||||
_ai_company[company].queue_tail = _ai_company[company].queue;
|
||||
} else {
|
||||
/* Add an item at the end */
|
||||
_ai_company[company].queue_tail->next = MallocT<AICommand>(1);
|
||||
_ai_company[company].queue_tail = _ai_company[company].queue_tail->next;
|
||||
}
|
||||
|
||||
/* This is our new item */
|
||||
com = _ai_company[company].queue_tail;
|
||||
|
||||
/* Assign the info */
|
||||
com->tile = tile;
|
||||
com->p1 = p1;
|
||||
com->p2 = p2;
|
||||
com->cmd = cmd;
|
||||
com->callback = callback;
|
||||
com->next = NULL;
|
||||
com->text = text == NULL ? NULL : strdup(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a raw DoCommand for the AI.
|
||||
*/
|
||||
CommandCost AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint32 cmd, CommandCallback *callback, const char *text)
|
||||
{
|
||||
CompanyID old_local_company;
|
||||
CommandCost res;
|
||||
|
||||
/* If you enable DC_EXEC with DC_QUERY_COST you are a really strange
|
||||
* person.. should we check for those funny jokes?
|
||||
*/
|
||||
|
||||
/* First, do a test-run to see if we can do this */
|
||||
res = DoCommand(tile, p1, p2, flags & ~DC_EXEC, cmd, text);
|
||||
/* The command failed, or you didn't want to execute, or you are quering, return */
|
||||
if (CmdFailed(res) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/* NetworkSend_Command needs _local_company to be set correctly, so
|
||||
* adjust it, and put it back right after the function */
|
||||
old_local_company = _local_company;
|
||||
_local_company = _current_company;
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
/* Send the command */
|
||||
if (_networking) {
|
||||
/* Network is easy, send it to his handler */
|
||||
NetworkSend_Command(tile, p1, p2, cmd, callback, text);
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
/* If we execute BuildCommands directly in SP, we have a big problem with events
|
||||
* so we need to delay is for 1 tick */
|
||||
AI_PutCommandInQueue(_current_company, tile, p1, p2, cmd, callback, text);
|
||||
}
|
||||
|
||||
/* Set _local_company back */
|
||||
_local_company = old_local_company;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
CommandCost AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint32 cmd, const char *text)
|
||||
{
|
||||
return AI_DoCommandCc(tile, p1, p2, flags, cmd, NULL, text);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run 1 tick of the AI. Don't overdo it, keep it realistic.
|
||||
*/
|
||||
static void AI_RunTick(CompanyID company)
|
||||
{
|
||||
extern void AiNewDoGameLoop(Company *c);
|
||||
|
||||
Company *c = GetCompany(company);
|
||||
_current_company = company;
|
||||
|
||||
if (_settings_game.ai.ainew_active) {
|
||||
AiNewDoGameLoop(c);
|
||||
} else {
|
||||
/* Enable all kind of cheats the old AI needs in order to operate correctly... */
|
||||
_is_old_ai_company = true;
|
||||
AiDoGameLoop(c);
|
||||
_is_old_ai_company = false;
|
||||
}
|
||||
|
||||
/* AI could change some track, so update signals */
|
||||
UpdateSignalsInBuffer();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The gameloop for AIs.
|
||||
* Handles one tick for all the AIs.
|
||||
*/
|
||||
void AI_RunGameLoop()
|
||||
{
|
||||
/* Don't do anything if ai is disabled */
|
||||
if (!_ai.enabled) return;
|
||||
|
||||
/* Don't do anything if we are a network-client, or the AI has been disabled */
|
||||
if (_networking && (!_network_server || !_settings_game.ai.ai_in_multiplayer)) return;
|
||||
|
||||
/* New tick */
|
||||
_ai.tick++;
|
||||
|
||||
/* Make sure the AI follows the difficulty rule.. */
|
||||
assert(_settings_game.difficulty.competitor_speed <= 4);
|
||||
if ((_ai.tick & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
|
||||
|
||||
/* Check for AI-client (so joining a network with an AI) */
|
||||
if (!_networking || _network_server) {
|
||||
/* Check if we want to run AIs (server or SP only) */
|
||||
const Company *c;
|
||||
|
||||
FOR_ALL_COMPANIES(c) {
|
||||
if (c->is_ai) {
|
||||
/* This should always be true, else something went wrong... */
|
||||
assert(_ai_company[c->index].active);
|
||||
|
||||
/* Run the script */
|
||||
AI_DequeueCommands(c->index);
|
||||
AI_RunTick(c->index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_current_company = OWNER_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* A new AI sees the day of light. You can do here what ever you think is needed.
|
||||
*/
|
||||
void AI_StartNewAI(CompanyID company)
|
||||
{
|
||||
assert(IsValidCompanyID(company));
|
||||
|
||||
/* Called if a new AI is booted */
|
||||
_ai_company[company].active = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This AI company died. Give it some chance to make a final puf.
|
||||
*/
|
||||
void AI_CompanyDied(CompanyID company)
|
||||
{
|
||||
/* Called if this AI died */
|
||||
_ai_company[company].active = false;
|
||||
|
||||
if (_companies_ainew[company].pathfinder == NULL) return;
|
||||
|
||||
AyStarMain_Free(_companies_ainew[company].pathfinder);
|
||||
delete _companies_ainew[company].pathfinder;
|
||||
_companies_ainew[company].pathfinder = NULL;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize some AI-related stuff.
|
||||
*/
|
||||
void AI_Initialize()
|
||||
{
|
||||
/* First, make sure all AIs are DEAD! */
|
||||
AI_Uninitialize();
|
||||
|
||||
memset(&_ai, 0, sizeof(_ai));
|
||||
memset(&_ai_company, 0, sizeof(_ai_company));
|
||||
|
||||
_ai.enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitializer for AI-related stuff.
|
||||
*/
|
||||
void AI_Uninitialize()
|
||||
{
|
||||
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) AI_CompanyDied(c);
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai.h Base functions for all AIs. */
|
||||
|
||||
#ifndef AI_H
|
||||
#define AI_H
|
||||
|
||||
#include "../network/network.h"
|
||||
#include "../command_type.h"
|
||||
#include "../core/random_func.hpp"
|
||||
#include "../settings_type.h"
|
||||
|
||||
/* How DoCommands look like for an AI */
|
||||
struct AICommand {
|
||||
uint32 tile;
|
||||
uint32 p1;
|
||||
uint32 p2;
|
||||
uint32 cmd;
|
||||
CommandCallback *callback;
|
||||
|
||||
char *text;
|
||||
uint uid;
|
||||
|
||||
AICommand *next;
|
||||
};
|
||||
|
||||
/* The struct for an AIScript Company */
|
||||
struct AICompany {
|
||||
bool active; ///< Is this AI active?
|
||||
AICommand *queue; ///< The commands that he has in his queue
|
||||
AICommand *queue_tail; ///< The tail of this queue
|
||||
};
|
||||
|
||||
/* The struct to keep some data about the AI in general */
|
||||
struct AIStruct {
|
||||
/* General */
|
||||
bool enabled; ///< Is AI enabled?
|
||||
uint tick; ///< The current tick (something like _frame_counter, only for AIs)
|
||||
};
|
||||
|
||||
extern AIStruct _ai;
|
||||
extern AICompany _ai_company[MAX_COMPANIES];
|
||||
|
||||
// ai.c
|
||||
void AI_StartNewAI(CompanyID company);
|
||||
void AI_CompanyDied(CompanyID company);
|
||||
void AI_RunGameLoop();
|
||||
void AI_Initialize();
|
||||
void AI_Uninitialize();
|
||||
CommandCost AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, const char *text = NULL);
|
||||
CommandCost AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, CommandCallback *callback, const char *text = NULL);
|
||||
|
||||
/** Is it allowed to start a new AI.
|
||||
* This function checks some boundries to see if we should launch a new AI.
|
||||
* @return True if we can start a new AI.
|
||||
*/
|
||||
static inline bool AI_AllowNewAI()
|
||||
{
|
||||
/* If disabled, no AI */
|
||||
if (!_ai.enabled)
|
||||
return false;
|
||||
|
||||
/* If in network, but no server, no AI */
|
||||
if (_networking && !_network_server)
|
||||
return false;
|
||||
|
||||
/* If in network, and server, possible AI */
|
||||
if (_networking && _network_server) {
|
||||
/* Do we want AIs in multiplayer? */
|
||||
if (!_settings_game.ai.ai_in_multiplayer)
|
||||
return false;
|
||||
|
||||
/* Only the NewAI is allowed... sadly enough the old AI just doesn't support this
|
||||
* system, because all commands are delayed by at least 1 tick, which causes
|
||||
* a big problem, because it uses variables that are only set AFTER the command
|
||||
* is really executed... */
|
||||
if (!_settings_game.ai.ainew_active)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define AI_CHANCE16(a, b) ((uint16) AI_Random() <= (uint16)((65536 * a) / b))
|
||||
#define AI_CHANCE16R(a, b, r) ((uint16)(r = AI_Random()) <= (uint16)((65536 * a) / b))
|
||||
|
||||
/**
|
||||
* The random-function that should be used by ALL AIs.
|
||||
*/
|
||||
static inline uint AI_RandomRange(uint max)
|
||||
{
|
||||
/* We pick RandomRange if we are in SP (so when saved, we do the same over and over)
|
||||
* but we pick InteractiveRandomRange if we are a network_server or network-client.
|
||||
*/
|
||||
if (_networking)
|
||||
return InteractiveRandomRange(max);
|
||||
else
|
||||
return RandomRange(max);
|
||||
}
|
||||
|
||||
/**
|
||||
* The random-function that should be used by ALL AIs.
|
||||
*/
|
||||
static inline uint32 AI_Random()
|
||||
{
|
||||
/* We pick RandomRange if we are in SP (so when saved, we do the same over and over)
|
||||
* but we pick InteractiveRandomRange if we are a network_server or network-client.
|
||||
*/
|
||||
if (_networking)
|
||||
return InteractiveRandom();
|
||||
else
|
||||
return Random();
|
||||
}
|
||||
|
||||
#endif /* AI_H */
|
@ -0,0 +1,102 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai.hpp Base functions for all AIs. */
|
||||
|
||||
#ifndef AI_HPP
|
||||
#define AI_HPP
|
||||
|
||||
#include "api/ai_event_types.hpp"
|
||||
|
||||
#ifndef AI_CONFIG_HPP
|
||||
struct ltstr { bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } };
|
||||
#endif /* AI_CONFIG_HPP */
|
||||
typedef std::map<const char *, class AIInfo *, ltstr> AIInfoList;
|
||||
|
||||
|
||||
void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2);
|
||||
|
||||
class AI {
|
||||
public:
|
||||
/**
|
||||
* Is it possible to start a new AI company?
|
||||
* @return True if a new AI company can be started.
|
||||
*/
|
||||
static bool CanStartNew();
|
||||
|
||||
/**
|
||||
* Start a new AI company.
|
||||
* @param company At which slot the AI company should start.
|
||||
*/
|
||||
static void StartNew(CompanyID company);
|
||||
|
||||
/**
|
||||
* Called every game-tick to let AIs do something.
|
||||
*/
|
||||
static void GameLoop();
|
||||
|
||||
/**
|
||||
* Get the current AI tick.
|
||||
*/
|
||||
static uint GetTick();
|
||||
|
||||
/**
|
||||
* Stop a company to be controlled by an AI.
|
||||
* @param company The company from which the AI needs to detach.
|
||||
* @pre !IsHumanCompany(company).
|
||||
*/
|
||||
static void Stop(CompanyID company);
|
||||
|
||||
/**
|
||||
* Kill any and all AIs we manage.
|
||||
*/
|
||||
static void KillAll();
|
||||
|
||||
/**
|
||||
* Initialize the AI system.
|
||||
*/
|
||||
static void Initialize();
|
||||
|
||||
/**
|
||||
* Uninitialize the AI system
|
||||
* @param keepConfig Should we keep AIConfigs, or can we free that memory?
|
||||
*/
|
||||
static void Uninitialize(bool keepConfig);
|
||||
|
||||
/**
|
||||
* Reset all AIConfigs, and make them reload their AIInfo.
|
||||
* If the AIInfo could no longer be found, an error is reported to the user.
|
||||
*/
|
||||
static void ResetConfig();
|
||||
|
||||
/**
|
||||
* Queue a new event for an AI.
|
||||
*/
|
||||
static void NewEvent(CompanyID company, AIEvent *event);
|
||||
|
||||
/**
|
||||
* Broadcast a new event to all active AIs.
|
||||
*/
|
||||
static void BroadcastNewEvent(AIEvent *event, CompanyID skip_company = MAX_COMPANIES);
|
||||
|
||||
/**
|
||||
* Save data from an AI to a savegame.
|
||||
*/
|
||||
static void Save(CompanyID company);
|
||||
|
||||
/**
|
||||
* Load data for an AI from a savegame.
|
||||
*/
|
||||
static void Load(CompanyID company);
|
||||
|
||||
static char *GetConsoleList(char *p, const char *last);
|
||||
static const AIInfoList *GetInfoList();
|
||||
static AIInfo *GetCompanyInfo(const char *name);
|
||||
static bool ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm);
|
||||
static void Rescan();
|
||||
|
||||
private:
|
||||
static uint frame_counter;
|
||||
static class AIScanner *ai_scanner;
|
||||
};
|
||||
|
||||
#endif /* AI_HPP */
|
@ -0,0 +1,143 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_config.cpp Implementation of AIConfig. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../openttd.h"
|
||||
#include "../settings_type.h"
|
||||
#include "ai.hpp"
|
||||
#include "ai_config.hpp"
|
||||
#include "ai_info.hpp"
|
||||
|
||||
void AIConfig::ChangeAI(const char *name)
|
||||
{
|
||||
free((void *)this->name);
|
||||
this->name = (name == NULL) ? NULL : strdup(name);
|
||||
this->info = (name == NULL) ? NULL : AI::GetCompanyInfo(this->name);
|
||||
|
||||
for (SettingValueList::iterator it = this->settings.begin(); it != this->settings.end(); it++) {
|
||||
free((void*)(*it).first);
|
||||
}
|
||||
this->settings.clear();
|
||||
}
|
||||
|
||||
AIConfig::AIConfig(const AIConfig *config) :
|
||||
name(NULL),
|
||||
info(NULL)
|
||||
{
|
||||
this->name = (config->name == NULL) ? NULL : strdup(config->name);
|
||||
this->info = config->info;
|
||||
|
||||
for (SettingValueList::const_iterator it = config->settings.begin(); it != config->settings.end(); it++) {
|
||||
this->settings[strdup((*it).first)] = (*it).second;
|
||||
}
|
||||
}
|
||||
|
||||
AIConfig::~AIConfig()
|
||||
{
|
||||
this->ChangeAI(NULL);
|
||||
}
|
||||
|
||||
AIInfo *AIConfig::GetInfo()
|
||||
{
|
||||
return this->info;
|
||||
}
|
||||
|
||||
bool AIConfig::ResetInfo()
|
||||
{
|
||||
this->info = AI::GetCompanyInfo(this->name);
|
||||
return this->info != NULL;
|
||||
}
|
||||
|
||||
AIConfig *AIConfig::GetConfig(CompanyID company, bool forceNewgameSetting)
|
||||
{
|
||||
AIConfig **config;
|
||||
if (!forceNewgameSetting) {
|
||||
config = (_game_mode == GM_MENU) ? &_settings_newgame.ai_config[company] : &_settings_game.ai_config[company];
|
||||
} else {
|
||||
config = &_settings_newgame.ai_config[company];
|
||||
}
|
||||
if (*config == NULL) *config = new AIConfig();
|
||||
return *config;
|
||||
}
|
||||
|
||||
int AIConfig::GetSetting(const char *name)
|
||||
{
|
||||
assert(this->info != NULL);
|
||||
|
||||
SettingValueList::iterator it = this->settings.find(name);
|
||||
/* Return the default value if the setting is not set, or if we are in a not-custom difficult level */
|
||||
if (it == this->settings.end() || ((_game_mode == GM_MENU) ? _settings_newgame.difficulty.diff_level : _settings_game.difficulty.diff_level) != 3) {
|
||||
return this->info->GetSettingDefaultValue(name);
|
||||
}
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
void AIConfig::SetSetting(const char *name, int value)
|
||||
{
|
||||
/* You can only set ai specific settings if an AI is selected. */
|
||||
assert(strcmp(name, "start_date") == 0 || this->info != NULL);
|
||||
|
||||
SettingValueList::iterator it = this->settings.find(name);
|
||||
if (it != this->settings.end()) {
|
||||
(*it).second = value;
|
||||
} else {
|
||||
this->settings[strdup(name)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool AIConfig::HasAI()
|
||||
{
|
||||
return this->info != NULL;
|
||||
}
|
||||
|
||||
const char *AIConfig::GetName()
|
||||
{
|
||||
return this->name;
|
||||
}
|
||||
|
||||
void AIConfig::StringToSettings(const char *value)
|
||||
{
|
||||
char *value_copy = strdup(value);
|
||||
char *s = value_copy;
|
||||
|
||||
while (s != NULL) {
|
||||
/* Analyze the string ('name=value,name=value\0') */
|
||||
char *item_name = s;
|
||||
s = strchr(s, '=');
|
||||
if (s == NULL) break;
|
||||
if (*s == '\0') break;
|
||||
*s = '\0';
|
||||
s++;
|
||||
|
||||
char *item_value = s;
|
||||
s = strchr(s, ',');
|
||||
if (s != NULL) {
|
||||
*s = '\0';
|
||||
s++;
|
||||
}
|
||||
|
||||
this->SetSetting(item_name, atoi(item_value));
|
||||
}
|
||||
free(value_copy);
|
||||
}
|
||||
|
||||
void AIConfig::SettingsToString(char *string, int size)
|
||||
{
|
||||
string[0] = '\0';
|
||||
for (SettingValueList::iterator it = this->settings.begin(); it != this->settings.end(); it++) {
|
||||
char no[10];
|
||||
snprintf(no, sizeof(no), "%d", (*it).second);
|
||||
|
||||
/* Check if the string would fit in the destination */
|
||||
size -= strlen((*it).first) - 1 - strlen(no) - 1;
|
||||
/* If it doesn't fit, skip the next settings */
|
||||
if (size <= 0) return;
|
||||
|
||||
strcat(string, (*it).first);
|
||||
strcat(string, "=");
|
||||
strcat(string, no);
|
||||
strcat(string, ",");
|
||||
}
|
||||
string[strlen(string) - 1] = '\0';
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_config.hpp AIConfig stores the configuration settings of every AI. */
|
||||
|
||||
#ifndef AI_CONFIG_HPP
|
||||
#define AI_CONFIG_HPP
|
||||
|
||||
#include <map>
|
||||
|
||||
#ifndef AI_HPP
|
||||
struct ltstr { bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } };
|
||||
#endif /* AI_HPP */
|
||||
|
||||
class AIConfig {
|
||||
private:
|
||||
typedef std::map<const char *, int, ltstr> SettingValueList;
|
||||
|
||||
public:
|
||||
AIConfig() :
|
||||
name(NULL),
|
||||
info(NULL)
|
||||
{}
|
||||
AIConfig(const AIConfig *config);
|
||||
~AIConfig();
|
||||
|
||||
/**
|
||||
* Set another AI to be loaded in this slot.
|
||||
*/
|
||||
void ChangeAI(const char *name);
|
||||
|
||||
/**
|
||||
* When ever the AI Scanner is reloaded, all infos become invalid. This
|
||||
* function tells AIConfig about this.
|
||||
* @return True if the reset was successfull, false if the AI was no longer
|
||||
* found.
|
||||
*/
|
||||
bool ResetInfo();
|
||||
|
||||
/**
|
||||
* Get the AIInfo linked to this AIConfig.
|
||||
*/
|
||||
class AIInfo *GetInfo();
|
||||
|
||||
/**
|
||||
* Get the config of a company.
|
||||
*/
|
||||
static AIConfig *GetConfig(CompanyID company, bool forceNewgameSetting = false);
|
||||
|
||||
/**
|
||||
* Get the value of a setting for this config. It might fallback to his
|
||||
* 'info' to find the default value (if not set or if not-custom difficulty
|
||||
* level).
|
||||
* @return The (default) value of the setting, or -1 if the setting was not
|
||||
* found.
|
||||
*/
|
||||
int GetSetting(const char *name);
|
||||
|
||||
/**
|
||||
* Set the value of a setting for this config.
|
||||
*/
|
||||
void SetSetting(const char *name, int value);
|
||||
|
||||
/**
|
||||
* Is this config attached to an AI?
|
||||
*/
|
||||
bool HasAI();
|
||||
|
||||
/**
|
||||
* Get the name of the AI.
|
||||
*/
|
||||
const char *GetName();
|
||||
|
||||
/**
|
||||
* Convert a string which is stored in the config file or savegames to
|
||||
* custom settings of this AI.
|
||||
*/
|
||||
void StringToSettings(const char *value);
|
||||
|
||||
/**
|
||||
* Convert the custom settings to a string that can be stored in the config
|
||||
* file or savegames.
|
||||
*/
|
||||
void SettingsToString(char *string, int size);
|
||||
|
||||
private:
|
||||
const char *name;
|
||||
class AIInfo *info;
|
||||
SettingValueList settings;
|
||||
};
|
||||
|
||||
#endif /* AI_CONFIG_HPP */
|
@ -0,0 +1,254 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_core.cpp Implementation of AI. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../openttd.h"
|
||||
#include "../company_type.h"
|
||||
#include "../company_base.h"
|
||||
#include "../company_func.h"
|
||||
#include "../debug.h"
|
||||
#include "../network/network.h"
|
||||
#include "../settings_type.h"
|
||||
#include "../window_type.h"
|
||||
#include "../window_func.h"
|
||||
#include "../command_func.h"
|
||||
#include "ai.hpp"
|
||||
#include "ai_info.hpp"
|
||||
#include "ai_scanner.hpp"
|
||||
#include "ai_instance.hpp"
|
||||
#include "ai_config.hpp"
|
||||
|
||||
/* static */ uint AI::frame_counter = 0;
|
||||
/* static */ AIScanner *AI::ai_scanner = NULL;
|
||||
|
||||
/* static */ bool AI::CanStartNew()
|
||||
{
|
||||
/* Only allow new AIs on the server and only when that is allowed in multiplayer */
|
||||
return !_networking || (_network_server && _settings_game.ai.ai_in_multiplayer);
|
||||
}
|
||||
|
||||
/* static */ void AI::StartNew(CompanyID company)
|
||||
{
|
||||
assert(IsValidCompanyID(company));
|
||||
|
||||
/* Clients shouldn't start AIs */
|
||||
if (_networking && !_network_server) return;
|
||||
|
||||
AIInfo *info = AIConfig::GetConfig(company)->GetInfo();
|
||||
if (info == NULL) {
|
||||
info = AI::ai_scanner->SelectRandomAI();
|
||||
assert(info != NULL);
|
||||
/* Load default data and store the name in the settings */
|
||||
AIConfig::GetConfig(company)->ChangeAI(info->GetDirName());
|
||||
}
|
||||
|
||||
_current_company = company;
|
||||
Company *c = GetCompany(company);
|
||||
|
||||
c->ai_info = info;
|
||||
c->ai_instance = new AIInstance(info);
|
||||
|
||||
InvalidateWindowData(WC_AI_DEBUG, 0, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* static */ void AI::GameLoop()
|
||||
{
|
||||
/* If we are in networking, only servers run this function, and that only if it is allowed */
|
||||
if (_networking && (!_network_server || !_settings_game.ai.ai_in_multiplayer)) return;
|
||||
|
||||
/* The speed with which AIs go, is limited by the 'competitor_speed' */
|
||||
AI::frame_counter++;
|
||||
assert(_settings_game.difficulty.competitor_speed <= 4);
|
||||
if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
|
||||
|
||||
const Company *c;
|
||||
FOR_ALL_COMPANIES(c) {
|
||||
if (!IsHumanCompany(c->index)) {
|
||||
_current_company = c->index;
|
||||
c->ai_instance->GameLoop();
|
||||
}
|
||||
}
|
||||
|
||||
_current_company = OWNER_NONE;
|
||||
}
|
||||
|
||||
/* static */ uint AI::GetTick()
|
||||
{
|
||||
return AI::frame_counter;
|
||||
}
|
||||
|
||||
/* static */ void AI::Stop(CompanyID company)
|
||||
{
|
||||
if (_networking && !_network_server) return;
|
||||
|
||||
_current_company = company;
|
||||
Company *c = GetCompany(company);
|
||||
|
||||
delete c->ai_instance;
|
||||
c->ai_instance = NULL;
|
||||
|
||||
InvalidateWindowData(WC_AI_DEBUG, 0, -1);
|
||||
}
|
||||
|
||||
/* static */ void AI::KillAll()
|
||||
{
|
||||
/* It might happen there are no companies .. than we have nothing to loop */
|
||||
if (GetCompanyPoolSize() == 0) return;
|
||||
|
||||
const Company *c;
|
||||
FOR_ALL_COMPANIES(c) {
|
||||
if (!IsHumanCompany(c->index)) AI::Stop(c->index);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AI::Initialize()
|
||||
{
|
||||
if (AI::ai_scanner != NULL) AI::Uninitialize(true);
|
||||
|
||||
AI::frame_counter = 0;
|
||||
if (AI::ai_scanner == NULL) AI::ai_scanner = new AIScanner();
|
||||
}
|
||||
|
||||
/* static */ void AI::Uninitialize(bool keepConfig)
|
||||
{
|
||||
AI::KillAll();
|
||||
|
||||
if (keepConfig) {
|
||||
/* Run a rescan, which indexes all AIInfos again, and check if we can
|
||||
* still load all the AIS, while keeping the configs in place */
|
||||
Rescan();
|
||||
} else {
|
||||
delete AI::ai_scanner;
|
||||
AI::ai_scanner = NULL;
|
||||
|
||||
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
||||
if (_settings_game.ai_config[c] != NULL) {
|
||||
delete _settings_game.ai_config[c];
|
||||
_settings_game.ai_config[c] = NULL;
|
||||
}
|
||||
if (_settings_newgame.ai_config[c] != NULL) {
|
||||
delete _settings_newgame.ai_config[c];
|
||||
_settings_newgame.ai_config[c] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AI::ResetConfig()
|
||||
{
|
||||
/* Check for both newgame as current game if we can reload the AIInfo insde
|
||||
* the AIConfig. If not, remove the AI from the list (which will assign
|
||||
* a random new AI on reload). */
|
||||
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
||||
if (_settings_game.ai_config[c] != NULL && _settings_game.ai_config[c]->HasAI()) {
|
||||
if (!_settings_game.ai_config[c]->ResetInfo()) {
|
||||
DEBUG(ai, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_game.ai_config[c]->GetName());
|
||||
_settings_game.ai_config[c]->ChangeAI(NULL);
|
||||
}
|
||||
}
|
||||
if (_settings_newgame.ai_config[c] != NULL && _settings_newgame.ai_config[c]->HasAI()) {
|
||||
if (!_settings_newgame.ai_config[c]->ResetInfo()) {
|
||||
DEBUG(ai, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_newgame.ai_config[c]->GetName());
|
||||
_settings_newgame.ai_config[c]->ChangeAI(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AI::NewEvent(CompanyID company, AIEvent *event)
|
||||
{
|
||||
/* Clients should ignore events */
|
||||
if (_networking && !_network_server) return;
|
||||
|
||||
/* Only AIs can have an event-queue */
|
||||
if (!IsValidCompanyID(company) || IsHumanCompany(company)) return;
|
||||
|
||||
/* Queue the event */
|
||||
CompanyID old_company = _current_company;
|
||||
_current_company = company;
|
||||
AIEventController::InsertEvent(event);
|
||||
_current_company = old_company;
|
||||
}
|
||||
|
||||
/* static */ void AI::BroadcastNewEvent(AIEvent *event, CompanyID skip_company)
|
||||
{
|
||||
/* Clients should ignore events */
|
||||
if (_networking && !_network_server) return;
|
||||
|
||||
/* Try to send the event to all AIs */
|
||||
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
|
||||
if (c != skip_company) AI::NewEvent(c, event);
|
||||
}
|
||||
}
|
||||
|
||||
void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2)
|
||||
{
|
||||
AIObject::SetLastCommandRes(success);
|
||||
|
||||
if (!success) {
|
||||
AIObject::SetLastError(AIError::StringToError(_error_message));
|
||||
} else {
|
||||
AIObject::IncreaseDoCommandCosts(AIObject::GetLastCost());
|
||||
}
|
||||
|
||||
GetCompany(_current_company)->ai_instance->Continue();
|
||||
}
|
||||
|
||||
/* static */ void AI::Save(CompanyID company)
|
||||
{
|
||||
if (!_networking || _network_server) {
|
||||
assert(IsValidCompanyID(company));
|
||||
assert(GetCompany(company)->ai_instance != NULL);
|
||||
|
||||
CompanyID old_company = _current_company;
|
||||
_current_company = company;
|
||||
GetCompany(company)->ai_instance->Save();
|
||||
_current_company = old_company;
|
||||
} else {
|
||||
AIInstance::SaveEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AI::Load(CompanyID company)
|
||||
{
|
||||
if (!_networking || _network_server) {
|
||||
assert(IsValidCompanyID(company));
|
||||
assert(GetCompany(company)->ai_instance != NULL);
|
||||
|
||||
CompanyID old_company = _current_company;
|
||||
_current_company = company;
|
||||
GetCompany(company)->ai_instance->Load();
|
||||
_current_company = old_company;
|
||||
} else {
|
||||
/* Read, but ignore, the load data */
|
||||
AIInstance::LoadEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ char *AI::GetConsoleList(char *p, const char *last)
|
||||
{
|
||||
return AI::ai_scanner->GetAIConsoleList(p, last);
|
||||
}
|
||||
|
||||
/* static */ const AIInfoList *AI::GetInfoList()
|
||||
{
|
||||
return AI::ai_scanner->GetAIInfoList();
|
||||
}
|
||||
|
||||
/* static */ AIInfo *AI::GetCompanyInfo(const char *name)
|
||||
{
|
||||
return AI::ai_scanner->FindAI(name);
|
||||
}
|
||||
|
||||
/* static */ bool AI::ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm)
|
||||
{
|
||||
return AI::ai_scanner->ImportLibrary(library, class_name, version, vm, GetCompany(_current_company)->ai_instance->GetController());
|
||||
}
|
||||
|
||||
/* static */ void AI::Rescan()
|
||||
{
|
||||
AI::ai_scanner->RescanAIDir();
|
||||
ResetConfig();
|
||||
}
|
@ -0,0 +1,257 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_gui.cpp Window for configuring the AIs */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../openttd.h"
|
||||
#include "../gui.h"
|
||||
#include "../window_gui.h"
|
||||
#include "../company_func.h"
|
||||
#include "../company_base.h"
|
||||
#include "../company_gui.h"
|
||||
#include "../economy_func.h"
|
||||
#include "../variables.h"
|
||||
#include "../cargotype.h"
|
||||
#include "../strings_func.h"
|
||||
#include "../core/alloc_func.hpp"
|
||||
#include "../window_func.h"
|
||||
#include "../date_func.h"
|
||||
#include "../gfx_func.h"
|
||||
#include "../debug.h"
|
||||
#include "../command_func.h"
|
||||
#include "../network/network.h"
|
||||
|
||||
#include "ai.hpp"
|
||||
#include "api/ai_types.hpp"
|
||||
#include "api/ai_controller.hpp"
|
||||
#include "api/ai_object.hpp"
|
||||
#include "api/ai_log.hpp"
|
||||
#include "ai_info.hpp"
|
||||
|
||||
#include "table/strings.h"
|
||||
#include "../table/sprites.h"
|
||||
|
||||
struct AIDebugWindow : public Window {
|
||||
enum AIDebugWindowWidgets {
|
||||
AID_WIDGET_CLOSEBOX = 0,
|
||||
AID_WIDGET_CAPTION,
|
||||
AID_WIDGET_VIEW,
|
||||
AID_WIDGET_NAME_TEXT,
|
||||
AID_WIDGET_RELOAD_TOGGLE,
|
||||
AID_WIDGET_LOG_PANEL,
|
||||
AID_WIDGET_SCROLLBAR,
|
||||
AID_WIDGET_UNUSED_1,
|
||||
AID_WIDGET_UNUSED_2,
|
||||
AID_WIDGET_UNUSED_3,
|
||||
AID_WIDGET_UNUSED_4,
|
||||
AID_WIDGET_UNUSED_5,
|
||||
AID_WIDGET_UNUSED_6,
|
||||
AID_WIDGET_UNUSED_7,
|
||||
|
||||
AID_WIDGET_COMPANY_BUTTON_START,
|
||||
AID_WIDGET_COMPANY_BUTTON_END = AID_WIDGET_COMPANY_BUTTON_START + 14,
|
||||
};
|
||||
|
||||
static CompanyID ai_debug_company;
|
||||
int redraw_timer;
|
||||
|
||||
AIDebugWindow(const WindowDesc *desc, WindowNumber number) : Window(desc, number)
|
||||
{
|
||||
/* Disable the companies who are not active or not an AI */
|
||||
for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
|
||||
this->SetWidgetDisabledState(i + AID_WIDGET_COMPANY_BUTTON_START, !IsValidCompanyID(i) || !GetCompany(i)->is_ai);
|
||||
}
|
||||
this->DisableWidget(AID_WIDGET_RELOAD_TOGGLE);
|
||||
|
||||
this->vscroll.cap = 14;
|
||||
this->vscroll.pos = 0;
|
||||
this->resize.step_height = 12;
|
||||
|
||||
if (ai_debug_company != INVALID_COMPANY) this->LowerWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START);
|
||||
|
||||
this->FindWindowPlacementAndResize(desc);
|
||||
}
|
||||
|
||||
virtual void OnPaint()
|
||||
{
|
||||
/* Check if the currently selected company is still active. */
|
||||
if (ai_debug_company == INVALID_COMPANY || !IsValidCompanyID(ai_debug_company)) {
|
||||
if (ai_debug_company != INVALID_COMPANY) {
|
||||
/* Raise and disable the widget for the previous selection. */
|
||||
this->RaiseWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START);
|
||||
this->DisableWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START);
|
||||
|
||||
ai_debug_company = INVALID_COMPANY;
|
||||
}
|
||||
|
||||
for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
|
||||
if (IsValidCompanyID(i) && GetCompany(i)->is_ai) {
|
||||
/* Lower the widget corresponding to this company. */
|
||||
this->LowerWidget(i + AID_WIDGET_COMPANY_BUTTON_START);
|
||||
|
||||
ai_debug_company = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update "Reload AI" button */
|
||||
this->SetWidgetDisabledState(AID_WIDGET_RELOAD_TOGGLE, ai_debug_company == INVALID_COMPANY);
|
||||
|
||||
/* Draw standard stuff */
|
||||
this->DrawWidgets();
|
||||
|
||||
/* If there are no active companies, don't display anything else. */
|
||||
if (ai_debug_company == INVALID_COMPANY) return;
|
||||
|
||||
/* Paint the company icons */
|
||||
for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
|
||||
if (!IsValidCompanyID(i) || !GetCompany(i)->is_ai) {
|
||||
/* Check if we have the company as an active company */
|
||||
if (!this->IsWidgetDisabled(i + AID_WIDGET_COMPANY_BUTTON_START)) {
|
||||
/* Bah, company gone :( */
|
||||
this->DisableWidget(i + AID_WIDGET_COMPANY_BUTTON_START);
|
||||
|
||||
/* We need a repaint */
|
||||
this->SetDirty();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if we have the company marked as inactive */
|
||||
if (this->IsWidgetDisabled(i + AID_WIDGET_COMPANY_BUTTON_START)) {
|
||||
/* New AI! Yippie :p */
|
||||
this->EnableWidget(i + AID_WIDGET_COMPANY_BUTTON_START);
|
||||
|
||||
/* We need a repaint */
|
||||
this->SetDirty();
|
||||
}
|
||||
|
||||
byte x = (i == ai_debug_company) ? 1 : 0;
|
||||
DrawCompanyIcon(i, (i % 8) * 37 + 13 + x, (i < 8 ? 0 : 13) + 16 + x);
|
||||
}
|
||||
|
||||
/* Draw the AI name */
|
||||
AIInfo *info = GetCompany(ai_debug_company)->ai_info;
|
||||
assert(info != NULL);
|
||||
DoDrawString(info->GetName(), 7, 47, TC_BLACK);
|
||||
|
||||
CompanyID old_company = _current_company;
|
||||
_current_company = ai_debug_company;
|
||||
AILog::LogData *log = (AILog::LogData *)AIObject::GetLogPointer();
|
||||
_current_company = old_company;
|
||||
|
||||
SetVScrollCount(this, (log == NULL) ? 0 : log->used);
|
||||
this->InvalidateWidget(AID_WIDGET_SCROLLBAR);
|
||||
if (log == NULL) return;
|
||||
|
||||
int y = 6;
|
||||
for (int i = this->vscroll.pos; i < (this->vscroll.cap + this->vscroll.pos); i++) {
|
||||
uint pos = (log->count + log->pos - i) % log->count;
|
||||
if (log->lines[pos] == NULL) break;
|
||||
|
||||
uint colour;
|
||||
switch (log->type[pos]) {
|
||||
case AILog::LOG_SQ_INFO: colour = TC_BLACK; break;
|
||||
case AILog::LOG_SQ_ERROR: colour = TC_RED; break;
|
||||
case AILog::LOG_INFO: colour = TC_BLACK; break;
|
||||
case AILog::LOG_WARNING: colour = TC_YELLOW; break;
|
||||
case AILog::LOG_ERROR: colour = TC_RED; break;
|
||||
default: colour = TC_BLACK; break;
|
||||
}
|
||||
|
||||
DoDrawStringTruncated(log->lines[pos], 7, this->widget[AID_WIDGET_LOG_PANEL].top + y, colour, this->widget[AID_WIDGET_LOG_PANEL].right - this->widget[AID_WIDGET_LOG_PANEL].left - 14);
|
||||
y += 12;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnClick(Point pt, int widget)
|
||||
{
|
||||
/* Check which button is clicked */
|
||||
if (IsInsideMM(widget, AID_WIDGET_COMPANY_BUTTON_START, AID_WIDGET_COMPANY_BUTTON_END + 1)) {
|
||||
/* Is it no on disable? */
|
||||
if (!this->IsWidgetDisabled(widget)) {
|
||||
this->RaiseWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START);
|
||||
ai_debug_company = (CompanyID)(widget - AID_WIDGET_COMPANY_BUTTON_START);
|
||||
this->LowerWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START);
|
||||
this->SetDirty();
|
||||
}
|
||||
}
|
||||
if (widget == AID_WIDGET_RELOAD_TOGGLE && !this->IsWidgetDisabled(widget)) {
|
||||
/* First kill the company of the AI, then start a new one. This should start the current AI again */
|
||||
DoCommandP(0, 2, ai_debug_company, CMD_COMPANY_CTRL);
|
||||
DoCommandP(0, 1, 0, CMD_COMPANY_CTRL);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnTimeout()
|
||||
{
|
||||
this->RaiseWidget(AID_WIDGET_RELOAD_TOGGLE);
|
||||
this->SetDirty();
|
||||
}
|
||||
|
||||
virtual void OnInvalidateData(int data = 0)
|
||||
{
|
||||
if (data == -1 || ai_debug_company == data) this->SetDirty();
|
||||
}
|
||||
|
||||
virtual void OnResize(Point new_size, Point delta)
|
||||
{
|
||||
this->vscroll.cap += delta.y / (int)this->resize.step_height;
|
||||
}
|
||||
};
|
||||
|
||||
CompanyID AIDebugWindow::ai_debug_company = INVALID_COMPANY;
|
||||
|
||||
static const Widget _ai_debug_widgets[] = {
|
||||
{ WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // AID_WIDGET_CLOSEBOX
|
||||
{ WWT_CAPTION, RESIZE_RIGHT, COLOUR_GREY, 11, 298, 0, 13, STR_AI_DEBUG, STR_018C_WINDOW_TITLE_DRAG_THIS}, // AID_WIDGET_CAPTION
|
||||
{ WWT_PANEL, RESIZE_RIGHT, COLOUR_GREY, 0, 298, 14, 40, 0x0, STR_NULL}, // AID_WIDGET_VIEW
|
||||
|
||||
{ WWT_PANEL, RESIZE_RIGHT, COLOUR_GREY, 0, 149, 41, 60, 0x0, STR_AI_DEBUG_NAME_TIP}, // AID_WIDGET_NAME_TEXT
|
||||
{ WWT_PUSHTXTBTN, RESIZE_LR, COLOUR_GREY, 150, 298, 41, 60, STR_AI_DEBUG_RELOAD, STR_AI_DEBUG_RELOAD_TIP}, // AID_WIDGET_RELOAD_TOGGLE
|
||||
{ WWT_PANEL, RESIZE_RB, COLOUR_GREY, 0, 286, 61, 240, 0x0, STR_NULL}, // AID_WIDGET_LOG_PANEL
|
||||
{ WWT_SCROLLBAR, RESIZE_LRB, COLOUR_GREY, 287, 298, 61, 228, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST}, // AID_WIDGET_SCROLLBAR
|
||||
/* As this is WIP, leave the next few so we can work a bit with the GUI */
|
||||
{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 101, 120, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_1
|
||||
{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 121, 140, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_2
|
||||
{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 141, 160, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_3
|
||||
{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 161, 180, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_4
|
||||
{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 181, 200, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_5
|
||||
{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 201, 220, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_6
|
||||
{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 221, 240, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_7
|
||||
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 2, 38, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, // AID_WIDGET_COMPANY_BUTTON_START
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 39, 75, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 76, 112, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 113, 149, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 150, 186, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 187, 223, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 224, 260, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 261, 297, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 2, 38, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 39, 75, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 76, 112, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 113, 149, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 150, 186, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 187, 223, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
|
||||
{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 224, 260, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, // AID_WIDGET_COMPANY_BUTTON_END
|
||||
{ WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_GREY, 287, 298, 229, 240, STR_NULL, STR_RESIZE_BUTTON},
|
||||
{ WIDGETS_END},
|
||||
};
|
||||
|
||||
static const WindowDesc _ai_debug_desc = {
|
||||
WDP_AUTO, WDP_AUTO, 299, 241, 299, 241,
|
||||
WC_AI_DEBUG, WC_NONE,
|
||||
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE,
|
||||
_ai_debug_widgets
|
||||
};
|
||||
|
||||
void ShowAIDebugWindow()
|
||||
{
|
||||
if (!_networking || _network_server) {
|
||||
AllocateWindowDescFront<AIDebugWindow>(&_ai_debug_desc, 0);
|
||||
} else {
|
||||
ShowErrorMessage(INVALID_STRING_ID, STR_AI_DEBUG_SERVER_ONLY, 0, 0);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_gui.hpp Window for configuring the AIs */
|
||||
|
||||
#ifndef AI_GUI_HPP
|
||||
#define AI_GUI_HPP
|
||||
|
||||
void ShowAIDebugWindow();
|
||||
|
||||
#endif /* AI_GUI_HPP */
|
@ -0,0 +1,319 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_info.cpp Implementation of AIFileInfo */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../core/alloc_func.hpp"
|
||||
|
||||
#include <squirrel.h>
|
||||
#include "../script/squirrel.hpp"
|
||||
#include "../script/squirrel_helper.hpp"
|
||||
#include "../script/squirrel_class.hpp"
|
||||
#include "../script/squirrel_std.hpp"
|
||||
#include "ai.hpp"
|
||||
#include "ai_info.hpp"
|
||||
#include "ai_scanner.hpp"
|
||||
#include "api/ai_controller.hpp"
|
||||
#include "../settings_type.h"
|
||||
#include "../openttd.h"
|
||||
|
||||
AIFileInfo::~AIFileInfo()
|
||||
{
|
||||
this->engine->ReleaseObject(this->SQ_instance);
|
||||
free((void *)this->author);
|
||||
free((void *)this->name);
|
||||
free((void *)this->description);
|
||||
free((void *)this->date);
|
||||
free((void *)this->instance_name);
|
||||
free(this->script_name);
|
||||
free(this->dir_name);
|
||||
free(this->SQ_instance);
|
||||
}
|
||||
|
||||
const char *AIFileInfo::GetAuthor()
|
||||
{
|
||||
if (this->author == NULL) this->author = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetAuthor");
|
||||
return this->author;
|
||||
}
|
||||
|
||||
const char *AIFileInfo::GetName()
|
||||
{
|
||||
if (this->name == NULL) this->name = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetName");
|
||||
return this->name;
|
||||
}
|
||||
|
||||
const char *AIFileInfo::GetDescription()
|
||||
{
|
||||
if (this->description == NULL) this->description = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetDescription");
|
||||
return this->description;
|
||||
}
|
||||
|
||||
int AIFileInfo::GetVersion()
|
||||
{
|
||||
return this->engine->CallIntegerMethod(*this->SQ_instance, "GetVersion");
|
||||
}
|
||||
|
||||
void AIFileInfo::GetSettings()
|
||||
{
|
||||
this->engine->CallMethod(*this->SQ_instance, "GetSettings", NULL, -1);
|
||||
}
|
||||
|
||||
const char *AIFileInfo::GetDate()
|
||||
{
|
||||
if (this->date == NULL) this->date = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetDate");
|
||||
return this->date;
|
||||
}
|
||||
|
||||
const char *AIFileInfo::GetInstanceName()
|
||||
{
|
||||
if (this->instance_name == NULL) this->instance_name = this->engine->CallStringMethodStrdup(*this->SQ_instance, "CreateInstance");
|
||||
return this->instance_name;
|
||||
}
|
||||
|
||||
bool AIFileInfo::AllowStartup()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *AIFileInfo::GetDirName()
|
||||
{
|
||||
return this->dir_name;
|
||||
}
|
||||
|
||||
const char *AIFileInfo::GetScriptName()
|
||||
{
|
||||
return this->script_name;
|
||||
}
|
||||
|
||||
void AIFileInfo::CheckMethods(SQInteger *res, const char *name)
|
||||
{
|
||||
if (!this->engine->MethodExists(*this->SQ_instance, name)) {
|
||||
char error[1024];
|
||||
snprintf(error, sizeof(error), "your AIFileInfo doesn't have the method '%s'", name);
|
||||
this->engine->ThrowError(error);
|
||||
*res = SQ_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ SQInteger AIFileInfo::Constructor(HSQUIRRELVM vm, AIFileInfo *info)
|
||||
{
|
||||
SQInteger res = 0;
|
||||
|
||||
/* Set some basic info from the parent */
|
||||
info->SQ_instance = MallocT<SQObject>(1);
|
||||
Squirrel::GetInstance(vm, info->SQ_instance, 2);
|
||||
/* Make sure the instance stays alive over time */
|
||||
sq_addref(vm, info->SQ_instance);
|
||||
info->base = ((AIScanner *)Squirrel::GetGlobalPointer(vm));
|
||||
info->engine = info->base->GetEngine();
|
||||
|
||||
/* Check if all needed fields are there */
|
||||
info->CheckMethods(&res, "GetAuthor");
|
||||
info->CheckMethods(&res, "GetName");
|
||||
info->CheckMethods(&res, "GetDescription");
|
||||
info->CheckMethods(&res, "GetVersion");
|
||||
info->CheckMethods(&res, "GetDate");
|
||||
info->CheckMethods(&res, "CreateInstance");
|
||||
|
||||
/* Abort if one method was missing */
|
||||
if (res != 0) return res;
|
||||
|
||||
info->script_name = strdup(info->base->GetCurrentScript());
|
||||
info->dir_name = strdup(info->base->GetCurrentDirName());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* static */ SQInteger AIInfo::Constructor(HSQUIRRELVM vm)
|
||||
{
|
||||
/* Get the AIInfo */
|
||||
SQUserPointer instance;
|
||||
sq_getinstanceup(vm, 2, &instance, 0);
|
||||
AIInfo *info = (AIInfo *)instance;
|
||||
|
||||
SQInteger res = AIFileInfo::Constructor(vm, info);
|
||||
if (res != 0) return res;
|
||||
|
||||
AIConfigItem config;
|
||||
config.name = strdup("start_date");
|
||||
config.description = strdup("The amount of months after the start of the last AI, this AI will start (give or take).");
|
||||
config.min_value = 0;
|
||||
config.max_value = 120;
|
||||
config.easy_value = 48;
|
||||
config.medium_value = 24;
|
||||
config.hard_value = 12;
|
||||
config.custom_value = 24;
|
||||
config.flags = AICONFIG_NONE;
|
||||
info->config_list.push_back(config);
|
||||
|
||||
/* Check if we have settings */
|
||||
if (info->engine->MethodExists(*info->SQ_instance, "GetSettings")) {
|
||||
info->GetSettings();
|
||||
}
|
||||
|
||||
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
|
||||
sq_setinstanceup(vm, 2, NULL);
|
||||
/* Register the AI to the base system */
|
||||
info->base->RegisterAI(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* static */ SQInteger AIInfo::DummyConstructor(HSQUIRRELVM vm)
|
||||
{
|
||||
/* Get the AIInfo */
|
||||
SQUserPointer instance;
|
||||
sq_getinstanceup(vm, 2, &instance, 0);
|
||||
AIInfo *info = (AIInfo *)instance;
|
||||
|
||||
SQInteger res = AIFileInfo::Constructor(vm, info);
|
||||
if (res != 0) return res;
|
||||
|
||||
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
|
||||
sq_setinstanceup(vm, 2, NULL);
|
||||
/* Register the AI to the base system */
|
||||
info->base->SetDummyAI(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AIInfo::~AIInfo()
|
||||
{
|
||||
/* Free all allocated strings */
|
||||
for (AIConfigItemList::iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
|
||||
free((char *)(*it).name);
|
||||
}
|
||||
this->config_list.clear();
|
||||
}
|
||||
|
||||
SQInteger AIInfo::AddSetting(HSQUIRRELVM vm)
|
||||
{
|
||||
AIConfigItem config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
int items = 0;
|
||||
|
||||
/* Read the table, and find all properties we care about */
|
||||
sq_pushnull(vm);
|
||||
while (SQ_SUCCEEDED(sq_next(vm, -2))) {
|
||||
const SQChar *sqkey;
|
||||
sq_getstring(vm, -2, &sqkey);
|
||||
const char *key = FS2OTTD(sqkey);
|
||||
|
||||
if (strcmp(key, "name") == 0) {
|
||||
const SQChar *sqvalue;
|
||||
sq_getstring(vm, -1, &sqvalue);
|
||||
config.name = strdup(FS2OTTD(sqvalue));
|
||||
char *s;
|
||||
/* Don't allow '=' and ',' in configure setting names, as we need those
|
||||
* 2 chars to nicely store the settings as a string. */
|
||||
while ((s = (char *)strchr(config.name, '=')) != NULL) *s = '_';
|
||||
while ((s = (char *)strchr(config.name, ',')) != NULL) *s = '_';
|
||||
items |= 0x001;
|
||||
} else if (strcmp(key, "description") == 0) {
|
||||
const SQChar *sqdescription;
|
||||
sq_getstring(vm, -1, &sqdescription);
|
||||
config.description = strdup(FS2OTTD(sqdescription));
|
||||
items |= 0x002;
|
||||
} else if (strcmp(key, "min_value") == 0) {
|
||||
SQInteger res;
|
||||
sq_getinteger(vm, -1, &res);
|
||||
config.min_value = res;
|
||||
items |= 0x004;
|
||||
} else if (strcmp(key, "max_value") == 0) {
|
||||
SQInteger res;
|
||||
sq_getinteger(vm, -1, &res);
|
||||
config.max_value = res;
|
||||
items |= 0x008;
|
||||
} else if (strcmp(key, "easy_value") == 0) {
|
||||
SQInteger res;
|
||||
sq_getinteger(vm, -1, &res);
|
||||
config.easy_value = res;
|
||||
items |= 0x010;
|
||||
} else if (strcmp(key, "medium_value") == 0) {
|
||||
SQInteger res;
|
||||
sq_getinteger(vm, -1, &res);
|
||||
config.medium_value = res;
|
||||
items |= 0x020;
|
||||
} else if (strcmp(key, "hard_value") == 0) {
|
||||
SQInteger res;
|
||||
sq_getinteger(vm, -1, &res);
|
||||
config.hard_value = res;
|
||||
items |= 0x040;
|
||||
} else if (strcmp(key, "custom_value") == 0) {
|
||||
SQInteger res;
|
||||
sq_getinteger(vm, -1, &res);
|
||||
config.custom_value = res;
|
||||
items |= 0x080;
|
||||
} else if (strcmp(key, "flags") == 0) {
|
||||
SQInteger res;
|
||||
sq_getinteger(vm, -1, &res);
|
||||
config.flags = (AIConfigFlags)res;
|
||||
items |= 0x100;
|
||||
} else {
|
||||
char error[1024];
|
||||
snprintf(error, sizeof(error), "unknown setting property '%s'", key);
|
||||
this->engine->ThrowError(error);
|
||||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
sq_pop(vm, 2);
|
||||
}
|
||||
sq_pop(vm, 1);
|
||||
|
||||
/* Make sure all properties are defined */
|
||||
if (items != 0x1FF) {
|
||||
char error[1024];
|
||||
snprintf(error, sizeof(error), "please define all properties of a setting");
|
||||
this->engine->ThrowError(error);
|
||||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
this->config_list.push_back(config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const AIConfigItemList *AIInfo::GetConfigList()
|
||||
{
|
||||
return &this->config_list;
|
||||
}
|
||||
|
||||
int AIInfo::GetSettingDefaultValue(const char *name)
|
||||
{
|
||||
for (AIConfigItemList::iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
|
||||
if (strcmp((*it).name, name) != 0) continue;
|
||||
/* The default value depends on the difficulty level */
|
||||
switch ((_game_mode == GM_MENU) ? _settings_newgame.difficulty.diff_level : _settings_game.difficulty.diff_level) {
|
||||
case 0: return (*it).easy_value;
|
||||
case 1: return (*it).medium_value;
|
||||
case 2: return (*it).hard_value;
|
||||
case 3: return (*it).custom_value;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/* There is no such setting */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm)
|
||||
{
|
||||
/* Create a new AIFileInfo */
|
||||
AILibrary *library = new AILibrary();
|
||||
|
||||
SQInteger res = AIFileInfo::Constructor(vm, library);
|
||||
if (res != 0) return res;
|
||||
|
||||
/* Register the Library to the base system */
|
||||
library->base->RegisterLibrary(library);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* static */ SQInteger AILibrary::Import(HSQUIRRELVM vm)
|
||||
{
|
||||
SQConvert::SQAutoFreePointers ptr;
|
||||
const char *library = GetParam(SQConvert::ForceType<const char *>(), vm, 2, &ptr);
|
||||
const char *class_name = GetParam(SQConvert::ForceType<const char *>(), vm, 3, &ptr);
|
||||
int version = GetParam(SQConvert::ForceType<int>(), vm, 4, &ptr);
|
||||
|
||||
if (!AI::ImportLibrary(library, class_name, version, vm)) return -1;
|
||||
return 1;
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_info.hpp AIInfo keeps track of all information of an AI, like Author, Description, ... */
|
||||
|
||||
#ifndef AI_INFO
|
||||
#define AI_INFO
|
||||
|
||||
#include <list>
|
||||
#include "api/ai_object.hpp"
|
||||
|
||||
enum AIConfigFlags {
|
||||
AICONFIG_NONE = 0x0,
|
||||
AICONFIG_RANDOM = 0x1, //!< When randomizing the AI, pick any value between min_value and max_value when on custom difficulty setting.
|
||||
AICONFIG_BOOLEAN = 0x2, //!< This value is a boolean (either 0 (false) or 1 (true) ).
|
||||
};
|
||||
|
||||
struct AIConfigItem {
|
||||
const char *name; //!< The name of the configuration setting.
|
||||
const char *description; //!< The description of the configuration setting.
|
||||
int min_value; //!< The minimal value this configuration setting can have.
|
||||
int max_value; //!< The maximal value this configuration setting can have.
|
||||
int custom_value; //!< The default value on custom difficulty setting.
|
||||
int easy_value; //!< The default value on easy difficulty setting.
|
||||
int medium_value; //!< The default value on medium difficulty setting.
|
||||
int hard_value; //!< The default value on hard difficulty setting.
|
||||
AIConfigFlags flags; //!< Flags for the configuration setting.
|
||||
};
|
||||
|
||||
typedef std::list<AIConfigItem> AIConfigItemList;
|
||||
|
||||
class AIFileInfo : public AIObject {
|
||||
public:
|
||||
friend class AIInfo;
|
||||
friend class AILibrary;
|
||||
|
||||
AIFileInfo() : author(NULL), name(NULL), description(NULL), date(NULL), instance_name(NULL) {};
|
||||
~AIFileInfo();
|
||||
|
||||
/**
|
||||
* Get the Author of the AI.
|
||||
*/
|
||||
const char *GetAuthor();
|
||||
|
||||
/**
|
||||
* Get the Name of the AI.
|
||||
*/
|
||||
const char *GetName();
|
||||
|
||||
/**
|
||||
* Get the description of the AI.
|
||||
*/
|
||||
const char *GetDescription();
|
||||
|
||||
/**
|
||||
* Get the version of the AI.
|
||||
*/
|
||||
int GetVersion();
|
||||
|
||||
/**
|
||||
* Get the settings of the AI.
|
||||
*/
|
||||
void GetSettings();
|
||||
|
||||
/**
|
||||
* Get the date of the AI.
|
||||
*/
|
||||
const char *GetDate();
|
||||
|
||||
/**
|
||||
* Get the name of the instance of the AI to create.
|
||||
*/
|
||||
const char *GetInstanceName();
|
||||
|
||||
/**
|
||||
* Check if we can start this AI.
|
||||
*/
|
||||
bool AllowStartup();
|
||||
|
||||
/**
|
||||
* Get the name of the dir this AI is in.
|
||||
*/
|
||||
const char *GetDirName();
|
||||
|
||||
/**
|
||||
* Get the complete script name of this AI.
|
||||
*/
|
||||
const char *GetScriptName();
|
||||
|
||||
/**
|
||||
* Check if a given method exists.
|
||||
*/
|
||||
void CheckMethods(SQInteger *res, const char *name);
|
||||
|
||||
/**
|
||||
* Process the creation of a FileInfo object.
|
||||
*/
|
||||
static SQInteger Constructor(HSQUIRRELVM vm, AIFileInfo *info);
|
||||
|
||||
private:
|
||||
class Squirrel *engine;
|
||||
HSQOBJECT *SQ_instance;
|
||||
char *script_name;
|
||||
char *dir_name;
|
||||
class AIScanner *base;
|
||||
const char *author;
|
||||
const char *name;
|
||||
const char *description;
|
||||
const char *date;
|
||||
const char *instance_name;
|
||||
};
|
||||
|
||||
class AIInfo : public AIFileInfo {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIInfo"; }
|
||||
|
||||
~AIInfo();
|
||||
|
||||
/**
|
||||
* Create an AI, using this AIInfo as start-template.
|
||||
*/
|
||||
static SQInteger Constructor(HSQUIRRELVM vm);
|
||||
static SQInteger DummyConstructor(HSQUIRRELVM vm);
|
||||
|
||||
/**
|
||||
* Get the config list for this AI.
|
||||
*/
|
||||
const AIConfigItemList *GetConfigList();
|
||||
|
||||
/**
|
||||
* Set a setting.
|
||||
*/
|
||||
SQInteger AddSetting(HSQUIRRELVM vm);
|
||||
|
||||
/**
|
||||
* Get the default value for a setting.
|
||||
*/
|
||||
int GetSettingDefaultValue(const char *name);
|
||||
|
||||
private:
|
||||
AIConfigItemList config_list;
|
||||
};
|
||||
|
||||
class AILibrary : public AIFileInfo {
|
||||
public:
|
||||
/**
|
||||
* Create an AI, using this AIInfo as start-template.
|
||||
*/
|
||||
static SQInteger Constructor(HSQUIRRELVM vm);
|
||||
|
||||
static SQInteger Import(HSQUIRRELVM vm);
|
||||
};
|
||||
|
||||
#endif /* AI_INFO */
|
@ -0,0 +1,66 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include <squirrel.h>
|
||||
#include "../stdafx.h"
|
||||
|
||||
/* The reason this exists in C++, is that a user can trash his ai/ dir,
|
||||
* leaving no AIs available. The complexity to solve this is insane, and
|
||||
* therefor the alternative is used, and make sure there is always an AI
|
||||
* available, no matter what the situation is. By defining it in C++, there
|
||||
* is simply now way a user can delete it, and therefor safe to use. It has
|
||||
* to be noted that this AI is complete invisible for the user, and impossible
|
||||
* to select manual. It is a fail-over in case no AIs are available.
|
||||
*/
|
||||
|
||||
const SQChar dummy_script_info[] = _SC(" \n\
|
||||
class DummyAI extends AIInfo { \n\
|
||||
function GetAuthor() { return \"OpenTTD NoAI Developers Team\"; } \n\
|
||||
function GetName() { return \"DummyAI\"; } \n\
|
||||
function GetDescription() { return \"A Dummy AI that is loaded when your ai/ dir is empty\"; }\n\
|
||||
function GetVersion() { return 1; } \n\
|
||||
function GetDate() { return \"2008-07-26\"; } \n\
|
||||
function CreateInstance() { return \"DummyAI\"; } \n\
|
||||
} \n\
|
||||
\n\
|
||||
RegisterDummyAI(DummyAI()); \n\
|
||||
");
|
||||
|
||||
const SQChar dummy_script[] = _SC(" \n\
|
||||
class DummyAI extends AIController { \n\
|
||||
function Start() { \n\
|
||||
AILog.Error(\"No suitable AI found to load.\"); \n\
|
||||
AILog.Error(\"This AI is a dummy AI and won't do anything.\"); \n\
|
||||
AILog.Error(\"Please add one or several AIs in your ai/ directory.\"); \n\
|
||||
} \n\
|
||||
} \n\
|
||||
");
|
||||
|
||||
void AI_CreateAIInfoDummy(HSQUIRRELVM vm)
|
||||
{
|
||||
sq_pushroottable(vm);
|
||||
|
||||
/* Load and run the script */
|
||||
if (SQ_SUCCEEDED(sq_compilebuffer(vm, dummy_script_info, scstrlen(dummy_script_info), _SC("dummy"), SQTrue))) {
|
||||
sq_push(vm, -2);
|
||||
if (SQ_SUCCEEDED(sq_call(vm, 1, SQFalse, SQTrue))) {
|
||||
sq_pop(vm, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
void AI_CreateAIDummy(HSQUIRRELVM vm)
|
||||
{
|
||||
sq_pushroottable(vm);
|
||||
|
||||
/* Load and run the script */
|
||||
if (SQ_SUCCEEDED(sq_compilebuffer(vm, dummy_script, scstrlen(dummy_script), _SC("dummy"), SQTrue))) {
|
||||
sq_push(vm, -2);
|
||||
if (SQ_SUCCEEDED(sq_call(vm, 1, SQFalse, SQTrue))) {
|
||||
sq_pop(vm, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
NOT_REACHED();
|
||||
}
|
@ -0,0 +1,628 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_instance.cpp Implementation of AIInstance. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../openttd.h"
|
||||
#include "../debug.h"
|
||||
#include "../company_func.h"
|
||||
#include "../core/alloc_func.hpp"
|
||||
#include "../string_func.h"
|
||||
#include "../settings_type.h"
|
||||
#include "../company_base.h"
|
||||
#include "../saveload/saveload.h"
|
||||
#include "table/strings.h"
|
||||
|
||||
#include <squirrel.h>
|
||||
#include "../script/squirrel.hpp"
|
||||
#include "../script/squirrel_helper.hpp"
|
||||
#include "../script/squirrel_class.hpp"
|
||||
#include "../script/squirrel_std.hpp"
|
||||
#include "ai.hpp"
|
||||
#include "api/ai_controller.hpp"
|
||||
#include "ai_info.hpp"
|
||||
#include "ai_storage.hpp"
|
||||
#include "ai_instance.hpp"
|
||||
|
||||
/* Convert all AI related classes to Squirrel data.
|
||||
* Note: this line a marker in squirrel_export.sh. Do not change! */
|
||||
#include "api/ai_abstractlist.hpp.sq"
|
||||
#include "api/ai_accounting.hpp.sq"
|
||||
#include "api/ai_airport.hpp.sq"
|
||||
#include "api/ai_base.hpp.sq"
|
||||
#include "api/ai_bridge.hpp.sq"
|
||||
#include "api/ai_bridgelist.hpp.sq"
|
||||
#include "api/ai_cargo.hpp.sq"
|
||||
#include "api/ai_cargolist.hpp.sq"
|
||||
#include "api/ai_company.hpp.sq"
|
||||
#include "api/ai_controller.hpp.sq"
|
||||
#include "api/ai_date.hpp.sq"
|
||||
#include "api/ai_depotlist.hpp.sq"
|
||||
#include "api/ai_engine.hpp.sq"
|
||||
#include "api/ai_enginelist.hpp.sq"
|
||||
#include "api/ai_error.hpp.sq"
|
||||
#include "api/ai_event.hpp.sq"
|
||||
#include "api/ai_event_types.hpp.sq"
|
||||
#include "api/ai_execmode.hpp.sq"
|
||||
#include "api/ai_gamesettings.hpp.sq"
|
||||
#include "api/ai_group.hpp.sq"
|
||||
#include "api/ai_grouplist.hpp.sq"
|
||||
#include "api/ai_industry.hpp.sq"
|
||||
#include "api/ai_industrylist.hpp.sq"
|
||||
#include "api/ai_industrytype.hpp.sq"
|
||||
#include "api/ai_industrytypelist.hpp.sq"
|
||||
#include "api/ai_list.hpp.sq"
|
||||
#include "api/ai_log.hpp.sq"
|
||||
#include "api/ai_map.hpp.sq"
|
||||
#include "api/ai_marine.hpp.sq"
|
||||
#include "api/ai_order.hpp.sq"
|
||||
#include "api/ai_rail.hpp.sq"
|
||||
#include "api/ai_railtypelist.hpp.sq"
|
||||
#include "api/ai_road.hpp.sq"
|
||||
#include "api/ai_sign.hpp.sq"
|
||||
#include "api/ai_station.hpp.sq"
|
||||
#include "api/ai_stationlist.hpp.sq"
|
||||
#include "api/ai_subsidy.hpp.sq"
|
||||
#include "api/ai_subsidylist.hpp.sq"
|
||||
#include "api/ai_testmode.hpp.sq"
|
||||
#include "api/ai_tile.hpp.sq"
|
||||
#include "api/ai_tilelist.hpp.sq"
|
||||
#include "api/ai_town.hpp.sq"
|
||||
#include "api/ai_townlist.hpp.sq"
|
||||
#include "api/ai_tunnel.hpp.sq"
|
||||
#include "api/ai_vehicle.hpp.sq"
|
||||
#include "api/ai_vehiclelist.hpp.sq"
|
||||
|
||||
/* static */ AIInstance *AIInstance::current_instance = NULL;
|
||||
|
||||
AIStorage::~AIStorage()
|
||||
{
|
||||
/* Free our pointers */
|
||||
if (event_data != NULL) AIEventController::FreeEventPointer();
|
||||
if (log_data != NULL) AILog::FreeLogPointer();
|
||||
}
|
||||
|
||||
static void PrintFunc(bool error_msg, const SQChar *message)
|
||||
{
|
||||
/* Convert to OpenTTD internal capable string */
|
||||
AIController::Print(error_msg, FS2OTTD(message));
|
||||
}
|
||||
|
||||
AIInstance::AIInstance(AIInfo *info) :
|
||||
controller(NULL),
|
||||
storage(NULL),
|
||||
engine(NULL),
|
||||
instance(NULL),
|
||||
is_started(false),
|
||||
is_dead(false),
|
||||
suspend(0),
|
||||
callback(NULL)
|
||||
{
|
||||
/* Set the instance already, so we can use AIObject::Set commands */
|
||||
GetCompany(_current_company)->ai_instance = this;
|
||||
AIInstance::current_instance = this;
|
||||
|
||||
this->controller = new AIController();
|
||||
this->storage = new AIStorage();
|
||||
this->engine = new Squirrel();
|
||||
this->engine->SetPrintFunction(&PrintFunc);
|
||||
|
||||
/* The import method is available at a very early stage */
|
||||
this->engine->AddMethod("import", &AILibrary::Import, 4, "?ssi");
|
||||
|
||||
/* Register the AIController */
|
||||
SQAIController_Register(this->engine);
|
||||
|
||||
/* Load and execute the script for this AI */
|
||||
const char *script_name = info->GetScriptName();
|
||||
if (strcmp(script_name, "%_dummy") == 0) {
|
||||
extern void AI_CreateAIDummy(HSQUIRRELVM vm);
|
||||
AI_CreateAIDummy(this->engine->GetVM());
|
||||
} else if (!this->engine->LoadScript(script_name)) {
|
||||
this->Died();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create the main-class */
|
||||
this->instance = MallocT<SQObject>(1);
|
||||
if (!this->engine->CreateClassInstance(info->GetInstanceName(), this->controller, this->instance)) {
|
||||
this->Died();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Register the API functions and classes */
|
||||
this->RegisterAPI();
|
||||
|
||||
/* Run the constructor if it exists. Don't allow any DoCommands in it. */
|
||||
if (this->engine->MethodExists(*this->instance, "constructor")) {
|
||||
AIObject::SetAllowDoCommand(false);
|
||||
this->engine->CallMethod(*this->instance, "constructor");
|
||||
AIObject::SetAllowDoCommand(true);
|
||||
}
|
||||
}
|
||||
|
||||
AIInstance::~AIInstance()
|
||||
{
|
||||
if (engine != NULL) delete this->engine;
|
||||
delete this->storage;
|
||||
delete this->controller;
|
||||
free(this->instance);
|
||||
}
|
||||
|
||||
void AIInstance::RegisterAPI()
|
||||
{
|
||||
/* Register all classes */
|
||||
squirrel_register_std(this->engine);
|
||||
SQAIAbstractList_Register(this->engine);
|
||||
SQAIAccounting_Register(this->engine);
|
||||
SQAIAirport_Register(this->engine);
|
||||
SQAIBase_Register(this->engine);
|
||||
SQAIBridge_Register(this->engine);
|
||||
SQAIBridgeList_Register(this->engine);
|
||||
SQAIBridgeList_Length_Register(this->engine);
|
||||
SQAICargo_Register(this->engine);
|
||||
SQAICargoList_Register(this->engine);
|
||||
SQAICargoList_IndustryAccepting_Register(this->engine);
|
||||
SQAICargoList_IndustryProducing_Register(this->engine);
|
||||
SQAICompany_Register(this->engine);
|
||||
SQAIDate_Register(this->engine);
|
||||
SQAIDepotList_Register(this->engine);
|
||||
SQAIEngine_Register(this->engine);
|
||||
SQAIEngineList_Register(this->engine);
|
||||
SQAIError_Register(this->engine);
|
||||
SQAIEvent_Register(this->engine);
|
||||
SQAIEventCompanyBankrupt_Register(this->engine);
|
||||
SQAIEventCompanyInTrouble_Register(this->engine);
|
||||
SQAIEventCompanyMerger_Register(this->engine);
|
||||
SQAIEventCompanyNew_Register(this->engine);
|
||||
SQAIEventController_Register(this->engine);
|
||||
SQAIEventEngineAvailable_Register(this->engine);
|
||||
SQAIEventEnginePreview_Register(this->engine);
|
||||
SQAIEventIndustryClose_Register(this->engine);
|
||||
SQAIEventIndustryOpen_Register(this->engine);
|
||||
SQAIEventStationFirstVehicle_Register(this->engine);
|
||||
SQAIEventSubsidyAwarded_Register(this->engine);
|
||||
SQAIEventSubsidyExpired_Register(this->engine);
|
||||
SQAIEventSubsidyOffer_Register(this->engine);
|
||||
SQAIEventSubsidyOfferExpired_Register(this->engine);
|
||||
SQAIEventTest_Register(this->engine);
|
||||
SQAIEventVehicleCrashed_Register(this->engine);
|
||||
SQAIEventVehicleLost_Register(this->engine);
|
||||
SQAIEventVehicleUnprofitable_Register(this->engine);
|
||||
SQAIEventVehicleWaitingInDepot_Register(this->engine);
|
||||
SQAIExecMode_Register(this->engine);
|
||||
SQAIGameSettings_Register(this->engine);
|
||||
SQAIGroup_Register(this->engine);
|
||||
SQAIGroupList_Register(this->engine);
|
||||
SQAIIndustry_Register(this->engine);
|
||||
SQAIIndustryList_Register(this->engine);
|
||||
SQAIIndustryList_CargoAccepting_Register(this->engine);
|
||||
SQAIIndustryList_CargoProducing_Register(this->engine);
|
||||
SQAIIndustryType_Register(this->engine);
|
||||
SQAIIndustryTypeList_Register(this->engine);
|
||||
SQAIList_Register(this->engine);
|
||||
SQAILog_Register(this->engine);
|
||||
SQAIMap_Register(this->engine);
|
||||
SQAIMarine_Register(this->engine);
|
||||
SQAIOrder_Register(this->engine);
|
||||
SQAIRail_Register(this->engine);
|
||||
SQAIRailTypeList_Register(this->engine);
|
||||
SQAIRoad_Register(this->engine);
|
||||
SQAISign_Register(this->engine);
|
||||
SQAIStation_Register(this->engine);
|
||||
SQAIStationList_Register(this->engine);
|
||||
SQAIStationList_Vehicle_Register(this->engine);
|
||||
SQAISubsidy_Register(this->engine);
|
||||
SQAISubsidyList_Register(this->engine);
|
||||
SQAITestMode_Register(this->engine);
|
||||
SQAITile_Register(this->engine);
|
||||
SQAITileList_Register(this->engine);
|
||||
SQAITileList_IndustryAccepting_Register(this->engine);
|
||||
SQAITileList_IndustryProducing_Register(this->engine);
|
||||
SQAITileList_StationType_Register(this->engine);
|
||||
SQAITown_Register(this->engine);
|
||||
SQAITownList_Register(this->engine);
|
||||
SQAITunnel_Register(this->engine);
|
||||
SQAIVehicle_Register(this->engine);
|
||||
SQAIVehicleList_Register(this->engine);
|
||||
SQAIVehicleList_Station_Register(this->engine);
|
||||
|
||||
this->engine->SetGlobalPointer(this->engine);
|
||||
}
|
||||
|
||||
void AIInstance::Continue()
|
||||
{
|
||||
assert(this->suspend < 0);
|
||||
this->suspend = -this->suspend - 1;
|
||||
}
|
||||
|
||||
void AIInstance::Died()
|
||||
{
|
||||
DEBUG(ai, 0, "The AI died unexpectedly.");
|
||||
this->is_dead = true;
|
||||
|
||||
delete this->engine;
|
||||
this->engine = NULL;
|
||||
}
|
||||
|
||||
void AIInstance::GameLoop()
|
||||
{
|
||||
if (this->is_dead) return;
|
||||
this->controller->ticks++;
|
||||
|
||||
if (this->suspend < -1) this->suspend++; // Multiplayer suspend, increase up to -1.
|
||||
if (this->suspend < 0) return; // Multiplayer suspend, wait for Continue().
|
||||
if (--this->suspend > 0) return; // Singleplayer suspend, decrease to 0.
|
||||
|
||||
/* If there is a callback to call, call that first */
|
||||
if (this->callback != NULL) {
|
||||
try {
|
||||
this->callback(this);
|
||||
} catch (AI_VMSuspend e) {
|
||||
this->suspend = e.GetSuspendTime();
|
||||
this->callback = e.GetSuspendCallback();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->suspend = 0;
|
||||
this->callback = NULL;
|
||||
|
||||
if (!this->is_started) {
|
||||
/* Start the AI by calling Start() */
|
||||
try {
|
||||
if (!this->engine->CallMethod(*this->instance, "Start", _settings_game.ai.ai_max_opcode_till_suspend)) this->Died();
|
||||
} catch (AI_VMSuspend e) {
|
||||
this->suspend = e.GetSuspendTime();
|
||||
this->callback = e.GetSuspendCallback();
|
||||
}
|
||||
|
||||
this->is_started = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Continue the VM */
|
||||
try {
|
||||
if (!this->engine->Resume(_settings_game.ai.ai_max_opcode_till_suspend)) this->Died();
|
||||
} catch (AI_VMSuspend e) {
|
||||
this->suspend = e.GetSuspendTime();
|
||||
this->callback = e.GetSuspendCallback();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::DoCommandReturn(AIInstance *instance)
|
||||
{
|
||||
instance->engine->InsertResult(AIObject::GetLastCommandRes());
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::DoCommandReturnVehicleID(AIInstance *instance)
|
||||
{
|
||||
instance->engine->InsertResult(AIObject::GetNewVehicleID());
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::DoCommandReturnSignID(AIInstance *instance)
|
||||
{
|
||||
instance->engine->InsertResult(AIObject::GetNewSignID());
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::DoCommandReturnGroupID(AIInstance *instance)
|
||||
{
|
||||
instance->engine->InsertResult(AIObject::GetNewGroupID());
|
||||
}
|
||||
|
||||
/* static */ AIStorage *AIInstance::GetStorage()
|
||||
{
|
||||
assert(IsValidCompanyID(_current_company) && !IsHumanCompany(_current_company));
|
||||
return GetCompany(_current_company)->ai_instance->storage;
|
||||
}
|
||||
|
||||
/*
|
||||
* All data is stored in the following format:
|
||||
* First 1 byte indicating if there is a data blob at all.
|
||||
* 1 byte indicating the type of data.
|
||||
* The data itself, this differs per type:
|
||||
* - integer: a binary representation of the integer (int32).
|
||||
* - string: First one byte with the string length, then a 0-terminated char
|
||||
* array. The string can't be longer then 255 bytes (including
|
||||
* terminating '\0').
|
||||
* - array: All data-elements of the array are saved recursive in this
|
||||
* format, and ended with an element of the type
|
||||
* SQSL_ARRAY_TABLE_END.
|
||||
* - table: All key/value pairs are saved in this format (first key 1, then
|
||||
* value 1, then key 2, etc.). All keys and values can have an
|
||||
* arbitrary type (as long as it is supported by the save function
|
||||
* of course). The table is ended with an element of the type
|
||||
* SQSL_ARRAY_TABLE_END.
|
||||
* - bool: A single byte with value 1 representing true and 0 false.
|
||||
* - null: No data.
|
||||
*/
|
||||
|
||||
/** The type of the data that follows in the savegame. */
|
||||
enum SQSaveLoadType {
|
||||
SQSL_INT = 0x00, ///< The following data is an integer.
|
||||
SQSL_STRING = 0x01, ///< The following data is an string.
|
||||
SQSL_ARRAY = 0x02, ///< The following data is an array.
|
||||
SQSL_TABLE = 0x03, ///< The following data is an table.
|
||||
SQSL_BOOL = 0x04, ///< The following data is a boolean.
|
||||
SQSL_NULL = 0x05, ///< A null variable.
|
||||
SQSL_ARRAY_TABLE_END = 0xFF, ///< Marks the end of an array or table, no data follows.
|
||||
};
|
||||
|
||||
static byte _ai_sl_byte;
|
||||
|
||||
static const SaveLoad _ai_byte[] = {
|
||||
SLEG_VAR(_ai_sl_byte, SLE_UINT8),
|
||||
SLE_END()
|
||||
};
|
||||
|
||||
enum {
|
||||
AISAVE_MAX_DEPTH = 25, ///< The maximum recursive depth for items stored in the savegame.
|
||||
};
|
||||
|
||||
/* static */ bool AIInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
|
||||
{
|
||||
if (max_depth == 0) {
|
||||
AILog::Error("Savedata can only be nested to 5 deep. No data saved.");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (sq_gettype(vm, index)) {
|
||||
case OT_INTEGER: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_INT;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
SQInteger res;
|
||||
sq_getinteger(vm, index, &res);
|
||||
if (!test) {
|
||||
int value = (int)res;
|
||||
SlArray(&value, 1, SLE_INT32);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_STRING: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_STRING;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
const SQChar *res;
|
||||
sq_getstring(vm, index, &res);
|
||||
/* @bug if a string longer than 512 characters is given to FS2OTTD, the
|
||||
* internal buffer overflows. */
|
||||
const char *buf = FS2OTTD(res);
|
||||
size_t len = strlen(buf) + 1;
|
||||
if (len >= 255) {
|
||||
AILog::Error("Maximum string length is 254 chars. No data saved.");
|
||||
return false;
|
||||
}
|
||||
if (!test) {
|
||||
_ai_sl_byte = (byte)len;
|
||||
SlObject(NULL, _ai_byte);
|
||||
SlArray((void*)buf, len, SLE_CHAR);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_ARRAY: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_ARRAY;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
sq_pushnull(vm);
|
||||
while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
|
||||
/* Store the value */
|
||||
bool res = SaveObject(vm, -1, max_depth - 1, test);
|
||||
sq_pop(vm, 2);
|
||||
if (!res) {
|
||||
sq_pop(vm, 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
sq_pop(vm, 1);
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_ARRAY_TABLE_END;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_TABLE: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_TABLE;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
sq_pushnull(vm);
|
||||
while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
|
||||
/* Store the key + value */
|
||||
bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
|
||||
sq_pop(vm, 2);
|
||||
if (!res) {
|
||||
sq_pop(vm, 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
sq_pop(vm, 1);
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_ARRAY_TABLE_END;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_BOOL: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_BOOL;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
SQBool res;
|
||||
sq_getbool(vm, index, &res);
|
||||
if (!test) {
|
||||
_ai_sl_byte = res ? 1 : 0;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case OT_NULL: {
|
||||
if (!test) {
|
||||
_ai_sl_byte = SQSL_NULL;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
AILog::Error("You tried to save unsupported type. No data saved.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::SaveEmpty()
|
||||
{
|
||||
_ai_sl_byte = 0;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
|
||||
void AIInstance::Save()
|
||||
{
|
||||
/* Don't save data if the AI didn't start yet. */
|
||||
if (this->engine == NULL) {
|
||||
SaveEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
/* We don't want to be interrupted during the save function. */
|
||||
AIObject::SetAllowDoCommand(false);
|
||||
|
||||
HSQOBJECT savedata;
|
||||
if (this->engine->MethodExists(*this->instance, "Save")) {
|
||||
this->engine->CallMethod(*this->instance, "Save", &savedata);
|
||||
if (!sq_istable(savedata)) {
|
||||
AILog::Error("Save function should return a table.");
|
||||
_ai_sl_byte = 0;
|
||||
SlObject(NULL, _ai_byte);
|
||||
AIObject::SetAllowDoCommand(true);
|
||||
return;
|
||||
}
|
||||
HSQUIRRELVM vm = this->engine->GetVM();
|
||||
sq_pushobject(vm, savedata);
|
||||
if (SaveObject(vm, -1, AISAVE_MAX_DEPTH, true)) {
|
||||
_ai_sl_byte = 1;
|
||||
SlObject(NULL, _ai_byte);
|
||||
SaveObject(vm, -1, AISAVE_MAX_DEPTH, false);
|
||||
} else {
|
||||
_ai_sl_byte = 0;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
sq_pop(vm, 1);
|
||||
} else {
|
||||
AILog::Warning("Save function is not implemented");
|
||||
_ai_sl_byte = 0;
|
||||
SlObject(NULL, _ai_byte);
|
||||
}
|
||||
|
||||
AIObject::SetAllowDoCommand(true);
|
||||
}
|
||||
|
||||
/* static */ bool AIInstance::LoadObjects(HSQUIRRELVM vm)
|
||||
{
|
||||
SlObject(NULL, _ai_byte);
|
||||
switch (_ai_sl_byte) {
|
||||
case SQSL_INT: {
|
||||
int value;
|
||||
SlArray(&value, 1, SLE_INT32);
|
||||
if (vm != NULL) sq_pushinteger(vm, (SQInteger)value);
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_STRING: {
|
||||
SlObject(NULL, _ai_byte);
|
||||
static char buf[256];
|
||||
SlArray(buf, _ai_sl_byte, SLE_CHAR);
|
||||
if (vm != NULL) sq_pushstring(vm, OTTD2FS(buf), -1);
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_ARRAY: {
|
||||
if (vm != NULL) sq_newarray(vm, 0);
|
||||
while (LoadObjects(vm)) {
|
||||
if (vm != NULL) sq_arrayappend(vm, -2);
|
||||
/* The value is popped from the stack by squirrel. */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_TABLE: {
|
||||
if (vm != NULL) sq_newtable(vm);
|
||||
while (LoadObjects(vm)) {
|
||||
LoadObjects(vm);
|
||||
if (vm != NULL) sq_rawset(vm, -3);
|
||||
/* The key (-2) and value (-1) are popped from the stack by squirrel. */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_BOOL: {
|
||||
SlObject(NULL, _ai_byte);
|
||||
if (vm != NULL) sq_pushinteger(vm, (SQBool)(_ai_sl_byte != 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_NULL: {
|
||||
if (vm != NULL) sq_pushnull(vm);
|
||||
return true;
|
||||
}
|
||||
|
||||
case SQSL_ARRAY_TABLE_END: {
|
||||
return false;
|
||||
}
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void AIInstance::LoadEmpty()
|
||||
{
|
||||
SlObject(NULL, _ai_byte);
|
||||
/* Check if there was anything saved at all. */
|
||||
if (_ai_sl_byte == 0) return;
|
||||
|
||||
LoadObjects(NULL);
|
||||
}
|
||||
|
||||
bool AIInstance::Load()
|
||||
{
|
||||
HSQUIRRELVM vm = (this->engine == NULL) ? NULL : this->engine->GetVM();
|
||||
|
||||
SlObject(NULL, _ai_byte);
|
||||
/* Check if there was anything saved at all. */
|
||||
if (_ai_sl_byte == 0) return true;
|
||||
AIObject::SetAllowDoCommand(false);
|
||||
|
||||
if (vm != NULL) {
|
||||
/* Go to the instance-root */
|
||||
sq_pushobject(vm, *this->instance);
|
||||
/* Find the function-name inside the script */
|
||||
sq_pushstring(vm, OTTD2FS("Load"), -1);
|
||||
if (SQ_FAILED(sq_get(vm, -2))) sq_pushnull(vm);
|
||||
sq_pushobject(vm, *this->instance);
|
||||
}
|
||||
|
||||
LoadObjects(vm);
|
||||
|
||||
if (this->engine != NULL) {
|
||||
if (this->engine->MethodExists(*this->instance, "Load")) {
|
||||
sq_call(vm, 2, SQFalse, SQFalse);
|
||||
} else {
|
||||
AILog::Warning("Loading failed: there was data for the AI to load, but the AI does not have a Load() function.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Pop 1) the object instance, 2) the function name, 3) the instance again, 4) the table. */
|
||||
if (vm != NULL) sq_pop(vm, 4);
|
||||
|
||||
AIObject::SetAllowDoCommand(true);
|
||||
return true;
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_instance.hpp The AIInstance tracks an AI. */
|
||||
|
||||
#ifndef AI_INSTANCE_HPP
|
||||
#define AI_INSTANCE_HPP
|
||||
|
||||
/**
|
||||
* The callback function when an AI suspends.
|
||||
*/
|
||||
typedef void (AISuspendCallbackProc)(class AIInstance *instance);
|
||||
|
||||
/**
|
||||
* A throw-class that is given when the VM wants to suspend.
|
||||
*/
|
||||
class AI_VMSuspend {
|
||||
public:
|
||||
AI_VMSuspend(int time, AISuspendCallbackProc *callback) :
|
||||
time(time),
|
||||
callback(callback)
|
||||
{}
|
||||
|
||||
int GetSuspendTime() { return time; }
|
||||
AISuspendCallbackProc *GetSuspendCallback() { return callback; }
|
||||
|
||||
private:
|
||||
int time;
|
||||
AISuspendCallbackProc *callback;
|
||||
};
|
||||
|
||||
class AIInstance {
|
||||
public:
|
||||
AIInstance(class AIInfo *info);
|
||||
~AIInstance();
|
||||
|
||||
/**
|
||||
* An AI in multiplayer waits for the server to handle his DoCommand.
|
||||
* It keeps waiting for this until this function is called.
|
||||
*/
|
||||
void Continue();
|
||||
|
||||
/**
|
||||
* Run the GameLoop of an AI.
|
||||
*/
|
||||
void GameLoop();
|
||||
|
||||
/**
|
||||
* Get the storage of this AI.
|
||||
*/
|
||||
static class AIStorage *GetStorage();
|
||||
|
||||
/**
|
||||
* Return a true/false reply for a DoCommand.
|
||||
*/
|
||||
static void DoCommandReturn(AIInstance *instance);
|
||||
|
||||
/**
|
||||
* Return a VehicleID reply for a DoCommand.
|
||||
*/
|
||||
static void DoCommandReturnVehicleID(AIInstance *instance);
|
||||
|
||||
/**
|
||||
* Return a SignID reply for a DoCommand.
|
||||
*/
|
||||
static void DoCommandReturnSignID(AIInstance *instance);
|
||||
|
||||
/**
|
||||
* Return a GroupID reply for a DoCommand.
|
||||
*/
|
||||
static void DoCommandReturnGroupID(AIInstance *instance);
|
||||
|
||||
/**
|
||||
* Get the controller attached to the instance.
|
||||
*/
|
||||
class AIController *GetController() { return controller; }
|
||||
|
||||
/**
|
||||
* Call the AI Save function and save all data in the savegame.
|
||||
*/
|
||||
void Save();
|
||||
|
||||
/**
|
||||
* Don't save any data in the savegame.
|
||||
*/
|
||||
static void SaveEmpty();
|
||||
|
||||
/**
|
||||
* Load data from a savegame and call the AI Load function if it
|
||||
* exists.
|
||||
* @return True if the loading was successfull.
|
||||
*/
|
||||
bool Load();
|
||||
|
||||
/**
|
||||
* Load and discard data from a savegame.
|
||||
*/
|
||||
static void LoadEmpty();
|
||||
|
||||
private:
|
||||
static class AIInstance *current_instance; //!< Static current AIInstance, so we can register AIs.
|
||||
|
||||
class AIController *controller;
|
||||
class AIStorage *storage;
|
||||
class Squirrel *engine;
|
||||
SQObject *instance;
|
||||
|
||||
bool is_started;
|
||||
bool is_dead;
|
||||
int suspend;
|
||||
AISuspendCallbackProc *callback;
|
||||
|
||||
/**
|
||||
* Register all API functions to the VM.
|
||||
*/
|
||||
void RegisterAPI();
|
||||
|
||||
/**
|
||||
* Tell the AI it died.
|
||||
*/
|
||||
void Died();
|
||||
|
||||
/**
|
||||
* Save one object (int / string / arrray / table) to the savegame.
|
||||
* @param index The index on the squirrel stack of the element to save.
|
||||
* @param max_depth The maximum depth recursive arrays / tables will be stored
|
||||
* with before an error is returned.
|
||||
* @param test If true, don't really store the data but only check if it is
|
||||
* valid.
|
||||
* @return True if the saving was successfull.
|
||||
*/
|
||||
static bool SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test);
|
||||
|
||||
/**
|
||||
* Load all objects from a savegame.
|
||||
* @return True if the loading was successfull.
|
||||
*/
|
||||
static bool LoadObjects(HSQUIRRELVM vm);
|
||||
};
|
||||
|
||||
#endif /* AI_INSTANCE_HPP */
|
@ -0,0 +1,395 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_scanner.cpp allows scanning AI scripts */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../debug.h"
|
||||
#include "../openttd.h"
|
||||
#include "../string_func.h"
|
||||
#include "../fileio_func.h"
|
||||
#include "../fios.h"
|
||||
#include "../network/network.h"
|
||||
#include "../core/random_func.hpp"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <squirrel.h>
|
||||
#include "../script/squirrel.hpp"
|
||||
#include "../script/squirrel_helper.hpp"
|
||||
#include "../script/squirrel_class.hpp"
|
||||
#include "ai.hpp"
|
||||
#include "ai_info.hpp"
|
||||
#include "ai_scanner.hpp"
|
||||
#include "api/ai_controller.hpp"
|
||||
|
||||
void AIScanner::ScanDir(const char *dirname, bool library_dir, char *library_depth)
|
||||
{
|
||||
extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
|
||||
extern bool FiosIsHiddenFile(const struct dirent *ent);
|
||||
|
||||
struct stat sb;
|
||||
struct dirent *dirent;
|
||||
DIR *dir;
|
||||
char d_name[MAX_PATH];
|
||||
char script_name[MAX_PATH];
|
||||
dir = ttd_opendir(dirname);
|
||||
/* Dir not found, so do nothing */
|
||||
if (dir == NULL) return;
|
||||
|
||||
/* Walk all dirs trying to find a dir in which 'main.nut' exists */
|
||||
while ((dirent = readdir(dir)) != NULL) {
|
||||
ttd_strlcpy(d_name, FS2OTTD(dirent->d_name), sizeof(d_name));
|
||||
|
||||
/* Valid file, not '.' or '..', not hidden */
|
||||
if (!FiosIsValidFile(dirname, dirent, &sb)) continue;
|
||||
if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
|
||||
if (FiosIsHiddenFile(dirent) && strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) != 0) continue;
|
||||
|
||||
if (S_ISDIR(sb.st_mode)) {
|
||||
/* Create the full-length script-name */
|
||||
ttd_strlcpy(script_name, dirname, sizeof(script_name));
|
||||
ttd_strlcat(script_name, d_name, sizeof(script_name));
|
||||
ttd_strlcat(script_name, PATHSEP, sizeof(script_name));
|
||||
|
||||
if (library_dir && library_depth == NULL) {
|
||||
ScanDir(script_name, library_dir, d_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (S_ISREG(sb.st_mode)) {
|
||||
if (library_dir) continue;
|
||||
|
||||
char *ext = strrchr(d_name, '.');
|
||||
if (ext == NULL || strcasecmp(ext, ".tar") != 0) continue;
|
||||
|
||||
/* Create the full path to the tarfile */
|
||||
char tarname[MAX_PATH];
|
||||
ttd_strlcpy(tarname, dirname, sizeof(tarname));
|
||||
ttd_strlcat(tarname, d_name, sizeof(tarname));
|
||||
|
||||
/* Now the script-name starts with the first dir in the tar */
|
||||
if (FioTarFirstDir(tarname) == NULL) continue;
|
||||
ttd_strlcpy(script_name, "%aitar%", sizeof(tarname));
|
||||
ttd_strlcat(script_name, FioTarFirstDir(tarname), sizeof(script_name));
|
||||
FioTarAddLink(script_name, FioTarFirstDir(tarname));
|
||||
|
||||
/* The name of the AI is the name of the tar minus the .tar */
|
||||
*ext = '\0';
|
||||
}
|
||||
|
||||
if (!library_dir) {
|
||||
/* We look for the file 'info.nut' inside the AI dir.. if it doesn't exists, it isn't an AI */
|
||||
ttd_strlcat(script_name, "info.nut", sizeof(script_name));
|
||||
if (FioCheckFileExists(script_name, AI_DIR)) {
|
||||
char load_script[MAX_PATH];
|
||||
ttd_strlcpy(load_script, script_name, sizeof(load_script));
|
||||
|
||||
/* Remove the 'info.nut' part and replace it with 'main.nut' */
|
||||
script_name[strlen(script_name) - 8] = '\0';
|
||||
ttd_strlcat(script_name, "main.nut", sizeof(script_name));
|
||||
|
||||
DEBUG(ai, 6, "[script] Loading script '%s' for AI handling", load_script);
|
||||
this->current_script = script_name;
|
||||
this->current_dir = d_name;
|
||||
this->engine->LoadScript(load_script);
|
||||
}
|
||||
} else {
|
||||
/* We look for the file 'library.nut' inside the library dir.. */
|
||||
ttd_strlcat(script_name, "library.nut", sizeof(script_name));
|
||||
if (FioCheckFileExists(script_name, AI_DIR)) {
|
||||
char load_script[MAX_PATH];
|
||||
char dir_name[MAX_PATH];
|
||||
char d_name_2[MAX_PATH];
|
||||
/* In case the directory has a dot in it, ignore it, as that is the
|
||||
* indicator for multiple versions of the same library */
|
||||
ttd_strlcpy(d_name_2, d_name, sizeof(d_name_2));
|
||||
char *e = strrchr(d_name_2, '.');
|
||||
if (e != NULL) *e = '\0';
|
||||
|
||||
ttd_strlcpy(load_script, script_name, sizeof(load_script));
|
||||
ttd_strlcpy(dir_name, library_depth, sizeof(dir_name));
|
||||
ttd_strlcat(dir_name, ".", sizeof(dir_name));
|
||||
ttd_strlcat(dir_name, d_name_2, sizeof(dir_name));
|
||||
|
||||
/* Remove the 'library.nut' part and replace it with 'main.nut' */
|
||||
script_name[strlen(script_name) - 11] = '\0';
|
||||
ttd_strlcat(script_name, "main.nut", sizeof(script_name));
|
||||
|
||||
DEBUG(ai, 6, "[script] Loading script '%s' for Squirrel library", load_script);
|
||||
this->current_script = script_name;
|
||||
this->current_dir = dir_name;
|
||||
this->engine->LoadScript(load_script);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void AIScanner::ScanAIDir()
|
||||
{
|
||||
char buf[MAX_PATH];
|
||||
Searchpath sp;
|
||||
|
||||
FOR_ALL_SEARCHPATHS(sp) {
|
||||
FioAppendDirectory(buf, MAX_PATH, sp, AI_DIR);
|
||||
if (FileExists(buf)) this->ScanDir(buf, false);
|
||||
ttd_strlcat(buf, "library" PATHSEP, MAX_PATH);
|
||||
if (FileExists(buf)) this->ScanDir(buf, true);
|
||||
}
|
||||
}
|
||||
|
||||
void AIScanner::RescanAIDir()
|
||||
{
|
||||
extern void ScanForTarFiles();
|
||||
ScanForTarFiles();
|
||||
this->ScanAIDir();
|
||||
}
|
||||
|
||||
AIScanner::AIScanner()
|
||||
{
|
||||
this->engine = new Squirrel();
|
||||
|
||||
/* Create the AIInfo class, and add the RegisterAI function */
|
||||
DefSQClass <AIInfo> SQAIInfo("AIInfo");
|
||||
SQAIInfo.PreRegister(engine);
|
||||
SQAIInfo.AddConstructor<void (AIInfo::*)(), 1>(engine, "x");
|
||||
SQAIInfo.DefSQAdvancedMethod(this->engine, &AIInfo::AddSetting, "AddSetting");
|
||||
SQAIInfo.PostRegister(engine);
|
||||
this->engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx");
|
||||
/* Create the AILibrary class, and add the RegisterLibrary function */
|
||||
this->engine->AddClassBegin("AILibrary");
|
||||
this->engine->AddClassEnd();
|
||||
this->engine->AddMethod("RegisterLibrary", &AILibrary::Constructor, 2, "tx");
|
||||
|
||||
/* Mark this class as global pointer */
|
||||
this->engine->SetGlobalPointer(this);
|
||||
|
||||
/* Scan the AI dir for scripts */
|
||||
this->ScanAIDir();
|
||||
|
||||
/* Create the dummy AI */
|
||||
this->engine->AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, 2, "tx");
|
||||
this->current_script = (char *)"%_dummy";
|
||||
this->current_dir = (char *)"%_dummy";
|
||||
extern void AI_CreateAIInfoDummy(HSQUIRRELVM vm);
|
||||
AI_CreateAIInfoDummy(this->engine->GetVM());
|
||||
}
|
||||
|
||||
AIScanner::~AIScanner()
|
||||
{
|
||||
AIInfoList::iterator it = this->info_list.begin();
|
||||
for (; it != this->info_list.end(); it++) {
|
||||
AIInfo *i = (*it).second;
|
||||
delete i;
|
||||
}
|
||||
|
||||
delete this->engine;
|
||||
}
|
||||
|
||||
bool AIScanner::ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm, AIController *controller)
|
||||
{
|
||||
/* Internally we store libraries as 'library.version' */
|
||||
char library_name[1024];
|
||||
snprintf(library_name, sizeof(library_name), "%s.%d", library, version);
|
||||
|
||||
/* Check if the library + version exists */
|
||||
AILibraryList::iterator iter = this->library_list.find(library_name);
|
||||
if (iter == this->library_list.end()) {
|
||||
char error[1024];
|
||||
|
||||
/* Now see if the version doesn't exist, or the library */
|
||||
iter = this->library_list.find(library);
|
||||
if (iter == this->library_list.end()) {
|
||||
snprintf(error, sizeof(error), "couldn't find library '%s'", library);
|
||||
} else {
|
||||
snprintf(error, sizeof(error), "this AI is expecting library '%s' to be version %d, but the latest available is version %d", library, version, (*iter).second->GetVersion());
|
||||
}
|
||||
sq_throwerror(vm, OTTD2FS(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the current table/class we belong to */
|
||||
HSQOBJECT parent;
|
||||
sq_getstackobj(vm, 1, &parent);
|
||||
|
||||
char fake_class[1024];
|
||||
int next_number;
|
||||
/* Check if we already have this library loaded.. if so, fill fake_class
|
||||
* with the class-name it is nested in */
|
||||
if (!controller->LoadedLibrary(library_name, &next_number, &fake_class[0], sizeof(fake_class))) {
|
||||
/* Create a new fake internal name */
|
||||
snprintf(fake_class, sizeof(fake_class), "_internalNA%d", next_number);
|
||||
|
||||
/* Load the library in a 'fake' namespace, so we can link it to the name the user requested */
|
||||
sq_pushroottable(vm);
|
||||
sq_pushstring(vm, OTTD2FS(fake_class), -1);
|
||||
sq_newclass(vm, SQFalse);
|
||||
/* Load the library */
|
||||
if (!Squirrel::LoadScript(vm, (*iter).second->GetScriptName(), false)) {
|
||||
char error[1024];
|
||||
snprintf(error, sizeof(error), "there was a compile error when importing '%s' version %d", library, version);
|
||||
sq_throwerror(vm, OTTD2FS(error));
|
||||
return false;
|
||||
}
|
||||
/* Create the fake class */
|
||||
sq_newslot(vm, -3, SQFalse);
|
||||
sq_pop(vm, 1);
|
||||
|
||||
controller->AddLoadedLibrary(library_name, fake_class);
|
||||
}
|
||||
|
||||
/* Find the real class inside the fake class (like 'sets.Vector') */
|
||||
sq_pushroottable(vm);
|
||||
sq_pushstring(vm, OTTD2FS(fake_class), -1);
|
||||
if (SQ_FAILED(sq_get(vm, -2))) {
|
||||
sq_throwerror(vm, _SC("internal error assigning library class"));
|
||||
return false;
|
||||
}
|
||||
sq_pushstring(vm, OTTD2FS((*iter).second->GetInstanceName()), -1);
|
||||
if (SQ_FAILED(sq_get(vm, -2))) {
|
||||
char error[1024];
|
||||
snprintf(error, sizeof(error), "unable to find class '%s' in the library '%s' version %d", (*iter).second->GetInstanceName(), library, version);
|
||||
sq_throwerror(vm, OTTD2FS(error));
|
||||
return false;
|
||||
}
|
||||
HSQOBJECT obj;
|
||||
sq_getstackobj(vm, -1, &obj);
|
||||
sq_pop(vm, 3);
|
||||
|
||||
if (StrEmpty(class_name)) {
|
||||
sq_pushobject(vm, obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Now link the name the user wanted to our 'fake' class */
|
||||
sq_pushobject(vm, parent);
|
||||
sq_pushstring(vm, OTTD2FS(class_name), -1);
|
||||
sq_pushobject(vm, obj);
|
||||
sq_newclass(vm, SQTrue);
|
||||
sq_newslot(vm, -3, SQFalse);
|
||||
sq_pop(vm, 1);
|
||||
|
||||
sq_pushobject(vm, obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AIScanner::RegisterLibrary(AILibrary *library)
|
||||
{
|
||||
const char *ai_name_without_version = library->GetDirName();
|
||||
char ai_name[1024];
|
||||
snprintf(ai_name, sizeof(ai_name), "%s.%d", library->GetDirName(), library->GetVersion());
|
||||
|
||||
/* Check if we register twice; than the first always wins */
|
||||
if (this->library_list.find(ai_name) != this->library_list.end()) {
|
||||
/* In case they are not the same dir, give a warning */
|
||||
if (strcasecmp(library->GetScriptName(), this->library_list[ai_name]->GetScriptName()) != 0) {
|
||||
DEBUG(ai, 0, "Registering two libraries with the same name");
|
||||
DEBUG(ai, 0, " 1: %s", this->library_list[ai_name]->GetScriptName());
|
||||
DEBUG(ai, 0, " 2: %s", library->GetScriptName());
|
||||
DEBUG(ai, 0, "The first is taking precedence");
|
||||
}
|
||||
/* Delete the new AILibrary, as we will be using the old one */
|
||||
delete library;
|
||||
return;
|
||||
}
|
||||
|
||||
this->library_list[strdup(ai_name)] = library;
|
||||
/* Insert the global name too, so we if the library is known at all */
|
||||
if (this->library_list.find(ai_name_without_version) == this->library_list.end()) {
|
||||
this->library_list[strdup(ai_name_without_version)] = library;
|
||||
} else if (this->library_list[ai_name_without_version]->GetVersion() < library->GetVersion()) {
|
||||
this->library_list[ai_name_without_version] = library;
|
||||
}
|
||||
}
|
||||
|
||||
void AIScanner::RegisterAI(AIInfo *info)
|
||||
{
|
||||
const char *ai_name = info->GetDirName();
|
||||
|
||||
/* Check if we register twice; than the first always wins */
|
||||
if (this->info_list.find(ai_name) != this->info_list.end()) {
|
||||
/* In case they are not the same dir, give a warning */
|
||||
if (strcasecmp(info->GetScriptName(), this->info_list[ai_name]->GetScriptName()) != 0) {
|
||||
DEBUG(ai, 0, "Registering two AIs with the same name");
|
||||
DEBUG(ai, 0, " 1: %s", this->info_list[ai_name]->GetScriptName());
|
||||
DEBUG(ai, 0, " 2: %s", info->GetScriptName());
|
||||
DEBUG(ai, 0, "The first is taking precedence");
|
||||
}
|
||||
/* Delete the new AIInfo, as we will be using the old one */
|
||||
delete info;
|
||||
return;
|
||||
}
|
||||
|
||||
this->info_list[strdup(ai_name)] = info;
|
||||
}
|
||||
|
||||
void AIScanner::UnregisterAI(AIInfo *info)
|
||||
{
|
||||
this->info_list.erase(info->GetDirName());
|
||||
}
|
||||
|
||||
AIInfo *AIScanner::SelectRandomAI()
|
||||
{
|
||||
if (this->info_list.size() == 0) {
|
||||
DEBUG(ai, 0, "No suitable AI found, loading 'dummy' AI.");
|
||||
return this->info_dummy;
|
||||
}
|
||||
|
||||
/* Find a random AI */
|
||||
uint pos;
|
||||
if (_networking) pos = InteractiveRandomRange((uint16)this->info_list.size());
|
||||
else pos = RandomRange((uint16)this->info_list.size());
|
||||
|
||||
/* Find the Nth item from the array */
|
||||
AIInfoList::iterator it = this->info_list.begin();
|
||||
for (; pos > 0; pos--) it++;
|
||||
AIInfoList::iterator first_it = it;
|
||||
AIInfo *i = (*it).second;
|
||||
|
||||
if (!i->AllowStartup()) {
|
||||
/* We can't start this AI, try to find the next best */
|
||||
do {
|
||||
it++;
|
||||
if (it == this->info_list.end()) it = this->info_list.begin();
|
||||
/* Back at the beginning? We can't start an AI. */
|
||||
if (first_it == it) {
|
||||
DEBUG(ai, 0, "No suitable AI found, loading 'dummy' AI.");
|
||||
return this->info_dummy;
|
||||
}
|
||||
|
||||
i = (*it).second;
|
||||
} while (!i->AllowStartup());
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
AIInfo *AIScanner::FindAI(const char *name)
|
||||
{
|
||||
if (this->info_list.size() == 0) return NULL;
|
||||
if (name == NULL) return NULL;
|
||||
|
||||
AIInfoList::iterator it = this->info_list.begin();
|
||||
for (; it != this->info_list.end(); it++) {
|
||||
AIInfo *i = (*it).second;
|
||||
|
||||
if (strcasecmp(name, (*it).first) == 0 && i->AllowStartup()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *AIScanner::GetAIConsoleList(char *p, const char *last)
|
||||
{
|
||||
p += seprintf(p, last, "List of AIs:\n");
|
||||
AIInfoList::iterator it = this->info_list.begin();
|
||||
for (; it != this->info_list.end(); it++) {
|
||||
AIInfo *i = (*it).second;
|
||||
if (!i->AllowStartup()) continue;
|
||||
p += seprintf(p, last, "%10s: %s\n", (*it).first, i->GetDescription());
|
||||
}
|
||||
p += seprintf(p, last, "\n");
|
||||
|
||||
return p;
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_scanner.hpp declarations of the class for AI scanner */
|
||||
|
||||
#ifndef AI_SCANNER_HPP
|
||||
#define AI_SCANNER_HPP
|
||||
|
||||
#include <map>
|
||||
|
||||
class AIScanner {
|
||||
public:
|
||||
AIScanner();
|
||||
~AIScanner();
|
||||
|
||||
/**
|
||||
* Import a library inside the Squirrel VM.
|
||||
*/
|
||||
bool ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm, class AIController *controller);
|
||||
|
||||
/**
|
||||
* Register a library to be put in the available list.
|
||||
*/
|
||||
void RegisterLibrary(class AILibrary *library);
|
||||
|
||||
/**
|
||||
* Register an AI to be put in the available list.
|
||||
*/
|
||||
void RegisterAI(class AIInfo *info);
|
||||
|
||||
void SetDummyAI(class AIInfo *info) { this->info_dummy = info; }
|
||||
|
||||
/**
|
||||
* Remove an AI from the available list.
|
||||
*/
|
||||
void UnregisterAI(class AIInfo *info);
|
||||
|
||||
/**
|
||||
* Select a Random AI.
|
||||
*/
|
||||
class AIInfo *SelectRandomAI();
|
||||
|
||||
/**
|
||||
* Find an AI by name.
|
||||
*/
|
||||
class AIInfo *FindAI(const char *name);
|
||||
|
||||
/**
|
||||
* Get the list of available AIs for the console.
|
||||
*/
|
||||
char *GetAIConsoleList(char *p, const char *last);
|
||||
|
||||
/**
|
||||
* Get the list of all registered AIs.
|
||||
*/
|
||||
const AIInfoList *GetAIInfoList() { return &this->info_list; }
|
||||
|
||||
/**
|
||||
* Get the engine of the main squirrel handler (it indexes all avialable squirrels).
|
||||
*/
|
||||
class Squirrel *GetEngine() { return this->engine; }
|
||||
|
||||
/**
|
||||
* Get the current script the ScanDir is looking at.
|
||||
*/
|
||||
const char *GetCurrentScript() { return this->current_script; }
|
||||
|
||||
/**
|
||||
* Get the directory of the current script the ScanDir is looking at.
|
||||
*/
|
||||
const char *GetCurrentDirName() { return this->current_dir; }
|
||||
|
||||
/**
|
||||
* Rescan the AI dir for scripts.
|
||||
*/
|
||||
void RescanAIDir();
|
||||
|
||||
private:
|
||||
typedef std::map<const char *, class AILibrary *, ltstr> AILibraryList;
|
||||
|
||||
/**
|
||||
* Scan the AI dir for scripts.
|
||||
*/
|
||||
void ScanAIDir();
|
||||
|
||||
/**
|
||||
* Scan a dir for AIs.
|
||||
* For non-library-scan, if an AI is found, AIInfo is created, and the AI
|
||||
* is registered to the central system.
|
||||
* For library-scan, if a library is found, AILibrary is created, and the
|
||||
* library is registered to the central system.
|
||||
*/
|
||||
void ScanDir(const char *dirname, bool library_dir, char *library_depth = NULL);
|
||||
|
||||
AIInfoList info_list;
|
||||
AIInfo *info_dummy;
|
||||
AILibraryList library_list;
|
||||
class Squirrel *engine;
|
||||
char *current_script;
|
||||
char *current_dir;
|
||||
};
|
||||
|
||||
#endif /* AI_SCANNER_HPP */
|
@ -0,0 +1,78 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_storage.hpp Defines AIStorage and includes all files required for it. */
|
||||
|
||||
#ifndef AI_STORAGE_HPP
|
||||
#define AI_STORAGE_HPP
|
||||
|
||||
#include "../command_func.h"
|
||||
#include "../map_func.h"
|
||||
#include "../network/network.h"
|
||||
#include "../company_func.h"
|
||||
#include "../signs_func.h"
|
||||
#include "../tunnelbridge.h"
|
||||
#include "../vehicle_func.h"
|
||||
#include "../group.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* The callback function for Mode-classes.
|
||||
*/
|
||||
typedef bool (AIModeProc)(TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCost costs);
|
||||
|
||||
/**
|
||||
* The storage for each AI. It keeps track of important information.
|
||||
*/
|
||||
class AIStorage {
|
||||
friend class AIObject;
|
||||
private:
|
||||
AIModeProc *mode; //!< The current build mode we are int.
|
||||
class AIObject *mode_instance; //!< The instance belonging to the current build mode.
|
||||
|
||||
uint delay; //!< The ticks of delay each DoCommand has.
|
||||
bool allow_do_command; //!< Is the usage of DoCommands restricted?
|
||||
|
||||
CommandCost costs; //!< The costs the AI is tracking.
|
||||
Money last_cost; //!< The last cost of the command.
|
||||
uint last_error; //!< The last error of the command.
|
||||
bool last_command_res; //!< The last result of the command.
|
||||
|
||||
VehicleID new_vehicle_id; //!< The ID of the new Vehicle.
|
||||
SignID new_sign_id; //!< The ID of the new Sign.
|
||||
TileIndex new_tunnel_endtile; //!< The TileIndex of the new Tunnel.
|
||||
GroupID new_group_id; //!< The ID of the new Group.
|
||||
|
||||
std::vector<int> callback_value; //!< The values which need to survive a callback.
|
||||
|
||||
RoadType road_type; //!< The current roadtype we build.
|
||||
RailType rail_type; //!< The current railtype we build.
|
||||
|
||||
void *event_data; //!< Pointer to the event data storage.
|
||||
void *log_data; //!< Pointer to the log data storage.
|
||||
|
||||
public:
|
||||
AIStorage() :
|
||||
mode (NULL),
|
||||
mode_instance (NULL),
|
||||
delay (1),
|
||||
allow_do_command (true),
|
||||
/* costs (can't be set) */
|
||||
last_cost (0),
|
||||
last_error (STR_NULL),
|
||||
last_command_res (true),
|
||||
new_vehicle_id (0),
|
||||
new_sign_id (0),
|
||||
new_tunnel_endtile(INVALID_TILE),
|
||||
new_group_id (0),
|
||||
/* calback_value (can't be set) */
|
||||
road_type (INVALID_ROADTYPE),
|
||||
rail_type (INVALID_RAILTYPE),
|
||||
event_data (NULL),
|
||||
log_data (NULL)
|
||||
{ }
|
||||
|
||||
~AIStorage();
|
||||
};
|
||||
|
||||
#endif /* AI_STORAGE_HPP */
|
@ -0,0 +1,244 @@
|
||||
# Doxyfile 1.5.4
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = "OpenTTD NoAI API "
|
||||
PROJECT_NUMBER =
|
||||
OUTPUT_DIRECTORY = ../../../docs/aidocs/
|
||||
CREATE_SUBDIRS = NO
|
||||
OUTPUT_LANGUAGE = English
|
||||
BRIEF_MEMBER_DESC = YES
|
||||
REPEAT_BRIEF = YES
|
||||
ABBREVIATE_BRIEF = "The $name class " \
|
||||
"The $name widget " \
|
||||
"The $name file " \
|
||||
is \
|
||||
provides \
|
||||
specifies \
|
||||
contains \
|
||||
represents \
|
||||
a \
|
||||
an \
|
||||
the
|
||||
ALWAYS_DETAILED_SEC = NO
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
FULL_PATH_NAMES = YES
|
||||
STRIP_FROM_PATH = ./
|
||||
STRIP_FROM_INC_PATH =
|
||||
SHORT_NAMES = NO
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
QT_AUTOBRIEF = NO
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
DETAILS_AT_TOP = NO
|
||||
INHERIT_DOCS = YES
|
||||
SEPARATE_MEMBER_PAGES = NO
|
||||
TAB_SIZE = 2
|
||||
ALIASES =
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
CPP_CLI_SUPPORT = NO
|
||||
SIP_SUPPORT = NO
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
SUBGROUPING = YES
|
||||
TYPEDEF_HIDES_STRUCT = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
EXTRACT_ALL = NO
|
||||
EXTRACT_PRIVATE = NO
|
||||
EXTRACT_STATIC = YES
|
||||
EXTRACT_LOCAL_CLASSES = YES
|
||||
EXTRACT_LOCAL_METHODS = YES
|
||||
EXTRACT_ANON_NSPACES = NO
|
||||
HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
HIDE_IN_BODY_DOCS = YES
|
||||
INTERNAL_DOCS = YES
|
||||
CASE_SENSE_NAMES = YES
|
||||
HIDE_SCOPE_NAMES = NO
|
||||
SHOW_INCLUDE_FILES = NO
|
||||
INLINE_INFO = YES
|
||||
SORT_MEMBER_DOCS = YES
|
||||
SORT_BRIEF_DOCS = NO
|
||||
SORT_BY_SCOPE_NAME = NO
|
||||
GENERATE_TODOLIST = NO
|
||||
GENERATE_TESTLIST = NO
|
||||
GENERATE_BUGLIST = NO
|
||||
GENERATE_DEPRECATEDLIST= NO
|
||||
ENABLED_SECTIONS =
|
||||
MAX_INITIALIZER_LINES = 30
|
||||
SHOW_USED_FILES = NO
|
||||
SHOW_DIRECTORIES = NO
|
||||
FILE_VERSION_FILTER =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to warning and progress messages
|
||||
#---------------------------------------------------------------------------
|
||||
QUIET = NO
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
WARN_NO_PARAMDOC = YES
|
||||
WARN_FORMAT = "$file:$line: $text "
|
||||
WARN_LOGFILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the input files
|
||||
#---------------------------------------------------------------------------
|
||||
INPUT = .
|
||||
INPUT_ENCODING = UTF-8
|
||||
FILE_PATTERNS = *.h \
|
||||
*.hpp
|
||||
RECURSIVE = YES
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
EXCLUDE_PATTERNS =
|
||||
EXCLUDE_SYMBOLS = GetClassName DECLARE_ENUM_AS_BIT_SET DECLARE_POSTFIX_INCREMENT
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATTERNS = *
|
||||
EXAMPLE_RECURSIVE = NO
|
||||
IMAGE_PATH =
|
||||
INPUT_FILTER =
|
||||
FILTER_PATTERNS =
|
||||
FILTER_SOURCE_FILES = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to source browsing
|
||||
#---------------------------------------------------------------------------
|
||||
SOURCE_BROWSER = NO
|
||||
INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
REFERENCED_BY_RELATION = NO
|
||||
REFERENCES_RELATION = NO
|
||||
REFERENCES_LINK_SOURCE = YES
|
||||
USE_HTAGS = NO
|
||||
VERBATIM_HEADERS = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
ALPHABETICAL_INDEX = NO
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
IGNORE_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the HTML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_HTML = YES
|
||||
HTML_OUTPUT = html
|
||||
HTML_FILE_EXTENSION = .html
|
||||
HTML_HEADER =
|
||||
HTML_FOOTER =
|
||||
HTML_STYLESHEET =
|
||||
HTML_ALIGN_MEMBERS = YES
|
||||
GENERATE_HTMLHELP = NO
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
GENERATE_CHI = NO
|
||||
BINARY_TOC = NO
|
||||
TOC_EXPAND = NO
|
||||
DISABLE_INDEX = NO
|
||||
ENUM_VALUES_PER_LINE = 1
|
||||
GENERATE_TREEVIEW = NO
|
||||
TREEVIEW_WIDTH = 250
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the LaTeX output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_LATEX = NO
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_CMD_NAME = latex
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
COMPACT_LATEX = NO
|
||||
PAPER_TYPE = a4wide
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
PDF_HYPERLINKS = NO
|
||||
USE_PDFLATEX = NO
|
||||
LATEX_BATCHMODE = NO
|
||||
LATEX_HIDE_INDICES = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_RTF = NO
|
||||
RTF_OUTPUT = rtf
|
||||
COMPACT_RTF = NO
|
||||
RTF_HYPERLINKS = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_MAN = NO
|
||||
MAN_OUTPUT = man
|
||||
MAN_EXTENSION = .3
|
||||
MAN_LINKS = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the XML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_XML = NO
|
||||
XML_OUTPUT = xml
|
||||
XML_SCHEMA =
|
||||
XML_DTD =
|
||||
XML_PROGRAMLISTING = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the Perl module output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_PERLMOD = NO
|
||||
PERLMOD_LATEX = NO
|
||||
PERLMOD_PRETTY = YES
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
#---------------------------------------------------------------------------
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
SEARCH_INCLUDES = YES
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
PREDEFINED = DOXYGEN_SKIP
|
||||
EXPAND_AS_DEFINED = DEF_COMMAND
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::additions related to external references
|
||||
#---------------------------------------------------------------------------
|
||||
TAGFILES =
|
||||
GENERATE_TAGFILE = openttd.tag
|
||||
ALLEXTERNALS = NO
|
||||
EXTERNAL_GROUPS = YES
|
||||
PERL_PATH = /usr/bin/perl
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
CLASS_DIAGRAMS = YES
|
||||
MSCGEN_PATH =
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HAVE_DOT = NO
|
||||
CLASS_GRAPH = YES
|
||||
COLLABORATION_GRAPH = YES
|
||||
GROUP_GRAPHS = YES
|
||||
UML_LOOK = NO
|
||||
TEMPLATE_RELATIONS = NO
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
CALL_GRAPH = NO
|
||||
CALLER_GRAPH = NO
|
||||
GRAPHICAL_HIERARCHY = YES
|
||||
DIRECTORY_GRAPH = YES
|
||||
DOT_IMAGE_FORMAT = png
|
||||
DOT_PATH =
|
||||
DOTFILE_DIRS =
|
||||
DOT_GRAPH_MAX_NODES = 50
|
||||
MAX_DOT_GRAPH_DEPTH = 1000
|
||||
DOT_TRANSPARENT = NO
|
||||
DOT_MULTI_TARGETS = NO
|
||||
GENERATE_LEGEND = NO
|
||||
DOT_CLEANUP = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::additions related to the search engine
|
||||
#---------------------------------------------------------------------------
|
||||
SEARCHENGINE = NO
|
@ -0,0 +1,818 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_abstractlist.cpp Implementation of AIAbstractList. */
|
||||
|
||||
#include <squirrel.h>
|
||||
#include "ai_abstractlist.hpp"
|
||||
#include "../../debug.h"
|
||||
#include "../../core/alloc_func.hpp"
|
||||
|
||||
/**
|
||||
* Base class for any AIAbstractList sorter.
|
||||
*/
|
||||
class AIAbstractListSorter {
|
||||
protected:
|
||||
AIAbstractList *list;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Virtual dtor, needed to mute warnings.
|
||||
*/
|
||||
virtual ~AIAbstractListSorter() { }
|
||||
|
||||
/**
|
||||
* Get the first item of the sorter.
|
||||
*/
|
||||
virtual int32 Begin() = 0;
|
||||
|
||||
/**
|
||||
* Stop iterating a sorter.
|
||||
*/
|
||||
virtual void End() = 0;
|
||||
|
||||
/**
|
||||
* Get the next item of the sorter.
|
||||
*/
|
||||
virtual int32 Next() = 0;
|
||||
|
||||
/**
|
||||
* See if there is a next item of the sorter.
|
||||
*/
|
||||
virtual bool HasNext() = 0;
|
||||
|
||||
/**
|
||||
* Callback from the list if an item gets removed.
|
||||
*/
|
||||
virtual void Remove(int item) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort by value, ascending.
|
||||
*/
|
||||
class AIAbstractListSorterValueAscending : public AIAbstractListSorter {
|
||||
private:
|
||||
AIAbstractList::AIAbstractListBucket::iterator bucket_iter;
|
||||
AIAbstractList::AIItemList *bucket_list;
|
||||
AIAbstractList::AIItemList::iterator bucket_list_iter;
|
||||
bool has_no_more_items;
|
||||
int32 item_next;
|
||||
|
||||
public:
|
||||
AIAbstractListSorterValueAscending(AIAbstractList *list)
|
||||
{
|
||||
this->list = list;
|
||||
this->End();
|
||||
}
|
||||
|
||||
int32 Begin()
|
||||
{
|
||||
if (this->list->buckets.empty()) return 0;
|
||||
this->has_no_more_items = false;
|
||||
|
||||
this->bucket_iter = this->list->buckets.begin();
|
||||
this->bucket_list = &(*this->bucket_iter).second;
|
||||
this->bucket_list_iter = this->bucket_list->begin();
|
||||
this->item_next = *this->bucket_list_iter;
|
||||
|
||||
int32 item_current = this->item_next;
|
||||
FindNext();
|
||||
return item_current;
|
||||
}
|
||||
|
||||
void End()
|
||||
{
|
||||
this->bucket_list = NULL;
|
||||
this->has_no_more_items = true;
|
||||
this->item_next = 0;
|
||||
}
|
||||
|
||||
void FindNext()
|
||||
{
|
||||
if (this->bucket_list == NULL) {
|
||||
this->has_no_more_items = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this->bucket_list_iter++;
|
||||
if (this->bucket_list_iter == this->bucket_list->end()) {
|
||||
this->bucket_iter++;
|
||||
if (this->bucket_iter == this->list->buckets.end()) {
|
||||
this->bucket_list = NULL;
|
||||
return;
|
||||
}
|
||||
this->bucket_list = &(*this->bucket_iter).second;
|
||||
this->bucket_list_iter = this->bucket_list->begin();
|
||||
}
|
||||
this->item_next = *this->bucket_list_iter;
|
||||
}
|
||||
|
||||
int32 Next()
|
||||
{
|
||||
if (!this->HasNext()) return 0;
|
||||
|
||||
int32 item_current = this->item_next;
|
||||
FindNext();
|
||||
return item_current;
|
||||
}
|
||||
|
||||
void Remove(int item)
|
||||
{
|
||||
if (!this->HasNext()) return;
|
||||
|
||||
/* If we remove the 'next' item, skip to the next */
|
||||
if (item == this->item_next) {
|
||||
FindNext();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasNext()
|
||||
{
|
||||
return !(this->list->buckets.empty() || this->has_no_more_items);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort by value, descending.
|
||||
*/
|
||||
class AIAbstractListSorterValueDescending : public AIAbstractListSorter {
|
||||
private:
|
||||
AIAbstractList::AIAbstractListBucket::iterator bucket_iter;
|
||||
AIAbstractList::AIItemList *bucket_list;
|
||||
AIAbstractList::AIItemList::iterator bucket_list_iter;
|
||||
bool has_no_more_items;
|
||||
int32 item_next;
|
||||
|
||||
public:
|
||||
AIAbstractListSorterValueDescending(AIAbstractList *list)
|
||||
{
|
||||
this->list = list;
|
||||
this->End();
|
||||
}
|
||||
|
||||
int32 Begin()
|
||||
{
|
||||
if (this->list->buckets.empty()) return 0;
|
||||
this->has_no_more_items = false;
|
||||
|
||||
/* Go to the end of the bucket-list */
|
||||
this->bucket_iter = this->list->buckets.begin();
|
||||
for (size_t i = this->list->buckets.size(); i > 1; i--) this->bucket_iter++;
|
||||
this->bucket_list = &(*this->bucket_iter).second;
|
||||
|
||||
/* Go to the end of the items in the bucket */
|
||||
this->bucket_list_iter = this->bucket_list->begin();
|
||||
for (size_t i = this->bucket_list->size(); i > 1; i--) this->bucket_list_iter++;
|
||||
this->item_next = *this->bucket_list_iter;
|
||||
|
||||
int32 item_current = this->item_next;
|
||||
FindNext();
|
||||
return item_current;
|
||||
}
|
||||
|
||||
void End() {
|
||||
this->bucket_list = NULL;
|
||||
this->has_no_more_items = true;
|
||||
this->item_next = 0;
|
||||
}
|
||||
|
||||
void FindNext()
|
||||
{
|
||||
if (this->bucket_list == NULL) {
|
||||
this->has_no_more_items = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->bucket_list_iter == this->bucket_list->begin()) {
|
||||
if (this->bucket_iter == this->list->buckets.begin()) {
|
||||
this->bucket_list = NULL;
|
||||
return;
|
||||
}
|
||||
this->bucket_iter--;
|
||||
this->bucket_list = &(*this->bucket_iter).second;
|
||||
/* Go to the end of the items in the bucket */
|
||||
this->bucket_list_iter = this->bucket_list->begin();
|
||||
for (size_t i = this->bucket_list->size(); i > 1; i--) this->bucket_list_iter++;
|
||||
} else {
|
||||
this->bucket_list_iter--;
|
||||
}
|
||||
this->item_next = *this->bucket_list_iter;
|
||||
}
|
||||
|
||||
int32 Next()
|
||||
{
|
||||
if (!this->HasNext()) return 0;
|
||||
|
||||
int32 item_current = this->item_next;
|
||||
FindNext();
|
||||
return item_current;
|
||||
}
|
||||
|
||||
void Remove(int item)
|
||||
{
|
||||
if (!this->HasNext()) return;
|
||||
|
||||
/* If we remove the 'next' item, skip to the next */
|
||||
if (item == this->item_next) {
|
||||
FindNext();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasNext()
|
||||
{
|
||||
return !(this->list->buckets.empty() || this->has_no_more_items);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort by item, ascending.
|
||||
*/
|
||||
class AIAbstractListSorterItemAscending : public AIAbstractListSorter {
|
||||
private:
|
||||
AIAbstractList::AIAbstractListMap::iterator item_iter;
|
||||
bool has_no_more_items;
|
||||
int32 item_next;
|
||||
|
||||
public:
|
||||
AIAbstractListSorterItemAscending(AIAbstractList *list)
|
||||
{
|
||||
this->list = list;
|
||||
this->End();
|
||||
}
|
||||
|
||||
int32 Begin()
|
||||
{
|
||||
if (this->list->items.empty()) return 0;
|
||||
this->has_no_more_items = false;
|
||||
|
||||
this->item_iter = this->list->items.begin();
|
||||
this->item_next = (*this->item_iter).first;
|
||||
|
||||
int32 item_current = this->item_next;
|
||||
FindNext();
|
||||
return item_current;
|
||||
}
|
||||
|
||||
void End()
|
||||
{
|
||||
this->has_no_more_items = true;
|
||||
}
|
||||
|
||||
void FindNext()
|
||||
{
|
||||
if (this->item_iter == this->list->items.end()) {
|
||||
this->has_no_more_items = true;
|
||||
return;
|
||||
}
|
||||
this->item_iter++;
|
||||
if (this->item_iter != this->list->items.end()) item_next = (*this->item_iter).first;
|
||||
}
|
||||
|
||||
int32 Next()
|
||||
{
|
||||
if (!this->HasNext()) return 0;
|
||||
|
||||
int32 item_current = this->item_next;
|
||||
FindNext();
|
||||
return item_current;
|
||||
}
|
||||
|
||||
void Remove(int item) {
|
||||
if (!this->HasNext()) return;
|
||||
|
||||
/* If we remove the 'next' item, skip to the next */
|
||||
if (item == this->item_next) {
|
||||
FindNext();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasNext()
|
||||
{
|
||||
return !(this->list->items.empty() || this->has_no_more_items);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort by item, descending.
|
||||
*/
|
||||
class AIAbstractListSorterItemDescending : public AIAbstractListSorter {
|
||||
private:
|
||||
AIAbstractList::AIAbstractListMap::iterator item_iter;
|
||||
bool has_no_more_items;
|
||||
int32 item_next;
|
||||
|
||||
public:
|
||||
AIAbstractListSorterItemDescending(AIAbstractList *list)
|
||||
{
|
||||
this->list = list;
|
||||
this->End();
|
||||
}
|
||||
|
||||
int32 Begin()
|
||||
{
|
||||
if (this->list->items.empty()) return 0;
|
||||
this->has_no_more_items = false;
|
||||
|
||||
this->item_iter = this->list->items.begin();
|
||||
for (size_t i = this->list->items.size(); i > 1; i--) this->item_iter++;
|
||||
this->item_next = (*this->item_iter).first;
|
||||
|
||||
int32 item_current = this->item_next;
|
||||
FindNext();
|
||||
return item_current;
|
||||
}
|
||||
|
||||
void End()
|
||||
{
|
||||
this->has_no_more_items = true;
|
||||
}
|
||||
|
||||
void FindNext()
|
||||
{
|
||||
if (this->item_iter == this->list->items.end()) {
|
||||
this->has_no_more_items = true;
|
||||
return;
|
||||
}
|
||||
this->item_iter--;
|
||||
if (this->item_iter != this->list->items.end()) item_next = (*this->item_iter).first;
|
||||
}
|
||||
|
||||
int32 Next()
|
||||
{
|
||||
if (!this->HasNext()) return 0;
|
||||
|
||||
int32 item_current = this->item_next;
|
||||
FindNext();
|
||||
return item_current;
|
||||
}
|
||||
|
||||
void Remove(int item)
|
||||
{
|
||||
if (!this->HasNext()) return;
|
||||
|
||||
/* If we remove the 'next' item, skip to the next */
|
||||
if (item == this->item_next) {
|
||||
FindNext();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasNext()
|
||||
{
|
||||
return !(this->list->items.empty() || this->has_no_more_items);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
AIAbstractList::AIAbstractList()
|
||||
{
|
||||
/* Default sorter */
|
||||
this->sorter = new AIAbstractListSorterValueDescending(this);
|
||||
this->sorter_type = SORT_BY_VALUE;
|
||||
this->sort_ascending = false;
|
||||
this->initialized = false;
|
||||
}
|
||||
|
||||
AIAbstractList::~AIAbstractList()
|
||||
{
|
||||
delete this->sorter;
|
||||
}
|
||||
|
||||
bool AIAbstractList::HasItem(int32 item)
|
||||
{
|
||||
return this->items.count(item) == 1;
|
||||
}
|
||||
|
||||
void AIAbstractList::Clear()
|
||||
{
|
||||
this->items.clear();
|
||||
this->buckets.clear();
|
||||
this->sorter->End();
|
||||
}
|
||||
|
||||
void AIAbstractList::AddItem(int32 item)
|
||||
{
|
||||
if (this->HasItem(item)) return;
|
||||
|
||||
this->items[item] = 0;
|
||||
this->buckets[0].insert(item);
|
||||
}
|
||||
|
||||
void AIAbstractList::RemoveItem(int32 item)
|
||||
{
|
||||
if (!this->HasItem(item)) return;
|
||||
|
||||
int32 value = this->GetValue(item);
|
||||
|
||||
this->sorter->Remove(item);
|
||||
this->buckets[value].erase(item);
|
||||
if (this->buckets[value].empty()) this->buckets.erase(value);
|
||||
this->items.erase(item);
|
||||
}
|
||||
|
||||
int32 AIAbstractList::Begin()
|
||||
{
|
||||
this->initialized = true;
|
||||
return this->sorter->Begin();
|
||||
}
|
||||
|
||||
int32 AIAbstractList::Next()
|
||||
{
|
||||
if (this->initialized == false) {
|
||||
DEBUG(ai, 0, "ERROR: Next() is invalid as Begin() is never called");
|
||||
return false;
|
||||
}
|
||||
return this->sorter->Next();
|
||||
}
|
||||
|
||||
bool AIAbstractList::IsEmpty()
|
||||
{
|
||||
return this->items.empty();
|
||||
}
|
||||
|
||||
bool AIAbstractList::HasNext()
|
||||
{
|
||||
if (this->initialized == false) {
|
||||
DEBUG(ai, 0, "ERROR: HasNext() is invalid as Begin() is never called");
|
||||
return false;
|
||||
}
|
||||
return this->sorter->HasNext();
|
||||
}
|
||||
|
||||
int32 AIAbstractList::Count()
|
||||
{
|
||||
return (int32)this->items.size();
|
||||
}
|
||||
|
||||
int32 AIAbstractList::GetValue(int32 item)
|
||||
{
|
||||
if (!this->HasItem(item)) return 0;
|
||||
|
||||
return this->items[item];
|
||||
}
|
||||
|
||||
bool AIAbstractList::SetValue(int32 item, int32 value)
|
||||
{
|
||||
if (!this->HasItem(item)) return false;
|
||||
|
||||
int32 value_old = this->GetValue(item);
|
||||
|
||||
this->sorter->Remove(item);
|
||||
this->buckets[value_old].erase(item);
|
||||
if (this->buckets[value_old].empty()) this->buckets.erase(value_old);
|
||||
this->items[item] = value;
|
||||
this->buckets[value].insert(item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AIAbstractList::Sort(SorterType sorter, bool ascending)
|
||||
{
|
||||
if (sorter != SORT_BY_VALUE && sorter != SORT_BY_ITEM) return;
|
||||
if (sorter == this->sorter_type && ascending == this->sort_ascending) return;
|
||||
|
||||
delete this->sorter;
|
||||
switch (sorter) {
|
||||
case SORT_BY_ITEM:
|
||||
if (ascending) this->sorter = new AIAbstractListSorterItemAscending(this);
|
||||
else this->sorter = new AIAbstractListSorterItemDescending(this);
|
||||
break;
|
||||
|
||||
case SORT_BY_VALUE:
|
||||
if (ascending) this->sorter = new AIAbstractListSorterValueAscending(this);
|
||||
else this->sorter = new AIAbstractListSorterValueDescending(this);
|
||||
break;
|
||||
|
||||
default:
|
||||
this->Sort(SORT_BY_ITEM, false);
|
||||
return;
|
||||
}
|
||||
this->sorter_type = sorter;
|
||||
this->sort_ascending = ascending;
|
||||
}
|
||||
|
||||
void AIAbstractList::AddList(AIAbstractList *list)
|
||||
{
|
||||
AIAbstractListMap *list_items = &list->items;
|
||||
for (AIAbstractListMap::iterator iter = list_items->begin(); iter != list_items->end(); iter++) {
|
||||
this->AddItem((*iter).first);
|
||||
this->SetValue((*iter).first, (*iter).second);
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::RemoveAboveValue(int32 value)
|
||||
{
|
||||
for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).second > value) this->items.erase(iter);
|
||||
}
|
||||
|
||||
for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).first > value) this->buckets.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::RemoveBelowValue(int32 value)
|
||||
{
|
||||
for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).second < value) this->items.erase(iter);
|
||||
}
|
||||
|
||||
for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).first < value) this->buckets.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::RemoveBetweenValue(int32 start, int32 end)
|
||||
{
|
||||
for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).second > start && (*iter).second < end) this->items.erase(iter);
|
||||
}
|
||||
|
||||
for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).first > start && (*iter).first < end) this->buckets.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::RemoveValue(int32 value)
|
||||
{
|
||||
for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).second == value) this->items.erase(iter);
|
||||
}
|
||||
|
||||
for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).first == value) this->buckets.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::RemoveTop(int32 count)
|
||||
{
|
||||
if (!this->sort_ascending) {
|
||||
this->Sort(this->sorter_type, !this->sort_ascending);
|
||||
this->RemoveBottom(count);
|
||||
this->Sort(this->sorter_type, !this->sort_ascending);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this->sorter_type) {
|
||||
default: NOT_REACHED();
|
||||
case SORT_BY_VALUE:
|
||||
for (AIAbstractListBucket::iterator iter = this->buckets.begin(); iter != this->buckets.end(); iter = this->buckets.begin()) {
|
||||
AIItemList *items = &(*iter).second;
|
||||
size_t size = items->size();
|
||||
for (AIItemList::iterator iter = items->begin(); iter != items->end(); iter = items->begin()) {
|
||||
if (--count < 0) return;
|
||||
this->RemoveItem(*iter);
|
||||
/* When the last item is removed from the bucket, the bucket itself is removed.
|
||||
* This means that the iterators can be invalid after a call to RemoveItem.
|
||||
*/
|
||||
if (--size == 0) break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SORT_BY_ITEM:
|
||||
for (AIAbstractListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter = this->items.begin()) {
|
||||
if (--count < 0) return;
|
||||
this->RemoveItem((*iter).first);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::RemoveBottom(int32 count)
|
||||
{
|
||||
if (!this->sort_ascending) {
|
||||
this->Sort(this->sorter_type, !this->sort_ascending);
|
||||
this->RemoveTop(count);
|
||||
this->Sort(this->sorter_type, !this->sort_ascending);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this->sorter_type) {
|
||||
default: NOT_REACHED();
|
||||
case SORT_BY_VALUE:
|
||||
for (AIAbstractListBucket::reverse_iterator iter = this->buckets.rbegin(); iter != this->buckets.rend(); iter = this->buckets.rbegin()) {
|
||||
AIItemList *items = &(*iter).second;
|
||||
size_t size = items->size();
|
||||
for (AIItemList::reverse_iterator iter = items->rbegin(); iter != items->rend(); iter = items->rbegin()) {
|
||||
if (--count < 0) return;
|
||||
this->RemoveItem(*iter);
|
||||
/* When the last item is removed from the bucket, the bucket itself is removed.
|
||||
* This means that the iterators can be invalid after a call to RemoveItem.
|
||||
*/
|
||||
if (--size == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
case SORT_BY_ITEM:
|
||||
for (AIAbstractListMap::reverse_iterator iter = this->items.rbegin(); iter != this->items.rend(); iter = this->items.rbegin()) {
|
||||
if (--count < 0) return;
|
||||
this->RemoveItem((*iter).first);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::RemoveList(AIAbstractList *list)
|
||||
{
|
||||
AIAbstractListMap *list_items = &list->items;
|
||||
for (AIAbstractListMap::iterator iter = list_items->begin(); iter != list_items->end(); iter++) {
|
||||
this->RemoveItem((*iter).first);
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::KeepAboveValue(int32 value)
|
||||
{
|
||||
for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).second <= value) this->items.erase(iter);
|
||||
}
|
||||
|
||||
for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).first <= value) this->buckets.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::KeepBelowValue(int32 value)
|
||||
{
|
||||
for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).second >= value) this->items.erase(iter);
|
||||
}
|
||||
|
||||
for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).first >= value) this->buckets.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::KeepBetweenValue(int32 start, int32 end)
|
||||
{
|
||||
for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).second <= start || (*iter).second >= end) this->items.erase(iter);
|
||||
}
|
||||
|
||||
for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).first <= start || (*iter).first >= end) this->buckets.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::KeepValue(int32 value)
|
||||
{
|
||||
for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).second != value) this->items.erase(iter);
|
||||
}
|
||||
|
||||
for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
|
||||
next_iter = iter; next_iter++;
|
||||
if ((*iter).first != value) this->buckets.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void AIAbstractList::KeepTop(int32 count)
|
||||
{
|
||||
this->RemoveBottom(this->Count() - count);
|
||||
}
|
||||
|
||||
void AIAbstractList::KeepBottom(int32 count)
|
||||
{
|
||||
this->RemoveTop(this->Count() - count);
|
||||
}
|
||||
|
||||
void AIAbstractList::KeepList(AIAbstractList *list)
|
||||
{
|
||||
AIAbstractList tmp;
|
||||
for (AIAbstractListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter++) {
|
||||
tmp.AddItem((*iter).first);
|
||||
tmp.SetValue((*iter).first, (*iter).second);
|
||||
}
|
||||
|
||||
tmp.RemoveList(list);
|
||||
this->RemoveList(&tmp);
|
||||
}
|
||||
|
||||
SQInteger AIAbstractList::_get(HSQUIRRELVM vm) {
|
||||
if (sq_gettype(vm, 2) != OT_INTEGER) return SQ_ERROR;
|
||||
|
||||
SQInteger idx;
|
||||
sq_getinteger(vm, 2, &idx);
|
||||
|
||||
if (!this->HasItem(idx)) return SQ_ERROR;
|
||||
|
||||
sq_pushinteger(vm, this->GetValue(idx));
|
||||
return 1;
|
||||
}
|
||||
|
||||
SQInteger AIAbstractList::_nexti(HSQUIRRELVM vm) {
|
||||
if (sq_gettype(vm, 2) == OT_NULL) {
|
||||
if (this->IsEmpty()) {
|
||||
sq_pushnull(vm);
|
||||
return 1;
|
||||
}
|
||||
sq_pushinteger(vm, this->Begin());
|
||||
return 1;
|
||||
}
|
||||
|
||||
SQInteger idx;
|
||||
sq_getinteger(vm, 2, &idx);
|
||||
|
||||
int val = this->Next();
|
||||
if (!this->HasNext()) {
|
||||
sq_pushnull(vm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sq_pushinteger(vm, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SQInteger AIAbstractList::Valuate(HSQUIRRELVM vm) {
|
||||
int nparam = sq_gettop(vm) - 2;
|
||||
|
||||
/* Get the list instance and the function to call */
|
||||
HSQOBJECT obj_list, obj_func;
|
||||
sq_getstackobj(vm, 1, &obj_list);
|
||||
sq_getstackobj(vm, 2, &obj_func);
|
||||
|
||||
if (sq_isclass(obj_list)) {
|
||||
return sq_throwerror(vm, _SC("parameter 1 has an invalid type (expected instance)"));
|
||||
}
|
||||
if (sq_isfunction(obj_func)) {
|
||||
return sq_throwerror(vm, _SC("parameter 2 has an invalid type (expected function)"));
|
||||
}
|
||||
|
||||
sq_addref(vm, &obj_func);
|
||||
|
||||
/* Read the params */
|
||||
HSQOBJECT *obj_params = AllocaM(HSQOBJECT, nparam);
|
||||
for (int i = 0; i < nparam; i++) {
|
||||
sq_getstackobj(vm, i + 3, &obj_params[i]);
|
||||
sq_addref(vm, &obj_params[i]);
|
||||
}
|
||||
/* Remove all unneeded stuff */
|
||||
sq_pop(vm, nparam + 1);
|
||||
|
||||
/* Walk all items, and query the result */
|
||||
this->buckets.clear();
|
||||
for (AIAbstractListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter++) {
|
||||
/* The function to call */
|
||||
sq_pushobject(vm, obj_func);
|
||||
/* The 'list' instance; this is most likely wrong, but we need to send something ;) */
|
||||
sq_pushobject(vm, obj_list);
|
||||
|
||||
/* Now send the params */
|
||||
sq_pushinteger(vm, (*iter).first);
|
||||
for (int i = 0; i < nparam; i++) {
|
||||
sq_pushobject(vm, obj_params[i]);
|
||||
}
|
||||
|
||||
/* Call the function */
|
||||
if (SQ_FAILED(sq_call(vm, nparam + 2, SQTrue, SQTrue))) return SQ_ERROR;
|
||||
|
||||
/* Retreive the return value */
|
||||
SQInteger value;
|
||||
switch (sq_gettype(vm, -1)) {
|
||||
case OT_INTEGER: {
|
||||
sq_getinteger(vm, -1, &value);
|
||||
} break;
|
||||
|
||||
case OT_BOOL: {
|
||||
SQBool v;
|
||||
sq_getbool(vm, -1, &v);
|
||||
value = v ? 1 : 0;
|
||||
} break;
|
||||
|
||||
default: {
|
||||
sq_pop(vm, 3);
|
||||
sq_release(vm, &obj_func);
|
||||
for (int i = 0; i < nparam; i++) sq_release(vm, &obj_params[i]);
|
||||
|
||||
return sq_throwerror(vm, _SC("return value of valuator is not valid (not integer/bool)"));
|
||||
}
|
||||
}
|
||||
/* Remove junk */
|
||||
sq_pop(vm, 2);
|
||||
|
||||
(*iter).second = (int32)value;
|
||||
this->buckets[(int32)value].insert((*iter).first);
|
||||
}
|
||||
|
||||
sq_release(vm, &obj_func);
|
||||
for (int i = 0; i < nparam; i++) sq_release(vm, &obj_params[i]);
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,263 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_abstractlist.hpp A list which can keep item/value pairs, which you can walk. */
|
||||
/** @defgroup AIList Classes that create a list of items. */
|
||||
|
||||
#ifndef AI_ABSTRACTLIST_HPP
|
||||
#define AI_ABSTRACTLIST_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
class AIAbstractListSorter;
|
||||
|
||||
/**
|
||||
* Class that creates a list which can keep item/value pairs, which you can walk.
|
||||
*/
|
||||
class AIAbstractList : public AIObject {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIAbstractList"; }
|
||||
|
||||
/** Type of sorter */
|
||||
enum SorterType {
|
||||
SORT_BY_VALUE, ///< Sort the list based on the value of the item.
|
||||
SORT_BY_ITEM, ///< Sort the list based on the item itself.
|
||||
};
|
||||
|
||||
private:
|
||||
AIAbstractListSorter *sorter;
|
||||
SorterType sorter_type;
|
||||
bool sort_ascending;
|
||||
bool initialized;
|
||||
|
||||
public:
|
||||
typedef std::set<int32> AIItemList; //!< The list of items inside the bucket
|
||||
typedef std::map<int32, AIItemList> AIAbstractListBucket; //!< The bucket list per value
|
||||
typedef std::map<int32, int32> AIAbstractListMap; //!< List per item
|
||||
|
||||
AIAbstractListMap items; //!< The items in the list
|
||||
AIAbstractListBucket buckets; //!< The items in the list, sorted by value
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Add a single item to the list.
|
||||
* @param item the item to add. Should be unique, otherwise it is ignored.
|
||||
* @note the value is set to 0 by default.
|
||||
*/
|
||||
void AddItem(int32 item);
|
||||
|
||||
/**
|
||||
* Remove a single item from the list.
|
||||
* @param item the item to remove. If not existing, it is ignored.
|
||||
*/
|
||||
void RemoveItem(int32 item);
|
||||
|
||||
public:
|
||||
AIAbstractList();
|
||||
~AIAbstractList();
|
||||
|
||||
/**
|
||||
* Clear the list, making Count() returning 0 and IsEmpty() returning true.
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
/**
|
||||
* Check if an item is in the list.
|
||||
* @param item the item to check for.
|
||||
* @return true if the item is in the list.
|
||||
*/
|
||||
bool HasItem(int32 item);
|
||||
|
||||
/**
|
||||
* Go to the beginning of the list.
|
||||
* @return the item value of the first item.
|
||||
*/
|
||||
int32 Begin();
|
||||
|
||||
/**
|
||||
* Go to the next item in the list.
|
||||
* @return the item value of the next item.
|
||||
* @note returns 0 if beyond end-of-list. Use HasNext() to check for end-of-list.
|
||||
*/
|
||||
int32 Next();
|
||||
|
||||
/**
|
||||
* Check if a list is empty.
|
||||
* @return true if the list is empty.
|
||||
*/
|
||||
bool IsEmpty();
|
||||
|
||||
/**
|
||||
* Check if there is a next element. In other words, if this is true,
|
||||
* Next() will return a valid item.
|
||||
* @return true if there is a next item.
|
||||
*/
|
||||
bool HasNext();
|
||||
|
||||
/**
|
||||
* Returns the amount of items in the list.
|
||||
* @return amount of items in the list.
|
||||
*/
|
||||
int32 Count();
|
||||
|
||||
/**
|
||||
* Get the value that belongs to this item.
|
||||
* @param item the item to get the value from
|
||||
* @return the value that belongs to this item.
|
||||
*/
|
||||
int32 GetValue(int32 item);
|
||||
|
||||
/**
|
||||
* Set a value of an item directly.
|
||||
* @param item the item to set the value for.
|
||||
* @param value the value to give to the item
|
||||
* @return true if we could set the item to value, false otherwise.
|
||||
* @note Changing values of items while looping through a list might cause
|
||||
* entries to be skipped. Be very careful with such operations.
|
||||
*/
|
||||
bool SetValue(int32 item, int32 value);
|
||||
|
||||
/**
|
||||
* Sort this list by the given sorter and direction.
|
||||
* @param sorter the type of sorter to use
|
||||
* @param ascending if true, lowest value is on top, else at bottom.
|
||||
* @note the current item stays at the same place.
|
||||
*/
|
||||
void Sort(SorterType sorter, bool ascending);
|
||||
|
||||
/**
|
||||
* Add one list to an other one.
|
||||
* @param list The list that will be added to the caller.
|
||||
* @post The list to be added ('list') stays unmodified.
|
||||
* @note All added items keep their value as it was in 'list'.
|
||||
* @note If the item already exists inside the caller, the value of the
|
||||
* list that is added is set on the item.
|
||||
*/
|
||||
void AddList(AIAbstractList *list);
|
||||
|
||||
/**
|
||||
* Removes all items with a higher value than 'value'.
|
||||
* @param value the value above which all items are removed.
|
||||
*/
|
||||
void RemoveAboveValue(int32 value);
|
||||
|
||||
/**
|
||||
* Removes all items with a lower value than 'value'.
|
||||
* @param value the value below which all items are removed.
|
||||
*/
|
||||
void RemoveBelowValue(int32 value);
|
||||
|
||||
/**
|
||||
* Removes all items with a value above start and below end.
|
||||
* @param start the lower bound of the to be removed values (exclusive).
|
||||
* @param end the upper bound of the to be removed valuens (exclusive).
|
||||
*/
|
||||
void RemoveBetweenValue(int32 start, int32 end);
|
||||
|
||||
/**
|
||||
* Remove all items with this value.
|
||||
* @param value the value to remove.
|
||||
*/
|
||||
void RemoveValue(int32 value);
|
||||
|
||||
/**
|
||||
* Remove the first count items.
|
||||
* @param count the amount of items to remove.
|
||||
*/
|
||||
void RemoveTop(int32 count);
|
||||
|
||||
/**
|
||||
* Remove the last count items.
|
||||
* @param count the amount of items to remove.
|
||||
*/
|
||||
void RemoveBottom(int32 count);
|
||||
|
||||
/**
|
||||
* Remove everything that is in the given list from this list (same item index that is).
|
||||
* @param list the list of items to remove.
|
||||
* @pre list != NULL
|
||||
*/
|
||||
void RemoveList(AIAbstractList *list);
|
||||
|
||||
/**
|
||||
* Keep all items with a higher value than 'value'.
|
||||
* @param value the value above which all items are kept.
|
||||
*/
|
||||
void KeepAboveValue(int32 value);
|
||||
|
||||
/**
|
||||
* Keep all items with a lower value than 'value'.
|
||||
* @param value the value below which all items are kept.
|
||||
*/
|
||||
void KeepBelowValue(int32 value);
|
||||
|
||||
/**
|
||||
* Keep all items with a value above start and below end.
|
||||
* @param start the lower bound of the to be kept values (exclusive).
|
||||
* @param end the upper bound of the to be kept values (exclusive).
|
||||
*/
|
||||
void KeepBetweenValue(int32 start, int32 end);
|
||||
|
||||
/**
|
||||
* Keep all items with this value.
|
||||
* @param value the value to keep.
|
||||
**/
|
||||
void KeepValue(int32 value);
|
||||
|
||||
/**
|
||||
* Keep the first count items, i.e. remove everything except the first count items.
|
||||
* @param count the amount of items to keep.
|
||||
*/
|
||||
void KeepTop(int32 count);
|
||||
|
||||
/**
|
||||
* Keep the last count items, i.e. remove everything except the last count items.
|
||||
* @param count the amount of items to keep.
|
||||
*/
|
||||
void KeepBottom(int32 count);
|
||||
|
||||
/**
|
||||
* Keeps everything that is in the given list from this list (same item index that is).
|
||||
* @param list the list of items to keep.
|
||||
* @pre list != NULL
|
||||
*/
|
||||
void KeepList(AIAbstractList *list);
|
||||
|
||||
#ifndef DOXYGEN_SKIP
|
||||
/**
|
||||
* Used for 'foreach()' and [] get from Squirrel.
|
||||
*/
|
||||
SQInteger _get(HSQUIRRELVM vm);
|
||||
|
||||
/**
|
||||
* Used for 'foreach()' from Squirrel.
|
||||
*/
|
||||
SQInteger _nexti(HSQUIRRELVM vm);
|
||||
|
||||
/**
|
||||
* The Valuate() wrapper from Squirrel.
|
||||
*/
|
||||
SQInteger Valuate(HSQUIRRELVM vm);
|
||||
#else
|
||||
/**
|
||||
* Give all items a value defined by the valuator you give.
|
||||
* @param valuator_function The function which will be doing the valuation.
|
||||
* @param params The params to give to the valuators (minus the first param,
|
||||
* which is always the index-value we are valuating).
|
||||
* @note You can write your own valuators and use them. Just remember that
|
||||
* the first parameter should be the index-value, and it should return
|
||||
* an integer.
|
||||
* @note Example:
|
||||
* list.Valuate(AIBridge.GetPrice, 5);
|
||||
* list.Valuate(AIBridge.GetMaxLength);
|
||||
* function MyVal(bridge_id, myparam) {
|
||||
* return myparam * bridge_id; // This is silly
|
||||
* }
|
||||
* list.Valuate(MyVal, 12);
|
||||
*/
|
||||
void Valuate(void *valuator_function, int params, ...);
|
||||
#endif /* DOXYGEN_SKIP */
|
||||
};
|
||||
|
||||
#endif /* AI_LIST_HPP */
|
@ -0,0 +1,59 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_abstractlist.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow enums to be used as Squirrel parameters */
|
||||
template <> AIAbstractList::SorterType GetParam(ForceType<AIAbstractList::SorterType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIAbstractList::SorterType)tmp; }
|
||||
template <> int Return<AIAbstractList::SorterType>(HSQUIRRELVM vm, AIAbstractList::SorterType res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
|
||||
/* Allow AIAbstractList to be used as Squirrel parameter */
|
||||
template <> AIAbstractList *GetParam(ForceType<AIAbstractList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAbstractList *)instance; }
|
||||
template <> AIAbstractList &GetParam(ForceType<AIAbstractList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAbstractList *)instance; }
|
||||
template <> const AIAbstractList *GetParam(ForceType<const AIAbstractList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAbstractList *)instance; }
|
||||
template <> const AIAbstractList &GetParam(ForceType<const AIAbstractList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAbstractList *)instance; }
|
||||
template <> int Return<AIAbstractList *>(HSQUIRRELVM vm, AIAbstractList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIAbstractList", res, NULL, DefSQDestructorCallback<AIAbstractList>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIAbstractList_Register(Squirrel *engine) {
|
||||
DefSQClass <AIAbstractList> SQAIAbstractList("AIAbstractList");
|
||||
SQAIAbstractList.PreRegister(engine);
|
||||
SQAIAbstractList.AddConstructor<void (AIAbstractList::*)(), 1>(engine, "x");
|
||||
|
||||
SQAIAbstractList.DefSQConst(engine, AIAbstractList::SORT_BY_VALUE, "SORT_BY_VALUE");
|
||||
SQAIAbstractList.DefSQConst(engine, AIAbstractList::SORT_BY_ITEM, "SORT_BY_ITEM");
|
||||
|
||||
SQAIAbstractList.DefSQStaticMethod(engine, &AIAbstractList::GetClassName, "GetClassName", 1, "x");
|
||||
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::Clear, "Clear", 1, "x");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::HasItem, "HasItem", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::Begin, "Begin", 1, "x");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::Next, "Next", 1, "x");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::IsEmpty, "IsEmpty", 1, "x");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::HasNext, "HasNext", 1, "x");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::Count, "Count", 1, "x");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::GetValue, "GetValue", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::SetValue, "SetValue", 3, "xii");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::Sort, "Sort", 3, "xib");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::AddList, "AddList", 2, "xx");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveAboveValue, "RemoveAboveValue", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveBelowValue, "RemoveBelowValue", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveBetweenValue, "RemoveBetweenValue", 3, "xii");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveValue, "RemoveValue", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveTop, "RemoveTop", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveBottom, "RemoveBottom", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveList, "RemoveList", 2, "xx");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepAboveValue, "KeepAboveValue", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepBelowValue, "KeepBelowValue", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepBetweenValue, "KeepBetweenValue", 3, "xii");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepValue, "KeepValue", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepTop, "KeepTop", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepBottom, "KeepBottom", 2, "xi");
|
||||
SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepList, "KeepList", 2, "xx");
|
||||
SQAIAbstractList.DefSQAdvancedMethod(engine, &AIAbstractList::_get, "_get");
|
||||
SQAIAbstractList.DefSQAdvancedMethod(engine, &AIAbstractList::_nexti, "_nexti");
|
||||
SQAIAbstractList.DefSQAdvancedMethod(engine, &AIAbstractList::Valuate, "Valuate");
|
||||
|
||||
SQAIAbstractList.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_accounting.cpp Implementation of AIAccounting. */
|
||||
|
||||
#include "ai_accounting.hpp"
|
||||
|
||||
Money AIAccounting::GetCosts()
|
||||
{
|
||||
return this->GetDoCommandCosts();
|
||||
}
|
||||
|
||||
void AIAccounting::ResetCosts()
|
||||
{
|
||||
this->SetDoCommandCosts(0);
|
||||
}
|
||||
|
||||
AIAccounting::AIAccounting()
|
||||
{
|
||||
this->last_costs = this->GetDoCommandCosts();
|
||||
this->SetDoCommandCosts(0);
|
||||
}
|
||||
|
||||
AIAccounting::~AIAccounting()
|
||||
{
|
||||
this->SetDoCommandCosts(this->last_costs);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_accounting.hpp Everything to handle AI accounting things. */
|
||||
|
||||
#ifndef AI_ACCOUNTING_HPP
|
||||
#define AI_ACCOUNTING_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
|
||||
/**
|
||||
* Class that keeps track of the costs, so you can request how much a block of
|
||||
* commands did cost in total. Works in both Execute as in Test mode.
|
||||
* Example:
|
||||
* {
|
||||
* local costs = AIAccounting();
|
||||
* BuildRoad(from_here, to_here);
|
||||
* BuildRoad(from_there, to_there);
|
||||
* print("Costs for route is: " + costs.GetCosts());
|
||||
* }
|
||||
*/
|
||||
class AIAccounting : public AIObject {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIAccounting"; }
|
||||
|
||||
/**
|
||||
* Creating instance of this class starts counting the costs of commands
|
||||
* from zero.
|
||||
* @note when the instance is destroyed, he restores the costs that was
|
||||
* current when the instance was created!
|
||||
*/
|
||||
AIAccounting();
|
||||
|
||||
/**
|
||||
* Destroying this instance reset the costs to the value it was
|
||||
* in when the instance was created.
|
||||
*/
|
||||
~AIAccounting();
|
||||
|
||||
/**
|
||||
* Get the current value of the costs.
|
||||
* @return The current costs.
|
||||
*/
|
||||
Money GetCosts();
|
||||
|
||||
/**
|
||||
* Reset the costs to zero.
|
||||
*/
|
||||
void ResetCosts();
|
||||
|
||||
private:
|
||||
Money last_costs;
|
||||
};
|
||||
|
||||
#endif /* AI_ACCOUNTING_HPP */
|
@ -0,0 +1,26 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_accounting.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AIAccounting to be used as Squirrel parameter */
|
||||
template <> AIAccounting *GetParam(ForceType<AIAccounting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAccounting *)instance; }
|
||||
template <> AIAccounting &GetParam(ForceType<AIAccounting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAccounting *)instance; }
|
||||
template <> const AIAccounting *GetParam(ForceType<const AIAccounting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAccounting *)instance; }
|
||||
template <> const AIAccounting &GetParam(ForceType<const AIAccounting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAccounting *)instance; }
|
||||
template <> int Return<AIAccounting *>(HSQUIRRELVM vm, AIAccounting *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIAccounting", res, NULL, DefSQDestructorCallback<AIAccounting>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIAccounting_Register(Squirrel *engine) {
|
||||
DefSQClass <AIAccounting> SQAIAccounting("AIAccounting");
|
||||
SQAIAccounting.PreRegister(engine);
|
||||
SQAIAccounting.AddConstructor<void (AIAccounting::*)(), 1>(engine, "x");
|
||||
|
||||
SQAIAccounting.DefSQStaticMethod(engine, &AIAccounting::GetClassName, "GetClassName", 1, "x");
|
||||
|
||||
SQAIAccounting.DefSQMethod(engine, &AIAccounting::GetCosts, "GetCosts", 1, "x");
|
||||
SQAIAccounting.DefSQMethod(engine, &AIAccounting::ResetCosts, "ResetCosts", 1, "x");
|
||||
|
||||
SQAIAccounting.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_airport.cpp Implementation of AIAirport. */
|
||||
|
||||
#include "ai_airport.hpp"
|
||||
#include "ai_station.hpp"
|
||||
#include "ai_error.hpp"
|
||||
#include "../../openttd.h"
|
||||
#include "../../variables.h"
|
||||
#include "../../station_map.h"
|
||||
#include "../../company_func.h"
|
||||
#include "../../settings_type.h"
|
||||
#include "../../command_type.h"
|
||||
#include "../../town.h"
|
||||
|
||||
/* static */ bool AIAirport::IsValidAirportType(AirportType type)
|
||||
{
|
||||
return type >= AT_SMALL && type <= AT_HELISTATION;
|
||||
}
|
||||
|
||||
/* static */ bool AIAirport::IsHangarTile(TileIndex tile)
|
||||
{
|
||||
if (!::IsValidTile(tile)) return false;
|
||||
|
||||
return ::IsTileType(tile, MP_STATION) && ::IsHangar(tile);
|
||||
}
|
||||
|
||||
/* static */ bool AIAirport::IsAirportTile(TileIndex tile)
|
||||
{
|
||||
if (!::IsValidTile(tile)) return false;
|
||||
|
||||
return ::IsTileType(tile, MP_STATION) && ::IsAirport(tile);
|
||||
}
|
||||
|
||||
/* static */ bool AIAirport::AirportAvailable(AirportType type)
|
||||
{
|
||||
if (!IsValidAirportType(type)) return false;
|
||||
|
||||
return HasBit(::GetValidAirports(), type);
|
||||
}
|
||||
|
||||
/* static */ int32 AIAirport::GetAirportWidth(AirportType type)
|
||||
{
|
||||
if (!IsValidAirportType(type)) return -1;
|
||||
|
||||
return ::GetAirport(type)->size_x;
|
||||
}
|
||||
|
||||
/* static */ int32 AIAirport::GetAirportHeight(AirportType type)
|
||||
{
|
||||
if (!IsValidAirportType(type)) return -1;
|
||||
|
||||
return ::GetAirport(type)->size_y;
|
||||
}
|
||||
|
||||
/* static */ int32 AIAirport::GetAirportCoverageRadius(AirportType type)
|
||||
{
|
||||
if (!IsValidAirportType(type)) return -1;
|
||||
|
||||
return _settings_game.station.modified_catchment ? ::GetAirport(type)->catchment : (uint)CA_UNMODIFIED;
|
||||
}
|
||||
|
||||
/* static */ bool AIAirport::BuildAirport(TileIndex tile, AirportType type, bool join_adjacent)
|
||||
{
|
||||
EnforcePrecondition(false, ::IsValidTile(tile));
|
||||
EnforcePrecondition(false, IsValidAirportType(type));
|
||||
|
||||
return AIObject::DoCommand(tile, type, (INVALID_STATION << 16) | (join_adjacent ? 0 : 1), CMD_BUILD_AIRPORT);
|
||||
}
|
||||
|
||||
/* static */ bool AIAirport::RemoveAirport(TileIndex tile)
|
||||
{
|
||||
EnforcePrecondition(false, ::IsValidTile(tile))
|
||||
EnforcePrecondition(false, IsAirportTile(tile) || IsHangarTile(tile));
|
||||
|
||||
return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
|
||||
}
|
||||
|
||||
/* static */ int32 AIAirport::GetNumHangars(TileIndex tile)
|
||||
{
|
||||
if (!::IsValidTile(tile)) return -1;
|
||||
if (!::IsTileType(tile, MP_STATION)) return -1;
|
||||
|
||||
const Station *st = ::GetStationByTile(tile);
|
||||
if (st->owner != _current_company) return -1;
|
||||
if ((st->facilities & FACIL_AIRPORT) == 0) return -1;
|
||||
|
||||
return st->Airport()->nof_depots;
|
||||
}
|
||||
|
||||
/* static */ TileIndex AIAirport::GetHangarOfAirport(TileIndex tile)
|
||||
{
|
||||
if (!::IsValidTile(tile)) return INVALID_TILE;
|
||||
if (!::IsTileType(tile, MP_STATION)) return INVALID_TILE;
|
||||
if (GetNumHangars(tile) < 1) return INVALID_TILE;
|
||||
|
||||
const Station *st = ::GetStationByTile(tile);
|
||||
if (st->owner != _current_company) return INVALID_TILE;
|
||||
if ((st->facilities & FACIL_AIRPORT) == 0) return INVALID_TILE;
|
||||
|
||||
return ::ToTileIndexDiff(st->Airport()->airport_depots[0]) + st->xy;
|
||||
}
|
||||
|
||||
/* static */ AIAirport::AirportType AIAirport::GetAirportType(TileIndex tile)
|
||||
{
|
||||
if (!AITile::IsStationTile(tile)) return AT_INVALID;
|
||||
|
||||
StationID station_id = ::GetStationIndex(tile);
|
||||
|
||||
if (!AIStation::HasStationType(station_id, AIStation::STATION_AIRPORT)) return AT_INVALID;
|
||||
|
||||
return (AirportType)::GetStation(station_id)->airport_type;
|
||||
}
|
||||
|
||||
|
||||
/* static */ int AIAirport::GetNoiseLevelIncrease(TileIndex tile, AirportType type)
|
||||
{
|
||||
extern uint8 GetAirportNoiseLevelForTown(const AirportFTAClass *afc, TileIndex town_tile, TileIndex tile);
|
||||
|
||||
if (!::IsValidTile(tile)) return -1;
|
||||
if (!IsValidAirportType(type)) return -1;
|
||||
|
||||
if (_settings_game.economy.station_noise_level) {
|
||||
const AirportFTAClass *afc = ::GetAirport(type);
|
||||
const Town *t = ::ClosestTownFromTile(tile, UINT_MAX);
|
||||
return GetAirportNoiseLevelForTown(afc, t->xy, tile);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_airport.hpp Everything to query and build airports. */
|
||||
|
||||
#ifndef AI_AIRPORT_HPP
|
||||
#define AI_AIRPORT_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
|
||||
/**
|
||||
* Class that handles all airport related functions.
|
||||
*/
|
||||
class AIAirport : public AIObject {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIAirport"; }
|
||||
|
||||
/**
|
||||
* The types of airports available in the game.
|
||||
*/
|
||||
enum AirportType {
|
||||
/* Note: the values _are_ important as they represent an in-game value */
|
||||
AT_SMALL = 0, //!< The small airport.
|
||||
AT_LARGE = 1, //!< The large airport.
|
||||
AT_METROPOLITAN = 3, //!< The metropolitan airport.
|
||||
AT_INTERNATIONAL = 4, //!< The international airport.
|
||||
AT_COMMUTER = 5, //!< The commuter airport.
|
||||
AT_INTERCON = 7, //!< The intercontinental airport.
|
||||
|
||||
/* Next are the airports which only have helicopter platforms */
|
||||
AT_HELIPORT = 2, //!< The heliport.
|
||||
AT_HELISTATION = 8, //!< The helistation.
|
||||
AT_HELIDEPOT = 6, //!< The helidepot.
|
||||
|
||||
AT_INVALID = 255, //!< Invalid airport.
|
||||
};
|
||||
|
||||
/**
|
||||
* All plane types available.
|
||||
*/
|
||||
enum PlaneType {
|
||||
/* Note: the values _are_ important as they represent an in-game value */
|
||||
PT_HELICOPTER = 0, //!< A helicopter.
|
||||
PT_SMALL_PLANE = 1, //!< A small plane.
|
||||
PT_BIG_PLANE = 3, //!< A big plane.
|
||||
|
||||
PT_INVALID = -1, //!< An invalid PlaneType
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the given AirportType is valid.
|
||||
* @param type The AirportType to check.
|
||||
* @return True if and only if the AirportTypeis valid.
|
||||
*/
|
||||
static bool IsValidAirportType(AirportType type);
|
||||
|
||||
/**
|
||||
* Checks whether the given tile is actually a tile with a hangar.
|
||||
* @param tile The tile to check.
|
||||
* @pre AIMap::IsValidTile(tile).
|
||||
* @return True if and only if the tile has a hangar.
|
||||
*/
|
||||
static bool IsHangarTile(TileIndex tile);
|
||||
|
||||
/**
|
||||
* Checks whether the given tile is actually a tile with a airport.
|
||||
* @param tile The tile to check.
|
||||
* @pre AIMap::IsValidTile(tile).
|
||||
* @return True if and only if the tile has a airport.
|
||||
*/
|
||||
static bool IsAirportTile(TileIndex tile);
|
||||
|
||||
/**
|
||||
* Check if a certain airport type is already available.
|
||||
* @param type The type of airport to check.
|
||||
*/
|
||||
static bool AirportAvailable(AirportType type);
|
||||
|
||||
/**
|
||||
* Get the width of this type of airport.
|
||||
* @param type The type of airport.
|
||||
* @return The width in tiles.
|
||||
*/
|
||||
static int32 GetAirportWidth(AirportType type);
|
||||
|
||||
/**
|
||||
* Get the height of this type of airport.
|
||||
* @param type The type of airport.
|
||||
* @return The height in tiles.
|
||||
*/
|
||||
static int32 GetAirportHeight(AirportType type);
|
||||
|
||||
/**
|
||||
* Get the coverage radius of this type of airport.
|
||||
* @param type The type of airport.
|
||||
* @return The radius in tiles.
|
||||
*/
|
||||
static int32 GetAirportCoverageRadius(AirportType type);
|
||||
|
||||
/**
|
||||
* Get the number of hangars of the airport.
|
||||
* @param tile Any tile of the airport.
|
||||
* @pre AIMap::IsValidTile(tile).
|
||||
* @return The number of hangars of the airport.
|
||||
*/
|
||||
static int32 GetNumHangars(TileIndex tile);
|
||||
|
||||
/**
|
||||
* Get the first hanger tile of the airport.
|
||||
* @param tile Any tile of the airport.
|
||||
* @pre AIMap::IsValidTile(tile).
|
||||
* @pre GetNumHangars(tile) > 0.
|
||||
* @return The first hanger tile of the airport.
|
||||
* @note Possible there are more hangars, but you won't be able to find them
|
||||
* without walking over all the tiles of the airport and using
|
||||
* IsHangarTile() on them.
|
||||
*/
|
||||
static TileIndex GetHangarOfAirport(TileIndex tile);
|
||||
|
||||
/**
|
||||
* Builds a airport with tile at the topleft corner.
|
||||
* @param tile The topleft corner of the airport.
|
||||
* @param type The type of airport to build.
|
||||
* @param join_adjacent When building next to an other station, don't create a new station when this flag is true.
|
||||
* @pre AIMap::IsValidTile(tile).
|
||||
* @pre AirportAvailable(type).
|
||||
* @exception AIError::ERR_AREA_NOT_CLEAR
|
||||
* @exception AIError::ERR_FLAT_LAND_REQUIRED
|
||||
* @exception AIError::ERR_LOCAL_AUTHORITY_REFUSES
|
||||
* @exception AIStation::ERR_STATION_TOO_LARGE
|
||||
* @exception AIStation::ERR_STATION_TOO_CLOSE_TO_OTHER_STATION
|
||||
* @return Whether the airport has been/can be build or not.
|
||||
*/
|
||||
static bool BuildAirport(TileIndex tile, AirportType type, bool join_adjacent);
|
||||
|
||||
/**
|
||||
* Removes a airport.
|
||||
* @param tile Any tile of the airport.
|
||||
* @pre AIMap::IsValidTile(tile).
|
||||
* @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
|
||||
* @return Whether the airport has been/can be removed or not.
|
||||
*/
|
||||
static bool RemoveAirport(TileIndex tile);
|
||||
|
||||
/**
|
||||
* Get the AirportType of an existing airport.
|
||||
* @param tile Any tile of the airport.
|
||||
* @pre AITile::IsStationTile(tile).
|
||||
* @pre AIStation::HasStationType(AIStation.GetStationID(tile), AIStation::STATION_AIRPORT).
|
||||
* @return The AirportType of the airport.
|
||||
*/
|
||||
static AirportType GetAirportType(TileIndex tile);
|
||||
|
||||
/**
|
||||
* Get the noise that will be added to the nearest town if an airport was
|
||||
* built at this tile.
|
||||
* @param tile The tile to check.
|
||||
* @param type The AirportType to check.
|
||||
* @return The TownID of the town closest to the tile.
|
||||
* @note The noise will be added to the town with TownID AITile.GetClosestTown(tile).
|
||||
*/
|
||||
static int GetNoiseLevelIncrease(TileIndex tile, AirportType type);
|
||||
};
|
||||
|
||||
#endif /* AI_AIRPORT_HPP */
|
@ -0,0 +1,57 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_airport.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow enums to be used as Squirrel parameters */
|
||||
template <> AIAirport::AirportType GetParam(ForceType<AIAirport::AirportType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIAirport::AirportType)tmp; }
|
||||
template <> int Return<AIAirport::AirportType>(HSQUIRRELVM vm, AIAirport::AirportType res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
template <> AIAirport::PlaneType GetParam(ForceType<AIAirport::PlaneType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIAirport::PlaneType)tmp; }
|
||||
template <> int Return<AIAirport::PlaneType>(HSQUIRRELVM vm, AIAirport::PlaneType res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
|
||||
/* Allow AIAirport to be used as Squirrel parameter */
|
||||
template <> AIAirport *GetParam(ForceType<AIAirport *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAirport *)instance; }
|
||||
template <> AIAirport &GetParam(ForceType<AIAirport &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAirport *)instance; }
|
||||
template <> const AIAirport *GetParam(ForceType<const AIAirport *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAirport *)instance; }
|
||||
template <> const AIAirport &GetParam(ForceType<const AIAirport &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAirport *)instance; }
|
||||
template <> int Return<AIAirport *>(HSQUIRRELVM vm, AIAirport *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIAirport", res, NULL, DefSQDestructorCallback<AIAirport>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIAirport_Register(Squirrel *engine) {
|
||||
DefSQClass <AIAirport> SQAIAirport("AIAirport");
|
||||
SQAIAirport.PreRegister(engine);
|
||||
SQAIAirport.AddConstructor<void (AIAirport::*)(), 1>(engine, "x");
|
||||
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::AT_SMALL, "AT_SMALL");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::AT_LARGE, "AT_LARGE");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::AT_METROPOLITAN, "AT_METROPOLITAN");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::AT_INTERNATIONAL, "AT_INTERNATIONAL");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::AT_COMMUTER, "AT_COMMUTER");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::AT_INTERCON, "AT_INTERCON");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::AT_HELIPORT, "AT_HELIPORT");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::AT_HELISTATION, "AT_HELISTATION");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::AT_HELIDEPOT, "AT_HELIDEPOT");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::AT_INVALID, "AT_INVALID");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::PT_HELICOPTER, "PT_HELICOPTER");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::PT_SMALL_PLANE, "PT_SMALL_PLANE");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::PT_BIG_PLANE, "PT_BIG_PLANE");
|
||||
SQAIAirport.DefSQConst(engine, AIAirport::PT_INVALID, "PT_INVALID");
|
||||
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetClassName, "GetClassName", 1, "x");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::IsValidAirportType, "IsValidAirportType", 2, "xi");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::IsHangarTile, "IsHangarTile", 2, "xi");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::IsAirportTile, "IsAirportTile", 2, "xi");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::AirportAvailable, "AirportAvailable", 2, "xi");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetAirportWidth, "GetAirportWidth", 2, "xi");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetAirportHeight, "GetAirportHeight", 2, "xi");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetAirportCoverageRadius, "GetAirportCoverageRadius", 2, "xi");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetNumHangars, "GetNumHangars", 2, "xi");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetHangarOfAirport, "GetHangarOfAirport", 2, "xi");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::BuildAirport, "BuildAirport", 4, "xiib");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::RemoveAirport, "RemoveAirport", 2, "xi");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetAirportType, "GetAirportType", 2, "xi");
|
||||
SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetNoiseLevelIncrease, "GetNoiseLevelIncrease", 3, "xii");
|
||||
|
||||
SQAIAirport.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_base.cpp Implementation of AIBase. */
|
||||
|
||||
#include "ai_base.hpp"
|
||||
#include "../../network/network.h"
|
||||
#include "../../core/random_func.hpp"
|
||||
|
||||
/* static */ uint32 AIBase::Rand()
|
||||
{
|
||||
/* We pick RandomRange if we are in SP (so when saved, we do the same over and over)
|
||||
* but we pick InteractiveRandomRange if we are a network_server or network-client. */
|
||||
if (_networking) return ::InteractiveRandom();
|
||||
return ::Random();
|
||||
}
|
||||
|
||||
/* static */ uint32 AIBase::RandItem(int unused_param)
|
||||
{
|
||||
return AIBase::Rand();
|
||||
}
|
||||
|
||||
/* static */ uint AIBase::RandRange(uint max)
|
||||
{
|
||||
/* We pick RandomRange if we are in SP (so when saved, we do the same over and over)
|
||||
* but we pick InteractiveRandomRange if we are a network_server or network-client. */
|
||||
if (_networking) return ::InteractiveRandomRange(max);
|
||||
return ::RandomRange(max);
|
||||
}
|
||||
|
||||
/* static */ uint32 AIBase::RandRangeItem(int unused_param, uint max)
|
||||
{
|
||||
return AIBase::RandRange(max);
|
||||
}
|
||||
|
||||
/* static */ bool AIBase::Chance(uint out, uint max)
|
||||
{
|
||||
return (uint16)Rand() <= (uint16)((65536 * out) / max);
|
||||
}
|
||||
|
||||
/* static */ bool AIBase::ChanceItem(int unused_param, uint out, uint max)
|
||||
{
|
||||
return AIBase::Chance(out, max);
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_base.hpp Everything to query basic things. */
|
||||
|
||||
#ifndef AI_BASE_HPP
|
||||
#define AI_BASE_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
|
||||
/**
|
||||
* Class that handles some basic functions.
|
||||
*
|
||||
* @note The random functions are not called Random and RandomRange, because
|
||||
* RANDOM_DEBUG does some tricky stuff, which messes with those names.
|
||||
* @note In MP we cannot use Random because that will cause desyncs (AIs are
|
||||
* ran on the server only, not on all clients). This means that
|
||||
* we use InteractiveRandom in MP. Rand() takes care of this for you.
|
||||
*/
|
||||
class AIBase : public AIObject {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIBase"; }
|
||||
|
||||
/**
|
||||
* Get a random value.
|
||||
* @return A random value between 0 and MAX(uint32).
|
||||
*/
|
||||
static uint32 Rand();
|
||||
|
||||
/**
|
||||
* Get a random value.
|
||||
* @param unused_param This param is not used, but is needed to work with lists.
|
||||
* @return A random value between 0 and MAX(uint32).
|
||||
*/
|
||||
static uint32 RandItem(int unused_param);
|
||||
|
||||
/**
|
||||
* Get a random value in a range.
|
||||
* @param max The first number this function will never return (the maximum it returns is max - 1).
|
||||
* @return A random value between 0 .. max - 1.
|
||||
*/
|
||||
static uint RandRange(uint max);
|
||||
|
||||
/**
|
||||
* Get a random value in a range.
|
||||
* @param unused_param This param is not used, but is needed to work with lists.
|
||||
* @param max The first number this function will never return (the maximum it returns is max - 1).
|
||||
* @return A random value between 0 .. max - 1.
|
||||
*/
|
||||
static uint RandRangeItem(int unused_param, uint max);
|
||||
|
||||
/**
|
||||
* Returns approximatelly 'out' times true when called 'max' times.
|
||||
* After all, it is a random function.
|
||||
* @param out How many times it should return true.
|
||||
* @param max Out of this many times.
|
||||
* @return True if the chance worked out.
|
||||
*/
|
||||
static bool Chance(uint out, uint max);
|
||||
|
||||
/**
|
||||
* Returns approximatelly 'out' times true when called 'max' times.
|
||||
* After all, it is a random function.
|
||||
* @param unused_param This param is not used, but is needed to work with lists.
|
||||
* @param out How many times it should return true.
|
||||
* @param max Out of this many times.
|
||||
* @return True if the chance worked out.
|
||||
*/
|
||||
static bool ChanceItem(int unused_param, uint out, uint max);
|
||||
};
|
||||
|
||||
#endif /* AI_BASE_HPP */
|
@ -0,0 +1,29 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_base.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AIBase to be used as Squirrel parameter */
|
||||
template <> AIBase *GetParam(ForceType<AIBase *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBase *)instance; }
|
||||
template <> AIBase &GetParam(ForceType<AIBase &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBase *)instance; }
|
||||
template <> const AIBase *GetParam(ForceType<const AIBase *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBase *)instance; }
|
||||
template <> const AIBase &GetParam(ForceType<const AIBase &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBase *)instance; }
|
||||
template <> int Return<AIBase *>(HSQUIRRELVM vm, AIBase *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIBase", res, NULL, DefSQDestructorCallback<AIBase>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIBase_Register(Squirrel *engine) {
|
||||
DefSQClass <AIBase> SQAIBase("AIBase");
|
||||
SQAIBase.PreRegister(engine);
|
||||
SQAIBase.AddConstructor<void (AIBase::*)(), 1>(engine, "x");
|
||||
|
||||
SQAIBase.DefSQStaticMethod(engine, &AIBase::GetClassName, "GetClassName", 1, "x");
|
||||
SQAIBase.DefSQStaticMethod(engine, &AIBase::Rand, "Rand", 1, "x");
|
||||
SQAIBase.DefSQStaticMethod(engine, &AIBase::RandItem, "RandItem", 2, "xi");
|
||||
SQAIBase.DefSQStaticMethod(engine, &AIBase::RandRange, "RandRange", 2, "xi");
|
||||
SQAIBase.DefSQStaticMethod(engine, &AIBase::RandRangeItem, "RandRangeItem", 3, "xii");
|
||||
SQAIBase.DefSQStaticMethod(engine, &AIBase::Chance, "Chance", 3, "xii");
|
||||
SQAIBase.DefSQStaticMethod(engine, &AIBase::ChanceItem, "ChanceItem", 4, "xiii");
|
||||
|
||||
SQAIBase.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_bridge.cpp Implementation of AIBridge. */
|
||||
|
||||
#include "ai_bridge.hpp"
|
||||
#include "ai_rail.hpp"
|
||||
#include "../ai_instance.hpp"
|
||||
#include "../../openttd.h"
|
||||
#include "../../bridge_map.h"
|
||||
#include "../../strings_func.h"
|
||||
#include "../../core/alloc_func.hpp"
|
||||
#include "../../economy_func.h"
|
||||
#include "../../settings_type.h"
|
||||
#include "../../road_map.h"
|
||||
#include "table/strings.h"
|
||||
|
||||
/* static */ bool AIBridge::IsValidBridge(BridgeID bridge_id)
|
||||
{
|
||||
return bridge_id < MAX_BRIDGES;
|
||||
}
|
||||
|
||||
/* static */ bool AIBridge::IsBridgeTile(TileIndex tile)
|
||||
{
|
||||
if (!::IsValidTile(tile)) return false;
|
||||
return ::IsBridgeTile(tile);
|
||||
}
|
||||
|
||||
static void _DoCommandReturnBuildBridge2(class AIInstance *instance)
|
||||
{
|
||||
if (!AIBridge::_BuildBridgeRoad2()) {
|
||||
AIObject::SetLastCommandRes(false);
|
||||
AIInstance::DoCommandReturn(instance);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This can never happen, as in test-mode this callback is never executed,
|
||||
* and in execute-mode, the other callback is called. */
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
static void _DoCommandReturnBuildBridge1(class AIInstance *instance)
|
||||
{
|
||||
if (!AIBridge::_BuildBridgeRoad1()) {
|
||||
AIObject::SetLastCommandRes(false);
|
||||
AIInstance::DoCommandReturn(instance);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This can never happen, as in test-mode this callback is never executed,
|
||||
* and in execute-mode, the other callback is called. */
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
/* static */ bool AIBridge::BuildBridge(AIVehicle::VehicleType vehicle_type, BridgeID bridge_id, TileIndex start, TileIndex end)
|
||||
{
|
||||
EnforcePrecondition(false, start != end);
|
||||
EnforcePrecondition(false, ::IsValidTile(start) && ::IsValidTile(end));
|
||||
EnforcePrecondition(false, TileX(start) == TileX(end) || TileY(start) == TileY(end));
|
||||
EnforcePrecondition(false, vehicle_type == AIVehicle::VEHICLE_ROAD || vehicle_type == AIVehicle::VEHICLE_RAIL || vehicle_type == AIVehicle::VEHICLE_WATER);
|
||||
EnforcePrecondition(false, vehicle_type != AIVehicle::VEHICLE_RAIL || AIRail::IsRailTypeAvailable(AIRail::GetCurrentRailType()));
|
||||
|
||||
uint type = 0;
|
||||
switch (vehicle_type) {
|
||||
case AIVehicle::VEHICLE_ROAD:
|
||||
type |= (TRANSPORT_ROAD << 15);
|
||||
type |= (RoadTypeToRoadTypes((::RoadType)AIObject::GetRoadType()) << 8);
|
||||
break;
|
||||
case AIVehicle::VEHICLE_RAIL:
|
||||
type |= (TRANSPORT_RAIL << 15);
|
||||
type |= (AIRail::GetCurrentRailType() << 8);
|
||||
break;
|
||||
case AIVehicle::VEHICLE_WATER:
|
||||
type |= (TRANSPORT_WATER << 15);
|
||||
break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
|
||||
/* For rail and water we do nothing special */
|
||||
if (vehicle_type == AIVehicle::VEHICLE_RAIL || vehicle_type == AIVehicle::VEHICLE_WATER) {
|
||||
return AIObject::DoCommand(end, start, type | bridge_id, CMD_BUILD_BRIDGE);
|
||||
}
|
||||
|
||||
AIObject::SetCallbackVariable(0, start);
|
||||
if (!AIObject::DoCommand(end, start, type | bridge_id, CMD_BUILD_BRIDGE, NULL, &_DoCommandReturnBuildBridge1)) return false;
|
||||
|
||||
/* In case of test-mode, test if we can build both road pieces */
|
||||
return _BuildBridgeRoad1();
|
||||
}
|
||||
|
||||
/* static */ bool AIBridge::_BuildBridgeRoad1()
|
||||
{
|
||||
/* Build the piece of road on the 'start' side of the bridge */
|
||||
TileIndex end = AIObject::GetCallbackVariable(0);
|
||||
TileIndex start = AIBridge::GetOtherBridgeEnd(end);
|
||||
|
||||
DiagDirection dir_1 = (DiagDirection)((::TileX(start) == ::TileX(end)) ? (::TileY(start) < ::TileY(end) ? DIAGDIR_NW : DIAGDIR_SE) : (::TileX(start) < ::TileX(end) ? DIAGDIR_NE : DIAGDIR_SW));
|
||||
DiagDirection dir_2 = ::ReverseDiagDir(dir_1);
|
||||
|
||||
if (!AIObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (AIObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD, NULL, &_DoCommandReturnBuildBridge2)) return false;
|
||||
|
||||
/* In case of test-mode, test the other road piece too */
|
||||
return _BuildBridgeRoad2();
|
||||
}
|
||||
|
||||
/* static */ bool AIBridge::_BuildBridgeRoad2()
|
||||
{
|
||||
/* Build the piece of road on the 'end' side of the bridge */
|
||||
TileIndex end = AIObject::GetCallbackVariable(0);
|
||||
TileIndex start = AIBridge::GetOtherBridgeEnd(end);
|
||||
|
||||
DiagDirection dir_1 = (DiagDirection)((::TileX(start) == ::TileX(end)) ? (::TileY(start) < ::TileY(end) ? DIAGDIR_NW : DIAGDIR_SE) : (::TileX(start) < ::TileX(end) ? DIAGDIR_NE : DIAGDIR_SW));
|
||||
DiagDirection dir_2 = ::ReverseDiagDir(dir_1);
|
||||
|
||||
return AIObject::DoCommand(end + ::TileOffsByDiagDir(dir_2), ::DiagDirToRoadBits(dir_1) | (AIObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD);
|
||||
}
|
||||
|
||||
/* static */ bool AIBridge::RemoveBridge(TileIndex tile)
|
||||
{
|
||||
EnforcePrecondition(false, IsBridgeTile(tile));
|
||||
return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
|
||||
}
|
||||
|
||||
/* static */ const char *AIBridge::GetName(BridgeID bridge_id)
|
||||
{
|
||||
if (!IsValidBridge(bridge_id)) return NULL;
|
||||
|
||||
static const int len = 64;
|
||||
char *bridge_name = MallocT<char>(len);
|
||||
|
||||
::GetString(bridge_name, ::GetBridgeSpec(bridge_id)->transport_name[0], &bridge_name[len - 1]);
|
||||
return bridge_name;
|
||||
}
|
||||
|
||||
/* static */ int32 AIBridge::GetMaxSpeed(BridgeID bridge_id)
|
||||
{
|
||||
if (!IsValidBridge(bridge_id)) return -1;
|
||||
|
||||
return ::GetBridgeSpec(bridge_id)->speed;
|
||||
}
|
||||
|
||||
/* static */ Money AIBridge::GetPrice(BridgeID bridge_id, uint length)
|
||||
{
|
||||
if (!IsValidBridge(bridge_id)) return -1;
|
||||
|
||||
return length * _price.build_bridge * ::GetBridgeSpec(bridge_id)->price >> 8;
|
||||
}
|
||||
|
||||
/* static */ int32 AIBridge::GetMaxLength(BridgeID bridge_id)
|
||||
{
|
||||
if (!IsValidBridge(bridge_id)) return -1;
|
||||
|
||||
uint max = ::GetBridgeSpec(bridge_id)->max_length;
|
||||
if (max >= 16 && _settings_game.construction.longbridges) max = 100;
|
||||
return max + 2;
|
||||
}
|
||||
|
||||
/* static */ int32 AIBridge::GetMinLength(BridgeID bridge_id)
|
||||
{
|
||||
if (!IsValidBridge(bridge_id)) return -1;
|
||||
|
||||
return ::GetBridgeSpec(bridge_id)->min_length + 2;
|
||||
}
|
||||
|
||||
/* static */ int32 AIBridge::GetYearAvailable(BridgeID bridge_id)
|
||||
{
|
||||
if (!IsValidBridge(bridge_id)) return -1;
|
||||
|
||||
return ::GetBridgeSpec(bridge_id)->avail_year;
|
||||
}
|
||||
|
||||
/* static */ TileIndex AIBridge::GetOtherBridgeEnd(TileIndex tile)
|
||||
{
|
||||
if (!::IsValidTile(tile)) return INVALID_TILE;
|
||||
if (!IsBridgeTile(tile)) return INVALID_TILE;
|
||||
|
||||
return ::GetOtherBridgeEnd(tile);
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_bridge.hpp Everything to query and build bridges. */
|
||||
|
||||
#ifndef AI_BRIDGE_HPP
|
||||
#define AI_BRIDGE_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
#include "ai_vehicle.hpp"
|
||||
#include "ai_error.hpp"
|
||||
|
||||
/**
|
||||
* Class that handles all bridge related functions.
|
||||
*/
|
||||
class AIBridge : public AIObject {
|
||||
public:
|
||||
/**
|
||||
* All bridge related error messages.
|
||||
*/
|
||||
enum ErrorMessages {
|
||||
/** Base for bridge related errors */
|
||||
ERR_BRIDGE_BASE = AIError::ERR_CAT_BRIDGE << AIError::ERR_CAT_BIT_SIZE,
|
||||
|
||||
/**
|
||||
* The bridge you want to build is not available yet,
|
||||
* or it is not available for the requested length.
|
||||
*/
|
||||
ERR_BRIDGE_TYPE_UNAVAILABLE, // [STR_5015_CAN_T_BUILD_BRIDGE_HERE]
|
||||
|
||||
/** One (or more) of the bridge head(s) ends in water. */
|
||||
ERR_BRIDGE_CANNOT_END_IN_WATER, // [STR_02A0_ENDS_OF_BRIDGE_MUST_BOTH]
|
||||
|
||||
/** The bride heads need to be on the same height */
|
||||
ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT, // [STR_BRIDGEHEADS_NOT_SAME_HEIGHT]
|
||||
};
|
||||
|
||||
static const char *GetClassName() { return "AIBridge"; }
|
||||
|
||||
/**
|
||||
* Checks whether the given bridge type is valid.
|
||||
* @param bridge_id The bridge to check.
|
||||
* @return True if and only if the bridge type is valid.
|
||||
*/
|
||||
static bool IsValidBridge(BridgeID bridge_id);
|
||||
|
||||
/**
|
||||
* Checks whether the given tile is actually a bridge start or end tile.
|
||||
* @param tile The tile to check.
|
||||
* @pre AIMap::IsValidTile(tile).
|
||||
* @return True if and only if the tile is the beginning or end of a bridge.
|
||||
*/
|
||||
static bool IsBridgeTile(TileIndex tile);
|
||||
|
||||
/**
|
||||
* Get the name of a bridge.
|
||||
* @param bridge_id The bridge to get the name of.
|
||||
* @pre IsValidBridge(bridge_id).
|
||||
* @return The name the bridge has.
|
||||
*/
|
||||
static const char *GetName(BridgeID bridge_id);
|
||||
|
||||
/**
|
||||
* Get the maximum speed of a bridge (in km/h).
|
||||
* @param bridge_id The bridge to get the maximum speed of.
|
||||
* @pre IsValidBridge(bridge_id).
|
||||
* @return The maximum speed the bridge has.
|
||||
*/
|
||||
static int32 GetMaxSpeed(BridgeID bridge_id);
|
||||
|
||||
/**
|
||||
* Get the new cost of a bridge.
|
||||
* @param bridge_id The bridge to get the new cost of.
|
||||
* @param length The length of the bridge.
|
||||
* @pre IsValidBridge(bridge_id).
|
||||
* @return The new cost the bridge has.
|
||||
*/
|
||||
static Money GetPrice(BridgeID bridge_id, uint length);
|
||||
|
||||
/**
|
||||
* Get the maximum length of a bridge.
|
||||
* @param bridge_id The bridge to get the maximum length of.
|
||||
* @pre IsValidBridge(bridge_id).
|
||||
* @returns The maximum length the bridge has.
|
||||
*/
|
||||
static int32 GetMaxLength(BridgeID bridge_id);
|
||||
|
||||
/**
|
||||
* Get the minimum length of a bridge.
|
||||
* @param bridge_id The bridge to get the minimum length of.
|
||||
* @pre IsValidBridge(bridge_id).
|
||||
* @returns The minimum length the bridge has.
|
||||
*/
|
||||
static int32 GetMinLength(BridgeID bridge_id);
|
||||
|
||||
/**
|
||||
* Get the year in which a bridge becomes available.
|
||||
* @param bridge_id The bridge to get the year of availability of.
|
||||
* @pre IsValidBridge(bridge_id).
|
||||
* @returns The year of availability the bridge has.
|
||||
* @note Years are like 2010, -10 (10 B.C.), 1950, ..
|
||||
*/
|
||||
static int32 GetYearAvailable(BridgeID bridge_id);
|
||||
|
||||
#ifndef DOXYGEN_SKIP
|
||||
/**
|
||||
* Internal function to help BuildBridge in case of road.
|
||||
*/
|
||||
static bool _BuildBridgeRoad1();
|
||||
|
||||
/**
|
||||
* Internal function to help BuildBridge in case of road.
|
||||
*/
|
||||
static bool _BuildBridgeRoad2();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Build a bridge from one tile to the other.
|
||||
* As an extra for road, this functions builds two half-pieces of road on
|
||||
* each end of the bridge, making it easier for you to connect it to your
|
||||
* network.
|
||||
* @param vehicle_type The vehicle-type of bridge to build.
|
||||
* @param bridge_id The bridge-type to build.
|
||||
* @param start Where to start the bridge.
|
||||
* @param end Where to end the bridge.
|
||||
* @pre AIMap::IsValidTile(start).
|
||||
* @pre AIMap::IsValidTile(end).
|
||||
* @pre 'start' and 'end' are in a straight line, i.e.
|
||||
* AIMap::GetTileX(start) == AIMap::GetTileX(end) or
|
||||
* AIMap::GetTileY(start) == AIMap::GetTileY(end).
|
||||
* @pre vehicle_type == AIVehicle::VEHICLE_ROAD || (vehicle_type == AIVehicle::VEHICLE_RAIL &&
|
||||
* AIRail::IsRailTypeAvailable(AIRail::GetCurrentRailType())) || vehicle_type == AIVehicle::VEHICLE_WATER.
|
||||
* @exception AIError::ERR_ALREADY_BUILT
|
||||
* @exception AIError::ERR_AREA_NOT_CLEAR
|
||||
* @exception AIError::ERR_LAND_SLOPED_WRONG
|
||||
* @exception AIError::ERR_VEHICLE_IN_THE_WAY
|
||||
* @exception AIBridge::ERR_BRIDGE_TYPE_UNAVAILABLE
|
||||
* @exception AIBridge::ERR_BRIDGE_CANNOT_END_IN_WATER
|
||||
* @exception AIBridge::ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT
|
||||
* @return Whether the bridge has been/can be build or not.
|
||||
* @note No matter if the road pieces were build or not, if building the
|
||||
* bridge succeeded, this function returns true.
|
||||
*/
|
||||
static bool BuildBridge(AIVehicle::VehicleType vehicle_type, BridgeID bridge_id, TileIndex start, TileIndex end);
|
||||
|
||||
/**
|
||||
* Removes a bridge, by executing it on either the start or end tile.
|
||||
* @param tile An end or start tile of the bridge.
|
||||
* @pre AIMap::IsValidTile(tile).
|
||||
* @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
|
||||
* @return Whether the bridge has been/can be removed or not.
|
||||
*/
|
||||
static bool RemoveBridge(TileIndex tile);
|
||||
|
||||
/**
|
||||
* Get the tile that is on the other end of a bridge starting at tile.
|
||||
* @param tile The tile that is an end of a bridge.
|
||||
* @pre AIMap::IsValidTile(tile).
|
||||
* @pre IsBridgeTile(tile).
|
||||
* @return The TileIndex that is the other end of the bridge.
|
||||
*/
|
||||
static TileIndex GetOtherBridgeEnd(TileIndex tile);
|
||||
};
|
||||
|
||||
#endif /* AI_BRIDGE_HPP */
|
@ -0,0 +1,51 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_bridge.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow enums to be used as Squirrel parameters */
|
||||
template <> AIBridge::ErrorMessages GetParam(ForceType<AIBridge::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIBridge::ErrorMessages)tmp; }
|
||||
template <> int Return<AIBridge::ErrorMessages>(HSQUIRRELVM vm, AIBridge::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
|
||||
/* Allow AIBridge to be used as Squirrel parameter */
|
||||
template <> AIBridge *GetParam(ForceType<AIBridge *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridge *)instance; }
|
||||
template <> AIBridge &GetParam(ForceType<AIBridge &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridge *)instance; }
|
||||
template <> const AIBridge *GetParam(ForceType<const AIBridge *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridge *)instance; }
|
||||
template <> const AIBridge &GetParam(ForceType<const AIBridge &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridge *)instance; }
|
||||
template <> int Return<AIBridge *>(HSQUIRRELVM vm, AIBridge *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIBridge", res, NULL, DefSQDestructorCallback<AIBridge>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIBridge_Register(Squirrel *engine) {
|
||||
DefSQClass <AIBridge> SQAIBridge("AIBridge");
|
||||
SQAIBridge.PreRegister(engine);
|
||||
SQAIBridge.AddConstructor<void (AIBridge::*)(), 1>(engine, "x");
|
||||
|
||||
SQAIBridge.DefSQConst(engine, AIBridge::ERR_BRIDGE_BASE, "ERR_BRIDGE_BASE");
|
||||
SQAIBridge.DefSQConst(engine, AIBridge::ERR_BRIDGE_TYPE_UNAVAILABLE, "ERR_BRIDGE_TYPE_UNAVAILABLE");
|
||||
SQAIBridge.DefSQConst(engine, AIBridge::ERR_BRIDGE_CANNOT_END_IN_WATER, "ERR_BRIDGE_CANNOT_END_IN_WATER");
|
||||
SQAIBridge.DefSQConst(engine, AIBridge::ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT, "ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT");
|
||||
|
||||
AIError::RegisterErrorMap(STR_5015_CAN_T_BUILD_BRIDGE_HERE, AIBridge::ERR_BRIDGE_TYPE_UNAVAILABLE);
|
||||
AIError::RegisterErrorMap(STR_02A0_ENDS_OF_BRIDGE_MUST_BOTH, AIBridge::ERR_BRIDGE_CANNOT_END_IN_WATER);
|
||||
AIError::RegisterErrorMap(STR_BRIDGEHEADS_NOT_SAME_HEIGHT, AIBridge::ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT);
|
||||
|
||||
AIError::RegisterErrorMapString(AIBridge::ERR_BRIDGE_TYPE_UNAVAILABLE, "ERR_BRIDGE_TYPE_UNAVAILABLE");
|
||||
AIError::RegisterErrorMapString(AIBridge::ERR_BRIDGE_CANNOT_END_IN_WATER, "ERR_BRIDGE_CANNOT_END_IN_WATER");
|
||||
AIError::RegisterErrorMapString(AIBridge::ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT, "ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT");
|
||||
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetClassName, "GetClassName", 1, "x");
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::IsValidBridge, "IsValidBridge", 2, "xi");
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::IsBridgeTile, "IsBridgeTile", 2, "xi");
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetName, "GetName", 2, "xi");
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetMaxSpeed, "GetMaxSpeed", 2, "xi");
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetPrice, "GetPrice", 3, "xii");
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetMaxLength, "GetMaxLength", 2, "xi");
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetMinLength, "GetMinLength", 2, "xi");
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetYearAvailable, "GetYearAvailable", 2, "xi");
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::BuildBridge, "BuildBridge", 5, "xiiii");
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::RemoveBridge, "RemoveBridge", 2, "xi");
|
||||
SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetOtherBridgeEnd, "GetOtherBridgeEnd", 2, "xi");
|
||||
|
||||
SQAIBridge.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_bridgelist.cpp Implementation of AIBridgeList and friends. */
|
||||
|
||||
#include "ai_bridgelist.hpp"
|
||||
#include "ai_bridge.hpp"
|
||||
#include "../../openttd.h"
|
||||
#include "../../bridge.h"
|
||||
#include "../../date_func.h"
|
||||
|
||||
AIBridgeList::AIBridgeList()
|
||||
{
|
||||
/* Add all bridges, no matter if they are available or not */
|
||||
for (byte j = 0; j < MAX_BRIDGES; j++)
|
||||
if (::GetBridgeSpec(j)->avail_year <= _cur_year)
|
||||
this->AddItem(j);
|
||||
}
|
||||
|
||||
AIBridgeList_Length::AIBridgeList_Length(uint length)
|
||||
{
|
||||
for (byte j = 0; j < MAX_BRIDGES; j++)
|
||||
if (::GetBridgeSpec(j)->avail_year <= _cur_year)
|
||||
if (length >= (uint)AIBridge::GetMinLength(j) && length <= (uint)AIBridge::GetMaxLength(j))
|
||||
this->AddItem(j);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_bridgelist.hpp List all the bridges. */
|
||||
|
||||
#ifndef AI_BRIDGELIST_HPP
|
||||
#define AI_BRIDGELIST_HPP
|
||||
|
||||
#include "ai_abstractlist.hpp"
|
||||
|
||||
/**
|
||||
* Create a list of bridges.
|
||||
* @ingroup AIList
|
||||
*/
|
||||
class AIBridgeList : public AIAbstractList {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIBridgeList"; }
|
||||
AIBridgeList();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a list of bridges that can be built on a specific length.
|
||||
* @ingroup AIList
|
||||
*/
|
||||
class AIBridgeList_Length : public AIAbstractList {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIBridgeList_Length"; }
|
||||
|
||||
/**
|
||||
* @param length The length of the bridge you want to build.
|
||||
*/
|
||||
AIBridgeList_Length(uint length);
|
||||
};
|
||||
|
||||
#endif /* AI_BRIDGELIST_HPP */
|
@ -0,0 +1,42 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_bridgelist.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AIBridgeList to be used as Squirrel parameter */
|
||||
template <> AIBridgeList *GetParam(ForceType<AIBridgeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridgeList *)instance; }
|
||||
template <> AIBridgeList &GetParam(ForceType<AIBridgeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridgeList *)instance; }
|
||||
template <> const AIBridgeList *GetParam(ForceType<const AIBridgeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridgeList *)instance; }
|
||||
template <> const AIBridgeList &GetParam(ForceType<const AIBridgeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridgeList *)instance; }
|
||||
template <> int Return<AIBridgeList *>(HSQUIRRELVM vm, AIBridgeList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIBridgeList", res, NULL, DefSQDestructorCallback<AIBridgeList>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIBridgeList_Register(Squirrel *engine) {
|
||||
DefSQClass <AIBridgeList> SQAIBridgeList("AIBridgeList");
|
||||
SQAIBridgeList.PreRegister(engine, "AIAbstractList");
|
||||
SQAIBridgeList.AddConstructor<void (AIBridgeList::*)(), 1>(engine, "x");
|
||||
|
||||
SQAIBridgeList.DefSQStaticMethod(engine, &AIBridgeList::GetClassName, "GetClassName", 1, "x");
|
||||
|
||||
SQAIBridgeList.PostRegister(engine);
|
||||
}
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AIBridgeList_Length to be used as Squirrel parameter */
|
||||
template <> AIBridgeList_Length *GetParam(ForceType<AIBridgeList_Length *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridgeList_Length *)instance; }
|
||||
template <> AIBridgeList_Length &GetParam(ForceType<AIBridgeList_Length &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridgeList_Length *)instance; }
|
||||
template <> const AIBridgeList_Length *GetParam(ForceType<const AIBridgeList_Length *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridgeList_Length *)instance; }
|
||||
template <> const AIBridgeList_Length &GetParam(ForceType<const AIBridgeList_Length &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridgeList_Length *)instance; }
|
||||
template <> int Return<AIBridgeList_Length *>(HSQUIRRELVM vm, AIBridgeList_Length *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIBridgeList_Length", res, NULL, DefSQDestructorCallback<AIBridgeList_Length>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIBridgeList_Length_Register(Squirrel *engine) {
|
||||
DefSQClass <AIBridgeList_Length> SQAIBridgeList_Length("AIBridgeList_Length");
|
||||
SQAIBridgeList_Length.PreRegister(engine, "AIAbstractList");
|
||||
SQAIBridgeList_Length.AddConstructor<void (AIBridgeList_Length::*)(uint length), 2>(engine, "xi");
|
||||
|
||||
SQAIBridgeList_Length.DefSQStaticMethod(engine, &AIBridgeList_Length::GetClassName, "GetClassName", 1, "x");
|
||||
|
||||
SQAIBridgeList_Length.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_cargo.cpp Implementation of AICargo. */
|
||||
|
||||
#include "ai_cargo.hpp"
|
||||
#include "../../openttd.h"
|
||||
#include "../../cargotype.h"
|
||||
#include "../../economy_func.h"
|
||||
#include "../../core/alloc_func.hpp"
|
||||
#include "../../core/bitmath_func.hpp"
|
||||
#include "../../newgrf_cargo.h"
|
||||
|
||||
/* static */ bool AICargo::IsValidCargo(CargoID cargo_type)
|
||||
{
|
||||
return (cargo_type < NUM_CARGO && ::GetCargo(cargo_type)->IsValid());
|
||||
}
|
||||
|
||||
/* static */ const char *AICargo::GetCargoLabel(CargoID cargo_type)
|
||||
{
|
||||
if (!IsValidCargo(cargo_type)) return NULL;
|
||||
const CargoSpec *cargo = ::GetCargo(cargo_type);
|
||||
|
||||
/* cargo->label is a uint32 packing a 4 character non-terminated string,
|
||||
* like "PASS", "COAL", "OIL_". New ones can be defined by NewGRFs */
|
||||
char *cargo_label = MallocT<char>(sizeof(cargo->label) + 1);
|
||||
for (uint i = 0; i < sizeof(cargo->label); i++) {
|
||||
cargo_label[i] = GB(cargo->label, (uint8)(sizeof(cargo->label) - i - 1) * 8, 8);
|
||||
}
|
||||
cargo_label[sizeof(cargo->label)] = '\0';
|
||||
return cargo_label;
|
||||
}
|
||||
|
||||
/* static */ bool AICargo::IsFreight(CargoID cargo_type)
|
||||
{
|
||||
if (!IsValidCargo(cargo_type)) return false;
|
||||
const CargoSpec *cargo = ::GetCargo(cargo_type);
|
||||
return cargo->is_freight;
|
||||
}
|
||||
|
||||
/* static */ bool AICargo::HasCargoClass(CargoID cargo_type, CargoClass cargo_class)
|
||||
{
|
||||
if (!IsValidCargo(cargo_type)) return false;
|
||||
return ::IsCargoInClass(cargo_type, (::CargoClass)cargo_class);
|
||||
}
|
||||
|
||||
/* static */ AICargo::TownEffect AICargo::GetTownEffect(CargoID cargo_type)
|
||||
{
|
||||
if (!IsValidCargo(cargo_type)) return TE_NONE;
|
||||
|
||||
return (AICargo::TownEffect)GetCargo(cargo_type)->town_effect;
|
||||
}
|
||||
|
||||
/* static */ Money AICargo::GetCargoIncome(CargoID cargo_type, uint32 distance, uint32 days_in_transit)
|
||||
{
|
||||
if (!IsValidCargo(cargo_type)) return -1;
|
||||
return ::GetTransportedGoodsIncome(1, distance, Clamp(days_in_transit * 2 / 5, 0, 255), cargo_type);
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_cargo.hpp Everything to query cargos. */
|
||||
|
||||
#ifndef AI_CARGO_HPP
|
||||
#define AI_CARGO_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
|
||||
/**
|
||||
* Class that handles all cargo related functions.
|
||||
*/
|
||||
class AICargo : public AIObject {
|
||||
public:
|
||||
static const char *GetClassName() { return "AICargo"; }
|
||||
|
||||
/**
|
||||
* The classes of cargo (from newgrf_cargo.h).
|
||||
*/
|
||||
enum CargoClass {
|
||||
CC_PASSENGERS = 1 << 0, ///< Passengers
|
||||
CC_MAIL = 1 << 1, ///< Mail
|
||||
CC_EXPRESS = 1 << 2, ///< Express cargo (Goods, Food, Candy, but also possible for passengers)
|
||||
CC_ARMOURED = 1 << 3, ///< Armoured cargo (Valuables, Gold, Diamonds)
|
||||
CC_BULK = 1 << 4, ///< Bulk cargo (Coal, Grain etc., Ores, Fruit)
|
||||
CC_PIECE_GOODS = 1 << 5, ///< Piece goods (Livestock, Wood, Steel, Paper)
|
||||
CC_LIQUID = 1 << 6, ///< Liquids (Oil, Water, Rubber)
|
||||
CC_REFRIGERATED = 1 << 7, ///< Refrigerated cargo (Food, Fruit)
|
||||
CC_HAZARDOUS = 1 << 8, ///< Hazardous cargo (Nuclear Fuel, Explosives, etc.)
|
||||
CC_COVERED = 1 << 9, ///< Covered/Sheltered Freight (Transporation in Box Vans, Silo Wagons, etc.)
|
||||
};
|
||||
|
||||
/**
|
||||
* The effects a cargo can have on a town.
|
||||
*/
|
||||
enum TownEffect {
|
||||
TE_NONE = 0, ///< This cargo has no effect on a town
|
||||
TE_PASSENGERS = 1, ///< This cargo supplies passengers to a town
|
||||
TE_MAIL = 2, ///< This cargo supplies mail to a town
|
||||
TE_GOODS = 3, ///< This cargo supplies goods to a town
|
||||
TE_WATER = 4, ///< This cargo supplies water to a town
|
||||
TE_FOOD = 5, ///< This cargo supplies food to a town
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the given cargo type is valid.
|
||||
* @param cargo_type The cargo to check.
|
||||
* @return True if and only if the cargo type is valid.
|
||||
*/
|
||||
static bool IsValidCargo(CargoID cargo_type);
|
||||
|
||||
/**
|
||||
* Gets the string representation of the cargo label.
|
||||
* @param cargo_type The cargo to get the string representation of.
|
||||
* @return The cargo label.
|
||||
* @note Never use this to check if it is a certain cargo. NewGRF can
|
||||
* redefine all of the names.
|
||||
*/
|
||||
static const char *GetCargoLabel(CargoID cargo_type);
|
||||
|
||||
/**
|
||||
* Checks whether the give cargo is a freight or not.
|
||||
* @param cargo_type The cargo to check on.
|
||||
* @return True if and only if the cargo is freight.
|
||||
*/
|
||||
static bool IsFreight(CargoID cargo_type);
|
||||
|
||||
/**
|
||||
* Check if this cargo is in the requested cargo class.
|
||||
* @param cargo_type The cargo to check on.
|
||||
* @param cargo_class The class to check for.
|
||||
* @return True if and only if the cargo is in the cargo class.
|
||||
*/
|
||||
static bool HasCargoClass(CargoID cargo_type, CargoClass cargo_class);
|
||||
|
||||
/**
|
||||
* Get the effect this cargo has on a town.
|
||||
* @param cargo_type The cargo to check on.
|
||||
* @return The effect this cargo has on a town, or TE_NONE if it has no effect.
|
||||
*/
|
||||
static TownEffect GetTownEffect(CargoID cargo_type);
|
||||
|
||||
/**
|
||||
* Get the income for transporting a piece of cargo over the
|
||||
* given distance within the specified time.
|
||||
* @param cargo_type The cargo to transport.
|
||||
* @param distance The distance the cargo travels from begin to end.
|
||||
* @param days_in_transit Amount of (game) days the cargo is in transit. The max value of this variable is 637. Any value higher returns the same as 637 would.
|
||||
* @return The amount of money that would be earned by this trip.
|
||||
*/
|
||||
static Money GetCargoIncome(CargoID cargo_type, uint32 distance, uint32 days_in_transit);
|
||||
};
|
||||
|
||||
#endif /* AI_CARGO_HPP */
|
@ -0,0 +1,52 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_cargo.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow enums to be used as Squirrel parameters */
|
||||
template <> AICargo::CargoClass GetParam(ForceType<AICargo::CargoClass>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AICargo::CargoClass)tmp; }
|
||||
template <> int Return<AICargo::CargoClass>(HSQUIRRELVM vm, AICargo::CargoClass res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
template <> AICargo::TownEffect GetParam(ForceType<AICargo::TownEffect>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AICargo::TownEffect)tmp; }
|
||||
template <> int Return<AICargo::TownEffect>(HSQUIRRELVM vm, AICargo::TownEffect res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
|
||||
/* Allow AICargo to be used as Squirrel parameter */
|
||||
template <> AICargo *GetParam(ForceType<AICargo *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargo *)instance; }
|
||||
template <> AICargo &GetParam(ForceType<AICargo &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargo *)instance; }
|
||||
template <> const AICargo *GetParam(ForceType<const AICargo *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargo *)instance; }
|
||||
template <> const AICargo &GetParam(ForceType<const AICargo &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargo *)instance; }
|
||||
template <> int Return<AICargo *>(HSQUIRRELVM vm, AICargo *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AICargo", res, NULL, DefSQDestructorCallback<AICargo>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAICargo_Register(Squirrel *engine) {
|
||||
DefSQClass <AICargo> SQAICargo("AICargo");
|
||||
SQAICargo.PreRegister(engine);
|
||||
SQAICargo.AddConstructor<void (AICargo::*)(), 1>(engine, "x");
|
||||
|
||||
SQAICargo.DefSQConst(engine, AICargo::CC_PASSENGERS, "CC_PASSENGERS");
|
||||
SQAICargo.DefSQConst(engine, AICargo::CC_MAIL, "CC_MAIL");
|
||||
SQAICargo.DefSQConst(engine, AICargo::CC_EXPRESS, "CC_EXPRESS");
|
||||
SQAICargo.DefSQConst(engine, AICargo::CC_ARMOURED, "CC_ARMOURED");
|
||||
SQAICargo.DefSQConst(engine, AICargo::CC_BULK, "CC_BULK");
|
||||
SQAICargo.DefSQConst(engine, AICargo::CC_PIECE_GOODS, "CC_PIECE_GOODS");
|
||||
SQAICargo.DefSQConst(engine, AICargo::CC_LIQUID, "CC_LIQUID");
|
||||
SQAICargo.DefSQConst(engine, AICargo::CC_REFRIGERATED, "CC_REFRIGERATED");
|
||||
SQAICargo.DefSQConst(engine, AICargo::CC_HAZARDOUS, "CC_HAZARDOUS");
|
||||
SQAICargo.DefSQConst(engine, AICargo::CC_COVERED, "CC_COVERED");
|
||||
SQAICargo.DefSQConst(engine, AICargo::TE_NONE, "TE_NONE");
|
||||
SQAICargo.DefSQConst(engine, AICargo::TE_PASSENGERS, "TE_PASSENGERS");
|
||||
SQAICargo.DefSQConst(engine, AICargo::TE_MAIL, "TE_MAIL");
|
||||
SQAICargo.DefSQConst(engine, AICargo::TE_GOODS, "TE_GOODS");
|
||||
SQAICargo.DefSQConst(engine, AICargo::TE_WATER, "TE_WATER");
|
||||
SQAICargo.DefSQConst(engine, AICargo::TE_FOOD, "TE_FOOD");
|
||||
|
||||
SQAICargo.DefSQStaticMethod(engine, &AICargo::GetClassName, "GetClassName", 1, "x");
|
||||
SQAICargo.DefSQStaticMethod(engine, &AICargo::IsValidCargo, "IsValidCargo", 2, "xi");
|
||||
SQAICargo.DefSQStaticMethod(engine, &AICargo::GetCargoLabel, "GetCargoLabel", 2, "xi");
|
||||
SQAICargo.DefSQStaticMethod(engine, &AICargo::IsFreight, "IsFreight", 2, "xi");
|
||||
SQAICargo.DefSQStaticMethod(engine, &AICargo::HasCargoClass, "HasCargoClass", 3, "xii");
|
||||
SQAICargo.DefSQStaticMethod(engine, &AICargo::GetTownEffect, "GetTownEffect", 2, "xi");
|
||||
SQAICargo.DefSQStaticMethod(engine, &AICargo::GetCargoIncome, "GetCargoIncome", 4, "xiii");
|
||||
|
||||
SQAICargo.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_cargolist.cpp Implementation of AICargoList and friends. */
|
||||
|
||||
#include "ai_cargolist.hpp"
|
||||
#include "ai_industry.hpp"
|
||||
#include "../../openttd.h"
|
||||
#include "../../cargotype.h"
|
||||
#include "../../tile_type.h"
|
||||
#include "../../industry.h"
|
||||
|
||||
AICargoList::AICargoList()
|
||||
{
|
||||
for (byte i = 0; i < NUM_CARGO; i++) {
|
||||
const CargoSpec *c = ::GetCargo(i);
|
||||
if (c->IsValid()) {
|
||||
this->AddItem(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AICargoList_IndustryAccepting::AICargoList_IndustryAccepting(IndustryID industry_id)
|
||||
{
|
||||
if (!AIIndustry::IsValidIndustry(industry_id)) return;
|
||||
|
||||
Industry *ind = ::GetIndustry(industry_id);
|
||||
for (uint i = 0; i < lengthof(ind->accepts_cargo); i++) {
|
||||
CargoID cargo_id = ind->accepts_cargo[i];
|
||||
if (cargo_id != CT_INVALID) {
|
||||
this->AddItem(cargo_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AICargoList_IndustryProducing::AICargoList_IndustryProducing(IndustryID industry_id)
|
||||
{
|
||||
if (!AIIndustry::IsValidIndustry(industry_id)) return;
|
||||
|
||||
Industry *ind = ::GetIndustry(industry_id);
|
||||
for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
|
||||
CargoID cargo_id = ind->produced_cargo[i];
|
||||
if (cargo_id != CT_INVALID) {
|
||||
this->AddItem(cargo_id);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_cargolist.hpp List all the cargos. */
|
||||
|
||||
#ifndef AI_CARGOLIST_HPP
|
||||
#define AI_CARGOLIST_HPP
|
||||
|
||||
#include "ai_abstractlist.hpp"
|
||||
|
||||
/**
|
||||
* Creates a list of cargos that can be produced in the current game.
|
||||
* @ingroup AIList
|
||||
*/
|
||||
class AICargoList : public AIAbstractList {
|
||||
public:
|
||||
static const char *GetClassName() { return "AICargoList"; }
|
||||
AICargoList();
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a list of cargos that the given industry accepts.
|
||||
* @ingroup AIList
|
||||
*/
|
||||
class AICargoList_IndustryAccepting : public AIAbstractList {
|
||||
public:
|
||||
static const char *GetClassName() { return "AICargoList_IndustryAccepting"; }
|
||||
|
||||
/**
|
||||
* @param industry_id The industry to get the list of cargos it accepts from.
|
||||
*/
|
||||
AICargoList_IndustryAccepting(IndustryID industry_id);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a list of cargos that the given industry can produce.
|
||||
* @ingroup AIList
|
||||
*/
|
||||
class AICargoList_IndustryProducing : public AIAbstractList {
|
||||
public:
|
||||
static const char *GetClassName() { return "AICargoList_IndustryProducing"; }
|
||||
|
||||
/**
|
||||
* @param industry_id The industry to get the list of cargos it produces from.
|
||||
*/
|
||||
AICargoList_IndustryProducing(IndustryID industry_id);
|
||||
};
|
||||
|
||||
#endif /* AI_CARGOLIST_HPP */
|
@ -0,0 +1,61 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_cargolist.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AICargoList to be used as Squirrel parameter */
|
||||
template <> AICargoList *GetParam(ForceType<AICargoList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList *)instance; }
|
||||
template <> AICargoList &GetParam(ForceType<AICargoList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList *)instance; }
|
||||
template <> const AICargoList *GetParam(ForceType<const AICargoList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList *)instance; }
|
||||
template <> const AICargoList &GetParam(ForceType<const AICargoList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList *)instance; }
|
||||
template <> int Return<AICargoList *>(HSQUIRRELVM vm, AICargoList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AICargoList", res, NULL, DefSQDestructorCallback<AICargoList>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAICargoList_Register(Squirrel *engine) {
|
||||
DefSQClass <AICargoList> SQAICargoList("AICargoList");
|
||||
SQAICargoList.PreRegister(engine, "AIAbstractList");
|
||||
SQAICargoList.AddConstructor<void (AICargoList::*)(), 1>(engine, "x");
|
||||
|
||||
SQAICargoList.DefSQStaticMethod(engine, &AICargoList::GetClassName, "GetClassName", 1, "x");
|
||||
|
||||
SQAICargoList.PostRegister(engine);
|
||||
}
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AICargoList_IndustryAccepting to be used as Squirrel parameter */
|
||||
template <> AICargoList_IndustryAccepting *GetParam(ForceType<AICargoList_IndustryAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList_IndustryAccepting *)instance; }
|
||||
template <> AICargoList_IndustryAccepting &GetParam(ForceType<AICargoList_IndustryAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList_IndustryAccepting *)instance; }
|
||||
template <> const AICargoList_IndustryAccepting *GetParam(ForceType<const AICargoList_IndustryAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList_IndustryAccepting *)instance; }
|
||||
template <> const AICargoList_IndustryAccepting &GetParam(ForceType<const AICargoList_IndustryAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList_IndustryAccepting *)instance; }
|
||||
template <> int Return<AICargoList_IndustryAccepting *>(HSQUIRRELVM vm, AICargoList_IndustryAccepting *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AICargoList_IndustryAccepting", res, NULL, DefSQDestructorCallback<AICargoList_IndustryAccepting>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAICargoList_IndustryAccepting_Register(Squirrel *engine) {
|
||||
DefSQClass <AICargoList_IndustryAccepting> SQAICargoList_IndustryAccepting("AICargoList_IndustryAccepting");
|
||||
SQAICargoList_IndustryAccepting.PreRegister(engine, "AIAbstractList");
|
||||
SQAICargoList_IndustryAccepting.AddConstructor<void (AICargoList_IndustryAccepting::*)(IndustryID industry_id), 2>(engine, "xi");
|
||||
|
||||
SQAICargoList_IndustryAccepting.DefSQStaticMethod(engine, &AICargoList_IndustryAccepting::GetClassName, "GetClassName", 1, "x");
|
||||
|
||||
SQAICargoList_IndustryAccepting.PostRegister(engine);
|
||||
}
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AICargoList_IndustryProducing to be used as Squirrel parameter */
|
||||
template <> AICargoList_IndustryProducing *GetParam(ForceType<AICargoList_IndustryProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList_IndustryProducing *)instance; }
|
||||
template <> AICargoList_IndustryProducing &GetParam(ForceType<AICargoList_IndustryProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList_IndustryProducing *)instance; }
|
||||
template <> const AICargoList_IndustryProducing *GetParam(ForceType<const AICargoList_IndustryProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList_IndustryProducing *)instance; }
|
||||
template <> const AICargoList_IndustryProducing &GetParam(ForceType<const AICargoList_IndustryProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList_IndustryProducing *)instance; }
|
||||
template <> int Return<AICargoList_IndustryProducing *>(HSQUIRRELVM vm, AICargoList_IndustryProducing *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AICargoList_IndustryProducing", res, NULL, DefSQDestructorCallback<AICargoList_IndustryProducing>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAICargoList_IndustryProducing_Register(Squirrel *engine) {
|
||||
DefSQClass <AICargoList_IndustryProducing> SQAICargoList_IndustryProducing("AICargoList_IndustryProducing");
|
||||
SQAICargoList_IndustryProducing.PreRegister(engine, "AIAbstractList");
|
||||
SQAICargoList_IndustryProducing.AddConstructor<void (AICargoList_IndustryProducing::*)(IndustryID industry_id), 2>(engine, "xi");
|
||||
|
||||
SQAICargoList_IndustryProducing.DefSQStaticMethod(engine, &AICargoList_IndustryProducing::GetClassName, "GetClassName", 1, "x");
|
||||
|
||||
SQAICargoList_IndustryProducing.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_company.cpp Implementation of AICompany. */
|
||||
|
||||
#include "ai_company.hpp"
|
||||
#include "ai_error.hpp"
|
||||
#include "ai_log.hpp"
|
||||
#include "../../openttd.h"
|
||||
#include "../../command_func.h"
|
||||
#include "../../company_func.h"
|
||||
#include "../../company_base.h"
|
||||
#include "../../economy_func.h"
|
||||
#include "../../strings_func.h"
|
||||
#include "../../tile_map.h"
|
||||
#include "../../variables.h"
|
||||
#include "../../core/alloc_func.hpp"
|
||||
#include "../../string_func.h"
|
||||
#include "table/strings.h"
|
||||
|
||||
/* static */ AICompany::CompanyID AICompany::ResolveCompanyID(AICompany::CompanyID company)
|
||||
{
|
||||
if (company == MY_COMPANY) return (CompanyID)((byte)_current_company);
|
||||
|
||||
return ::IsValidCompanyID((::CompanyID)company) ? company : INVALID_COMPANY;
|
||||
}
|
||||
|
||||
/* static */ bool AICompany::IsMine(AICompany::CompanyID company)
|
||||
{
|
||||
return ResolveCompanyID(company) == ResolveCompanyID(MY_COMPANY);
|
||||
}
|
||||
|
||||
/* static */ bool AICompany::SetCompanyName(const char *name)
|
||||
{
|
||||
AILog::Error("AICompany::SetCompanyName is obsolete. Use AICompany::SetName instead.");
|
||||
return AICompany::SetName(name);
|
||||
}
|
||||
|
||||
/* static */ bool AICompany::SetName(const char *name)
|
||||
{
|
||||
EnforcePrecondition(false, !::StrEmpty(name));
|
||||
EnforcePreconditionCustomError(false, ::strlen(name) < MAX_LENGTH_COMPANY_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG);
|
||||
|
||||
return AIObject::DoCommand(0, 0, 0, CMD_RENAME_COMPANY, name);
|
||||
}
|
||||
|
||||
/* static */ const char *AICompany::GetCompanyName(AICompany::CompanyID company)
|
||||
{
|
||||
AILog::Error("AICompany::GetCompanyName is obsolete. Use AICompany::GetName instead.");
|
||||
return AICompany::GetName(company);
|
||||
}
|
||||
|
||||
/* static */ const char *AICompany::GetName(AICompany::CompanyID company)
|
||||
{
|
||||
company = ResolveCompanyID(company);
|
||||
if (company == INVALID_COMPANY) return NULL;
|
||||
|
||||
static const int len = 64;
|
||||
char *company_name = MallocT<char>(len);
|
||||
|
||||
::SetDParam(0, company);
|
||||
::GetString(company_name, STR_COMPANY_NAME, &company_name[len - 1]);
|
||||
return company_name;
|
||||
}
|
||||
|
||||
/* static */ bool AICompany::SetPresidentName(const char *name)
|
||||
{
|
||||
EnforcePrecondition(false, !::StrEmpty(name));
|
||||
|
||||
return AIObject::DoCommand(0, 0, 0, CMD_RENAME_PRESIDENT, name);
|
||||
}
|
||||
|
||||
/* static */ const char *AICompany::GetPresidentName(AICompany::CompanyID company)
|
||||
{
|
||||
company = ResolveCompanyID(company);
|
||||
|
||||
static const int len = 64;
|
||||
char *president_name = MallocT<char>(len);
|
||||
if (company != INVALID_COMPANY) {
|
||||
::SetDParam(0, company);
|
||||
::GetString(president_name, STR_PRESIDENT_NAME, &president_name[len - 1]);
|
||||
} else {
|
||||
*president_name = '\0';
|
||||
}
|
||||
|
||||
return president_name;
|
||||
}
|
||||
|
||||
/* static */ Money AICompany::GetCompanyValue(AICompany::CompanyID company)
|
||||
{
|
||||
company = ResolveCompanyID(company);
|
||||
if (company == INVALID_COMPANY) return -1;
|
||||
|
||||
return ::CalculateCompanyValue(::GetCompany((CompanyID)company));
|
||||
}
|
||||
|
||||
/* static */ Money AICompany::GetBankBalance(AICompany::CompanyID company)
|
||||
{
|
||||
company = ResolveCompanyID(company);
|
||||
if (company == INVALID_COMPANY) return -1;
|
||||
|
||||
return ::GetCompany((CompanyID)company)->money;
|
||||
}
|
||||
|
||||
/* static */ Money AICompany::GetLoanAmount()
|
||||
{
|
||||
return ::GetCompany(_current_company)->current_loan;
|
||||
}
|
||||
|
||||
/* static */ Money AICompany::GetMaxLoanAmount()
|
||||
{
|
||||
return _economy.max_loan;
|
||||
}
|
||||
|
||||
/* static */ Money AICompany::GetLoanInterval()
|
||||
{
|
||||
return LOAN_INTERVAL;
|
||||
}
|
||||
|
||||
/* static */ bool AICompany::SetLoanAmount(int32 loan)
|
||||
{
|
||||
EnforcePrecondition(false, loan >= 0);
|
||||
EnforcePrecondition(false, (loan % GetLoanInterval()) == 0);
|
||||
EnforcePrecondition(false, loan <= GetMaxLoanAmount());
|
||||
EnforcePrecondition(false, (loan - GetLoanAmount() + GetBankBalance(MY_COMPANY)) >= 0);
|
||||
|
||||
if (loan == GetLoanAmount()) return true;
|
||||
|
||||
return AIObject::DoCommand(0,
|
||||
abs(loan - GetLoanAmount()), 2,
|
||||
(loan > GetLoanAmount()) ? CMD_INCREASE_LOAN : CMD_DECREASE_LOAN);
|
||||
}
|
||||
|
||||
/* static */ bool AICompany::SetMinimumLoanAmount(int32 loan)
|
||||
{
|
||||
EnforcePrecondition(false, loan >= 0);
|
||||
|
||||
int32 over_interval = loan % GetLoanInterval();
|
||||
if (over_interval != 0) loan += GetLoanInterval() - over_interval;
|
||||
|
||||
EnforcePrecondition(false, loan <= GetMaxLoanAmount());
|
||||
|
||||
SetLoanAmount(loan);
|
||||
|
||||
return GetLoanAmount() == loan;
|
||||
}
|
||||
|
||||
/* static */ bool AICompany::BuildCompanyHQ(TileIndex tile)
|
||||
{
|
||||
EnforcePrecondition(false, ::IsValidTile(tile));
|
||||
|
||||
return AIObject::DoCommand(tile, 0, 0, CMD_BUILD_COMPANY_HQ);
|
||||
}
|
||||
|
||||
/* static */ TileIndex AICompany::GetCompanyHQ(CompanyID company)
|
||||
{
|
||||
company = ResolveCompanyID(company);
|
||||
if (company == INVALID_COMPANY) return INVALID_TILE;
|
||||
|
||||
TileIndex loc = ::GetCompany((CompanyID)company)->location_of_HQ;
|
||||
return (loc == 0) ? INVALID_TILE : loc;
|
||||
}
|
||||
|
||||
/* static */ bool AICompany::SetAutoRenewStatus(bool autorenew)
|
||||
{
|
||||
return AIObject::DoCommand(0, 0, autorenew ? 1 : 0, CMD_SET_AUTOREPLACE);
|
||||
}
|
||||
|
||||
/* static */ bool AICompany::GetAutoRenewStatus(CompanyID company)
|
||||
{
|
||||
company = ResolveCompanyID(company);
|
||||
if (company == INVALID_COMPANY) return false;
|
||||
|
||||
return ::GetCompany((CompanyID)company)->engine_renew;
|
||||
}
|
||||
|
||||
/* static */ bool AICompany::SetAutoRenewMonths(int16 months)
|
||||
{
|
||||
return AIObject::DoCommand(0, 1, months, CMD_SET_AUTOREPLACE);
|
||||
}
|
||||
|
||||
/* static */ int16 AICompany::GetAutoRenewMonths(CompanyID company)
|
||||
{
|
||||
company = ResolveCompanyID(company);
|
||||
if (company == INVALID_COMPANY) return 0;
|
||||
|
||||
return ::GetCompany((CompanyID)company)->engine_renew_months;
|
||||
}
|
||||
|
||||
/* static */ bool AICompany::SetAutoRenewMoney(uint32 money)
|
||||
{
|
||||
return AIObject::DoCommand(0, 2, money, CMD_SET_AUTOREPLACE);
|
||||
}
|
||||
|
||||
/* static */ uint32 AICompany::GetAutoRenewMoney(CompanyID company)
|
||||
{
|
||||
company = ResolveCompanyID(company);
|
||||
if (company == INVALID_COMPANY) return 0;
|
||||
|
||||
return ::GetCompany((CompanyID)company)->engine_renew_money;
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_company.hpp Everything to query a company's financials and statistics or build company related buildings. */
|
||||
|
||||
#ifndef AI_COMPANY_HPP
|
||||
#define AI_COMPANY_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
|
||||
/**
|
||||
* Class that handles all company related functions.
|
||||
*/
|
||||
class AICompany : public AIObject {
|
||||
public:
|
||||
static const char *GetClassName() { return "AICompany"; }
|
||||
|
||||
/** Different constants related to CompanyID. */
|
||||
enum CompanyID {
|
||||
INVALID_COMPANY = -1, //!< An invalid company.
|
||||
FIRST_COMPANY = 0, //!< The first available company.
|
||||
LAST_COMPANY = 7, //!< The last available company.
|
||||
MY_COMPANY = 8, //!< Constant that gets resolved to the correct company index for your company.
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolved the given company index to the correct index for the company. If
|
||||
* the company index was MY_COMPANY it will be resolved to the index of
|
||||
* your company. If the company with the given index does not exist it will
|
||||
* return INVALID_COMPANY.
|
||||
* @param company The company index to resolve.
|
||||
* @return The resolved company index.
|
||||
*/
|
||||
static CompanyID ResolveCompanyID(CompanyID company);
|
||||
|
||||
/**
|
||||
* Check if a CompanyID is your CompanyID, to ease up checks.
|
||||
* @param company The company index to check.
|
||||
* @return True if and only if this company is your CompanyID.
|
||||
*/
|
||||
static bool IsMine(CompanyID company);
|
||||
|
||||
/**
|
||||
* Obsolete, use AICompany::SetName instead.
|
||||
*/
|
||||
static bool SetCompanyName(const char *name);
|
||||
|
||||
/**
|
||||
* Set the name of your company.
|
||||
* @param name The new name of the company.
|
||||
* @pre 'name' must have at least one character.
|
||||
* @pre 'name' must have at most 30 characters.
|
||||
* @exception AIError::ERR_NAME_IS_NOT_UNIQUE
|
||||
* @return True if the name was changed.
|
||||
*/
|
||||
static bool SetName(const char *name);
|
||||
|
||||
/**
|
||||
* Obsolete, use AICompany::GetName instead.
|
||||
*/
|
||||
static const char *GetCompanyName(CompanyID company);
|
||||
|
||||
/**
|
||||
* Get the name of the given company.
|
||||
* @param company The company to get the name for.
|
||||
* @pre ResolveCompanyID(company) != INVALID_COMPANY
|
||||
* @return The name of the given company.
|
||||
*/
|
||||
static const char *GetName(CompanyID company);
|
||||
|
||||
/**
|
||||
* Set the name of your president.
|
||||
* @param name The new name of the president.
|
||||
* @pre 'name' must have at least one character.
|
||||
* @exception AIError::ERR_NAME_IS_NOT_UNIQUE
|
||||
* @return True if the name was changed.
|
||||
*/
|
||||
static bool SetPresidentName(const char *name);
|
||||
|
||||
/**
|
||||
* Get the name of the president of the given company.
|
||||
* @param company The company to get the president's name for.
|
||||
* @pre ResolveCompanyID(company) != INVALID_COMPANY
|
||||
* @return The name of the president of the given company.
|
||||
*/
|
||||
static const char *GetPresidentName(CompanyID company);
|
||||
|
||||
/**
|
||||
* Sets the amount to loan.
|
||||
* @param loan The amount to loan (multiplier of GetLoanInterval()).
|
||||
* @pre 'loan' must be non-negative.
|
||||
* @pre GetLoanInterval() must be a multiplier of GetLoanInterval().
|
||||
* @pre 'loan' must be below GetMaxLoanAmount().
|
||||
* @pre 'loan' - GetLoanAmount() + GetBankBalance() must be non-negative.
|
||||
* @return True if the loan could be set to your requested amount.
|
||||
*/
|
||||
static bool SetLoanAmount(int32 loan);
|
||||
|
||||
/**
|
||||
* Sets the minimum amount to loan, i.e. the given amount of loan rounded up.
|
||||
* @param loan The amount to loan (any positive number).
|
||||
* @pre 'loan' must be non-negative.
|
||||
* @pre 'loan' must be below GetMaxLoanAmount().
|
||||
* @return True if we could allocate a minimum of "loan" loan.
|
||||
*/
|
||||
static bool SetMinimumLoanAmount(int32 loan);
|
||||
|
||||
/**
|
||||
* Gets the amount your company have loaned.
|
||||
* @return The amount loaned money.
|
||||
* @post The return value is always non-negative.
|
||||
* @post GetLoanInterval() is always a multiplier of the return value.
|
||||
*/
|
||||
static Money GetLoanAmount();
|
||||
|
||||
/**
|
||||
* Gets the maximum amount your company can loan.
|
||||
* @return The maximum amount your company can loan.
|
||||
* @post The return value is always non-negative.
|
||||
* @post GetLoanInterval() is always a multiplier of the return value.
|
||||
*/
|
||||
static Money GetMaxLoanAmount();
|
||||
|
||||
/**
|
||||
* Gets the interval/loan step.
|
||||
* @return The loan step.
|
||||
* @post Return value is always positive.
|
||||
*/
|
||||
static Money GetLoanInterval();
|
||||
|
||||
/**
|
||||
* Gets the current value of the given company.
|
||||
* @param company The company to get the company value of.
|
||||
* @pre ResolveCompanyID(company) != INVALID_COMPANY
|
||||
* @return The current value of the given company.
|
||||
*/
|
||||
static Money GetCompanyValue(CompanyID company);
|
||||
|
||||
/**
|
||||
* Gets the bank balance. In other words, the amount of money the given company can spent.
|
||||
* @param company The company to get the bank balance of.
|
||||
* @pre ResolveCompanyID(company) != INVALID_COMPANY
|
||||
* @return The actual bank balance.
|
||||
*/
|
||||
static Money GetBankBalance(CompanyID company);
|
||||
|
||||
/**
|
||||
* Build your company's HQ on the given tile.
|
||||
* @param tile The tile to build your HQ on, this tile is the most nothern tile of your HQ.
|
||||
* @pre AIMap::IsValidTile(tile).
|
||||
* @exception AIError::ERR_AREA_NOT_CLEAR
|
||||
* @exception AIError::ERR_FLAT_LAND_REQUIRED
|
||||
* @return True if the HQ could be build.
|
||||
* @note An HQ can not be removed, only by water or rebuilding; If an HQ is
|
||||
* build again, the old one is removed.
|
||||
*/
|
||||
static bool BuildCompanyHQ(TileIndex tile);
|
||||
|
||||
/**
|
||||
* Return the location of a company's HQ.
|
||||
* @param company The company the get the HQ of.
|
||||
* @pre ResolveCompanyID(company) != INVALID_COMPANY.
|
||||
* @return The tile of the company's HQ, this tile is the most nothern tile of that HQ, or INVALID_TILE if there is no HQ yet.
|
||||
*/
|
||||
static TileIndex GetCompanyHQ(CompanyID company);
|
||||
|
||||
/**
|
||||
* Set whether autorenew is enabled for your company.
|
||||
* @param autorenew The new autorenew status.
|
||||
* @return True if autorenew status has been modified.
|
||||
*/
|
||||
static bool SetAutoRenewStatus(bool autorenew);
|
||||
|
||||
/**
|
||||
* Return whether autorenew is enabled for a company.
|
||||
* @param company The company to get the autorenew status of.
|
||||
* @pre ResolveCompanyID(company) != INVALID_COMPANY.
|
||||
* @return True if autorenew is enabled.
|
||||
*/
|
||||
static bool GetAutoRenewStatus(CompanyID company);
|
||||
|
||||
/**
|
||||
* Set the number of months before/after max age to autorenew an engine for your company.
|
||||
* @param months The new months between autorenew.
|
||||
* @return True if autorenew months has been modified.
|
||||
*/
|
||||
static bool SetAutoRenewMonths(int16 months);
|
||||
|
||||
/**
|
||||
* Return the number of months before/after max age to autorenew an engine for a company.
|
||||
* @param company The company to get the autorenew months of.
|
||||
* @pre ResolveCompanyID(company) != INVALID_COMPANY.
|
||||
* @return The months before/after max age of engine.
|
||||
*/
|
||||
static int16 GetAutoRenewMonths(CompanyID company);
|
||||
|
||||
/**
|
||||
* Set the minimum money needed to autorenew an engine for your company.
|
||||
* @param money The new minimum required money for autorenew to work.
|
||||
* @return True if autorenew money has been modified.
|
||||
*/
|
||||
static bool SetAutoRenewMoney(uint32 money);
|
||||
|
||||
/**
|
||||
* Return the minimum money needed to autorenew an engine for a company.
|
||||
* @param company The company to get the autorenew money of.
|
||||
* @pre ResolveCompanyID(company) != INVALID_COMPANY.
|
||||
* @return The minimum required money for autorenew to work.
|
||||
*/
|
||||
static uint32 GetAutoRenewMoney(CompanyID company);
|
||||
};
|
||||
|
||||
DECLARE_POSTFIX_INCREMENT(AICompany::CompanyID);
|
||||
|
||||
#endif /* AI_COMPANY_HPP */
|
@ -0,0 +1,55 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_company.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow enums to be used as Squirrel parameters */
|
||||
template <> AICompany::CompanyID GetParam(ForceType<AICompany::CompanyID>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AICompany::CompanyID)tmp; }
|
||||
template <> int Return<AICompany::CompanyID>(HSQUIRRELVM vm, AICompany::CompanyID res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
|
||||
/* Allow AICompany to be used as Squirrel parameter */
|
||||
template <> AICompany *GetParam(ForceType<AICompany *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICompany *)instance; }
|
||||
template <> AICompany &GetParam(ForceType<AICompany &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICompany *)instance; }
|
||||
template <> const AICompany *GetParam(ForceType<const AICompany *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICompany *)instance; }
|
||||
template <> const AICompany &GetParam(ForceType<const AICompany &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICompany *)instance; }
|
||||
template <> int Return<AICompany *>(HSQUIRRELVM vm, AICompany *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AICompany", res, NULL, DefSQDestructorCallback<AICompany>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAICompany_Register(Squirrel *engine) {
|
||||
DefSQClass <AICompany> SQAICompany("AICompany");
|
||||
SQAICompany.PreRegister(engine);
|
||||
SQAICompany.AddConstructor<void (AICompany::*)(), 1>(engine, "x");
|
||||
|
||||
SQAICompany.DefSQConst(engine, AICompany::INVALID_COMPANY, "INVALID_COMPANY");
|
||||
SQAICompany.DefSQConst(engine, AICompany::FIRST_COMPANY, "FIRST_COMPANY");
|
||||
SQAICompany.DefSQConst(engine, AICompany::LAST_COMPANY, "LAST_COMPANY");
|
||||
SQAICompany.DefSQConst(engine, AICompany::MY_COMPANY, "MY_COMPANY");
|
||||
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetClassName, "GetClassName", 1, "x");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::ResolveCompanyID, "ResolveCompanyID", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::IsMine, "IsMine", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::SetCompanyName, "SetCompanyName", 2, "xs");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::SetName, "SetName", 2, "xs");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetCompanyName, "GetCompanyName", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetName, "GetName", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::SetPresidentName, "SetPresidentName", 2, "xs");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetPresidentName, "GetPresidentName", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::SetLoanAmount, "SetLoanAmount", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::SetMinimumLoanAmount, "SetMinimumLoanAmount", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetLoanAmount, "GetLoanAmount", 1, "x");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetMaxLoanAmount, "GetMaxLoanAmount", 1, "x");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetLoanInterval, "GetLoanInterval", 1, "x");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetCompanyValue, "GetCompanyValue", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetBankBalance, "GetBankBalance", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::BuildCompanyHQ, "BuildCompanyHQ", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetCompanyHQ, "GetCompanyHQ", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::SetAutoRenewStatus, "SetAutoRenewStatus", 2, "xb");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetAutoRenewStatus, "GetAutoRenewStatus", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::SetAutoRenewMonths, "SetAutoRenewMonths", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetAutoRenewMonths, "GetAutoRenewMonths", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::SetAutoRenewMoney, "SetAutoRenewMoney", 2, "xi");
|
||||
SQAICompany.DefSQStaticMethod(engine, &AICompany::GetAutoRenewMoney, "GetAutoRenewMoney", 2, "xi");
|
||||
|
||||
SQAICompany.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_controller.cpp Implementation of AIControler. */
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../openttd.h"
|
||||
#include "../../company_func.h"
|
||||
#include "../../core/alloc_func.hpp"
|
||||
#include "../../string_func.h"
|
||||
#include "../../settings_type.h"
|
||||
#include "../../company_base.h"
|
||||
#include "../../saveload/saveload.h"
|
||||
#include "table/strings.h"
|
||||
|
||||
#include "../ai.hpp"
|
||||
#include "ai_controller.hpp"
|
||||
#include "../ai_info.hpp"
|
||||
#include "../ai_storage.hpp"
|
||||
#include "../ai_instance.hpp"
|
||||
#include "../ai_config.hpp"
|
||||
#include "ai_log.hpp"
|
||||
|
||||
/* static */ void AIController::SetCommandDelay(int ticks)
|
||||
{
|
||||
if (ticks <= 0) return;
|
||||
AIObject::SetDoCommandDelay(ticks);
|
||||
}
|
||||
|
||||
/* static */ void AIController::Sleep(int ticks)
|
||||
{
|
||||
if (ticks <= 0) {
|
||||
AILog::Warning("Sleep() value should be > 0. Assuming value 1.");
|
||||
ticks = 1;
|
||||
}
|
||||
|
||||
throw AI_VMSuspend(ticks, NULL);
|
||||
}
|
||||
|
||||
/* static */ void AIController::Print(bool error_msg, const char *message)
|
||||
{
|
||||
AILog::Log(error_msg ? AILog::LOG_SQ_ERROR : AILog::LOG_SQ_INFO, message);
|
||||
}
|
||||
|
||||
AIController::AIController() :
|
||||
ticks(0),
|
||||
loaded_library_count(0)
|
||||
{
|
||||
}
|
||||
|
||||
AIController::~AIController()
|
||||
{
|
||||
for (LoadedLibraryList::iterator iter = this->loaded_library.begin(); iter != this->loaded_library.end(); iter++) {
|
||||
free((void *)(*iter).second);
|
||||
free((void *)(*iter).first);
|
||||
}
|
||||
|
||||
this->loaded_library.clear();
|
||||
}
|
||||
|
||||
uint AIController::GetTick()
|
||||
{
|
||||
return this->ticks;
|
||||
}
|
||||
|
||||
int AIController::GetSetting(const char *name)
|
||||
{
|
||||
return AIConfig::GetConfig(_current_company)->GetSetting(name);
|
||||
}
|
||||
|
||||
bool AIController::LoadedLibrary(const char *library_name, int *next_number, char *fake_class_name, int fake_class_name_len)
|
||||
{
|
||||
LoadedLibraryList::iterator iter = this->loaded_library.find(library_name);
|
||||
if (iter == this->loaded_library.end()) {
|
||||
*next_number = ++this->loaded_library_count;
|
||||
return false;
|
||||
}
|
||||
|
||||
ttd_strlcpy(fake_class_name, (*iter).second, fake_class_name_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AIController::AddLoadedLibrary(const char *library_name, const char *fake_class_name)
|
||||
{
|
||||
this->loaded_library[strdup(library_name)] = strdup(fake_class_name);
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_controller.hpp The controller of the AI. */
|
||||
|
||||
#ifndef AI_CONTROLLER_HPP
|
||||
#define AI_CONTROLLER_HPP
|
||||
|
||||
#include <map>
|
||||
#ifndef AI_HPP
|
||||
struct ltstr { bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } };
|
||||
#endif /* AI_HPP */
|
||||
|
||||
/**
|
||||
* The Controller, the class each AI should extend. It creates the AI, makes
|
||||
* sure the logic kicks in correctly, and that GetTick() has a valid value.
|
||||
*/
|
||||
class AIController {
|
||||
friend class AIScanner;
|
||||
friend class AIInstance;
|
||||
|
||||
public:
|
||||
static const char *GetClassName() { return "AIController"; }
|
||||
|
||||
/**
|
||||
* Initializer of the AIController.
|
||||
*/
|
||||
AIController();
|
||||
|
||||
/**
|
||||
* Destructor of the AIController.
|
||||
*/
|
||||
~AIController();
|
||||
|
||||
/**
|
||||
* This function is called to start your AI. Your AI starts here. If you
|
||||
* return from this function, your AI dies, so make sure that doesn't
|
||||
* happen.
|
||||
* @note Cannot be called from within your AI.
|
||||
*/
|
||||
void Start();
|
||||
|
||||
/**
|
||||
* Find at which tick your AI currently is.
|
||||
* @return returns the current tick.
|
||||
*/
|
||||
uint GetTick();
|
||||
|
||||
/**
|
||||
* Get the value of one of your settings you set via info.nut.
|
||||
* @param name The name of the setting.
|
||||
* @return the value for the setting, or -1 if the setting is not known.
|
||||
*/
|
||||
int GetSetting(const char *name);
|
||||
|
||||
/**
|
||||
* Change the minimum amount of time the AI should be put in suspend mode
|
||||
* when you execute a command. Normally in SP this is 1, and in MP it is
|
||||
* what ever delay the server has been programmed to delay commands
|
||||
* (normally between 1 and 5). To give a more 'real' effect to your AI,
|
||||
* you can control that number here.
|
||||
* @param ticks The minimum amount of ticks to wait.
|
||||
* @pre Ticks should be positive. Too big values will influence performance of the AI.
|
||||
* @note If the number is lower then the MP setting, the MP setting wins.
|
||||
*/
|
||||
static void SetCommandDelay(int ticks);
|
||||
|
||||
/**
|
||||
* Sleep for X ticks. The code continues after this line when the X AI ticks
|
||||
* are passed. Mind that an AI tick is different from in-game ticks and
|
||||
* differ per AI speed.
|
||||
* @param ticks the ticks to wait
|
||||
* @pre ticks > 0.
|
||||
* @post the value of GetTick() will be changed exactly 'ticks' in value after
|
||||
* calling this.
|
||||
*/
|
||||
static void Sleep(int ticks);
|
||||
|
||||
/**
|
||||
* When Squirrel triggers a print, this function is called.
|
||||
* Squirrel calls this when 'print' is used, or when the script made an error.
|
||||
* @param error_msg If true, it is a Squirrel error message.
|
||||
* @param message The message Squirrel logged.
|
||||
* @note Use AILog.Info/Warning/Error instead of 'print'.
|
||||
*/
|
||||
static void Print(bool error_msg, const char *message);
|
||||
|
||||
private:
|
||||
typedef std::map<const char *, const char *, ltstr> LoadedLibraryList;
|
||||
|
||||
uint ticks;
|
||||
LoadedLibraryList loaded_library;
|
||||
int loaded_library_count;
|
||||
|
||||
/**
|
||||
* Register all classes that are known inside the NoAI API.
|
||||
*/
|
||||
void RegisterClasses();
|
||||
|
||||
/**
|
||||
* Check if a library is already loaded. If found, fake_class_name is filled
|
||||
* with the fake class name as given via AddLoadedLibrary. If not found,
|
||||
* next_number is set to the next number available for the fake namespace.
|
||||
* @param library_name The library to check if already loaded.
|
||||
* @param next_number The next available number for a library if not already loaded.
|
||||
* @param fake_class_name The name the library has if already loaded.
|
||||
* @param fake_class_name_len The maximum length of fake_class_name.
|
||||
* @return True if the library is already loaded.
|
||||
*/
|
||||
bool LoadedLibrary(const char *library_name, int *next_number, char *fake_class_name, int fake_class_name_len);
|
||||
|
||||
/**
|
||||
* Add a library as loaded.
|
||||
*/
|
||||
void AddLoadedLibrary(const char *library_name, const char *fake_class_name);
|
||||
};
|
||||
|
||||
#endif /* AI_CONTROLLER_HPP */
|
@ -0,0 +1,12 @@
|
||||
#include "ai_controller.hpp"
|
||||
|
||||
void SQAIController_Register(Squirrel *engine) {
|
||||
DefSQClass <AIController> SQAIController("AIController");
|
||||
SQAIController.PreRegister(engine);
|
||||
SQAIController.DefSQMethod(engine, &AIController::GetTick, "GetTick", 1, "x");
|
||||
SQAIController.DefSQStaticMethod(engine, &AIController::SetCommandDelay, "SetCommandDelay", 2, "xi");
|
||||
SQAIController.DefSQStaticMethod(engine, &AIController::Sleep, "Sleep", 2, "xi");
|
||||
SQAIController.DefSQStaticMethod(engine, &AIController::GetSetting, "GetSetting", 2, "xs");
|
||||
SQAIController.DefSQStaticMethod(engine, &AIController::Print, "Print", 3, "xbs");
|
||||
SQAIController.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_date.cpp Implementation of AIDate. */
|
||||
|
||||
#include "ai_date.hpp"
|
||||
#include "../../date_func.h"
|
||||
|
||||
/* static */ int32 AIDate::GetCurrentDate()
|
||||
{
|
||||
return ::_date;
|
||||
}
|
||||
|
||||
/* static */ int32 AIDate::GetYear(int32 date)
|
||||
{
|
||||
if (date < 0) return -1;
|
||||
|
||||
::YearMonthDay ymd;
|
||||
::ConvertDateToYMD(date, &ymd);
|
||||
return ymd.year;
|
||||
}
|
||||
|
||||
/* static */ int32 AIDate::GetMonth(int32 date)
|
||||
{
|
||||
if (date < 0) return -1;
|
||||
|
||||
::YearMonthDay ymd;
|
||||
::ConvertDateToYMD(date, &ymd);
|
||||
return ymd.month + 1;
|
||||
}
|
||||
|
||||
/* static */ int32 AIDate::GetDayOfMonth(int32 date)
|
||||
{
|
||||
if (date < 0) return -1;
|
||||
|
||||
::YearMonthDay ymd;
|
||||
::ConvertDateToYMD(date, &ymd);
|
||||
return ymd.day;
|
||||
}
|
||||
|
||||
/* static */ int32 AIDate::GetDate(int32 year, int32 month, int32 day_of_month)
|
||||
{
|
||||
if (month < 1 || month > 12) return -1;
|
||||
if (day_of_month < 1 || day_of_month > 31) return -1;
|
||||
if (year < 0 || year > MAX_YEAR) return -1;
|
||||
|
||||
return ::ConvertYMDToDate(year, month - 1, day_of_month);
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_date.hpp Everything to query and manipulate date related information. */
|
||||
|
||||
#ifndef AI_DATE_HPP
|
||||
#define AI_DATE_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
|
||||
/**
|
||||
* Class that handles all date related (calculation) functions.
|
||||
*
|
||||
* @note Months and days of month are 1-based; the first month of the
|
||||
* year is 1 and the first day of the month is also 1.
|
||||
* @note Years are zero based; they start with the year 0.
|
||||
* @note Dates can be used to determine the number of days between
|
||||
* two different moments in time because they count the number
|
||||
* of days since the year 0.
|
||||
*/
|
||||
class AIDate : public AIObject {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIDate"; }
|
||||
|
||||
/**
|
||||
* Get the current date.
|
||||
* This is the number of days since epoch under the assumption that
|
||||
* there is a leap year every 4 years, except when dividable by
|
||||
* 100 but not by 400.
|
||||
* @return The current date.
|
||||
*/
|
||||
static int32 GetCurrentDate();
|
||||
|
||||
/**
|
||||
* Get the year of the given date.
|
||||
* @param date The date to get the year of.
|
||||
* @return The year.
|
||||
*/
|
||||
static int32 GetYear(int32 date);
|
||||
|
||||
/**
|
||||
* Get the month of the given date.
|
||||
* @param date The date to get the month of.
|
||||
* @return The month.
|
||||
*/
|
||||
static int32 GetMonth(int32 date);
|
||||
|
||||
/**
|
||||
* Get the day (of the month) of the given date.
|
||||
* @param date The date to get the day of.
|
||||
* @return The day.
|
||||
*/
|
||||
static int32 GetDayOfMonth(int32 date);
|
||||
|
||||
/**
|
||||
* Get the date given a year, month and day of month.
|
||||
* @param year The year of the to-be determined date.
|
||||
* @param month The month of the to-be determined date.
|
||||
* @param day_of_month The day of month of the to-be determined date.
|
||||
* @return The date.
|
||||
*/
|
||||
static int32 GetDate(int32 year, int32 month, int32 day_of_month);
|
||||
};
|
||||
|
||||
#endif /* AI_DATE_HPP */
|
@ -0,0 +1,28 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_date.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AIDate to be used as Squirrel parameter */
|
||||
template <> AIDate *GetParam(ForceType<AIDate *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIDate *)instance; }
|
||||
template <> AIDate &GetParam(ForceType<AIDate &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIDate *)instance; }
|
||||
template <> const AIDate *GetParam(ForceType<const AIDate *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIDate *)instance; }
|
||||
template <> const AIDate &GetParam(ForceType<const AIDate &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIDate *)instance; }
|
||||
template <> int Return<AIDate *>(HSQUIRRELVM vm, AIDate *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIDate", res, NULL, DefSQDestructorCallback<AIDate>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIDate_Register(Squirrel *engine) {
|
||||
DefSQClass <AIDate> SQAIDate("AIDate");
|
||||
SQAIDate.PreRegister(engine);
|
||||
SQAIDate.AddConstructor<void (AIDate::*)(), 1>(engine, "x");
|
||||
|
||||
SQAIDate.DefSQStaticMethod(engine, &AIDate::GetClassName, "GetClassName", 1, "x");
|
||||
SQAIDate.DefSQStaticMethod(engine, &AIDate::GetCurrentDate, "GetCurrentDate", 1, "x");
|
||||
SQAIDate.DefSQStaticMethod(engine, &AIDate::GetYear, "GetYear", 2, "xi");
|
||||
SQAIDate.DefSQStaticMethod(engine, &AIDate::GetMonth, "GetMonth", 2, "xi");
|
||||
SQAIDate.DefSQStaticMethod(engine, &AIDate::GetDayOfMonth, "GetDayOfMonth", 2, "xi");
|
||||
SQAIDate.DefSQStaticMethod(engine, &AIDate::GetDate, "GetDate", 4, "xiii");
|
||||
|
||||
SQAIDate.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_depotlist.cpp Implementation of AIDepotList and friends. */
|
||||
|
||||
#include "ai_depotlist.hpp"
|
||||
#include "../../core/math_func.hpp"
|
||||
#include "../../tile_map.h"
|
||||
#include "../../company_func.h"
|
||||
#include "../../depot_base.h"
|
||||
#include "../../station_base.h"
|
||||
|
||||
AIDepotList::AIDepotList(AITile::TransportType transport_type)
|
||||
{
|
||||
::TileType tile_type;
|
||||
switch (transport_type) {
|
||||
default: return;
|
||||
|
||||
case AITile::TRANSPORT_ROAD: tile_type = ::MP_ROAD; break;
|
||||
case AITile::TRANSPORT_RAIL: tile_type = ::MP_RAILWAY; break;
|
||||
case AITile::TRANSPORT_WATER: tile_type = ::MP_WATER; break;
|
||||
|
||||
case AITile::TRANSPORT_AIR: {
|
||||
/* Hangars are not seen as real depots by the depot code. */
|
||||
const Station *st;
|
||||
FOR_ALL_STATIONS(st) {
|
||||
if (st->owner == ::_current_company) {
|
||||
const AirportFTAClass *afc = st->Airport();
|
||||
for (uint i = 0; i < afc->nof_depots; i++) {
|
||||
this->AddItem(st->xy + ToTileIndexDiff(afc->airport_depots[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle 'standard' depots. */
|
||||
const Depot *depot;
|
||||
FOR_ALL_DEPOTS(depot) {
|
||||
if (::GetTileOwner(depot->xy) == ::_current_company && ::IsTileType(depot->xy, tile_type)) this->AddItem(depot->xy);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_depotlist.hpp List all the depots (you own). */
|
||||
|
||||
#ifndef AI_DEPOTLIST_HPP
|
||||
#define AI_DEPOTLIST_HPP
|
||||
|
||||
#include "ai_abstractlist.hpp"
|
||||
#include "ai_tile.hpp"
|
||||
|
||||
/**
|
||||
* Creates a list of the locations of the depots (and hangars) of which you are the owner.
|
||||
* @ingroup AIList
|
||||
*/
|
||||
class AIDepotList : public AIAbstractList {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIDepotList"; }
|
||||
|
||||
/**
|
||||
* @param transport_type The type of transport to make a list of depots for.
|
||||
*/
|
||||
AIDepotList(AITile::TransportType transport_type);
|
||||
};
|
||||
|
||||
#endif /* AI_DEPOTLIST_HPP */
|
@ -0,0 +1,23 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_depotlist.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AIDepotList to be used as Squirrel parameter */
|
||||
template <> AIDepotList *GetParam(ForceType<AIDepotList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIDepotList *)instance; }
|
||||
template <> AIDepotList &GetParam(ForceType<AIDepotList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIDepotList *)instance; }
|
||||
template <> const AIDepotList *GetParam(ForceType<const AIDepotList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIDepotList *)instance; }
|
||||
template <> const AIDepotList &GetParam(ForceType<const AIDepotList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIDepotList *)instance; }
|
||||
template <> int Return<AIDepotList *>(HSQUIRRELVM vm, AIDepotList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIDepotList", res, NULL, DefSQDestructorCallback<AIDepotList>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIDepotList_Register(Squirrel *engine) {
|
||||
DefSQClass <AIDepotList> SQAIDepotList("AIDepotList");
|
||||
SQAIDepotList.PreRegister(engine, "AIAbstractList");
|
||||
SQAIDepotList.AddConstructor<void (AIDepotList::*)(AITile::TransportType transport_type), 2>(engine, "xi");
|
||||
|
||||
SQAIDepotList.DefSQStaticMethod(engine, &AIDepotList::GetClassName, "GetClassName", 1, "x");
|
||||
|
||||
SQAIDepotList.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,295 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_engine.cpp Implementation of AIEngine. */
|
||||
|
||||
#include "ai_engine.hpp"
|
||||
#include "ai_cargo.hpp"
|
||||
#include "ai_rail.hpp"
|
||||
#include "../../openttd.h"
|
||||
#include "../../company_func.h"
|
||||
#include "../../strings_func.h"
|
||||
#include "../../roadveh.h"
|
||||
#include "../../train.h"
|
||||
#include "../../ship.h"
|
||||
#include "../../aircraft.h"
|
||||
#include "../../vehicle_func.h"
|
||||
#include "../../core/alloc_func.hpp"
|
||||
#include "../../economy_func.h"
|
||||
#include "../../core/bitmath_func.hpp"
|
||||
#include "../../settings_type.h"
|
||||
#include "../../articulated_vehicles.h"
|
||||
#include "table/strings.h"
|
||||
|
||||
/* static */ bool AIEngine::IsValidEngine(EngineID engine_id)
|
||||
{
|
||||
return ::IsEngineIndex(engine_id) && HasBit(::GetEngine(engine_id)->company_avail, _current_company);
|
||||
}
|
||||
|
||||
/* static */ const char *AIEngine::GetName(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return NULL;
|
||||
|
||||
static const int len = 64;
|
||||
char *engine_name = MallocT<char>(len);
|
||||
|
||||
::SetDParam(0, engine_id);
|
||||
::GetString(engine_name, STR_ENGINE_NAME, &engine_name[len - 1]);
|
||||
return engine_name;
|
||||
}
|
||||
|
||||
/* static */ CargoID AIEngine::GetCargoType(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return CT_INVALID;
|
||||
|
||||
switch (::GetEngine(engine_id)->type) {
|
||||
case VEH_ROAD: {
|
||||
const RoadVehicleInfo *vi = ::RoadVehInfo(engine_id);
|
||||
return vi->cargo_type;
|
||||
} break;
|
||||
|
||||
case VEH_TRAIN: {
|
||||
const RailVehicleInfo *vi = ::RailVehInfo(engine_id);
|
||||
return vi->cargo_type;
|
||||
} break;
|
||||
|
||||
case VEH_SHIP: {
|
||||
const ShipVehicleInfo *vi = ::ShipVehInfo(engine_id);
|
||||
return vi->cargo_type;
|
||||
} break;
|
||||
|
||||
case VEH_AIRCRAFT: {
|
||||
return CT_PASSENGERS;
|
||||
} break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool AIEngine::CanRefitCargo(EngineID engine_id, CargoID cargo_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return false;
|
||||
if (!AICargo::IsValidCargo(cargo_id)) return false;
|
||||
|
||||
if (GetCargoType(engine_id) == cargo_id) return true;
|
||||
if (cargo_id == CT_MAIL && ::GetEngine(engine_id)->type == VEH_AIRCRAFT) return true;
|
||||
if (::GetEngine(engine_id)->type == VEH_SHIP && !ShipVehInfo(engine_id)->refittable) return false;
|
||||
return ::CanRefitTo(engine_id, cargo_id);
|
||||
}
|
||||
|
||||
/* static */ bool AIEngine::CanPullCargo(EngineID engine_id, CargoID cargo_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return false;
|
||||
if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return false;
|
||||
if (!AICargo::IsValidCargo(cargo_id)) return false;
|
||||
|
||||
return (::RailVehInfo(engine_id)->ai_passenger_only != 1) || AICargo::HasCargoClass(cargo_id, AICargo::CC_PASSENGERS);
|
||||
}
|
||||
|
||||
|
||||
/* static */ int32 AIEngine::GetCapacity(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return -1;
|
||||
|
||||
switch (::GetEngine(engine_id)->type) {
|
||||
case VEH_ROAD:
|
||||
case VEH_TRAIN: {
|
||||
uint16 *capacities = GetCapacityOfArticulatedParts(engine_id, ::GetEngine(engine_id)->type);
|
||||
for (CargoID c = 0; c < NUM_CARGO; c++) {
|
||||
if (capacities[c] == 0) continue;
|
||||
return capacities[c];
|
||||
}
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case VEH_SHIP: {
|
||||
const ShipVehicleInfo *vi = ::ShipVehInfo(engine_id);
|
||||
return vi->capacity;
|
||||
} break;
|
||||
|
||||
case VEH_AIRCRAFT: {
|
||||
const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine_id);
|
||||
return vi->passenger_capacity;
|
||||
} break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ int32 AIEngine::GetReliability(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return -1;
|
||||
|
||||
return (::GetEngine(engine_id)->reliability * 100 >> 16);
|
||||
}
|
||||
|
||||
/* static */ int32 AIEngine::GetMaxSpeed(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return -1;
|
||||
|
||||
switch (::GetEngine(engine_id)->type) {
|
||||
case VEH_ROAD: {
|
||||
const RoadVehicleInfo *vi = ::RoadVehInfo(engine_id);
|
||||
/* Internal speeds are km/h * 2 */
|
||||
return vi->max_speed / 2;
|
||||
} break;
|
||||
|
||||
case VEH_TRAIN: {
|
||||
const RailVehicleInfo *vi = ::RailVehInfo(engine_id);
|
||||
return vi->max_speed;
|
||||
} break;
|
||||
|
||||
case VEH_SHIP: {
|
||||
const ShipVehicleInfo *vi = ::ShipVehInfo(engine_id);
|
||||
/* Internal speeds are km/h * 2 */
|
||||
return vi->max_speed / 2;
|
||||
} break;
|
||||
|
||||
case VEH_AIRCRAFT: {
|
||||
const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine_id);
|
||||
return vi->max_speed / _settings_game.vehicle.plane_speed;
|
||||
} break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ Money AIEngine::GetPrice(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return -1;
|
||||
|
||||
switch (::GetEngine(engine_id)->type) {
|
||||
case VEH_ROAD: {
|
||||
const RoadVehicleInfo *vi = ::RoadVehInfo(engine_id);
|
||||
return (_price.roadveh_base >> 3) * vi->cost_factor >> 5;
|
||||
} break;
|
||||
|
||||
case VEH_TRAIN: {
|
||||
const RailVehicleInfo *vi = ::RailVehInfo(engine_id);
|
||||
return (_price.build_railvehicle >> 3) * vi->cost_factor >> 5;
|
||||
} break;
|
||||
|
||||
case VEH_SHIP: {
|
||||
const ShipVehicleInfo *vi = ::ShipVehInfo(engine_id);
|
||||
return (_price.ship_base >> 3) * vi->cost_factor >> 5;
|
||||
} break;
|
||||
|
||||
case VEH_AIRCRAFT: {
|
||||
const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine_id);
|
||||
return (_price.aircraft_base >> 3) * vi->cost_factor >> 5;
|
||||
} break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ int32 AIEngine::GetMaxAge(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return -1;
|
||||
|
||||
return ::GetEngine(engine_id)->lifelength * 366;
|
||||
}
|
||||
|
||||
/* static */ Money AIEngine::GetRunningCost(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return -1;
|
||||
|
||||
/* We need to create an instance in order to obtain GetRunningCost.
|
||||
* This means we temporary allocate a vehicle in the pool, but
|
||||
* there is no other way.. */
|
||||
Vehicle *vehicle;
|
||||
switch (::GetEngine(engine_id)->type) {
|
||||
case VEH_ROAD: {
|
||||
vehicle = new RoadVehicle();
|
||||
} break;
|
||||
|
||||
case VEH_TRAIN: {
|
||||
vehicle = new Train();
|
||||
} break;
|
||||
|
||||
case VEH_SHIP: {
|
||||
vehicle = new Ship();
|
||||
} break;
|
||||
|
||||
case VEH_AIRCRAFT: {
|
||||
vehicle = new Aircraft();
|
||||
} break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
|
||||
vehicle->engine_type = engine_id;
|
||||
Money runningCost = vehicle->GetRunningCost();
|
||||
delete vehicle;
|
||||
return runningCost >> 8;
|
||||
}
|
||||
|
||||
/* static */ AIVehicle::VehicleType AIEngine::GetVehicleType(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return AIVehicle::VEHICLE_INVALID;
|
||||
|
||||
switch (::GetEngine(engine_id)->type) {
|
||||
case VEH_ROAD: return AIVehicle::VEHICLE_ROAD;
|
||||
case VEH_TRAIN: return AIVehicle::VEHICLE_RAIL;
|
||||
case VEH_SHIP: return AIVehicle::VEHICLE_WATER;
|
||||
case VEH_AIRCRAFT: return AIVehicle::VEHICLE_AIR;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool AIEngine::IsWagon(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return false;
|
||||
if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return false;
|
||||
|
||||
return ::RailVehInfo(engine_id)->power == 0;
|
||||
}
|
||||
|
||||
/* static */ bool AIEngine::CanRunOnRail(EngineID engine_id, AIRail::RailType track_rail_type)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return false;
|
||||
if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return false;
|
||||
if (!AIRail::IsRailTypeAvailable(track_rail_type)) return false;
|
||||
|
||||
return ::IsCompatibleRail((::RailType)::RailVehInfo(engine_id)->railtype, (::RailType)track_rail_type);
|
||||
}
|
||||
|
||||
/* static */ bool AIEngine::HasPowerOnRail(EngineID engine_id, AIRail::RailType track_rail_type)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return false;
|
||||
if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return false;
|
||||
if (!AIRail::IsRailTypeAvailable(track_rail_type)) return false;
|
||||
|
||||
return ::HasPowerOnRail((::RailType)::RailVehInfo(engine_id)->railtype, (::RailType)track_rail_type);
|
||||
}
|
||||
|
||||
/* static */ AIRoad::RoadType AIEngine::GetRoadType(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return AIRoad::ROADTYPE_INVALID;
|
||||
if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_ROAD) return AIRoad::ROADTYPE_INVALID;
|
||||
|
||||
return HasBit(::EngInfo(engine_id)->misc_flags, EF_ROAD_TRAM) ? AIRoad::ROADTYPE_TRAM : AIRoad::ROADTYPE_ROAD;
|
||||
}
|
||||
|
||||
/* static */ AIRail::RailType AIEngine::GetRailType(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return AIRail::RAILTYPE_INVALID;
|
||||
if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return AIRail::RAILTYPE_INVALID;
|
||||
|
||||
return (AIRail::RailType)(uint)::RailVehInfo(engine_id)->railtype;
|
||||
}
|
||||
|
||||
/* static */ bool AIEngine::IsArticulated(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return false;
|
||||
if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_ROAD && GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return false;
|
||||
|
||||
return CountArticulatedParts(engine_id, true) != 0;
|
||||
}
|
||||
|
||||
/* static */ AIAirport::PlaneType AIEngine::GetPlaneType(EngineID engine_id)
|
||||
{
|
||||
if (!IsValidEngine(engine_id)) return AIAirport::PT_INVALID;
|
||||
if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_AIR) return AIAirport::PT_INVALID;
|
||||
|
||||
return (AIAirport::PlaneType)::AircraftVehInfo(engine_id)->subtype;
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_engine.hpp Everything to query and build engines. */
|
||||
|
||||
#ifndef AI_ENGINE_HPP
|
||||
#define AI_ENGINE_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
#include "ai_vehicle.hpp"
|
||||
#include "ai_road.hpp"
|
||||
#include "ai_rail.hpp"
|
||||
#include "ai_airport.hpp"
|
||||
|
||||
/**
|
||||
* Class that handles all engine related functions.
|
||||
*/
|
||||
class AIEngine : public AIObject {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEngine"; }
|
||||
|
||||
/**
|
||||
* Checks whether the given engine type is valid and buildable by you.
|
||||
* @param engine_id The engine to check.
|
||||
* @return True if and only if the engine type is valid.
|
||||
*/
|
||||
static bool IsValidEngine(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Get the name of an engine.
|
||||
* @param engine_id The engine to get the name of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @return The name the engine has.
|
||||
*/
|
||||
static const char *GetName(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Get the cargo-type of an engine. In case it can transport 2 cargos, it
|
||||
* returns the first.
|
||||
* @param engine_id The engine to get the cargo-type of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @return The cargo-type of the engine.
|
||||
*/
|
||||
static CargoID GetCargoType(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Check if the cargo of an engine can be refitted to your requested. If
|
||||
* the engine already allows this cargo, the function also returns true.
|
||||
* @param engine_id The engine to check for refitting.
|
||||
* @param cargo_id The cargo to check for refitting.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @pre AICargo::IsValidCargo(cargo_id).
|
||||
* @return True if the engine can carry this cargo, either via refit, or
|
||||
* by default.
|
||||
*/
|
||||
static bool CanRefitCargo(EngineID engine_id, CargoID cargo_id);
|
||||
|
||||
/**
|
||||
* Check if the engine can pull a wagon with the given cargo.
|
||||
* @param engine_id The engine to check.
|
||||
* @param cargo_id The cargo to check.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_RAIL.
|
||||
* @pre AICargo::IsValidCargo(cargo_id).
|
||||
* @return True if the engine can pull wagons carrying this cargo.
|
||||
* @note This function is not exhaustive; a true here does not mean
|
||||
* that the vehicle can pull the wagons, a false does mean it can't.
|
||||
*/
|
||||
static bool CanPullCargo(EngineID engine_id, CargoID cargo_id);
|
||||
|
||||
/**
|
||||
* Get the capacity of an engine. In case it can transport 2 cargos, it
|
||||
* returns the first.
|
||||
* @param engine_id The engine to get the capacity of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @return The capacity of the engine.
|
||||
*/
|
||||
static int32 GetCapacity(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Get the reliability of an engine. The value is between 0 and 100, where
|
||||
* 100 means 100% reliability (never breaks down) and 0 means 0%
|
||||
* reliability (you most likely don't want to buy it).
|
||||
* @param engine_id The engine to get the reliability of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @return The reliability the engine has.
|
||||
*/
|
||||
static int32 GetReliability(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Get the maximum speed of an engine.
|
||||
* @param engine_id The engine to get the maximum speed of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @return The maximum speed the engine has.
|
||||
* @note The speed is in km/h.
|
||||
*/
|
||||
static int32 GetMaxSpeed(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Get the new cost of an engine.
|
||||
* @param engine_id The engine to get the new cost of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @return The new cost the engine has.
|
||||
*/
|
||||
static Money GetPrice(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Get the maximum age of a brand new engine.
|
||||
* @param engine_id The engine to get the maximum age of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @returns The maximum age of a new engine in days.
|
||||
* @note Age is in days; divide by 366 to get per year.
|
||||
*/
|
||||
static int32 GetMaxAge(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Get the running cost of an engine.
|
||||
* @param engine_id The engine to get the running cost of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @return The running cost of a vehicle per year.
|
||||
* @note Cost is per year; divide by 364 to get per day.
|
||||
*/
|
||||
static Money GetRunningCost(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Get the type of an engine.
|
||||
* @param engine_id The engine to get the type of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @return The type the engine has.
|
||||
*/
|
||||
static AIVehicle::VehicleType GetVehicleType(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Check if an engine is a wagon.
|
||||
* @param engine_id The engine to check.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_RAIL.
|
||||
* @return Whether or not the engine is a wagon.
|
||||
*/
|
||||
static bool IsWagon(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Check if a train vehicle can run on a RailType.
|
||||
* @param engine_id The engine to check.
|
||||
* @param track_rail_type The type you want to check.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @pre GetVehicleType(engine_id) == AIVehicle::VEHICLE_RAIL.
|
||||
* @pre AIRail::IsRailTypeAvailable(track_rail_type).
|
||||
* @return Whether an engine of type 'engine_id' can run on 'track_rail_type'.
|
||||
* @note Even if a train can run on a RailType that doesn't mean that it'll be
|
||||
* able to power the train. Use HasPowerOnRail for that.
|
||||
*/
|
||||
static bool CanRunOnRail(EngineID engine_id, AIRail::RailType track_rail_type);
|
||||
|
||||
/**
|
||||
* Check if a train engine has power on a RailType.
|
||||
* @param engine_id The engine to check.
|
||||
* @param track_rail_type Another RailType.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @pre GetVehicleType(engine_id) == AIVehicle::VEHICLE_RAIL.
|
||||
* @pre AIRail::IsRailTypeAvailable(track_rail_type).
|
||||
* @return Whether an engine of type 'engine_id' has power on 'track_rail_type'.
|
||||
*/
|
||||
static bool HasPowerOnRail(EngineID engine_id, AIRail::RailType track_rail_type);
|
||||
|
||||
/**
|
||||
* Get the RoadType of the engine.
|
||||
* @param engine_id The engine to get the RoadType of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_ROAD.
|
||||
* @return The RoadType the engine has.
|
||||
*/
|
||||
static AIRoad::RoadType GetRoadType(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Get the RailType of the engine.
|
||||
* @param engine_id The engine to get the RailType of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_RAIL.
|
||||
* @return The RailType the engine has.
|
||||
*/
|
||||
static AIRail::RailType GetRailType(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Check if the engine is articulated.
|
||||
* @param engine_id The engine to check.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_ROAD || GetVehicleType(engine_id) == AIVehicle.VEHICLE_RAIL.
|
||||
* @return True if the engine is articulated.
|
||||
*/
|
||||
static bool IsArticulated(EngineID engine_id);
|
||||
|
||||
/**
|
||||
* Get the PlaneType of the engine.
|
||||
* @param engine_id The engine to get the PlaneType of.
|
||||
* @pre IsValidEngine(engine_id).
|
||||
* @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_AIR.
|
||||
* @return The PlaneType the engine has.
|
||||
*/
|
||||
static AIAirport::PlaneType GetPlaneType(EngineID engine_id);
|
||||
};
|
||||
|
||||
#endif /* AI_ENGINE_HPP */
|
@ -0,0 +1,42 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_engine.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AIEngine to be used as Squirrel parameter */
|
||||
template <> AIEngine *GetParam(ForceType<AIEngine *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEngine *)instance; }
|
||||
template <> AIEngine &GetParam(ForceType<AIEngine &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEngine *)instance; }
|
||||
template <> const AIEngine *GetParam(ForceType<const AIEngine *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEngine *)instance; }
|
||||
template <> const AIEngine &GetParam(ForceType<const AIEngine &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEngine *)instance; }
|
||||
template <> int Return<AIEngine *>(HSQUIRRELVM vm, AIEngine *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEngine", res, NULL, DefSQDestructorCallback<AIEngine>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIEngine_Register(Squirrel *engine) {
|
||||
DefSQClass <AIEngine> SQAIEngine("AIEngine");
|
||||
SQAIEngine.PreRegister(engine);
|
||||
SQAIEngine.AddConstructor<void (AIEngine::*)(), 1>(engine, "x");
|
||||
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetClassName, "GetClassName", 1, "x");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::IsValidEngine, "IsValidEngine", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetName, "GetName", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetCargoType, "GetCargoType", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::CanRefitCargo, "CanRefitCargo", 3, "xii");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::CanPullCargo, "CanPullCargo", 3, "xii");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetCapacity, "GetCapacity", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetReliability, "GetReliability", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetMaxSpeed, "GetMaxSpeed", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetPrice, "GetPrice", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetMaxAge, "GetMaxAge", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetRunningCost, "GetRunningCost", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetVehicleType, "GetVehicleType", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::IsWagon, "IsWagon", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::CanRunOnRail, "CanRunOnRail", 3, "xii");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::HasPowerOnRail, "HasPowerOnRail", 3, "xii");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetRoadType, "GetRoadType", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetRailType, "GetRailType", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::IsArticulated, "IsArticulated", 2, "xi");
|
||||
SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetPlaneType, "GetPlaneType", 2, "xi");
|
||||
|
||||
SQAIEngine.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_enginelist.cpp Implementation of AIEngineList and friends. */
|
||||
|
||||
#include "ai_enginelist.hpp"
|
||||
#include "../../company_func.h"
|
||||
#include "../../engine_base.h"
|
||||
#include "../../core/bitmath_func.hpp"
|
||||
|
||||
AIEngineList::AIEngineList(AIVehicle::VehicleType vehicle_type)
|
||||
{
|
||||
Engine *e;
|
||||
FOR_ALL_ENGINES_OF_TYPE(e, (::VehicleType)vehicle_type) {
|
||||
if (HasBit(e->company_avail, _current_company)) this->AddItem(e->index);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_enginelist.hpp List all the engines. */
|
||||
|
||||
#ifndef AI_ENGINELIST_HPP
|
||||
#define AI_ENGINELIST_HPP
|
||||
|
||||
#include "ai_abstractlist.hpp"
|
||||
#include "ai_vehicle.hpp"
|
||||
|
||||
/**
|
||||
* Create a list of engines based on a vehicle type.
|
||||
* @ingroup AIList
|
||||
*/
|
||||
class AIEngineList : public AIAbstractList {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEngineList"; }
|
||||
|
||||
/**
|
||||
* @param vehicle_type The type of vehicle to make a list of engines for.
|
||||
*/
|
||||
AIEngineList(AIVehicle::VehicleType vehicle_type);
|
||||
};
|
||||
|
||||
#endif /* AI_ENGINELIST_HPP */
|
@ -0,0 +1,23 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_enginelist.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AIEngineList to be used as Squirrel parameter */
|
||||
template <> AIEngineList *GetParam(ForceType<AIEngineList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEngineList *)instance; }
|
||||
template <> AIEngineList &GetParam(ForceType<AIEngineList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEngineList *)instance; }
|
||||
template <> const AIEngineList *GetParam(ForceType<const AIEngineList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEngineList *)instance; }
|
||||
template <> const AIEngineList &GetParam(ForceType<const AIEngineList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEngineList *)instance; }
|
||||
template <> int Return<AIEngineList *>(HSQUIRRELVM vm, AIEngineList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEngineList", res, NULL, DefSQDestructorCallback<AIEngineList>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIEngineList_Register(Squirrel *engine) {
|
||||
DefSQClass <AIEngineList> SQAIEngineList("AIEngineList");
|
||||
SQAIEngineList.PreRegister(engine, "AIAbstractList");
|
||||
SQAIEngineList.AddConstructor<void (AIEngineList::*)(AIVehicle::VehicleType vehicle_type), 2>(engine, "xi");
|
||||
|
||||
SQAIEngineList.DefSQStaticMethod(engine, &AIEngineList::GetClassName, "GetClassName", 1, "x");
|
||||
|
||||
SQAIEngineList.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_error.cpp Implementation of AIError. */
|
||||
|
||||
#include "ai_error.hpp"
|
||||
#include "table/strings.h"
|
||||
#include "../../core/bitmath_func.hpp"
|
||||
|
||||
AIError::AIErrorMap AIError::error_map = AIError::AIErrorMap();
|
||||
AIError::AIErrorMapString AIError::error_map_string = AIError::AIErrorMapString();
|
||||
|
||||
/* static */ AIErrorType AIError::GetLastError()
|
||||
{
|
||||
return AIObject::GetLastError();
|
||||
}
|
||||
|
||||
/* static */ const char *AIError::GetLastErrorString()
|
||||
{
|
||||
return (*error_map_string.find(AIError::GetLastError())).second;
|
||||
}
|
||||
|
||||
/* static */ AIErrorType AIError::StringToError(StringID internal_string_id)
|
||||
{
|
||||
uint index = GB(internal_string_id, 11, 5);
|
||||
switch (GB(internal_string_id, 11, 5)) {
|
||||
case 26: case 28: case 29: case 30: // NewGRF strings.
|
||||
return ERR_NEWGRF_SUPPLIED_ERROR;
|
||||
|
||||
/* DO NOT SWAP case 14 and 4 because that will break StringToError due
|
||||
* to the index dependency that relies on FALL THROUGHs. */
|
||||
case 14: if (index < 0xE4) break; // Player name
|
||||
case 4: if (index < 0xC0) break; // Town name
|
||||
case 15: // Custom name
|
||||
case 31: // Dynamic strings
|
||||
/* These strings are 'random' and have no meaning.
|
||||
* They actually shouldn't even be returned as error messages. */
|
||||
return ERR_UNKNOWN;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
AIErrorMap::iterator it = error_map.find(internal_string_id);
|
||||
if (it == error_map.end()) return ERR_UNKNOWN;
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
/* static */ void AIError::RegisterErrorMap(StringID internal_string_id, AIErrorType ai_error_msg)
|
||||
{
|
||||
error_map[internal_string_id] = ai_error_msg;
|
||||
}
|
||||
|
||||
/* static */ void AIError::RegisterErrorMapString(AIErrorType ai_error_msg, const char *message)
|
||||
{
|
||||
error_map_string[ai_error_msg] = message;
|
||||
}
|
||||
|
||||
/* static */ AIError::ErrorCategories AIError::GetErrorCategory() {
|
||||
return (AIError::ErrorCategories)(GetLastError() >> (uint)ERR_CAT_BIT_SIZE);
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_error.hpp Everything to query errors. */
|
||||
|
||||
#ifndef AI_ERROR_HPP
|
||||
#define AI_ERROR_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* Helper to write precondition enforcers for the AI API in an abbreviated manner.
|
||||
* @param returnval The value to return on failure.
|
||||
* @param condition The condition that must be obeyed.
|
||||
*/
|
||||
#define EnforcePrecondition(returnval, condition) \
|
||||
if (!(condition)) { \
|
||||
AIObject::SetLastError(AIError::ERR_PRECONDITION_FAILED); \
|
||||
return returnval; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to write precondition enforcers for the AI API in an abbreviated manner.
|
||||
* @param returnval The value to return on failure.
|
||||
* @param condition The condition that must be obeyed.
|
||||
* @param error_code The error code passed to AIObject::SetLastError.
|
||||
*/
|
||||
#define EnforcePreconditionCustomError(returnval, condition, error_code) \
|
||||
if (!(condition)) { \
|
||||
AIObject::SetLastError(error_code); \
|
||||
return returnval; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that handles all error related functions.
|
||||
*/
|
||||
class AIError : public AIObject {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIError"; }
|
||||
|
||||
/**
|
||||
* All categories errors can be divided in.
|
||||
*/
|
||||
enum ErrorCategories {
|
||||
ERR_CAT_NONE = 0, //!< Error messages not related to any category.
|
||||
ERR_CAT_GENERAL, //!< Error messages related to general things.
|
||||
ERR_CAT_VEHICLE, //!< Error messages related to building / maintaining vehicles.
|
||||
ERR_CAT_STATION, //!< Error messages related to building / maintaining stations.
|
||||
ERR_CAT_BRIDGE, //!< Error messages related to building / removing bridges.
|
||||
ERR_CAT_TUNNEL, //!< Error messages related to building / removing tunnels.
|
||||
ERR_CAT_TILE, //!< Error messages related to raising / lowering and demolishing tiles.
|
||||
ERR_CAT_SIGN, //!< Error messages related to building / removing signs.
|
||||
ERR_CAT_RAIL, //!< Error messages related to building / maintaining rails.
|
||||
ERR_CAT_ROAD, //!< Error messages related to building / maintaining roads.
|
||||
ERR_CAT_ORDER, //!< Error messages related to managing orders.
|
||||
ERR_CAT_MARINE, //!< Error messages related to building / removing ships, docks and channels.
|
||||
|
||||
/**
|
||||
* DO NOT USE! The error bitsize determines how many errors can be stored in
|
||||
* a category and what the offsets are of all categories.
|
||||
*/
|
||||
ERR_CAT_BIT_SIZE = 8,
|
||||
};
|
||||
|
||||
/**
|
||||
* All general related error messages.
|
||||
*/
|
||||
enum ErrorMessages {
|
||||
/** Initial error value */
|
||||
ERR_NONE = ERR_CAT_NONE << ERR_CAT_BIT_SIZE, // []
|
||||
/** If an error occured and the error wasn't mapped */
|
||||
ERR_UNKNOWN, // []
|
||||
/** If a precondition is not met */
|
||||
ERR_PRECONDITION_FAILED, // []
|
||||
/** A string supplied was too long */
|
||||
ERR_PRECONDITION_STRING_TOO_LONG, // []
|
||||
/** An error returned by a NewGRF. No possibility to get the exact error in an AI readable format */
|
||||
ERR_NEWGRF_SUPPLIED_ERROR, // []
|
||||
|
||||
/** Base for general errors */
|
||||
ERR_GENERAL_BASE = ERR_CAT_GENERAL << ERR_CAT_BIT_SIZE,
|
||||
|
||||
/** Not enough cash to perform the previous action */
|
||||
ERR_NOT_ENOUGH_CASH, // [STR_0003_NOT_ENOUGH_CASH_REQUIRES]
|
||||
|
||||
/** Local authority won't allow the previous action */
|
||||
ERR_LOCAL_AUTHORITY_REFUSES, // [STR_2009_LOCAL_AUTHORITY_REFUSES]
|
||||
|
||||
/** The piece of infrastructure you tried to build is already in place */
|
||||
ERR_ALREADY_BUILT, // [STR_1007_ALREADY_BUILT, STR_5007_MUST_DEMOLISH_BRIDGE_FIRST]
|
||||
|
||||
/** Area isn't clear, try to demolish the building on it */
|
||||
ERR_AREA_NOT_CLEAR, // [STR_2004_BUILDING_MUST_BE_DEMOLISHED, STR_5007_MUST_DEMOLISH_BRIDGE_FIRST, STR_300B_MUST_DEMOLISH_RAILROAD, STR_300E_MUST_DEMOLISH_AIRPORT_FIRST, STR_MUST_DEMOLISH_CARGO_TRAM_STATION, STR_3047_MUST_DEMOLISH_TRUCK_STATION, STR_MUST_DEMOLISH_PASSENGER_TRAM_STATION, STR_3046_MUST_DEMOLISH_BUS_STATION, STR_306A_BUOY_IN_THE_WAY, STR_304D_MUST_DEMOLISH_DOCK_FIRST, STR_4800_IN_THE_WAY, STR_5804_COMPANY_HEADQUARTERS_IN, STR_5800_OBJECT_IN_THE_WAY, STR_1801_MUST_REMOVE_ROAD_FIRST, STR_1008_MUST_REMOVE_RAILROAD_TRACK, STR_5007_MUST_DEMOLISH_BRIDGE_FIRST, STR_5006_MUST_DEMOLISH_TUNNEL_FIRST, STR_1002_EXCAVATION_WOULD_DAMAGE]
|
||||
|
||||
/** Area / property is owned by another company */
|
||||
ERR_OWNED_BY_ANOTHER_COMPANY, // [STR_1024_AREA_IS_OWNED_BY_ANOTHER, STR_013B_OWNED_BY]
|
||||
|
||||
/** The name given is not unique for the object type */
|
||||
ERR_NAME_IS_NOT_UNIQUE, // [STR_NAME_MUST_BE_UNIQUE]
|
||||
|
||||
/** The building you want to build requires flat land */
|
||||
ERR_FLAT_LAND_REQUIRED, // [STR_0007_FLAT_LAND_REQUIRED]
|
||||
|
||||
/** Land is sloped in the wrong direction for this build action */
|
||||
ERR_LAND_SLOPED_WRONG, // [STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION]
|
||||
|
||||
/** A vehicle is in the way */
|
||||
ERR_VEHICLE_IN_THE_WAY, // [STR_8803_TRAIN_IN_THE_WAY, STR_9000_ROAD_VEHICLE_IN_THE_WAY, STR_980E_SHIP_IN_THE_WAY, STR_A015_AIRCRAFT_IN_THE_WAY]
|
||||
|
||||
/** Site is unsuitable */
|
||||
ERR_SITE_UNSUITABLE, // [STR_0239_SITE_UNSUITABLE, STR_304B_SITE_UNSUITABLE]
|
||||
|
||||
/** Too close to the edge of the map */
|
||||
ERR_TOO_CLOSE_TO_EDGE, // [STR_0002_TOO_CLOSE_TO_EDGE_OF_MAP]
|
||||
|
||||
/** Station is too spread out */
|
||||
ERR_STATION_TOO_SPREAD_OUT, // [STR_306C_STATION_TOO_SPREAD_OUT]
|
||||
};
|
||||
|
||||
/**
|
||||
* Check the membership of the last thrown error.
|
||||
* @return The category the error belongs to.
|
||||
* @note The last throw error can be aquired by calling GetLastError().
|
||||
*/
|
||||
static ErrorCategories GetErrorCategory();
|
||||
|
||||
/**
|
||||
* Get the last error.
|
||||
* @return An ErrorMessages enum value.
|
||||
*/
|
||||
static AIErrorType GetLastError();
|
||||
|
||||
/**
|
||||
* Get the last error in string format (for human readability).
|
||||
* @return An ErrorMessage enum item, as string.
|
||||
*/
|
||||
static const char *GetLastErrorString();
|
||||
|
||||
/**
|
||||
* Get the error based on the OpenTTD StringID.
|
||||
* @note DO NOT INVOKE THIS METHOD YOURSELF!
|
||||
* @param internal_string_id The string to convert.
|
||||
* @return The NoAI equivalent error message.
|
||||
*/
|
||||
static AIErrorType StringToError(StringID internal_string_id);
|
||||
|
||||
/**
|
||||
* Map an internal OpenTTD error message to it's NoAI equivalent.
|
||||
* @note DO NOT INVOKE THIS METHOD YOURSELF! The calls are autogenerated.
|
||||
* @param internal_string_id The OpenTTD StringID used for an error.
|
||||
* @param ai_error_msg The NoAI equivalent error message.
|
||||
*/
|
||||
static void RegisterErrorMap(StringID internal_string_id, AIErrorType ai_error_msg);
|
||||
|
||||
/**
|
||||
* Map an internal OpenTTD error message to it's NoAI equivalent.
|
||||
* @note DO NOT INVOKE THIS METHOD YOURSELF! The calls are autogenerated.
|
||||
* @param ai_error_msg The NoAI error message representation.
|
||||
* @param message The string representation of this error message, used for debug purposes.
|
||||
*/
|
||||
static void RegisterErrorMapString(AIErrorType ai_error_msg, const char *message);
|
||||
|
||||
private:
|
||||
typedef std::map<StringID, AIErrorType> AIErrorMap;
|
||||
typedef std::map<AIErrorType, const char *> AIErrorMapString;
|
||||
|
||||
static AIErrorMap error_map;
|
||||
static AIErrorMapString error_map_string;
|
||||
};
|
||||
|
||||
#endif /* AI_ERROR_HPP */
|
@ -0,0 +1,121 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_error.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow enums to be used as Squirrel parameters */
|
||||
template <> AIError::ErrorCategories GetParam(ForceType<AIError::ErrorCategories>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIError::ErrorCategories)tmp; }
|
||||
template <> int Return<AIError::ErrorCategories>(HSQUIRRELVM vm, AIError::ErrorCategories res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
template <> AIError::ErrorMessages GetParam(ForceType<AIError::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIError::ErrorMessages)tmp; }
|
||||
template <> int Return<AIError::ErrorMessages>(HSQUIRRELVM vm, AIError::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
|
||||
/* Allow AIError to be used as Squirrel parameter */
|
||||
template <> AIError *GetParam(ForceType<AIError *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIError *)instance; }
|
||||
template <> AIError &GetParam(ForceType<AIError &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIError *)instance; }
|
||||
template <> const AIError *GetParam(ForceType<const AIError *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIError *)instance; }
|
||||
template <> const AIError &GetParam(ForceType<const AIError &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIError *)instance; }
|
||||
template <> int Return<AIError *>(HSQUIRRELVM vm, AIError *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIError", res, NULL, DefSQDestructorCallback<AIError>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIError_Register(Squirrel *engine) {
|
||||
DefSQClass <AIError> SQAIError("AIError");
|
||||
SQAIError.PreRegister(engine);
|
||||
SQAIError.AddConstructor<void (AIError::*)(), 1>(engine, "x");
|
||||
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_NONE, "ERR_CAT_NONE");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_GENERAL, "ERR_CAT_GENERAL");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_VEHICLE, "ERR_CAT_VEHICLE");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_STATION, "ERR_CAT_STATION");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_BRIDGE, "ERR_CAT_BRIDGE");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_TUNNEL, "ERR_CAT_TUNNEL");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_TILE, "ERR_CAT_TILE");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_SIGN, "ERR_CAT_SIGN");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_RAIL, "ERR_CAT_RAIL");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_ROAD, "ERR_CAT_ROAD");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_ORDER, "ERR_CAT_ORDER");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_MARINE, "ERR_CAT_MARINE");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_CAT_BIT_SIZE, "ERR_CAT_BIT_SIZE");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_NONE, "ERR_NONE");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_UNKNOWN, "ERR_UNKNOWN");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_PRECONDITION_FAILED, "ERR_PRECONDITION_FAILED");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_PRECONDITION_STRING_TOO_LONG, "ERR_PRECONDITION_STRING_TOO_LONG");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_NEWGRF_SUPPLIED_ERROR, "ERR_NEWGRF_SUPPLIED_ERROR");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_GENERAL_BASE, "ERR_GENERAL_BASE");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_NOT_ENOUGH_CASH, "ERR_NOT_ENOUGH_CASH");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_LOCAL_AUTHORITY_REFUSES, "ERR_LOCAL_AUTHORITY_REFUSES");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_ALREADY_BUILT, "ERR_ALREADY_BUILT");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_AREA_NOT_CLEAR, "ERR_AREA_NOT_CLEAR");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_OWNED_BY_ANOTHER_COMPANY, "ERR_OWNED_BY_ANOTHER_COMPANY");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_NAME_IS_NOT_UNIQUE, "ERR_NAME_IS_NOT_UNIQUE");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_FLAT_LAND_REQUIRED, "ERR_FLAT_LAND_REQUIRED");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_LAND_SLOPED_WRONG, "ERR_LAND_SLOPED_WRONG");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_VEHICLE_IN_THE_WAY, "ERR_VEHICLE_IN_THE_WAY");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_SITE_UNSUITABLE, "ERR_SITE_UNSUITABLE");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_TOO_CLOSE_TO_EDGE, "ERR_TOO_CLOSE_TO_EDGE");
|
||||
SQAIError.DefSQConst(engine, AIError::ERR_STATION_TOO_SPREAD_OUT, "ERR_STATION_TOO_SPREAD_OUT");
|
||||
|
||||
AIError::RegisterErrorMap(STR_0003_NOT_ENOUGH_CASH_REQUIRES, AIError::ERR_NOT_ENOUGH_CASH);
|
||||
AIError::RegisterErrorMap(STR_2009_LOCAL_AUTHORITY_REFUSES, AIError::ERR_LOCAL_AUTHORITY_REFUSES);
|
||||
AIError::RegisterErrorMap(STR_1007_ALREADY_BUILT, AIError::ERR_ALREADY_BUILT);
|
||||
AIError::RegisterErrorMap(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST, AIError::ERR_ALREADY_BUILT);
|
||||
AIError::RegisterErrorMap(STR_2004_BUILDING_MUST_BE_DEMOLISHED, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_300B_MUST_DEMOLISH_RAILROAD, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_300E_MUST_DEMOLISH_AIRPORT_FIRST, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_MUST_DEMOLISH_CARGO_TRAM_STATION, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_3047_MUST_DEMOLISH_TRUCK_STATION, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_MUST_DEMOLISH_PASSENGER_TRAM_STATION, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_3046_MUST_DEMOLISH_BUS_STATION, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_306A_BUOY_IN_THE_WAY, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_304D_MUST_DEMOLISH_DOCK_FIRST, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_4800_IN_THE_WAY, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_5804_COMPANY_HEADQUARTERS_IN, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_5800_OBJECT_IN_THE_WAY, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_1801_MUST_REMOVE_ROAD_FIRST, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_1008_MUST_REMOVE_RAILROAD_TRACK, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_5006_MUST_DEMOLISH_TUNNEL_FIRST, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_1002_EXCAVATION_WOULD_DAMAGE, AIError::ERR_AREA_NOT_CLEAR);
|
||||
AIError::RegisterErrorMap(STR_1024_AREA_IS_OWNED_BY_ANOTHER, AIError::ERR_OWNED_BY_ANOTHER_COMPANY);
|
||||
AIError::RegisterErrorMap(STR_013B_OWNED_BY, AIError::ERR_OWNED_BY_ANOTHER_COMPANY);
|
||||
AIError::RegisterErrorMap(STR_NAME_MUST_BE_UNIQUE, AIError::ERR_NAME_IS_NOT_UNIQUE);
|
||||
AIError::RegisterErrorMap(STR_0007_FLAT_LAND_REQUIRED, AIError::ERR_FLAT_LAND_REQUIRED);
|
||||
AIError::RegisterErrorMap(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION, AIError::ERR_LAND_SLOPED_WRONG);
|
||||
AIError::RegisterErrorMap(STR_8803_TRAIN_IN_THE_WAY, AIError::ERR_VEHICLE_IN_THE_WAY);
|
||||
AIError::RegisterErrorMap(STR_9000_ROAD_VEHICLE_IN_THE_WAY, AIError::ERR_VEHICLE_IN_THE_WAY);
|
||||
AIError::RegisterErrorMap(STR_980E_SHIP_IN_THE_WAY, AIError::ERR_VEHICLE_IN_THE_WAY);
|
||||
AIError::RegisterErrorMap(STR_A015_AIRCRAFT_IN_THE_WAY, AIError::ERR_VEHICLE_IN_THE_WAY);
|
||||
AIError::RegisterErrorMap(STR_0239_SITE_UNSUITABLE, AIError::ERR_SITE_UNSUITABLE);
|
||||
AIError::RegisterErrorMap(STR_304B_SITE_UNSUITABLE, AIError::ERR_SITE_UNSUITABLE);
|
||||
AIError::RegisterErrorMap(STR_0002_TOO_CLOSE_TO_EDGE_OF_MAP, AIError::ERR_TOO_CLOSE_TO_EDGE);
|
||||
AIError::RegisterErrorMap(STR_306C_STATION_TOO_SPREAD_OUT, AIError::ERR_STATION_TOO_SPREAD_OUT);
|
||||
|
||||
AIError::RegisterErrorMapString(AIError::ERR_NONE, "ERR_NONE");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_UNKNOWN, "ERR_UNKNOWN");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_PRECONDITION_FAILED, "ERR_PRECONDITION_FAILED");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_PRECONDITION_STRING_TOO_LONG, "ERR_PRECONDITION_STRING_TOO_LONG");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_NEWGRF_SUPPLIED_ERROR, "ERR_NEWGRF_SUPPLIED_ERROR");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_NOT_ENOUGH_CASH, "ERR_NOT_ENOUGH_CASH");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_LOCAL_AUTHORITY_REFUSES, "ERR_LOCAL_AUTHORITY_REFUSES");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_ALREADY_BUILT, "ERR_ALREADY_BUILT");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_AREA_NOT_CLEAR, "ERR_AREA_NOT_CLEAR");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_OWNED_BY_ANOTHER_COMPANY, "ERR_OWNED_BY_ANOTHER_COMPANY");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_NAME_IS_NOT_UNIQUE, "ERR_NAME_IS_NOT_UNIQUE");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_FLAT_LAND_REQUIRED, "ERR_FLAT_LAND_REQUIRED");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_LAND_SLOPED_WRONG, "ERR_LAND_SLOPED_WRONG");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_VEHICLE_IN_THE_WAY, "ERR_VEHICLE_IN_THE_WAY");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_SITE_UNSUITABLE, "ERR_SITE_UNSUITABLE");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_TOO_CLOSE_TO_EDGE, "ERR_TOO_CLOSE_TO_EDGE");
|
||||
AIError::RegisterErrorMapString(AIError::ERR_STATION_TOO_SPREAD_OUT, "ERR_STATION_TOO_SPREAD_OUT");
|
||||
|
||||
SQAIError.DefSQStaticMethod(engine, &AIError::GetClassName, "GetClassName", 1, "x");
|
||||
SQAIError.DefSQStaticMethod(engine, &AIError::GetErrorCategory, "GetErrorCategory", 1, "x");
|
||||
SQAIError.DefSQStaticMethod(engine, &AIError::GetLastError, "GetLastError", 1, "x");
|
||||
SQAIError.DefSQStaticMethod(engine, &AIError::GetLastErrorString, "GetLastErrorString", 1, "x");
|
||||
SQAIError.DefSQStaticMethod(engine, &AIError::StringToError, "StringToError", 2, "xi");
|
||||
SQAIError.DefSQStaticMethod(engine, &AIError::RegisterErrorMap, "RegisterErrorMap", 3, "xii");
|
||||
SQAIError.DefSQStaticMethod(engine, &AIError::RegisterErrorMapString, "RegisterErrorMapString", 3, "xis");
|
||||
|
||||
SQAIError.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_event.cpp Implementation of AIEvent. */
|
||||
|
||||
#include "ai_event.hpp"
|
||||
#include "ai_event_types.hpp"
|
||||
|
||||
#include <queue>
|
||||
#include <set>
|
||||
|
||||
struct AIEventData {
|
||||
std::queue<AIEvent *> stack;
|
||||
};
|
||||
|
||||
/* static */ void AIEventController::CreateEventPointer()
|
||||
{
|
||||
assert(AIObject::GetEventPointer() == NULL);
|
||||
|
||||
AIObject::GetEventPointer() = new AIEventData();
|
||||
}
|
||||
|
||||
/* static */ void AIEventController::FreeEventPointer()
|
||||
{
|
||||
AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
|
||||
|
||||
/* Free all waiting events (if any) */
|
||||
while (!data->stack.empty()) {
|
||||
AIEvent *e = data->stack.front();
|
||||
data->stack.pop();
|
||||
e->Release();
|
||||
}
|
||||
|
||||
/* Now kill our data pointer */
|
||||
delete data;
|
||||
}
|
||||
|
||||
/* static */ bool AIEventController::IsEventWaiting()
|
||||
{
|
||||
if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer();
|
||||
AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
|
||||
|
||||
return !data->stack.empty();
|
||||
}
|
||||
|
||||
/* static */ AIEvent *AIEventController::GetNextEvent()
|
||||
{
|
||||
if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer();
|
||||
AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
|
||||
|
||||
if (data->stack.empty()) return NULL;
|
||||
|
||||
AIEvent *e = data->stack.front();
|
||||
data->stack.pop();
|
||||
return e;
|
||||
}
|
||||
|
||||
/* static */ void AIEventController::InsertEvent(AIEvent *event)
|
||||
{
|
||||
if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer();
|
||||
AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
|
||||
|
||||
event->AddRef();
|
||||
data->stack.push(event);
|
||||
}
|
||||
|
@ -0,0 +1,107 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_event.hpp Everything to handle events from the game. */
|
||||
|
||||
#ifndef AI_EVENT_HPP
|
||||
#define AI_EVENT_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
|
||||
/**
|
||||
* Class that handles all event related functions.
|
||||
* You can lookup the type, and than convert it to the real event-class.
|
||||
* That way you can request more detailed information about the event.
|
||||
*/
|
||||
class AIEvent : public AIObject {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEvent"; }
|
||||
|
||||
/**
|
||||
* The type of event. Needed to lookup the detailed class.
|
||||
*/
|
||||
enum AIEventType {
|
||||
AI_ET_INVALID = 0,
|
||||
AI_ET_TEST,
|
||||
AI_ET_SUBSIDY_OFFER,
|
||||
AI_ET_SUBSIDY_OFFER_EXPIRED,
|
||||
AI_ET_SUBSIDY_AWARDED,
|
||||
AI_ET_SUBSIDY_EXPIRED,
|
||||
AI_ET_ENGINE_PREVIEW,
|
||||
AI_ET_COMPANY_NEW,
|
||||
AI_ET_COMPANY_IN_TROUBLE,
|
||||
AI_ET_COMPANY_MERGER,
|
||||
AI_ET_COMPANY_BANKRUPT,
|
||||
AI_ET_VEHICLE_CRASHED,
|
||||
AI_ET_VEHICLE_LOST,
|
||||
AI_ET_VEHICLE_WAITING_IN_DEPOT,
|
||||
AI_ET_VEHICLE_UNPROFITABLE,
|
||||
AI_ET_INDUSTRY_OPEN,
|
||||
AI_ET_INDUSTRY_CLOSE,
|
||||
AI_ET_ENGINE_AVAILABLE,
|
||||
AI_ET_STATION_FIRST_VEHICLE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor of AIEvent, to get the type of event.
|
||||
*/
|
||||
AIEvent(AIEvent::AIEventType type) :
|
||||
type(type)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Get the event-type.
|
||||
* @return The @c AIEventType.
|
||||
*/
|
||||
AIEventType GetEventType() { return this->type; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The type of this event.
|
||||
*/
|
||||
AIEventType type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class that handles all event related functions.
|
||||
* @note it is not needed to create an instance of AIEvent to access it, as
|
||||
* all members are static, and all data is stored AI-wide.
|
||||
*/
|
||||
class AIEventController : public AIObject {
|
||||
public:
|
||||
/**
|
||||
* The name of the class, needed by several sub-processes.
|
||||
*/
|
||||
static const char *GetClassName() { return "AIEventController"; }
|
||||
|
||||
/**
|
||||
* Check if there is an event waiting.
|
||||
* @return true if there is an event on the stack.
|
||||
*/
|
||||
static bool IsEventWaiting();
|
||||
|
||||
/**
|
||||
* Get the next event.
|
||||
* @return a class of the event-child issues.
|
||||
*/
|
||||
static AIEvent *GetNextEvent();
|
||||
|
||||
/**
|
||||
* Insert an event to the queue for the company.
|
||||
* @param event The event to insert.
|
||||
*/
|
||||
static void InsertEvent(AIEvent *event);
|
||||
|
||||
/**
|
||||
* Free the event pointer.
|
||||
* @note DO NOT CALL YOURSELF; leave it to the internal AI programming.
|
||||
*/
|
||||
static void FreeEventPointer();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Create the event pointer.
|
||||
*/
|
||||
static void CreateEventPointer();
|
||||
};
|
||||
|
||||
#endif /* AI_EVENT_HPP */
|
@ -0,0 +1,72 @@
|
||||
/* $Id$ */
|
||||
/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
|
||||
|
||||
#include "ai_event.hpp"
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow enums to be used as Squirrel parameters */
|
||||
template <> AIEvent::AIEventType GetParam(ForceType<AIEvent::AIEventType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIEvent::AIEventType)tmp; }
|
||||
template <> int Return<AIEvent::AIEventType>(HSQUIRRELVM vm, AIEvent::AIEventType res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
|
||||
/* Allow AIEvent to be used as Squirrel parameter */
|
||||
template <> AIEvent *GetParam(ForceType<AIEvent *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEvent *)instance; }
|
||||
template <> AIEvent &GetParam(ForceType<AIEvent &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEvent *)instance; }
|
||||
template <> const AIEvent *GetParam(ForceType<const AIEvent *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEvent *)instance; }
|
||||
template <> const AIEvent &GetParam(ForceType<const AIEvent &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEvent *)instance; }
|
||||
template <> int Return<AIEvent *>(HSQUIRRELVM vm, AIEvent *res) { if (res == NULL) { sq_pushnull(vm); return 1; } Squirrel::CreateClassInstanceVM(vm, "AIEvent", res, NULL, DefSQDestructorCallback<AIEvent>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIEvent_Register(Squirrel *engine) {
|
||||
DefSQClass <AIEvent> SQAIEvent("AIEvent");
|
||||
SQAIEvent.PreRegister(engine);
|
||||
SQAIEvent.AddConstructor<void (AIEvent::*)(AIEvent::AIEventType type), 2>(engine, "xi");
|
||||
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_INVALID, "AI_ET_INVALID");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_TEST, "AI_ET_TEST");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_SUBSIDY_OFFER, "AI_ET_SUBSIDY_OFFER");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_SUBSIDY_OFFER_EXPIRED, "AI_ET_SUBSIDY_OFFER_EXPIRED");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_SUBSIDY_AWARDED, "AI_ET_SUBSIDY_AWARDED");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_SUBSIDY_EXPIRED, "AI_ET_SUBSIDY_EXPIRED");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_ENGINE_PREVIEW, "AI_ET_ENGINE_PREVIEW");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_NEW, "AI_ET_COMPANY_NEW");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_IN_TROUBLE, "AI_ET_COMPANY_IN_TROUBLE");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_MERGER, "AI_ET_COMPANY_MERGER");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_BANKRUPT, "AI_ET_COMPANY_BANKRUPT");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_VEHICLE_CRASHED, "AI_ET_VEHICLE_CRASHED");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_VEHICLE_LOST, "AI_ET_VEHICLE_LOST");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_VEHICLE_WAITING_IN_DEPOT, "AI_ET_VEHICLE_WAITING_IN_DEPOT");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_VEHICLE_UNPROFITABLE, "AI_ET_VEHICLE_UNPROFITABLE");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_INDUSTRY_OPEN, "AI_ET_INDUSTRY_OPEN");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_INDUSTRY_CLOSE, "AI_ET_INDUSTRY_CLOSE");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_ENGINE_AVAILABLE, "AI_ET_ENGINE_AVAILABLE");
|
||||
SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_STATION_FIRST_VEHICLE, "AI_ET_STATION_FIRST_VEHICLE");
|
||||
|
||||
SQAIEvent.DefSQStaticMethod(engine, &AIEvent::GetClassName, "GetClassName", 1, "x");
|
||||
|
||||
SQAIEvent.DefSQMethod(engine, &AIEvent::GetEventType, "GetEventType", 1, "x");
|
||||
|
||||
SQAIEvent.PostRegister(engine);
|
||||
}
|
||||
|
||||
namespace SQConvert {
|
||||
/* Allow AIEventController to be used as Squirrel parameter */
|
||||
template <> AIEventController *GetParam(ForceType<AIEventController *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventController *)instance; }
|
||||
template <> AIEventController &GetParam(ForceType<AIEventController &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventController *)instance; }
|
||||
template <> const AIEventController *GetParam(ForceType<const AIEventController *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventController *)instance; }
|
||||
template <> const AIEventController &GetParam(ForceType<const AIEventController &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventController *)instance; }
|
||||
template <> int Return<AIEventController *>(HSQUIRRELVM vm, AIEventController *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventController", res, NULL, DefSQDestructorCallback<AIEventController>); return 1; }
|
||||
}; // namespace SQConvert
|
||||
|
||||
void SQAIEventController_Register(Squirrel *engine) {
|
||||
DefSQClass <AIEventController> SQAIEventController("AIEventController");
|
||||
SQAIEventController.PreRegister(engine);
|
||||
SQAIEventController.AddConstructor<void (AIEventController::*)(), 1>(engine, "x");
|
||||
|
||||
SQAIEventController.DefSQStaticMethod(engine, &AIEventController::GetClassName, "GetClassName", 1, "x");
|
||||
SQAIEventController.DefSQStaticMethod(engine, &AIEventController::IsEventWaiting, "IsEventWaiting", 1, "x");
|
||||
SQAIEventController.DefSQStaticMethod(engine, &AIEventController::GetNextEvent, "GetNextEvent", 1, "x");
|
||||
SQAIEventController.DefSQStaticMethod(engine, &AIEventController::InsertEvent, "InsertEvent", 2, "xx");
|
||||
SQAIEventController.DefSQStaticMethod(engine, &AIEventController::FreeEventPointer, "FreeEventPointer", 1, "x");
|
||||
|
||||
SQAIEventController.PostRegister(engine);
|
||||
}
|
@ -0,0 +1,187 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_event_types.cpp Implementation of all EventTypes. */
|
||||
|
||||
#include "ai_event_types.hpp"
|
||||
#include "../../openttd.h"
|
||||
#include "../../core/alloc_func.hpp"
|
||||
#include "../../strings_func.h"
|
||||
#include "../../roadveh.h"
|
||||
#include "../../train.h"
|
||||
#include "../../ship.h"
|
||||
#include "../../aircraft.h"
|
||||
#include "../../settings_type.h"
|
||||
#include "../../articulated_vehicles.h"
|
||||
#include "table/strings.h"
|
||||
|
||||
bool AIEventVehicleCrashed::CloneCrashedVehicle(TileIndex depot)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *AIEventEnginePreview::GetName()
|
||||
{
|
||||
static const int len = 64;
|
||||
char *engine_name = MallocT<char>(len);
|
||||
|
||||
::SetDParam(0, engine);
|
||||
::GetString(engine_name, STR_ENGINE_NAME, &engine_name[len - 1]);
|
||||
return engine_name;
|
||||
}
|
||||
|
||||
CargoID AIEventEnginePreview::GetCargoType()
|
||||
{
|
||||
switch (::GetEngine(engine)->type) {
|
||||
case VEH_ROAD: {
|
||||
const RoadVehicleInfo *vi = ::RoadVehInfo(engine);
|
||||
return vi->cargo_type;
|
||||
} break;
|
||||
|
||||
case VEH_TRAIN: {
|
||||
const RailVehicleInfo *vi = ::RailVehInfo(engine);
|
||||
return vi->cargo_type;
|
||||
} break;
|
||||
|
||||
case VEH_SHIP: {
|
||||
const ShipVehicleInfo *vi = ::ShipVehInfo(engine);
|
||||
return vi->cargo_type;
|
||||
} break;
|
||||
|
||||
case VEH_AIRCRAFT: {
|
||||
return CT_PASSENGERS;
|
||||
} break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
int32 AIEventEnginePreview::GetCapacity()
|
||||
{
|
||||
switch (::GetEngine(engine)->type) {
|
||||
case VEH_ROAD:
|
||||
case VEH_TRAIN: {
|
||||
uint16 *capacities = GetCapacityOfArticulatedParts(engine, ::GetEngine(engine)->type);
|
||||
for (CargoID c = 0; c < NUM_CARGO; c++) {
|
||||
if (capacities[c] == 0) continue;
|
||||
return capacities[c];
|
||||
}
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case VEH_SHIP: {
|
||||
const ShipVehicleInfo *vi = ::ShipVehInfo(engine);
|
||||
return vi->capacity;
|
||||
} break;
|
||||
|
||||
case VEH_AIRCRAFT: {
|
||||
const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine);
|
||||
return vi->passenger_capacity;
|
||||
} break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
int32 AIEventEnginePreview::GetMaxSpeed()
|
||||
{
|
||||
switch (::GetEngine(engine)->type) {
|
||||
case VEH_ROAD: {
|
||||
const RoadVehicleInfo *vi = ::RoadVehInfo(engine);
|
||||
/* Internal speeds are km/h * 2 */
|
||||
return vi->max_speed / 2;
|
||||
} break;
|
||||
|
||||
case VEH_TRAIN: {
|
||||
const RailVehicleInfo *vi = ::RailVehInfo(engine);
|
||||
return vi->max_speed;
|
||||
} break;
|
||||
|
||||
case VEH_SHIP: {
|
||||
const ShipVehicleInfo *vi = ::ShipVehInfo(engine);
|
||||
/* Internal speeds are km/h * 2 */
|
||||
return vi->max_speed / 2;
|
||||
} break;
|
||||
|
||||
case VEH_AIRCRAFT: {
|
||||
const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine);
|
||||
return vi->max_speed / _settings_game.vehicle.plane_speed;
|
||||
} break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
Money AIEventEnginePreview::GetPrice()
|
||||
{
|
||||
switch (::GetEngine(engine)->type) {
|
||||
case VEH_ROAD: {
|
||||
const RoadVehicleInfo *vi = ::RoadVehInfo(engine);
|
||||
return (_price.roadveh_base >> 3) * vi->cost_factor >> 5;
|
||||
} break;
|
||||
|
||||
case VEH_TRAIN: {
|
||||
const RailVehicleInfo *vi = ::RailVehInfo(engine);
|
||||
return (_price.build_railvehicle >> 3) * vi->cost_factor >> 5;
|
||||
} break;
|
||||
|
||||
case VEH_SHIP: {
|
||||
const ShipVehicleInfo *vi = ::ShipVehInfo(engine);
|
||||
return (_price.ship_base >> 3) * vi->cost_factor >> 5;
|
||||
} break;
|
||||
|
||||
case VEH_AIRCRAFT: {
|
||||
const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine);
|
||||
return (_price.aircraft_base >> 3) * vi->cost_factor >> 5;
|
||||
} break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
Money AIEventEnginePreview::GetRunningCost()
|
||||
{
|
||||
/* We need to create an instance in order to obtain GetRunningCost.
|
||||
* This means we temporary allocate a vehicle in the pool, but
|
||||
* there is no other way.. */
|
||||
Vehicle *vehicle;
|
||||
switch (::GetEngine(engine)->type) {
|
||||
case VEH_ROAD: {
|
||||
vehicle = new RoadVehicle();
|
||||
} break;
|
||||
|
||||
case VEH_TRAIN: {
|
||||
vehicle = new Train();
|
||||
} break;
|
||||
|
||||
case VEH_SHIP: {
|
||||
vehicle = new Ship();
|
||||
} break;
|
||||
|
||||
case VEH_AIRCRAFT: {
|
||||
vehicle = new Aircraft();
|
||||
} break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
|
||||
vehicle->engine_type = engine;
|
||||
Money runningCost = vehicle->GetRunningCost();
|
||||
delete vehicle;
|
||||
return runningCost >> 8;
|
||||
}
|
||||
|
||||
AIVehicle::VehicleType AIEventEnginePreview::GetVehicleType()
|
||||
{
|
||||
switch (::GetEngine(engine)->type) {
|
||||
case VEH_ROAD: return AIVehicle::VEHICLE_ROAD;
|
||||
case VEH_TRAIN: return AIVehicle::VEHICLE_RAIL;
|
||||
case VEH_SHIP: return AIVehicle::VEHICLE_WATER;
|
||||
case VEH_AIRCRAFT: return AIVehicle::VEHICLE_AIR;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
bool AIEventEnginePreview::AcceptPreview()
|
||||
{
|
||||
return AIObject::DoCommand(0, engine, 0, CMD_WANT_ENGINE_PREVIEW);
|
||||
}
|
@ -0,0 +1,684 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file ai_event_types.hpp The detailed types of all events. */
|
||||
|
||||
#ifndef AI_EVENT_TYPES_HPP
|
||||
#define AI_EVENT_TYPES_HPP
|
||||
|
||||
#include "ai_object.hpp"
|
||||
#include "ai_event.hpp"
|
||||
#include "ai_town.hpp"
|
||||
#include "ai_industry.hpp"
|
||||
#include "ai_engine.hpp"
|
||||
#include "ai_subsidy.hpp"
|
||||
|
||||
/**
|
||||
* Event Test: a simple test event, to see if the event system is working.
|
||||
* Triggered via AIEventController::Test();
|
||||
*/
|
||||
class AIEventTest : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventTest"; }
|
||||
|
||||
/**
|
||||
* @param test A test value.
|
||||
*/
|
||||
AIEventTest(uint test) :
|
||||
AIEvent(AI_ET_TEST),
|
||||
test(test)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventTest *Convert(AIEvent *instance) { return (AIEventTest *)instance; }
|
||||
|
||||
/**
|
||||
* Return the test value.
|
||||
* @return The test value.
|
||||
*/
|
||||
uint GetTest() { return this->test; }
|
||||
|
||||
private:
|
||||
uint test;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Vehicle Crash, indicating a vehicle of yours is crashed.
|
||||
* It contains both the crash site as the vehicle crashed. It has a nice
|
||||
* helper that creates a new vehicle in a depot with the same type
|
||||
* and orders as the crashed one. In case the vehicle type isn't available
|
||||
* anymore, it will find the next best.
|
||||
*/
|
||||
class AIEventVehicleCrashed : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventVehicleCrashed"; }
|
||||
|
||||
/**
|
||||
* @param vehicle The vehicle that crashed.
|
||||
* @param crash_site Where the vehicle crashed.
|
||||
*/
|
||||
AIEventVehicleCrashed(VehicleID vehicle, TileIndex crash_site) :
|
||||
AIEvent(AI_ET_VEHICLE_CRASHED),
|
||||
crash_site(crash_site),
|
||||
vehicle(vehicle)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventVehicleCrashed *Convert(AIEvent *instance) { return (AIEventVehicleCrashed *)instance; }
|
||||
|
||||
/**
|
||||
* Get the VehicleID of the crashed vehicle.
|
||||
* @return The crashed vehicle.
|
||||
*/
|
||||
VehicleID GetVehicleID() { return vehicle; }
|
||||
|
||||
/**
|
||||
* Find the tile the vehicle crashed.
|
||||
* @return The crash site.
|
||||
*/
|
||||
TileIndex GetCrashSite() { return crash_site; }
|
||||
|
||||
/**
|
||||
* Clone the crashed vehicle and send it on its way again.
|
||||
* @param depot the depot to build the vehicle in.
|
||||
* @return True when the cloning succeeded.
|
||||
* @note This function isn't implemented yet.
|
||||
*/
|
||||
bool CloneCrashedVehicle(TileIndex depot);
|
||||
|
||||
private:
|
||||
TileIndex crash_site;
|
||||
VehicleID vehicle;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Subsidy Offered, indicating someone offered a subsidy.
|
||||
*/
|
||||
class AIEventSubsidyOffer : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventSubsidyOffer"; }
|
||||
|
||||
/**
|
||||
* @param subsidy_id The index of this subsidy in the _subsidies array.
|
||||
*/
|
||||
AIEventSubsidyOffer(SubsidyID subsidy_id) :
|
||||
AIEvent(AI_ET_SUBSIDY_OFFER),
|
||||
subsidy_id(subsidy_id)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventSubsidyOffer *Convert(AIEvent *instance) { return (AIEventSubsidyOffer *)instance; }
|
||||
|
||||
/**
|
||||
* Get the SubsidyID of the subsidy.
|
||||
* @return The subsidy id.
|
||||
*/
|
||||
SubsidyID GetSubsidyID() { return subsidy_id; }
|
||||
|
||||
private:
|
||||
SubsidyID subsidy_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Subsidy Offer Expired, indicating a subsidy will no longer be awarded.
|
||||
*/
|
||||
class AIEventSubsidyOfferExpired : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventSubsidyOfferExpired"; }
|
||||
|
||||
/**
|
||||
* @param subsidy_id The index of this subsidy in the _subsidies array.
|
||||
*/
|
||||
AIEventSubsidyOfferExpired(SubsidyID subsidy_id) :
|
||||
AIEvent(AI_ET_SUBSIDY_OFFER_EXPIRED),
|
||||
subsidy_id(subsidy_id)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventSubsidyOfferExpired *Convert(AIEvent *instance) { return (AIEventSubsidyOfferExpired *)instance; }
|
||||
|
||||
/**
|
||||
* Get the SubsidyID of the subsidy.
|
||||
* @return The subsidy id.
|
||||
*/
|
||||
SubsidyID GetSubsidyID() { return subsidy_id; }
|
||||
|
||||
private:
|
||||
SubsidyID subsidy_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Subidy Awarded, indicating a subsidy is awarded to some company.
|
||||
*/
|
||||
class AIEventSubsidyAwarded : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventSubsidyAwarded"; }
|
||||
|
||||
/**
|
||||
* @param subsidy_id The index of this subsidy in the _subsidies array.
|
||||
*/
|
||||
AIEventSubsidyAwarded(SubsidyID subsidy_id) :
|
||||
AIEvent(AI_ET_SUBSIDY_AWARDED),
|
||||
subsidy_id(subsidy_id)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventSubsidyAwarded *Convert(AIEvent *instance) { return (AIEventSubsidyAwarded *)instance; }
|
||||
|
||||
/**
|
||||
* Get the SubsidyID of the subsidy.
|
||||
* @return The subsidy id.
|
||||
*/
|
||||
SubsidyID GetSubsidyID() { return subsidy_id; }
|
||||
|
||||
private:
|
||||
SubsidyID subsidy_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Subsidy Expired, indicating a route that was once subsidized no longer is.
|
||||
*/
|
||||
class AIEventSubsidyExpired : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventSubsidyExpired"; }
|
||||
|
||||
/**
|
||||
* @param subsidy_id The index of this subsidy in the _subsidies array.
|
||||
*/
|
||||
AIEventSubsidyExpired(SubsidyID subsidy_id) :
|
||||
AIEvent(AI_ET_SUBSIDY_EXPIRED),
|
||||
subsidy_id(subsidy_id)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventSubsidyExpired *Convert(AIEvent *instance) { return (AIEventSubsidyExpired *)instance; }
|
||||
|
||||
/**
|
||||
* Get the SubsidyID of the subsidy.
|
||||
* @return The subsidy id.
|
||||
*/
|
||||
SubsidyID GetSubsidyID() { return subsidy_id; }
|
||||
|
||||
private:
|
||||
SubsidyID subsidy_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Engine Preview, indicating a manufacturer offer you to test a new engine.
|
||||
* You can get the same information about the offered engine as a real user
|
||||
* would see in the offer window. And you can also accept the offer.
|
||||
*/
|
||||
class AIEventEnginePreview : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventEnginePreview"; }
|
||||
|
||||
/**
|
||||
* @param engine The engine offered to test.
|
||||
*/
|
||||
AIEventEnginePreview(EngineID engine) :
|
||||
AIEvent(AI_ET_ENGINE_PREVIEW),
|
||||
engine(engine)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventEnginePreview *Convert(AIEvent *instance) { return (AIEventEnginePreview *)instance; }
|
||||
|
||||
/**
|
||||
* Get the name of the offered engine.
|
||||
* @return The name the engine has.
|
||||
*/
|
||||
const char *GetName();
|
||||
|
||||
/**
|
||||
* Get the cargo-type of the offered engine. In case it can transport 2 cargos, it
|
||||
* returns the first.
|
||||
* @return The cargo-type of the engine.
|
||||
*/
|
||||
CargoID GetCargoType();
|
||||
|
||||
/**
|
||||
* Get the capacity of the offered engine. In case it can transport 2 cargos, it
|
||||
* returns the first.
|
||||
* @return The capacity of the engine.
|
||||
*/
|
||||
int32 GetCapacity();
|
||||
|
||||
/**
|
||||
* Get the maximum speed of the offered engine.
|
||||
* @return The maximum speed the engine has.
|
||||
* @note The speed is in km/h.
|
||||
*/
|
||||
int32 GetMaxSpeed();
|
||||
|
||||
/**
|
||||
* Get the new cost of the offered engine.
|
||||
* @return The new cost the engine has.
|
||||
*/
|
||||
Money GetPrice();
|
||||
|
||||
/**
|
||||
* Get the running cost of the offered engine.
|
||||
* @return The running cost of the vehicle per year.
|
||||
* @note Cost is per year; divide by 364 to get per day.
|
||||
*/
|
||||
Money GetRunningCost();
|
||||
|
||||
/**
|
||||
* Get the type of the offered engine.
|
||||
* @return The type the engine has.
|
||||
*/
|
||||
AIVehicle::VehicleType GetVehicleType();
|
||||
|
||||
/**
|
||||
* Accept the engine preview.
|
||||
* @return True when the accepting succeeded.
|
||||
*/
|
||||
bool AcceptPreview();
|
||||
|
||||
private:
|
||||
EngineID engine;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Company New, indicating a new company has been created.
|
||||
*/
|
||||
class AIEventCompanyNew : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventCompanyNew"; }
|
||||
|
||||
/**
|
||||
* @param owner The new company.
|
||||
*/
|
||||
AIEventCompanyNew(Owner owner) :
|
||||
AIEvent(AI_ET_COMPANY_NEW),
|
||||
owner((AICompany::CompanyID)(byte)owner)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventCompanyNew *Convert(AIEvent *instance) { return (AIEventCompanyNew *)instance; }
|
||||
|
||||
/**
|
||||
* Get the CompanyID of the company that has been created.
|
||||
* @return The CompanyID of the company.
|
||||
*/
|
||||
AICompany::CompanyID GetCompanyID() { return owner; }
|
||||
|
||||
private:
|
||||
AICompany::CompanyID owner;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Company In Trouble, indicating a company is in trouble and might go
|
||||
* bankrupt soon.
|
||||
*/
|
||||
class AIEventCompanyInTrouble : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventCompanyInTrouble"; }
|
||||
|
||||
/**
|
||||
* @param owner The company that is in trouble.
|
||||
*/
|
||||
AIEventCompanyInTrouble(Owner owner) :
|
||||
AIEvent(AI_ET_COMPANY_IN_TROUBLE),
|
||||
owner((AICompany::CompanyID)(byte)owner)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventCompanyInTrouble *Convert(AIEvent *instance) { return (AIEventCompanyInTrouble *)instance; }
|
||||
|
||||
/**
|
||||
* Get the CompanyID of the company that is in trouble.
|
||||
* @return The CompanyID of the company in trouble.
|
||||
*/
|
||||
AICompany::CompanyID GetCompanyID() { return owner; }
|
||||
|
||||
private:
|
||||
AICompany::CompanyID owner;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Company Merger, indicating a company has been bought by another
|
||||
* company.
|
||||
*/
|
||||
class AIEventCompanyMerger : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventCompanyMerger"; }
|
||||
|
||||
/**
|
||||
* @param old_owner The company bought off.
|
||||
* @param new_owner The company that bougth owner.
|
||||
*/
|
||||
AIEventCompanyMerger(Owner old_owner, Owner new_owner) :
|
||||
AIEvent(AI_ET_COMPANY_MERGER),
|
||||
old_owner((AICompany::CompanyID)(byte)old_owner),
|
||||
new_owner((AICompany::CompanyID)(byte)new_owner)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventCompanyMerger *Convert(AIEvent *instance) { return (AIEventCompanyMerger *)instance; }
|
||||
|
||||
/**
|
||||
* Get the CompanyID of the company that has been bought.
|
||||
* @return The CompanyID of the company that has been bought.
|
||||
* @note: The value below is not valid anymore as CompanyID, and
|
||||
* AICompany::ResolveCompanyID will return INVALID_COMPANY. It's
|
||||
* only usefull if you're keeping track of company's yourself.
|
||||
*/
|
||||
AICompany::CompanyID GetOldCompanyID() { return old_owner; }
|
||||
|
||||
/**
|
||||
* Get the CompanyID of the new owner.
|
||||
* @return The CompanyID of the new owner.
|
||||
*/
|
||||
AICompany::CompanyID GetNewCompanyID() { return new_owner; }
|
||||
|
||||
private:
|
||||
AICompany::CompanyID old_owner;
|
||||
AICompany::CompanyID new_owner;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Company Bankrupt, indicating a company has gone bankrupt.
|
||||
*/
|
||||
class AIEventCompanyBankrupt : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventCompanyBankrupt"; }
|
||||
|
||||
/**
|
||||
* @param owner The company that has gone bankrupt.
|
||||
*/
|
||||
AIEventCompanyBankrupt(Owner owner) :
|
||||
AIEvent(AI_ET_COMPANY_BANKRUPT),
|
||||
owner((AICompany::CompanyID)(byte)owner)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventCompanyBankrupt *Convert(AIEvent *instance) { return (AIEventCompanyBankrupt *)instance; }
|
||||
|
||||
/**
|
||||
* Get the CompanyID of the company that has gone bankrupt.
|
||||
* @return The CompanyID of the company that has gone bankrupt.
|
||||
*/
|
||||
AICompany::CompanyID GetCompanyID() { return owner; }
|
||||
|
||||
private:
|
||||
AICompany::CompanyID owner;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Vehicle Lost, indicating a vehicle can't find its way to its destination.
|
||||
*/
|
||||
class AIEventVehicleLost : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventVehicleLost"; }
|
||||
|
||||
/**
|
||||
* @param vehicle_id The vehicle that is lost.
|
||||
*/
|
||||
AIEventVehicleLost(VehicleID vehicle_id) :
|
||||
AIEvent(AI_ET_VEHICLE_LOST),
|
||||
vehicle_id(vehicle_id)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventVehicleLost *Convert(AIEvent *instance) { return (AIEventVehicleLost *)instance; }
|
||||
|
||||
/**
|
||||
* Get the VehicleID of the vehicle that is lost.
|
||||
* @return The VehicleID of the vehicle that is lost.
|
||||
*/
|
||||
VehicleID GetVehicleID() { return vehicle_id; }
|
||||
|
||||
private:
|
||||
VehicleID vehicle_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event VehicleWaitingInDepot, indicating a vehicle has arrived a depot and is now waiting there.
|
||||
*/
|
||||
class AIEventVehicleWaitingInDepot : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventVehicleWaitingInDepot"; }
|
||||
|
||||
/**
|
||||
* @param vehicle_id The vehicle that is waiting in a depot.
|
||||
*/
|
||||
AIEventVehicleWaitingInDepot(VehicleID vehicle_id) :
|
||||
AIEvent(AI_ET_VEHICLE_WAITING_IN_DEPOT),
|
||||
vehicle_id(vehicle_id)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventVehicleWaitingInDepot *Convert(AIEvent *instance) { return (AIEventVehicleWaitingInDepot *)instance; }
|
||||
|
||||
/**
|
||||
* Get the VehicleID of the vehicle that is waiting in a depot.
|
||||
* @return The VehicleID of the vehicle that is waiting in a depot.
|
||||
*/
|
||||
VehicleID GetVehicleID() { return vehicle_id; }
|
||||
|
||||
private:
|
||||
VehicleID vehicle_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Vehicle Unprofitable, indicating a vehicle lost money last year.
|
||||
*/
|
||||
class AIEventVehicleUnprofitable : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventVehicleUnprofitable"; }
|
||||
|
||||
/**
|
||||
* @param vehicle_id The vehicle that was unprofitable.
|
||||
*/
|
||||
AIEventVehicleUnprofitable(VehicleID vehicle_id) :
|
||||
AIEvent(AI_ET_VEHICLE_UNPROFITABLE),
|
||||
vehicle_id(vehicle_id)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventVehicleUnprofitable *Convert(AIEvent *instance) { return (AIEventVehicleUnprofitable *)instance; }
|
||||
|
||||
/**
|
||||
* Get the VehicleID of the vehicle that lost money.
|
||||
* @return The VehicleID of the vehicle that lost money.
|
||||
*/
|
||||
VehicleID GetVehicleID() { return vehicle_id; }
|
||||
|
||||
private:
|
||||
VehicleID vehicle_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Industry Open, indicating a new industry has been created.
|
||||
*/
|
||||
class AIEventIndustryOpen : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventIndustryOpen"; }
|
||||
|
||||
/**
|
||||
* @param industry_id The new industry.
|
||||
*/
|
||||
AIEventIndustryOpen(IndustryID industry_id) :
|
||||
AIEvent(AI_ET_INDUSTRY_OPEN),
|
||||
industry_id(industry_id)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventIndustryOpen *Convert(AIEvent *instance) { return (AIEventIndustryOpen *)instance; }
|
||||
|
||||
/**
|
||||
* Get the IndustryID of the new industry.
|
||||
* @return The IndustryID of the industry.
|
||||
*/
|
||||
IndustryID GetIndustryID() { return industry_id; }
|
||||
|
||||
private:
|
||||
IndustryID industry_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Industry Close, indicating an industry is going to be closed.
|
||||
*/
|
||||
class AIEventIndustryClose : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventIndustryClose"; }
|
||||
|
||||
/**
|
||||
* @param industry_id The new industry.
|
||||
*/
|
||||
AIEventIndustryClose(IndustryID industry_id) :
|
||||
AIEvent(AI_ET_INDUSTRY_CLOSE),
|
||||
industry_id(industry_id)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventIndustryClose *Convert(AIEvent *instance) { return (AIEventIndustryClose *)instance; }
|
||||
|
||||
/**
|
||||
* Get the IndustryID of the closing industry.
|
||||
* @return The IndustryID of the industry.
|
||||
*/
|
||||
IndustryID GetIndustryID() { return industry_id; }
|
||||
|
||||
private:
|
||||
IndustryID industry_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Engine Available, indicating a new engine is available.
|
||||
*/
|
||||
class AIEventEngineAvailable : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventEngineAvailable"; }
|
||||
|
||||
/**
|
||||
* @param engine The engine that is available.
|
||||
*/
|
||||
AIEventEngineAvailable(EngineID engine) :
|
||||
AIEvent(AI_ET_ENGINE_AVAILABLE),
|
||||
engine(engine)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventEngineAvailable *Convert(AIEvent *instance) { return (AIEventEngineAvailable *)instance; }
|
||||
|
||||
/**
|
||||
* Get the EngineID of the new engine.
|
||||
* @return The EngineID of the new engine.
|
||||
*/
|
||||
EngineID GetEngineID() { return engine; }
|
||||
|
||||
private:
|
||||
EngineID engine;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event Station First Vehicle, indicating a station has been visited by a vehicle for the first time.
|
||||
*/
|
||||
class AIEventStationFirstVehicle : public AIEvent {
|
||||
public:
|
||||
static const char *GetClassName() { return "AIEventStationFirstVehicle"; }
|
||||
|
||||
/**
|
||||
* @param station The station visited for the first time.
|
||||
* @param vehicle The vehicle visiting the station.
|
||||
*/
|
||||
AIEventStationFirstVehicle(StationID station, VehicleID vehicle) :
|
||||
AIEvent(AI_ET_STATION_FIRST_VEHICLE),
|
||||
station(station),
|
||||
vehicle(vehicle)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Convert an AIEvent to the real instance.
|
||||
* @param instance The instance to convert.
|
||||
* @return The converted instance.
|
||||
*/
|
||||
static AIEventStationFirstVehicle *Convert(AIEvent *instance) { return (AIEventStationFirstVehicle *)instance; }
|
||||
|
||||
/**
|
||||
* Get the StationID of the visited station.
|
||||
* @return The StationID of the visited station.
|
||||
*/
|
||||
StationID GetStationID() { return station; }
|
||||
|
||||
/**
|
||||
* Get the VehicleID of the first vehicle.
|
||||
* @return The VehicleID of the first vehicle.
|
||||
*/
|
||||
VehicleID GetVehicleID() { return vehicle; }
|
||||
|
||||
private:
|
||||
StationID station;
|
||||
VehicleID vehicle;
|
||||
};
|
||||
|
||||
#endif /* AI_EVENT_TYPES_HPP */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue